From 8a0fe2996bf578cc858d95174814a927483ccba3 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 21 Jan 2019 14:17:09 -0800 Subject: [PATCH 1/6] add merged event rendering --- preview-src/index.css | 33 +++++++++++--- preview-src/pullRequestOverviewRenderer.ts | 51 +++++++++++++++++++++- resources/icons/merge_icon.svg | 1 + src/common/timelineEvent.ts | 17 +++++++- src/github/graphql.ts | 5 +++ src/github/queries.gql | 5 +++ src/github/utils.ts | 8 ++++ 7 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 resources/icons/merge_icon.svg diff --git a/preview-src/index.css b/preview-src/index.css index 886580243c..83424e8092 100644 --- a/preview-src/index.css +++ b/preview-src/index.css @@ -152,7 +152,8 @@ body .comment-container .review-comment-header { body .comment-container .review-comment-header > span, body .comment-container .review-comment-header > a, -body .commit .commit-message > a { +body .commit .commit-message > a, +body .merged .merged-message > a { margin-right: 4px; } @@ -329,12 +330,13 @@ body .overview-title .button-group button { fill: var(--vscode-foreground); } -.comment-container.commit { +.comment-container.commit, +.comment-container.merged { padding-left: 16px; box-sizing: border-box } -.commit, .review { +.commit, .review, .merged { display: flex; width: 100%; border: none; @@ -347,24 +349,28 @@ body .overview-title .button-group button { padding: 4px 0; } -.commit .commit-message { +.commit .commit-message, +.merged .merged-message { display: flex; align-items: center; line-height: 18px; overflow: hidden; } -.commit .commit-message .avatar-container { +.commit .commit-message .avatar-container, +.merged .merged-message .avatar-container { margin-right: 4px; flex-shrink: 0; } -.commit .avatar-container .avatar { +.commit .avatar-container .avatar, +.merged .avatar-container .avatar { width: 18px; height: 18px; } -.commit .commit-message .message { +.commit .commit-message .message, +.merged .merged-message .message { overflow: inherit; text-overflow: ellipsis; white-space: nowrap; @@ -374,6 +380,19 @@ body .overview-title .button-group button { margin-left: auto; } +.merged .merged-message .message, +.merged .inline-sha { + margin: 0 4px 0 0; +} + +.merged svg { + width: 14px; + height: auto; + margin-right: 8px; + flex-shrink: 0; + fill: var(--vscode-foreground); +} + .details { display: flex; flex-direction: column; diff --git a/preview-src/pullRequestOverviewRenderer.ts b/preview-src/pullRequestOverviewRenderer.ts index b88a6b54e1..823f7a833a 100644 --- a/preview-src/pullRequestOverviewRenderer.ts +++ b/preview-src/pullRequestOverviewRenderer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { dateFromNow } from '../src/common/utils'; -import { TimelineEvent, CommitEvent, ReviewEvent, CommentEvent, isCommentEvent, isReviewEvent, isCommitEvent } from '../src/common/timelineEvent'; +import { TimelineEvent, CommitEvent, ReviewEvent, CommentEvent, isCommentEvent, isReviewEvent, isCommitEvent, isMergedEvent, MergedEvent } from '../src/common/timelineEvent'; import { PullRequestStateEnum } from '../src/github/interface'; import md from './mdRenderer'; import { MessageHandler } from './message'; @@ -12,6 +12,7 @@ import { getState, updateState, PullRequest } from './cache'; import { Comment } from '../src/common/comment'; const commitIconSvg = require('../resources/icons/commit_icon.svg'); +const mergeIconSvg = require('../resources/icons/merge_icon.svg'); const editIcon = require('../resources/icons/edit.svg'); const deleteIcon = require('../resources/icons/delete.svg'); const checkIcon = require('../resources/icons/check.svg'); @@ -496,6 +497,50 @@ export function renderCommit(timelineEvent: CommitEvent): HTMLElement { return commentContainer; } +export function renderMergedEvent(timelineEvent: MergedEvent): HTMLElement { + const shaShort = timelineEvent.sha.substring(0, 7); + + const mergedMessageContainer: HTMLDivElement = document.createElement('div'); + mergedMessageContainer.classList.add('comment-container', 'merged'); + const mergedMessage: HTMLDivElement = document.createElement('div'); + mergedMessage.className = 'merged-message'; + mergedMessage.insertAdjacentHTML('beforeend', mergeIconSvg); + + const userIcon = renderUserIcon(timelineEvent.user.url, timelineEvent.user.avatarUrl); + mergedMessage.appendChild(userIcon); + + const login: HTMLAnchorElement = document.createElement('a'); + login.className = 'author'; + login.href = timelineEvent.user.url; + login.textContent = timelineEvent.user.login!; + mergedMessage.appendChild(login); + + const message: HTMLSpanElement = document.createElement('span'); + message.className = 'message'; + message.textContent = 'merged commit'; + mergedMessage.appendChild(message); + + const sha: HTMLAnchorElement = document.createElement('a'); + sha.className = 'inline-sha'; + sha.href = timelineEvent.commitUrl; + sha.textContent = shaShort; + mergedMessage.appendChild(sha); + + const ref: HTMLSpanElement = document.createElement('span'); + ref.className = 'message'; + ref.textContent = `into ${timelineEvent.mergeRef}`; + mergedMessage.appendChild(ref); + + const timestamp: HTMLAnchorElement = document.createElement('a'); + timestamp.className = 'timestamp'; + timestamp.href = timelineEvent.url; + timestamp.textContent = dateFromNow(timelineEvent.createdAt); + mergedMessage.appendChild(timestamp); + + mergedMessageContainer.appendChild(mergedMessage); + return mergedMessageContainer; +} + function getDiffChangeClass(type: DiffChangeType) { switch (type) { case DiffChangeType.Add: @@ -707,6 +752,10 @@ export function renderTimelineEvent(timelineEvent: TimelineEvent, messageHandler return renderComment(timelineEvent, messageHandler); } + if (isMergedEvent(timelineEvent)) { + return renderMergedEvent(timelineEvent); + } + return undefined; } diff --git a/resources/icons/merge_icon.svg b/resources/icons/merge_icon.svg new file mode 100644 index 0000000000..8af4eead39 --- /dev/null +++ b/resources/icons/merge_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/common/timelineEvent.ts b/src/common/timelineEvent.ts index 66b3fd7db2..7b3f902e39 100644 --- a/src/common/timelineEvent.ts +++ b/src/common/timelineEvent.ts @@ -58,7 +58,18 @@ export interface CommitEvent { bodyHTML?: string; } -export type TimelineEvent = CommitEvent | ReviewEvent | CommentEvent; +export interface MergedEvent { + graphNodeId: string; + user: IAccount; + createdAt: string; + mergeRef: string; + sha: string; + commitUrl: string; + event: string; + url: string; +} + +export type TimelineEvent = CommitEvent | ReviewEvent | CommentEvent | MergedEvent; export function isReviewEvent(event: TimelineEvent): event is ReviewEvent { return Number(event.event) === EventType.Reviewed; @@ -71,3 +82,7 @@ export function isCommitEvent(event: TimelineEvent): event is CommitEvent { export function isCommentEvent(event: TimelineEvent): event is CommentEvent { return Number(event.event) === EventType.Commented; } + +export function isMergedEvent(event: TimelineEvent): event is MergedEvent { + return Number(event.event) === EventType.Merged; +} \ No newline at end of file diff --git a/src/github/graphql.ts b/src/github/graphql.ts index b05b7a0c63..0c8dd9587c 100644 --- a/src/github/graphql.ts +++ b/src/github/graphql.ts @@ -14,6 +14,11 @@ export interface MergedEvent { mergeRef: { name: string; }; + commit: { + oid: string; + commitUrl: string; + }; + url: string; } export interface IssueComment { diff --git a/src/github/queries.gql b/src/github/queries.gql index 19684b339b..260668affa 100644 --- a/src/github/queries.gql +++ b/src/github/queries.gql @@ -14,6 +14,11 @@ fragment Merged on MergedEvent { mergeRef { name } + commit { + oid + commitUrl + } + url } fragment Comment on IssueComment { diff --git a/src/github/utils.ts b/src/github/utils.ts index 1ae0c8ebd3..dff1c60de2 100644 --- a/src/github/utils.ts +++ b/src/github/utils.ts @@ -198,6 +198,14 @@ export function parseGraphQLTimelineEvents(events: any[]): TimelineEvent[] { event.author = event.author.user || { login: event.committer.name, avatarUrl: event.committer.avatarUrl }; event.htmlUrl = event.url; } + + if (event.event === EventType.Merged) { + event.user = event.actor; + event.mergeRef = event.mergeRef.name; + event.sha = event.commit.oid; + event.commitUrl = event.commit.commitUrl; + event.graphNodeId = event.id; + } }); return events; From f99298e51dc10d7dba212c20fd6e88887b785b11 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 21 Jan 2019 14:27:19 -0800 Subject: [PATCH 2/6] add review and approval info --- preview-src/pullRequestOverviewRenderer.ts | 4 ++++ src/common/timelineEvent.ts | 1 + src/github/graphql.ts | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/preview-src/pullRequestOverviewRenderer.ts b/preview-src/pullRequestOverviewRenderer.ts index 823f7a833a..c221b67e40 100644 --- a/preview-src/pullRequestOverviewRenderer.ts +++ b/preview-src/pullRequestOverviewRenderer.ts @@ -608,6 +608,9 @@ class ReviewNode { userLogin.href = this._review.user.url; userLogin.textContent = this._review.user.login; + const authorAssociation: HTMLSpanElement = document.createElement('span'); + authorAssociation.textContent = `(${this._review.authorAssociation.toLocaleLowerCase()})`; + const reviewState = document.createElement('span'); switch (this._review.state.toLowerCase()) { case 'approved': @@ -635,6 +638,7 @@ class ReviewNode { commentHeader.appendChild(userIcon); commentHeader.appendChild(userLogin); + commentHeader.appendChild(authorAssociation); commentHeader.appendChild(reviewState); commentHeader.appendChild(timestamp); diff --git a/src/common/timelineEvent.ts b/src/common/timelineEvent.ts index 7b3f902e39..84d19d9bce 100644 --- a/src/common/timelineEvent.ts +++ b/src/common/timelineEvent.ts @@ -44,6 +44,7 @@ export interface ReviewEvent { body: string; htmlUrl: string; user: IAccount; + authorAssociation: string; state: string; id: number; } diff --git a/src/github/graphql.ts b/src/github/graphql.ts index 0c8dd9587c..209c1036e8 100644 --- a/src/github/graphql.ts +++ b/src/github/graphql.ts @@ -102,7 +102,7 @@ export interface AssignedEvent { export interface Review { id: string; databaseId: number; - authorAssocation: string; + authorAssociation: string; author: { login: string; avatarUrl: string; From 8144ac0b012c928196408720085f7d5ac70063de Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 21 Jan 2019 15:52:55 -0800 Subject: [PATCH 3/6] add more bodyHTML --- preview-src/pullRequestOverviewRenderer.ts | 2 +- src/common/timelineEvent.ts | 1 + src/github/githubRepository.ts | 46 +++++++++++++++------- src/github/graphql.ts | 45 +++++++++++++++++++++ src/github/interface.ts | 5 ++- src/github/pullRequestModel.ts | 1 + src/github/queries.gql | 46 +++++++++++++++++++++- src/github/utils.ts | 37 ++++++++++++++++- 8 files changed, 163 insertions(+), 20 deletions(-) diff --git a/preview-src/pullRequestOverviewRenderer.ts b/preview-src/pullRequestOverviewRenderer.ts index c221b67e40..45f8c77b22 100644 --- a/preview-src/pullRequestOverviewRenderer.ts +++ b/preview-src/pullRequestOverviewRenderer.ts @@ -654,7 +654,7 @@ class ReviewNode { const reviewBody: HTMLDivElement = document.createElement('div'); reviewBody.className = 'review-body'; if (this._review.body) { - reviewBody.innerHTML = md.render(emoji.emojify(this._review.body)); + reviewBody.innerHTML = this._review.bodyHTML ? this._review.bodyHTML : md.render(emoji.emojify(this._review.body)); reviewCommentContainer.appendChild(reviewBody); } diff --git a/src/common/timelineEvent.ts b/src/common/timelineEvent.ts index 84d19d9bce..2356d8f257 100644 --- a/src/common/timelineEvent.ts +++ b/src/common/timelineEvent.ts @@ -42,6 +42,7 @@ export interface ReviewEvent { comments: Comment[]; submittedAt: string; body: string; + bodyHTML?: string; htmlUrl: string; user: IAccount; authorAssociation: string; diff --git a/src/github/githubRepository.ts b/src/github/githubRepository.ts index 66193a449b..e52ac5365b 100644 --- a/src/github/githubRepository.ts +++ b/src/github/githubRepository.ts @@ -12,7 +12,9 @@ import { PullRequestModel } from './pullRequestModel'; import { CredentialStore, GitHub } from './credentials'; import { AuthenticationError } from '../common/authentication'; import { QueryOptions, MutationOptions, ApolloQueryResult } from 'apollo-boost'; -import { convertRESTPullRequestToRawPullRequest } from './utils'; +import { convertRESTPullRequestToRawPullRequest, parseGraphQLPullRequest } from './utils'; +import { PullRequestResponse } from './graphql'; +const queries = require('./queries.gql'); export const PULL_REQUEST_PAGE_SIZE = 20; @@ -263,22 +265,36 @@ export class GitHubRepository implements IGitHubRepository { async getPullRequest(id: number): Promise { try { Logger.debug(`Fetch pull request ${id} - enter`, GitHubRepository.ID); - const { octokit, remote } = await this.ensure(); - let { data } = await octokit.pullRequests.get({ - owner: remote.owner, - repo: remote.repositoryName, - number: id - }); - Logger.debug(`Fetch pull request ${id} - done`, GitHubRepository.ID); - - if (!data.head.repo) { - Logger.appendLine('The remote branch for this PR was already deleted.', GitHubRepository.ID); - return null; - } + const { octokit, query, remote } = await this.ensure(); - let item = convertRESTPullRequestToRawPullRequest(data); + if (this.supportsGraphQl()) { + const { data } = await query({ + query: queries.PullRequest, + variables: { + owner: remote.owner, + name: remote.repositoryName, + number: id + } + }); - return new PullRequestModel(this, remote, item); + Logger.debug(`Fetch pull request ${id} - done`, GitHubRepository.ID); + return new PullRequestModel(this, remote, parseGraphQLPullRequest(data)); + } else { + let { data } = await octokit.pullRequests.get({ + owner: remote.owner, + repo: remote.repositoryName, + number: id + }); + Logger.debug(`Fetch pull request ${id} - done`, GitHubRepository.ID); + + if (!data.head.repo) { + Logger.appendLine('The remote branch for this PR was already deleted.', GitHubRepository.ID); + return null; + } + + let item = convertRESTPullRequestToRawPullRequest(data); + return new PullRequestModel(this, remote, item); + } } catch (e) { Logger.appendLine(`GithubRepository> Unable to fetch PR: ${e}`); return null; diff --git a/src/github/graphql.ts b/src/github/graphql.ts index 209c1036e8..ef20d2f991 100644 --- a/src/github/graphql.ts +++ b/src/github/graphql.ts @@ -32,6 +32,7 @@ export interface IssueComment { }; url: string; body: string; + bodyHTML: string; updatedAt: string; createdAt: string; viewerCanUpdate: boolean; @@ -51,6 +52,7 @@ export interface ReviewComment { path: string; originalPosition: number; body: string; + bodyHTML: string; diffHunk: string; position: number; state: string; @@ -177,4 +179,47 @@ export interface DeleteReviewResponse { } } }; +} + +export interface PullRequestResponse { + repository: { + pullRequest: { + number: number; + url: string; + state: string; + body: string; + bodyHTML: string; + title: string; + author: { + login: string; + url: string; + avatarUrl: string; + } + createdAt: string; + updatedAt: string; + headRef: { + name: string; + repository: { + nameWithOwner: string; + url: string; + } + target: { + oid: string; + } + } + baseRef: { + name: string; + repository: { + nameWithOwner: string; + url: string; + } + target: { + oid: string + } + } + merged: boolean; + mergeable: boolean; + id: string; + } + }; } \ No newline at end of file diff --git a/src/github/interface.ts b/src/github/interface.ts index ce9093963d..0408529ef5 100644 --- a/src/github/interface.ts +++ b/src/github/interface.ts @@ -57,14 +57,15 @@ export interface PullRequest { number: number; state: string; body: string; + bodyHTML?: string; title: string; - assignee: IAccount; + assignee?: IAccount; createdAt: string; updatedAt: string; head: IGitHubRef; base: IGitHubRef; user: IAccount; - labels: ILabel[]; + labels?: ILabel[]; merged: boolean; mergeable?: boolean; nodeId: string; diff --git a/src/github/pullRequestModel.ts b/src/github/pullRequestModel.ts index 6319c89e5e..767ba3307b 100644 --- a/src/github/pullRequestModel.ts +++ b/src/github/pullRequestModel.ts @@ -72,6 +72,7 @@ export class PullRequestModel { update(prItem: PullRequest): void { this.prNumber = prItem.number; this.title = prItem.title; + this.bodyHTML = prItem.bodyHTML; this.html_url = prItem.url; this.author = prItem.user; this.labels = prItem.labels.map(label => label.name); diff --git a/src/github/queries.gql b/src/github/queries.gql index 260668affa..98226329e9 100644 --- a/src/github/queries.gql +++ b/src/github/queries.gql @@ -32,6 +32,7 @@ fragment Comment on IssueComment { } url body + bodyHTML updatedAt createdAt viewerCanUpdate @@ -82,6 +83,7 @@ fragment Review on PullRequestReview { } state body + bodyHTML submittedAt updatedAt createdAt @@ -90,7 +92,6 @@ fragment Review on PullRequestReview { query TimelineEvents($owner: String!, $name: String!, $number: Int!, $last: Int = 100) { repository(owner: $owner, name: $name) { pullRequest(number: $number) { - bodyHTML timeline(last: $last) { edges { node { @@ -153,6 +154,49 @@ query PullRequestComments($owner:String!, $name:String!, $number:Int!, $first:In } } +query PullRequest($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name) { + pullRequest(number: $number) { + number + url + state + body + bodyHTML + title + author { + login + url + avatarUrl + } + createdAt + updatedAt + headRef { + name + repository { + nameWithOwner + url + } + target { + oid + } + } + baseRef { + name + repository { + nameWithOwner + url + } + target { + oid + } + } + merged + mergeable + id + } + } +} + mutation AddComment($input: AddPullRequestReviewCommentInput!) { addPullRequestReviewComment(input: $input) { comment { diff --git a/src/github/utils.ts b/src/github/utils.ts index dff1c60de2..f0bbdaedd3 100644 --- a/src/github/utils.ts +++ b/src/github/utils.ts @@ -9,7 +9,7 @@ import { IAccount, PullRequest } from './interface'; import { Comment } from '../common/comment'; import { parseDiffHunk, DiffHunk } from '../common/diffHunk'; import { EventType, TimelineEvent } from '../common/timelineEvent'; -import { ReviewComment } from './graphql'; +import { ReviewComment, PullRequestResponse } from './graphql'; export function convertRESTUserToAccount(user: Octokit.PullRequestsGetAllResponseItemUser): IAccount { return { @@ -171,6 +171,41 @@ export function parseGraphQLComment(comment: ReviewComment): Comment { return c; } +export function parseGraphQLPullRequest(pullRequest: PullRequestResponse): PullRequest { + const graphQLPullRequest = pullRequest.repository.pullRequest; + return { + url: graphQLPullRequest.url, + number: graphQLPullRequest.number, + state: graphQLPullRequest.state, + body: graphQLPullRequest.body, + bodyHTML: graphQLPullRequest.bodyHTML, + title: graphQLPullRequest.title, + createdAt: graphQLPullRequest.createdAt, + updatedAt: graphQLPullRequest.updatedAt, + head: { + label: graphQLPullRequest.headRef.name, + ref: graphQLPullRequest.headRef.repository.nameWithOwner, + sha: graphQLPullRequest.headRef.target.oid, + repo: { + cloneUrl: graphQLPullRequest.headRef.repository.url + } + }, + base: { + label: graphQLPullRequest.baseRef.name, + ref: graphQLPullRequest.baseRef.repository.nameWithOwner, + sha: graphQLPullRequest.baseRef.target.oid, + repo: { + cloneUrl: graphQLPullRequest.baseRef.repository.url + } + }, + user: graphQLPullRequest.author, + merged: graphQLPullRequest.merged, + mergeable: graphQLPullRequest.mergeable, + nodeId: graphQLPullRequest.id, + labels: [] + }; +} + export function parseGraphQLTimelineEvents(events: any[]): TimelineEvent[] { events.forEach(event => { let type = convertGraphQLEventType(event.__typename); From 88d39b510589f86b9e4c1823029e5b8c5dd8d2f7 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 22 Jan 2019 10:57:52 -0800 Subject: [PATCH 4/6] headRef, baseRef and authorAssociation can be undefined --- preview-src/pullRequestOverviewRenderer.ts | 13 ++++++++----- src/github/graphql.ts | 4 ++-- src/github/interface.ts | 4 ++-- src/github/utils.ts | 8 ++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/preview-src/pullRequestOverviewRenderer.ts b/preview-src/pullRequestOverviewRenderer.ts index 45f8c77b22..a9f05e28ff 100644 --- a/preview-src/pullRequestOverviewRenderer.ts +++ b/preview-src/pullRequestOverviewRenderer.ts @@ -608,8 +608,14 @@ class ReviewNode { userLogin.href = this._review.user.url; userLogin.textContent = this._review.user.login; - const authorAssociation: HTMLSpanElement = document.createElement('span'); - authorAssociation.textContent = `(${this._review.authorAssociation.toLocaleLowerCase()})`; + commentHeader.appendChild(userIcon); + commentHeader.appendChild(userLogin); + + if (this._review.authorAssociation && this._review.authorAssociation !== 'NONE') { + const authorAssociation: HTMLSpanElement = document.createElement('span'); + authorAssociation.textContent = `(${this._review.authorAssociation.toLocaleLowerCase()})`; + commentHeader.appendChild(authorAssociation); + } const reviewState = document.createElement('span'); switch (this._review.state.toLowerCase()) { @@ -636,9 +642,6 @@ class ReviewNode { timestamp.classList.add('pending'); } - commentHeader.appendChild(userIcon); - commentHeader.appendChild(userLogin); - commentHeader.appendChild(authorAssociation); commentHeader.appendChild(reviewState); commentHeader.appendChild(timestamp); diff --git a/src/github/graphql.ts b/src/github/graphql.ts index ef20d2f991..6fffa7bff3 100644 --- a/src/github/graphql.ts +++ b/src/github/graphql.ts @@ -197,7 +197,7 @@ export interface PullRequestResponse { } createdAt: string; updatedAt: string; - headRef: { + headRef?: { name: string; repository: { nameWithOwner: string; @@ -207,7 +207,7 @@ export interface PullRequestResponse { oid: string; } } - baseRef: { + baseRef?: { name: string; repository: { nameWithOwner: string; diff --git a/src/github/interface.ts b/src/github/interface.ts index 0408529ef5..76f02a56a4 100644 --- a/src/github/interface.ts +++ b/src/github/interface.ts @@ -62,8 +62,8 @@ export interface PullRequest { assignee?: IAccount; createdAt: string; updatedAt: string; - head: IGitHubRef; - base: IGitHubRef; + head?: IGitHubRef; + base?: IGitHubRef; user: IAccount; labels?: ILabel[]; merged: boolean; diff --git a/src/github/utils.ts b/src/github/utils.ts index f0bbdaedd3..44d5f92bdc 100644 --- a/src/github/utils.ts +++ b/src/github/utils.ts @@ -182,22 +182,22 @@ export function parseGraphQLPullRequest(pullRequest: PullRequestResponse): PullR title: graphQLPullRequest.title, createdAt: graphQLPullRequest.createdAt, updatedAt: graphQLPullRequest.updatedAt, - head: { + head: graphQLPullRequest.headRef ? { label: graphQLPullRequest.headRef.name, ref: graphQLPullRequest.headRef.repository.nameWithOwner, sha: graphQLPullRequest.headRef.target.oid, repo: { cloneUrl: graphQLPullRequest.headRef.repository.url } - }, - base: { + } : undefined, + base: graphQLPullRequest.baseRef ? { label: graphQLPullRequest.baseRef.name, ref: graphQLPullRequest.baseRef.repository.nameWithOwner, sha: graphQLPullRequest.baseRef.target.oid, repo: { cloneUrl: graphQLPullRequest.baseRef.repository.url } - }, + } : undefined, user: graphQLPullRequest.author, merged: graphQLPullRequest.merged, mergeable: graphQLPullRequest.mergeable, From 8b719ea0166e56639298d542fe2655be9a2bf036 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 22 Jan 2019 11:32:13 -0800 Subject: [PATCH 5/6] use graphql typings --- src/common/timelineEvent.ts | 26 +++++--- src/github/graphql.ts | 8 +++ src/github/utils.ts | 128 +++++++++++++++++++++++------------- 3 files changed, 108 insertions(+), 54 deletions(-) diff --git a/src/common/timelineEvent.ts b/src/common/timelineEvent.ts index 2356d8f257..ddd2c6ae2f 100644 --- a/src/common/timelineEvent.ts +++ b/src/common/timelineEvent.ts @@ -38,7 +38,7 @@ export interface CommentEvent { } export interface ReviewEvent { - event: string; + event: EventType; comments: Comment[]; submittedAt: string; body: string; @@ -52,7 +52,7 @@ export interface ReviewEvent { export interface CommitEvent { author: IAccount; - event: string; + event: EventType; sha: string; url: string; htmlUrl: string; @@ -67,24 +67,34 @@ export interface MergedEvent { mergeRef: string; sha: string; commitUrl: string; - event: string; + event: EventType; url: string; } -export type TimelineEvent = CommitEvent | ReviewEvent | CommentEvent | MergedEvent; +export interface AssignEvent { + event: EventType; + user: IAccount; + actor: IAccount; +} + +export type TimelineEvent = CommitEvent | ReviewEvent | CommentEvent | MergedEvent | AssignEvent; export function isReviewEvent(event: TimelineEvent): event is ReviewEvent { - return Number(event.event) === EventType.Reviewed; + return event.event === EventType.Reviewed; } export function isCommitEvent(event: TimelineEvent): event is CommitEvent { - return Number(event.event) === EventType.Committed; + return event.event === EventType.Committed; } export function isCommentEvent(event: TimelineEvent): event is CommentEvent { - return Number(event.event) === EventType.Commented; + return event.event === EventType.Commented; } export function isMergedEvent(event: TimelineEvent): event is MergedEvent { - return Number(event.event) === EventType.Merged; + return event.event === EventType.Merged; +} + +export function isAssignEvent(event: TimelineEvent): event is AssignEvent { + return event.event === EventType.Assigned; } \ No newline at end of file diff --git a/src/github/graphql.ts b/src/github/graphql.ts index 6fffa7bff3..fbbb1fda94 100644 --- a/src/github/graphql.ts +++ b/src/github/graphql.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ export interface MergedEvent { + __typename: string; id: string; actor: { login: string; @@ -22,6 +23,7 @@ export interface MergedEvent { } export interface IssueComment { + __typename: string; id: string; databaseId: number; authorAssocation: string; @@ -41,6 +43,7 @@ export interface IssueComment { } export interface ReviewComment { + __typename: string; id: string; databaseId: number; url: string; @@ -71,6 +74,7 @@ export interface ReviewComment { } export interface Commit { + __typename: string; id: string; author: { user: { @@ -89,6 +93,7 @@ export interface Commit { } export interface AssignedEvent { + __typename: string; actor: { login: string; avatarUrl: string; @@ -102,9 +107,11 @@ export interface AssignedEvent { } export interface Review { + __typename: string; id: string; databaseId: number; authorAssociation: string; + url: string; author: { login: string; avatarUrl: string; @@ -112,6 +119,7 @@ export interface Review { }; state: string; body: string; + bodyHTML?: string; submittedAt: string; updatedAt: string; createdAt: string; diff --git a/src/github/utils.ts b/src/github/utils.ts index 44d5f92bdc..1f6865a746 100644 --- a/src/github/utils.ts +++ b/src/github/utils.ts @@ -8,8 +8,8 @@ import * as Octokit from '@octokit/rest'; import { IAccount, PullRequest } from './interface'; import { Comment } from '../common/comment'; import { parseDiffHunk, DiffHunk } from '../common/diffHunk'; -import { EventType, TimelineEvent } from '../common/timelineEvent'; -import { ReviewComment, PullRequestResponse } from './graphql'; +import * as Common from '../common/timelineEvent'; +import * as GraphQL from './graphql'; export function convertRESTUserToAccount(user: Octokit.PullRequestsGetAllResponseItemUser): IAccount { return { @@ -125,26 +125,26 @@ export function convertPullRequestsGetCommentsResponseItemToComment(comment: Oct export function convertGraphQLEventType(text: string) { switch (text) { case 'Commit': - return EventType.Committed; + return Common.EventType.Committed; case 'LabeledEvent': - return EventType.Labeled; + return Common.EventType.Labeled; case 'MilestonedEvent': - return EventType.Milestoned; + return Common.EventType.Milestoned; case 'AssignedEvent': - return EventType.Assigned; + return Common.EventType.Assigned; case 'IssueComment': - return EventType.Commented; + return Common.EventType.Commented; case 'PullRequestReview': - return EventType.Reviewed; + return Common.EventType.Reviewed; case 'MergedEvent': - return EventType.Merged; + return Common.EventType.Merged; default: - return EventType.Other; + return Common.EventType.Other; } } -export function parseGraphQLComment(comment: ReviewComment): Comment { +export function parseGraphQLComment(comment: GraphQL.ReviewComment): Comment { const c: Comment = { id: comment.databaseId, url: comment.url, @@ -171,7 +171,7 @@ export function parseGraphQLComment(comment: ReviewComment): Comment { return c; } -export function parseGraphQLPullRequest(pullRequest: PullRequestResponse): PullRequest { +export function parseGraphQLPullRequest(pullRequest: GraphQL.PullRequestResponse): PullRequest { const graphQLPullRequest = pullRequest.repository.pullRequest; return { url: graphQLPullRequest.url, @@ -206,58 +206,94 @@ export function parseGraphQLPullRequest(pullRequest: PullRequestResponse): PullR }; } -export function parseGraphQLTimelineEvents(events: any[]): TimelineEvent[] { +export function parseGraphQLTimelineEvents(events: (GraphQL.MergedEvent | GraphQL.Review | GraphQL.IssueComment | GraphQL.Commit | GraphQL.AssignedEvent)[]): Common.TimelineEvent[] { + let ret: Common.TimelineEvent[] = []; events.forEach(event => { let type = convertGraphQLEventType(event.__typename); - event.event = type; - if (event.event === EventType.Commented) { - event.canEdit = event.viewerCanUpdate; - event.canDelete = event.viewerCanDelete; - event.user = event.author; - event.id = event.databaseId; - event.htmlUrl = event.url; - } - - if (event.event === EventType.Reviewed) { - event.user = event.author; - event.canEdit = event.viewerCanUpdate; - event.canDelete = event.viewerCanDelete; - event.id = event.databaseId; - event.htmlUrl = event.url; - event.submittedAt = event.submittedAt; - } + switch (type) { + case Common.EventType.Commented: + let commentEvent = event as GraphQL.IssueComment; + ret.push({ + htmlUrl: commentEvent.url, + body: commentEvent.body, + bodyHTML: commentEvent.bodyHTML, + user: commentEvent.author, + event: type, + canEdit: commentEvent.viewerCanUpdate, + canDelete: commentEvent.viewerCanDelete, + id: commentEvent.databaseId, + createdAt: commentEvent.createdAt + } as Common.CommentEvent); + return; + case Common.EventType.Reviewed: + let reviewEvent = event as GraphQL.Review; + ret.push({ + event: type, + comments: [], + submittedAt: reviewEvent.submittedAt, + body: reviewEvent.body, + bodyHTML: reviewEvent.bodyHTML, + htmlUrl: reviewEvent.url, + user: reviewEvent.author, + authorAssociation: reviewEvent.authorAssociation, + state: reviewEvent.state, + id: reviewEvent.databaseId, + } as Common.ReviewEvent); + return; + case Common.EventType.Committed: + let commitEv = event as GraphQL.Commit; + ret.push({ + event: type, + sha: commitEv.oid, + author: commitEv.author.user || { login: commitEv.committer.name, avatarUrl: commitEv.committer.avatarUrl }, + htmlUrl: commitEv.url, + message: commitEv.message + } as Common.CommitEvent); + return; + case Common.EventType.Merged: + let mergeEv = event as GraphQL.MergedEvent; - if (event.event === EventType.Committed) { - event.sha = event.oid; - event.author = event.author.user || { login: event.committer.name, avatarUrl: event.committer.avatarUrl }; - event.htmlUrl = event.url; - } + ret.push({ + event: type, + user: mergeEv.actor, + createdAt: mergeEv.createdAt, + mergeRef: mergeEv.mergeRef.name, + sha: mergeEv.commit.oid, + commitUrl: mergeEv.commit.commitUrl, + url: mergeEv.url, + graphNodeId: mergeEv.id + } as Common.MergedEvent); + return; + case Common.EventType.Assigned: + let assignEv = event as GraphQL.AssignedEvent; - if (event.event === EventType.Merged) { - event.user = event.actor; - event.mergeRef = event.mergeRef.name; - event.sha = event.commit.oid; - event.commitUrl = event.commit.commitUrl; - event.graphNodeId = event.id; + ret.push({ + event: type, + user: assignEv.user, + actor: assignEv.actor + } as Common.AssignEvent); + return; + default: + break; } }); - return events; + return ret; } -export function convertRESTTimelineEvents(events: any[]): TimelineEvent[] { +export function convertRESTTimelineEvents(events: any[]): Common.TimelineEvent[] { events.forEach(event => { - if (event.event === EventType.Commented) { + if (event.event === Common.EventType.Commented) { } - if (event.event === EventType.Reviewed) { + if (event.event === Common.EventType.Reviewed) { event.submittedAt = event.submitted_at; event.htmlUrl = event.html_url; } - if (event.event === EventType.Committed) { + if (event.event === Common.EventType.Committed) { event.htmlUrl = event.html_url; } }); From 108ae1796ff91855758c38e55fdaa6109c053714 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 28 Jan 2019 17:19:33 -0800 Subject: [PATCH 6/6] catch null error --- src/github/githubRepository.ts | 6 +++--- src/github/pullRequestModel.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/github/githubRepository.ts b/src/github/githubRepository.ts index e25870e85b..324fec5301 100644 --- a/src/github/githubRepository.ts +++ b/src/github/githubRepository.ts @@ -280,14 +280,14 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable { try { Logger.debug(`Fetch pull request ${id} - enter`, GitHubRepository.ID); const { octokit, query, remote } = await this.ensure(); - let { data } = await octokit.pullRequests.get({ + let prsResult = await octokit.pullRequests.get({ owner: remote.owner, repo: remote.repositoryName, number: id }); Logger.debug(`Fetch pull request ${id} - done`, GitHubRepository.ID); - if (!data.head.repo) { + if (!prsResult.data.head.repo) { Logger.appendLine('The remote branch for this PR was already deleted.', GitHubRepository.ID); return; } @@ -314,7 +314,7 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable { if (!data.head.repo) { Logger.appendLine('The remote branch for this PR was already deleted.', GitHubRepository.ID); - return null; + return; } let item = convertRESTPullRequestToRawPullRequest(data); diff --git a/src/github/pullRequestModel.ts b/src/github/pullRequestModel.ts index 06e5a4b684..8fb6d3e391 100644 --- a/src/github/pullRequestModel.ts +++ b/src/github/pullRequestModel.ts @@ -75,7 +75,7 @@ export class PullRequestModel { this.bodyHTML = prItem.bodyHTML; this.html_url = prItem.url; this.author = prItem.user; - this.labels = prItem.labels.map(label => label.name); + this.labels = (prItem.labels || []).map(label => label.name); if (prItem.state === 'open') { this.state = PullRequestStateEnum.Open; @@ -90,8 +90,8 @@ export class PullRequestModel { this.createdAt = prItem.createdAt; this.updatedAt = prItem.updatedAt ? prItem.updatedAt : this.createdAt; - this.head = new GitHubRef(prItem.head.ref, prItem.head.label, prItem.head.sha, prItem.head.repo.cloneUrl); - this.base = new GitHubRef(prItem.base.ref, prItem.base.label, prItem.base.sha, prItem.base.repo.cloneUrl); + this.head = new GitHubRef(prItem.head!.ref, prItem.head!.label, prItem.head!.sha, prItem.head!.repo.cloneUrl); + this.base = new GitHubRef(prItem.base!.ref, prItem.base!.label, prItem.base!.sha, prItem.base!.repo.cloneUrl); } equals(other: PullRequestModel | undefined): boolean {