Skip to content

Commit cd6351c

Browse files
committed
Merge remote-tracking branch 'origin/master' into pr/JeffreyCA/879
2 parents 031f605 + 6d11839 commit cd6351c

17 files changed

+218
-119
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"githubPullRequests.hosts": {
4141
"type": "array",
4242
"default": [],
43-
"description": "List of host credentials. For example, \"github.hosts\": [ { \"host\": \"https://github.com\", \"token\": \"GITHUB TOKEN\" } ]",
43+
"description": "List of host credentials. For example, \"githubPullRequests.hosts\": [ { \"host\": \"https://github.com\", \"token\": \"GITHUB TOKEN\" } ]",
4444
"items": {
4545
"type": "object",
4646
"properties": {
@@ -434,6 +434,7 @@
434434
"devDependencies": {
435435
"@types/chai": "^4.1.4",
436436
"@types/debounce": "^3.0.0",
437+
"@types/graphql": "^14.0.5",
437438
"@types/keytar": "^4.0.1",
438439
"@types/lodash": "^4.14.106",
439440
"@types/markdown-it": "^0.0.5",

preview-src/cache.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ export function getState(): PullRequest {
3838
}
3939

4040
export function setState(pullRequest: PullRequest): void {
41+
let oldPullRequest = getState();
42+
43+
if (oldPullRequest.number && oldPullRequest.number === pullRequest.number) {
44+
pullRequest = Object.assign(pullRequest, {
45+
pendingCommentText: oldPullRequest.pendingCommentText
46+
});
47+
}
48+
4149
vscode.setState(pullRequest);
4250
}
4351

preview-src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ body button.checkedOut svg {
286286
padding-top: 5px;
287287
margin-left: auto;
288288
display: flex;
289+
align-self: start;
289290
}
290291

291292
.title-container {

preview-src/index.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as debounce from 'debounce';
77
import { dateFromNow } from '../src/common/utils';
88
import { EventType, isReviewEvent } from '../src/common/timelineEvent';
99
import { PullRequestStateEnum } from '../src/github/interface';
10-
import { renderTimelineEvent, getStatus, renderComment, renderReview, ActionsBar, renderStatusChecks, updatePullRequestState, ElementIds, EditAction } from './pullRequestOverviewRenderer';
10+
import { renderTimelineEvent, getStatus, renderComment, renderReview, ActionsBar, renderStatusChecks, updatePullRequestState, ElementIds } from './pullRequestOverviewRenderer';
1111
import md from './mdRenderer';
1212
const emoji = require('node-emoji');
1313
import { getMessageHandler } from './message';
@@ -110,7 +110,8 @@ function renderTitle(pr: PullRequest): HTMLElement {
110110
title.textContent = text;
111111
}
112112

113-
const editAction = new EditAction(
113+
const actionsBar = new ActionsBar(
114+
titleContainer,
114115
{
115116
body: pr.title,
116117
id: pr.number.toString()
@@ -119,11 +120,21 @@ function renderTitle(pr: PullRequest): HTMLElement {
119120
messageHandler,
120121
updateTitle,
121122
'pr.edit-title',
123+
undefined,
124+
undefined,
122125
[prNumber]
123-
);
126+
);
127+
128+
const renderedActionsBar = actionsBar.render();
129+
actionsBar.registerActionBarListeners();
130+
titleHeader.appendChild(renderedActionsBar);
131+
132+
if (pr.pendingCommentDrafts && pr.pendingCommentDrafts[pr.number]) {
133+
actionsBar.startEdit(pr.pendingCommentDrafts[pr.number]);
134+
}
124135

125136
title.addEventListener('click', () => {
126-
editAction.startEdit();
137+
actionsBar.startEdit();
127138
});
128139
}
129140

@@ -142,6 +153,7 @@ function renderDescription(pr: PullRequest): HTMLElement {
142153
commentHeader.classList.add('description-header');
143154

144155
const commentBody = document.createElement('div');
156+
commentBody.className = 'comment-body';
145157
commentBody.innerHTML = pr.bodyHTML ?
146158
pr.bodyHTML :
147159
pr.body

preview-src/pullRequestOverviewRenderer.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,8 @@ export class ActionsBar {
469469
private _updateHandler: (value: any) => void,
470470
private _editCommand?: string,
471471
private _deleteCommand?: string,
472-
private _review?: ReviewNode) {
472+
private _review?: ReviewNode,
473+
private _elementsToHide?: HTMLElement[]) {
473474

474475
}
475476

@@ -480,7 +481,7 @@ export class ActionsBar {
480481
if (this._editCommand) {
481482
const editButton = document.createElement('button');
482483
editButton.innerHTML = editIcon;
483-
this._editAction = new EditAction(this._data, this._renderedComment, this._messageHandler, this._updateHandler, this._editCommand, [this._actionsBar]);
484+
this._editAction = new EditAction(this._data, this._renderedComment, this._messageHandler, this._updateHandler, this._editCommand, (this._elementsToHide || []).concat(this._actionsBar));
484485
editButton.addEventListener('click', () => this._editAction.startEdit());
485486
this._actionsBar.appendChild(editButton);
486487
}

src/authentication/githubServer.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,22 @@ export class GitHubManager {
9292

9393
public static validateScopes(host: vscode.Uri, scopes: string): boolean {
9494
if (!scopes) {
95-
return false;
95+
Logger.appendLine(`[SKIP] validateScopes(${host.toString()}): No scopes available.`);
96+
return true;
9697
}
9798
const tokenScopes = scopes.split(', ');
98-
return this.AppScopes.every(x =>
99+
const scopesNotFound = this.AppScopes.filter(x => !(
99100
tokenScopes.indexOf(x) >= 0 ||
100101
tokenScopes.indexOf(this.getScopeSuperset(x)) >= 0 ||
101102
// some scopes don't exist on older versions of GHE, treat them as optional
102103
(this.isDotCom(host) || GHE_OPTIONAL_SCOPES[x])
103-
);
104+
));
105+
if (scopesNotFound.length) {
106+
Logger.appendLine(`[FAIL] validateScopes(${host.toString()}): ${scopesNotFound.length} scopes missing`);
107+
scopesNotFound.forEach(scope => Logger.appendLine(` - ${scope}`));
108+
return false;
109+
}
110+
return true;
104111
}
105112

106113
private static getScopeSuperset(scope: string): string {
@@ -186,19 +193,15 @@ export class GitHubServer {
186193

187194
return new Promise<IHostConfiguration>((resolve, _) => {
188195
const get = https.request(options, res => {
189-
let hostConfig: IHostConfiguration | undefined;
190196
try {
191197
if (res.statusCode === 200) {
192198
const scopes = res.headers['x-oauth-scopes'] as string;
193-
if (GitHubManager.validateScopes(this.hostUri, scopes)) {
194-
this.hostConfiguration.token = token;
195-
hostConfig = this.hostConfiguration;
196-
}
199+
GitHubManager.validateScopes(this.hostUri, scopes);
200+
resolve(this.hostConfiguration);
197201
}
198202
} catch (e) {
199203
Logger.appendLine(`validate() error ${e}`);
200204
}
201-
resolve(hostConfig);
202205
});
203206

204207
get.end();

src/github/credentials.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as Octokit from '@octokit/rest';
7-
import { ApolloClient, InMemoryCache, NormalizedCacheObject } from 'apollo-boost';
7+
import { ApolloClient, InMemoryCache, NormalizedCacheObject, gql } from 'apollo-boost';
88
import { setContext } from 'apollo-link-context';
99
import * as vscode from 'vscode';
1010
import { agent } from '../common/net';
@@ -25,7 +25,7 @@ const AUTH_INPUT_TOKEN_CMD = 'auth.inputTokenCallback';
2525

2626
export interface GitHub {
2727
octokit: Octokit;
28-
graphql: ApolloClient<NormalizedCacheObject>;
28+
graphql: ApolloClient<NormalizedCacheObject> | null;
2929
}
3030

3131
export class CredentialStore {
@@ -74,7 +74,7 @@ export class CredentialStore {
7474

7575
if (token) {
7676
if (await server.validate(token)) {
77-
octokit = this.createHub({ host, token });
77+
octokit = await this.createHub({ host, token });
7878
} else {
7979
Logger.debug(`Token is no longer valid for host ${host}.`, 'Authentication');
8080
}
@@ -137,7 +137,7 @@ export class CredentialStore {
137137
this.willStartLogin(authority);
138138
const login = await server.login();
139139
if (login && login.token) {
140-
octokit = this.createHub(login);
140+
octokit = await this.createHub(login);
141141
await setToken(login.host, login.token, { emit: false });
142142
vscode.window.showInformationMessage(`You are now signed in to ${authority}`);
143143
}
@@ -174,7 +174,7 @@ export class CredentialStore {
174174
return octokit && (octokit as any).currentUser && (octokit as any).currentUser.login === username;
175175
}
176176

177-
private createHub(creds: IHostConfiguration): GitHub {
177+
private async createHub(creds: IHostConfiguration): Promise<GitHub> {
178178
const baseUrl = `${HostHelper.getApiHost(creds).toString().slice(0, -1)}${HostHelper.getApiPath(creds, '')}`;
179179
let octokit = new Octokit({
180180
agent,
@@ -187,17 +187,30 @@ export class CredentialStore {
187187
token: creds.token || '',
188188
});
189189

190-
return {
191-
octokit,
192-
graphql: new ApolloClient({
193-
link: link(baseUrl, creds.token || ''),
194-
cache: new InMemoryCache,
195-
defaultOptions: {
196-
query: {
197-
fetchPolicy: 'no-cache'
198-
}
190+
const graphql = new ApolloClient({
191+
link: link(baseUrl, creds.token || ''),
192+
cache: new InMemoryCache,
193+
defaultOptions: {
194+
query: {
195+
fetchPolicy: 'no-cache'
199196
}
197+
}
198+
});
199+
200+
let supportsGraphQL = true;
201+
await graphql.query({ query: gql `query { viewer { login } }` })
202+
.then(result => {
203+
Logger.appendLine(`${baseUrl}: GraphQL support detected`);
204+
Logger.appendLine(JSON.stringify(result, null, 2));
200205
})
206+
.catch(err => {
207+
Logger.appendLine(`${baseUrl}: GraphQL not supported (${err.message})`);
208+
supportsGraphQL = false;
209+
});
210+
211+
return {
212+
octokit,
213+
graphql: supportsGraphQL ? graphql : null,
201214
};
202215
}
203216

src/github/githubRepository.ts

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { PRType, IGitHubRepository, IAccount } from './interface';
1111
import { PullRequestModel } from './pullRequestModel';
1212
import { CredentialStore, GitHub } from './credentials';
1313
import { AuthenticationError } from '../common/authentication';
14-
import { QueryOptions, MutationOptions, ApolloQueryResult, NetworkStatus } from 'apollo-boost';
14+
import { QueryOptions, MutationOptions, ApolloQueryResult, NetworkStatus, FetchResult } from 'apollo-boost';
1515
import { PRDocumentCommentProvider, PRDocumentCommentProviderGraphQL } from '../view/prDocumentCommentProvider';
1616
import { convertRESTPullRequestToRawPullRequest, parseGraphQLPullRequest } from './utils';
1717
import { PullRequestResponse, MentionableUsersResponse } from './graphql';
@@ -53,7 +53,7 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable {
5353
}
5454

5555
await this.ensure();
56-
this.commentsProvider = this.supportsGraphQl() ? new PRDocumentCommentProviderGraphQL() : new PRDocumentCommentProvider();
56+
this.commentsProvider = this.supportsGraphQl ? new PRDocumentCommentProviderGraphQL() : new PRDocumentCommentProvider();
5757
this._toDispose.push(vscode.workspace.registerDocumentCommentProvider(this.commentsProvider));
5858
} catch (e) {
5959
console.log(e);
@@ -72,7 +72,7 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable {
7272
constructor(public remote: Remote, private readonly _credentialStore: CredentialStore) {
7373
}
7474

75-
supportsGraphQl(): boolean {
75+
get supportsGraphQl(): boolean {
7676
return !!(this.hub && this.hub.graphql);
7777
}
7878

@@ -94,7 +94,7 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable {
9494
return rsp;
9595
}
9696

97-
mutate = async <T>(mutation: MutationOptions): Promise<ApolloQueryResult<T>> => {
97+
mutate = async <T>(mutation: MutationOptions): Promise<FetchResult<T>> => {
9898
const gql = this.hub && this.hub.graphql;
9999
if (!gql) {
100100
Logger.debug(`Not available for query: ${mutation}`, GRAPHQL_COMPONENT_ID);
@@ -120,8 +120,8 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable {
120120
}
121121
const { octokit, remote } = await this.ensure();
122122
const result = await octokit.repos.get({
123-
owner: remote.owner,
124-
repo: remote.repositoryName
123+
owner: remote.owner,
124+
repo: remote.repositoryName
125125
});
126126
Logger.debug(`Fetch metadata ${remote.owner}/${remote.repositoryName} - done`, GitHubRepository.ID);
127127
this._metadata = Object.assign(result.data, { currentUser: (octokit as any).currentUser });
@@ -130,7 +130,7 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable {
130130

131131
async resolveRemote(): Promise<void> {
132132
try {
133-
const {clone_url} = await this.getMetadata();
133+
const { clone_url } = await this.getMetadata();
134134
this.remote = parseRemote(this.remote.remoteName, clone_url, this.remote.gitProtocol)!;
135135
} catch (e) {
136136
Logger.appendLine(`Unable to resolve remote: ${e}`);
@@ -279,20 +279,9 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable {
279279
async getPullRequest(id: number): Promise<PullRequestModel | undefined> {
280280
try {
281281
Logger.debug(`Fetch pull request ${id} - enter`, GitHubRepository.ID);
282-
const { octokit, query, remote } = await this.ensure();
283-
let prsResult = await octokit.pullRequests.get({
284-
owner: remote.owner,
285-
repo: remote.repositoryName,
286-
number: id
287-
});
288-
Logger.debug(`Fetch pull request ${id} - done`, GitHubRepository.ID);
289-
290-
if (!prsResult.data.head.repo) {
291-
Logger.appendLine('The remote branch for this PR was already deleted.', GitHubRepository.ID);
292-
return;
293-
}
282+
const { octokit, query, remote, supportsGraphQl } = await this.ensure();
294283

295-
if (this.supportsGraphQl()) {
284+
if (supportsGraphQl) {
296285
const { data } = await query<PullRequestResponse>({
297286
query: queries.PullRequest,
298287
variables: {
@@ -327,16 +316,16 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable {
327316
}
328317

329318
async getMentionableUsers(): Promise<IAccount[]> {
330-
try {
331-
Logger.debug(`Fetch mentionable users - enter`, GitHubRepository.ID);
332-
const { query, supportsGraphQl, remote } = await this.ensure();
319+
Logger.debug(`Fetch mentionable users - enter`, GitHubRepository.ID);
320+
const { query, supportsGraphQl, remote } = await this.ensure();
333321

334-
if (supportsGraphQl) {
335-
let after = null;
336-
let hasNextPage = false;
337-
let ret: IAccount[] = [];
322+
if (supportsGraphQl) {
323+
let after = null;
324+
let hasNextPage = false;
325+
let ret: IAccount[] = [];
338326

339-
do {
327+
do {
328+
try {
340329
const result: { data: MentionableUsersResponse } = await query<MentionableUsersResponse>({
341330
query: queries.GetMentionableUsers,
342331
variables: {
@@ -359,13 +348,13 @@ export class GitHubRepository implements IGitHubRepository, vscode.Disposable {
359348

360349
hasNextPage = result.data.repository.mentionableUsers.pageInfo.hasNextPage;
361350
after = result.data.repository.mentionableUsers.pageInfo.endCursor;
362-
} while (hasNextPage);
351+
} catch (e) {
352+
Logger.debug(`Unable to fetch mentionable users: ${e}`, GitHubRepository.ID);
353+
return ret;
354+
}
355+
} while (hasNextPage);
363356

364-
return ret;
365-
}
366-
} catch (e) {
367-
Logger.appendLine(`Unable to fetch mentionable users: ${e}`, GitHubRepository.ID);
368-
return [];
357+
return ret;
369358
}
370359

371360
return [];

0 commit comments

Comments
 (0)