diff --git a/package.json b/package.json index 641072cfa..fd357b0d6 100644 --- a/package.json +++ b/package.json @@ -1441,6 +1441,8 @@ "@types/temp": "0.8.34", "@typescript-eslint/eslint-plugin": "4.18.0", "@typescript-eslint/parser": "4.18.0", + "buffer": "^6.0.3", + "crypto-browserify": "3.12.0", "css-loader": "5.1.3", "esbuild-loader": "2.10.0", "eslint": "7.22.0", @@ -1466,6 +1468,7 @@ "react-testing-library": "7.0.1", "sinon": "9.0.0", "source-map-support": "0.5.19", + "stream-browserify": "^3.0.0", "style-loader": "2.0.0", "svg-inline-loader": "^0.8.2", "temp": "0.9.4", diff --git a/src/github/graphql.ts b/src/github/graphql.ts index fc9b9c275..a0da809ca 100644 --- a/src/github/graphql.ts +++ b/src/github/graphql.ts @@ -43,6 +43,7 @@ export interface AbbreviatedIssueComment { login: string; avatarUrl: string; url: string; + email?: string }; body: string; databaseId: number; @@ -387,6 +388,8 @@ export interface PullRequest { nodes: { login: string; url: string; + email: string; + avatarUrl: string; }[]; }; author: { diff --git a/src/github/queries.gql b/src/github/queries.gql index e0a8080c4..1fb90c68d 100644 --- a/src/github/queries.gql +++ b/src/github/queries.gql @@ -53,6 +53,12 @@ fragment Comment on IssueComment { login avatarUrl url + ... on User { + email + } + ... on Organization { + email + } } url body @@ -72,6 +78,7 @@ fragment Commit on PullRequestCommit { login avatarUrl url + email } } committer { @@ -107,6 +114,12 @@ fragment Review on PullRequestReview { login avatarUrl url + ... on User { + email + } + ... on Organization { + email + } } state body @@ -189,6 +202,12 @@ fragment ReviewComment on PullRequestReviewComment { login avatarUrl url + ... on User { + email + } + ... on Organization { + email + } } path originalPosition @@ -277,6 +296,12 @@ query PullRequest($owner: String!, $name: String!, $number: Int!) { login url avatarUrl + ... on User { + email + } + ... on Organization { + email + } } createdAt updatedAt @@ -361,6 +386,12 @@ query Issue($owner: String!, $name: String!, $number: Int!) { login url avatarUrl + ... on User { + email + } + ... on Organization { + email + } } createdAt updatedAt @@ -389,6 +420,12 @@ query IssueWithComments($owner: String!, $name: String!, $number: Int!) { login url avatarUrl + ... on User { + email + } + ... on Organization { + email + } } createdAt updatedAt @@ -406,6 +443,12 @@ query IssueWithComments($owner: String!, $name: String!, $number: Int!) { login url avatarUrl + ... on User { + email + } + ... on Organization { + email + } } body databaseId @@ -648,12 +691,19 @@ query IssuesWithoutMilestone($owner: String!, $name: String!, $assignee: String! nodes { login url + email } } author { login url avatarUrl(size: 50) + ... on User { + email + } + ... on Organization { + email + } } createdAt updatedAt @@ -719,6 +769,8 @@ query GetMilestones($owner: String!, $name: String!, $assignee: String!) { title assignees(first: 10) { nodes { + avatarUrl + email login url } @@ -727,6 +779,12 @@ query GetMilestones($owner: String!, $name: String!, $assignee: String!) { login url avatarUrl(size: 50) + ... on User { + email + } + ... on Organization { + email + } } createdAt updatedAt @@ -773,6 +831,8 @@ query Issues($query: String!) { title assignees(first: 10) { nodes { + avatarUrl + email login url } @@ -781,6 +841,12 @@ query Issues($query: String!) { login url avatarUrl(size: 50) + ... on User { + email + } + ... on Organization { + email + } } createdAt updatedAt diff --git a/src/github/utils.ts b/src/github/utils.ts index 61d976f1d..908e26645 100644 --- a/src/github/utils.ts +++ b/src/github/utils.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import * as crypto from 'crypto'; import * as vscode from 'vscode'; import { Repository } from '../api/api'; import { GitApiImpl } from '../api/api1'; @@ -142,7 +143,7 @@ export function convertRESTUserToAccount( return { login: user.login, url: user.html_url, - avatarUrl: githubRepository.isGitHubDotCom ? user.avatar_url : undefined, + avatarUrl: getAvatarWithEnterpriseFallback(user.avatar_url, user.gravatar_id, githubRepository.remote.authProviderId), }; } @@ -432,14 +433,15 @@ function parseRef(ref: GraphQL.Ref | undefined): IGitHubRef | undefined { } function parseAuthor( - author: { login: string; url: string; avatarUrl: string } | null, + author: { login: string; url: string; avatarUrl: string; email?: string } | null, githubRepository: GitHubRepository, ): IAccount { if (author) { return { login: author.login, url: author.url, - avatarUrl: githubRepository.isGitHubDotCom ? author.avatarUrl : undefined, + avatarUrl: getAvatarWithEnterpriseFallback(author.avatarUrl, author.email, githubRepository.remote.authProviderId), + email: author.email }; } else { return { @@ -501,7 +503,7 @@ export function parseGraphQLPullRequest( suggestedReviewers: parseSuggestedReviewers(graphQLPullRequest.suggestedReviewers), comments: parseComments(graphQLPullRequest.comments?.nodes, githubRepository), milestone: parseMilestone(graphQLPullRequest.milestone), - assignees: graphQLPullRequest.assignees?.nodes, + assignees: graphQLPullRequest.assignees?.nodes.map(assignee => parseAuthor(assignee, githubRepository)), }; } @@ -537,7 +539,7 @@ export function parseGraphQLIssue(issue: GraphQL.PullRequest, githubRepository: title: issue.title, createdAt: issue.createdAt, updatedAt: issue.updatedAt, - assignees: issue.assignees.nodes, + assignees: issue.assignees.nodes.map(assignee => parseAuthor(assignee, githubRepository)), user: parseAuthor(issue.author, githubRepository), labels: issue.labels.nodes, repositoryName: issue.repository?.name, @@ -938,3 +940,12 @@ export function hasEnterpriseUri(): boolean { return !!getEnterpriseUri(); } +export function generateGravatarUrl(gravatarId: string, size: number = 200): string | undefined { + return !!gravatarId ? `https://www.gravatar.com/avatar/${gravatarId}?s=${size}&d=retro` : undefined; +} + +export function getAvatarWithEnterpriseFallback(avatarUrl: string, email: string, authProviderId: AuthProvider): string | undefined { + return authProviderId === AuthProvider.github ? avatarUrl : generateGravatarUrl( + crypto.createHash('md5').update(email?.trim()?.toLowerCase()).digest('hex') + ); +} diff --git a/src/test/builders/graphql/pullRequestBuilder.ts b/src/test/builders/graphql/pullRequestBuilder.ts index 59d4dbdc7..bdd55b007 100644 --- a/src/test/builders/graphql/pullRequestBuilder.ts +++ b/src/test/builders/graphql/pullRequestBuilder.ts @@ -37,6 +37,8 @@ export const PullRequestBuilder = createBuilderClass()({ nodes: { default: [ { + avatarUrl: undefined, + email: undefined, login: 'me', url: 'https://github.com/me', }, diff --git a/webpack.config.js b/webpack.config.js index d0211b581..1ea5965df 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -135,7 +135,9 @@ async function getWebviewConfig(mode, env, entry) { resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.svg'], fallback: { + crypto: require.resolve("crypto-browserify"), path: require.resolve('path-browserify'), + stream: require.resolve("stream-browserify"), }, }, plugins: plugins, @@ -305,7 +307,9 @@ async function getExtensionConfig(target, mode, env) { fallback: target === 'webworker' ? { + crypto: require.resolve("crypto-browserify"), path: require.resolve('path-browserify'), + stream: require.resolve("stream-browserify"), url: false, } : undefined,