Skip to content

Commit 4fa0f44

Browse files
committed
access token generation, updated docs
1 parent 684be00 commit 4fa0f44

6 files changed

Lines changed: 161 additions & 17 deletions

File tree

‎README.md‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,17 @@ For Python code viewed on [Sourcegraph](https://sourcegraph.com) and when using
1616

1717
## Usage on your own Sourcegraph instance
1818

19-
Run the language server (@chrismwendt will deploy this to to hub.docker.com soon):
19+
Run the language server Docker container:
2020

21-
- Check out https://github.com/sourcegraph/python-language-server/tree/wip2 locally
22-
- Make sure you have `dotnet` installed (e.g. instructions for macOS http://www.joshka.net/2016/08/installing-dotnet-osx)
23-
- Run `make docker`
24-
- Run `make run-docker` (it will listen on port 4288)
21+
```
22+
docker run --rm -p 4288:4288 sourcegraph/lang-python:insiders
23+
```
2524

26-
Add the address to your Python language server and an access token (for private repositories) to your Sourcegraph settings:
25+
Add these to your Sourcegraph settings:
2726

2827
```
2928
"python.languageServer.url": "ws://localhost:4288",
30-
"python.accessToken": "*****",
29+
"python.sourcegraph.url": "http://host.docker.internal:7080",
3130
```
3231

3332
## Usage on Sourcegraph.com

‎package.json‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@
1010
"url": "https://github.com/sourcegraph/sourcegraph-python"
1111
},
1212
"wip": true,
13-
"categories": ["Programming languages"],
14-
"tags": ["python", "language-server"],
13+
"categories": [
14+
"Programming languages"
15+
],
16+
"tags": [
17+
"python",
18+
"language-server"
19+
],
1520
"contributes": {
1621
"configuration": {
1722
"title": "Python settings",
@@ -57,6 +62,7 @@
5762
"dependencies": {
5863
"@types/ws": "^6.0.1",
5964
"rxjs": "^6.3.3",
65+
"tagged-template-noop": "^2.1.0",
6066
"vscode-jsonrpc": "^4.0.0",
6167
"vscode-languageserver-protocol": "^3.13.0",
6268
"vscode-ws-jsonrpc": "^0.0.3",

‎src/access-token.ts‎

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import * as sourcegraph from 'sourcegraph'
2+
import gql from 'tagged-template-noop'
3+
4+
interface AccessTokenResponse {
5+
currentUser: {
6+
accessTokens: {
7+
nodes: { note: string }[]
8+
pageInfo: {
9+
hasNextPage: boolean
10+
}
11+
}
12+
}
13+
errors: string[]
14+
}
15+
16+
async function userHasAccessTokenWithNote(note: string): Promise<boolean> {
17+
const response: AccessTokenResponse = await queryGraphQL(`
18+
query {
19+
currentUser {
20+
accessTokens(first: 1000) {
21+
nodes {
22+
note
23+
},
24+
pageInfo {
25+
hasNextPage
26+
}
27+
}
28+
}
29+
}
30+
`)
31+
32+
if (
33+
!response ||
34+
!response.currentUser ||
35+
!response.currentUser.accessTokens ||
36+
!response.currentUser.accessTokens.nodes ||
37+
!Array.isArray(response.currentUser.accessTokens.nodes)
38+
) {
39+
return false
40+
}
41+
if (
42+
response.currentUser.accessTokens.pageInfo &&
43+
response.currentUser.accessTokens.pageInfo.hasNextPage === true
44+
) {
45+
throw new Error('You have too many access tokens (over 1000).')
46+
}
47+
return response.currentUser.accessTokens.nodes.some(
48+
token => token.note === note
49+
)
50+
}
51+
52+
async function queryGraphQL(query: string, variables: any = {}): Promise<any> {
53+
const { data, errors } = await sourcegraph.commands.executeCommand(
54+
'queryGraphQL',
55+
query,
56+
variables
57+
)
58+
if (errors) {
59+
throw Object.assign(
60+
new Error(errors.map((err: any) => err.message).join('\n')),
61+
{ errors }
62+
)
63+
}
64+
return data
65+
}
66+
67+
const NOTE_FOR_PYTHON_ACCESS_TOKEN = 'python'
68+
69+
// Undefined means the current user is anonymous.
70+
let accessTokenPromise: Promise<string | undefined>
71+
export async function getOrTryToCreateAccessToken(): Promise<
72+
string | undefined
73+
> {
74+
const hasToken = await userHasAccessTokenWithNote(
75+
NOTE_FOR_PYTHON_ACCESS_TOKEN
76+
)
77+
const setting = sourcegraph.configuration
78+
.get<Settings>()
79+
.get('python.accessToken')
80+
if (hasToken && setting) {
81+
return setting
82+
} else {
83+
return (
84+
accessTokenPromise ||
85+
(accessTokenPromise = tryToCreateAccessToken())
86+
)
87+
}
88+
}
89+
90+
async function tryToCreateAccessToken(): Promise<string | undefined> {
91+
const { currentUser } = await queryGraphQL(gql`
92+
query {
93+
currentUser {
94+
id
95+
}
96+
}
97+
`)
98+
if (!currentUser) {
99+
return undefined
100+
} else {
101+
const currentUserId: string = currentUser.id
102+
const result = await queryGraphQL(
103+
gql`
104+
mutation CreateAccessToken(
105+
$user: ID!
106+
$scopes: [String!]!
107+
$note: String!
108+
) {
109+
createAccessToken(
110+
user: $user
111+
scopes: $scopes
112+
note: $note
113+
) {
114+
id
115+
token
116+
}
117+
}
118+
`,
119+
{
120+
user: currentUserId,
121+
scopes: ['user:all'],
122+
note: NOTE_FOR_PYTHON_ACCESS_TOKEN,
123+
}
124+
)
125+
const token: string = result.createAccessToken.token
126+
await sourcegraph.configuration
127+
.get<Settings>()
128+
.update('python.accessToken', token)
129+
return token
130+
}
131+
}

‎src/settings.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
interface Settings {
2+
['python.languageServer.url']?: string
3+
['python.accessToken']?: string
4+
['python.sourcegraph.url']?: string
5+
}

‎src/sourcegraph-python.ts‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,10 @@ import {
1717
ReferencesRequest,
1818
TextDocumentPositionParams,
1919
} from 'vscode-languageserver-protocol'
20+
import { getOrTryToCreateAccessToken } from './access-token'
2021
import { createUriConverter, UriConverter } from './converters'
2122
import { LanguageServerConnectionManager } from './lsp'
2223

23-
interface Settings {
24-
['python.languageServer.url']?: string
25-
['python.accessToken']?: string
26-
}
27-
2824
function fromSubscribable<T>(sub: {
2925
subscribe(next: (value?: T) => void): sourcegraph.Unsubscribable
3026
}): Observable<T> {
@@ -71,10 +67,12 @@ export function activate(ctx: sourcegraph.ExtensionContext = DUMMY_CTX): void {
7167

7268
const settings = sourcegraph.configuration.get<Settings>().value
7369
const zipUrl = new URL(
74-
sourcegraph.internal.sourcegraphURL.toString()
70+
settings['python.sourcegraph.url'] ||
71+
sourcegraph.internal.sourcegraphURL.toString()
7572
)
76-
if (settings['python.accessToken']) {
77-
zipUrl.username = settings['python.accessToken']
73+
const accessToken = await getOrTryToCreateAccessToken()
74+
if (accessToken) {
75+
zipUrl.username = accessToken
7876
}
7977
zipUrl.pathname = `/${repo}@${rev}/-/raw`
8078

‎yarn.lock‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4829,6 +4829,11 @@ svgo@^1.0.0, svgo@^1.0.5:
48294829
unquote "~1.1.1"
48304830
util.promisify "~1.0.0"
48314831

4832+
tagged-template-noop@^2.1.0:
4833+
version "2.1.0"
4834+
resolved "https://registry.yarnpkg.com/tagged-template-noop/-/tagged-template-noop-2.1.0.tgz#3501d67fd75612ab40e6040056ea881d1161c4f5"
4835+
integrity sha512-VOPA7ZucOLvew9xq+AwABXil7AHQZa3G7T3QOQoUveNIl3EwNsMN79JHn8v7zgpM1Pd8R36MrntUvaWqLqjg1w==
4836+
48324837
tar@^4:
48334838
version "4.4.6"
48344839
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b"

0 commit comments

Comments
 (0)