Skip to content
This repository was archived by the owner on Mar 10, 2022. It is now read-only.

Commit 2b20aae

Browse files
committed
fix: handle the case when the access token is invalid
Previously, the extension would get stuck if the provided access token was invalid. Now, the extension prompts the user to update the access token if it's invalid.
1 parent 6620d93 commit 2b20aae

2 files changed

Lines changed: 66 additions & 29 deletions

File tree

src/queries/graphqlQuery.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CancellationToken } from 'vscode'
33
import { log } from '../log'
44
import { debugEnabledSetting } from '../settings/debugEnabledSetting'
55
import { endpointHostnameSetting, endpointSetting } from '../settings/endpointSetting'
6-
import { accessTokenSetting } from '../settings/accessTokenSetting'
6+
import { accessTokenSetting, promptUserForAccessTokenSetting } from '../settings/accessTokenSetting'
77

88
export function graphqlQuery<A, B>(query: string, variables: A, token: CancellationToken): Promise<B | undefined> {
99
return accessTokenSetting().then(accessToken => graphqlQueryWithAccessToken(query, variables, token, accessToken))
@@ -31,38 +31,53 @@ export function graphqlQueryWithAccessToken<A, B>(
3131
'Content-Length': data.length,
3232
},
3333
}
34+
const curlCommand = (): string => {
35+
const data: string = JSON.stringify({ query: query.replace(/\s+/g, ' '), variables })
36+
return `curl -H 'Authorization: token ${accessToken}' -d '${data}' ${endpointSetting()}/.api/graphql`
37+
}
38+
const onReject = async (error: any) => {
39+
if (error === 'Invalid access token.\n') {
40+
// Prompt the user to update the access token setting and try again with the new setting.
41+
try {
42+
const newToken = await promptUserForAccessTokenSetting('Invalid Sourcegraph Access Token')
43+
const newResult = await graphqlQueryWithAccessToken<A, B>(query, variables, token, newToken)
44+
resolve(newResult)
45+
} catch (newError) {
46+
reject(newError)
47+
}
48+
} else {
49+
reject(error)
50+
}
51+
}
3452
const req = request(options, res => {
3553
const body: Uint8Array[] = []
3654
res.on('data', json => {
3755
body.push(json)
3856
})
39-
res.on('error', reject)
57+
res.on('error', onReject)
4058
const onClose = () => {
59+
const json = Buffer.concat(body).toString()
4160
if (res.statusCode === 200) {
4261
try {
43-
const json = Buffer.concat(body).toString()
4462
const parsed: B = JSON.parse(json)
4563
resolve(parsed)
4664
} catch (error) {
47-
log.error(`graphql(${data})`, error)
48-
reject(error)
65+
log.error(`graphql(${curlCommand()})`, error)
66+
onReject(error)
4967
}
5068
} else {
51-
log.error(`graphql(${data}), statusCode=${res.statusCode}`, body)
52-
reject(body.join(''))
69+
log.error(`graphql(${curlCommand()}), statusCode=${res.statusCode}`, json)
70+
onReject(json)
5371
}
5472
}
5573
res.on('close', onClose)
5674
res.on('end', onClose)
5775
})
58-
req.on('error', reject)
76+
req.on('error', onReject)
5977
req.write(data)
6078
req.end()
6179
if (debugEnabledSetting()) {
62-
const data: string = JSON.stringify({ query: query.replace(/\s+/g, ' '), variables })
63-
log.appendLine(
64-
`curl -H 'Authorization: token ${accessToken}' -d '${data}' ${endpointSetting()}/.api/graphql`
65-
)
80+
log.appendLine(curlCommand())
6681
}
6782
token.onCancellationRequested(() => {
6883
req.destroy()

src/settings/accessTokenSetting.ts

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,30 @@ import { readConfiguration } from './readConfiguration'
88
let cachedAccessToken: Promise<string> | undefined
99

1010
export function accessTokenSetting(): Promise<string> {
11+
const fromSettings = readConfiguration().get<string>('accessToken', '')
12+
if (fromSettings) {
13+
return Promise.resolve(fromSettings)
14+
}
15+
1116
const environmentVariable = process.env.SRC_ACCESS_TOKEN
1217
if (environmentVariable) {
1318
return Promise.resolve(environmentVariable)
1419
}
1520

16-
const fromSettings = readConfiguration().get<string>('accessToken', '')
17-
if (fromSettings) {
18-
return Promise.resolve(fromSettings)
21+
return promptUserForAccessTokenSetting()
22+
}
23+
24+
export async function deleteAccessTokenSetting(tokenValueToDelete: string): Promise<void> {
25+
const currentValue = readConfiguration().get<string>('accessToken')
26+
if (currentValue === tokenValueToDelete) {
27+
cachedAccessToken = undefined
28+
await readConfiguration().update('accessToken', undefined)
1929
}
30+
}
2031

32+
export async function promptUserForAccessTokenSetting(title?: string): Promise<string> {
2133
if (!cachedAccessToken) {
22-
cachedAccessToken = askUserToCreateAccessToken()
34+
cachedAccessToken = unconditionallyPromptUserForAccessTokenSetting(title)
2335
cachedAccessToken.then(
2436
() => {},
2537
error => {
@@ -30,36 +42,42 @@ export function accessTokenSetting(): Promise<string> {
3042
}
3143
return cachedAccessToken
3244
}
33-
34-
async function askUserToCreateAccessToken(): Promise<string> {
45+
async function unconditionallyPromptUserForAccessTokenSetting(title?: string): Promise<string> {
3546
const openBrowserMessage = 'Open browser to create an access token'
3647
const learnMore = 'Learn more about access tokens'
48+
const pasteAccessToken = 'Paste existing access token'
3749
const userChoice = await vscode.window.showErrorMessage(
38-
'Missing Sourcegraph Access Token',
50+
title || 'Missing Sourcegraph Access Token',
3951
{
4052
modal: true,
4153
detail: 'An access token is required to use the Sourcegraph extension. To fix this problem, create a new access token on the Sourcegraph website or set the $SRC_ACCESS_TOKEN environment variable and restart VS Code.',
4254
},
4355
openBrowserMessage,
44-
learnMore
56+
learnMore,
57+
pasteAccessToken
4558
)
46-
const openUrl =
47-
userChoice === openBrowserMessage
48-
? `${endpointSetting()}/user/settings/tokens`
49-
: userChoice === learnMore
50-
? 'https://docs.sourcegraph.com/cli/how-tos/creating_an_access_token'
51-
: undefined
52-
if (openUrl) {
53-
await open(openUrl)
59+
if (userChoice) {
60+
const openUrl =
61+
userChoice === openBrowserMessage
62+
? `${endpointSetting()}/user/settings/tokens`
63+
: userChoice === learnMore
64+
? 'https://docs.sourcegraph.com/cli/how-tos/creating_an_access_token'
65+
: undefined
66+
if (openUrl) {
67+
await open(openUrl)
68+
}
5469
const token = await vscode.window.showInputBox({
5570
title: 'Paste your Sourcegraph access token here',
5671
ignoreFocusOut: true,
5772
})
5873
if (token) {
5974
try {
6075
const currentUser = await currentUserQuery(token)
61-
log.appendLine(`Logged in successfully as '${currentUser}'`)
76+
const successMessage = `Successfully logged into Sourcegraph as user '${currentUser}'`
77+
log.appendLine(successMessage)
78+
await vscode.window.showInformationMessage(successMessage)
6279
await readConfiguration().update('accessToken', token, vscode.ConfigurationTarget.Global)
80+
cachedAccessToken = undefined
6381
return token
6482
} catch {
6583
await vscode.window.showErrorMessage(
@@ -74,3 +92,7 @@ async function askUserToCreateAccessToken(): Promise<string> {
7492
}
7593
throw new Error('No access token')
7694
}
95+
96+
export async function updateAccessTokenSetting(newValue?: string): Promise<void> {
97+
await readConfiguration().update('accessToken', newValue)
98+
}

0 commit comments

Comments
 (0)