Skip to content

Commit 8506ac3

Browse files
Move fetch user call to api.ts
1 parent 3ea95f6 commit 8506ac3

4 files changed

Lines changed: 47 additions & 58 deletions

File tree

src/cloud/api.ts

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,39 @@
11
import * as vscode from "vscode"
22
import { getExtensionVersion } from "../extension"
33
import { AUTH_PROVIDER_ID } from "./auth"
4-
import type { App, Deployment, ListResponse, Team } from "./types"
4+
import type { App, Deployment, ListResponse, Team, UserInfo } from "./types"
55

66
export interface UploadInfo {
77
url: string
88
fields: Record<string, string>
99
}
1010

11+
export const BASE_URL = "https://api.fastapicloud.com/api/v1"
12+
export const DASHBOARD_URL = "https://dashboard.fastapicloud.com"
13+
1114
function getUserAgentHeaders(): Record<string, string> {
12-
// User-Agent is a forbidden header in browsers and causes fetch to fail
1315
if (vscode.env.uiKind === vscode.UIKind.Web) return {}
1416
return { "User-Agent": `fastapi-vscode/${getExtensionVersion()}` }
1517
}
1618

1719
export class ApiService {
18-
public static readonly BASE_URL = "https://api.fastapicloud.com/api/v1"
19-
public static readonly DASHBOARD_URL = "https://dashboard.fastapicloud.com"
20+
static async fetchUserInfo(token: string): Promise<UserInfo | null> {
21+
try {
22+
const response = await fetch(`${BASE_URL}/users/me`, {
23+
headers: {
24+
Authorization: `Bearer ${token}`,
25+
"Content-Type": "application/json",
26+
},
27+
})
28+
if (!response.ok) return null
29+
return (await response.json()) as UserInfo
30+
} catch {
31+
return null
32+
}
33+
}
2034

2135
static getDashboardUrl(teamSlug: string, appSlug: string): string {
22-
return `${ApiService.DASHBOARD_URL}/${teamSlug}/apps/${appSlug}/general`
36+
return `${DASHBOARD_URL}/${teamSlug}/apps/${appSlug}/general`
2337
}
2438

2539
private async request<T>(
@@ -36,7 +50,7 @@ export class ApiService {
3650
}
3751
const token = session.accessToken
3852

39-
const response = await fetch(`${ApiService.BASE_URL}${endpoint}`, {
53+
const response = await fetch(`${BASE_URL}${endpoint}`, {
4054
...options,
4155
headers: {
4256
Authorization: `Bearer ${token}`,
@@ -117,17 +131,14 @@ export class ApiService {
117131
expires_in?: number
118132
interval?: number
119133
}> {
120-
const response = await fetch(
121-
`${ApiService.BASE_URL}/login/device/authorization`,
122-
{
123-
method: "POST",
124-
headers: {
125-
"Content-Type": "application/x-www-form-urlencoded",
126-
...getUserAgentHeaders(),
127-
},
128-
body: new URLSearchParams({ client_id: clientId }).toString(),
134+
const response = await fetch(`${BASE_URL}/login/device/authorization`, {
135+
method: "POST",
136+
headers: {
137+
"Content-Type": "application/x-www-form-urlencoded",
138+
...getUserAgentHeaders(),
129139
},
130-
)
140+
body: new URLSearchParams({ client_id: clientId }).toString(),
141+
})
131142

132143
if (!response.ok) {
133144
throw new Error(
@@ -168,22 +179,19 @@ export class ApiService {
168179
throw new Error("Sign-in cancelled")
169180
}
170181

171-
const response = await fetch(
172-
`${ApiService.BASE_URL}/login/device/token`,
173-
{
174-
method: "POST",
175-
headers: {
176-
"Content-Type": "application/x-www-form-urlencoded",
177-
...getUserAgentHeaders(),
178-
},
179-
body: new URLSearchParams({
180-
client_id: clientId,
181-
device_code: deviceCode,
182-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
183-
}).toString(),
184-
signal,
182+
const response = await fetch(`${BASE_URL}/login/device/token`, {
183+
method: "POST",
184+
headers: {
185+
"Content-Type": "application/x-www-form-urlencoded",
186+
...getUserAgentHeaders(),
185187
},
186-
)
188+
body: new URLSearchParams({
189+
client_id: clientId,
190+
device_code: deviceCode,
191+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
192+
}).toString(),
193+
signal,
194+
})
187195

188196
const data = (await response.json()) as {
189197
access_token?: string

src/cloud/auth.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ interface AuthConfig {
2626
access_token: string
2727
}
2828

29-
interface UserInfo {
30-
email: string
31-
full_name: string
32-
}
33-
3429
export function isTokenExpired(token: string): boolean {
3530
try {
3631
const parts = token.split(".")
@@ -124,22 +119,6 @@ export class CloudAuthenticationProvider
124119
return this._onDidChangeSessions.event
125120
}
126121

127-
private async fetchUserInfo(token: string): Promise<UserInfo | null> {
128-
try {
129-
const response = await fetch(`${ApiService.BASE_URL}/users/me`, {
130-
headers: {
131-
Authorization: `Bearer ${token}`,
132-
"Content-Type": "application/json",
133-
},
134-
})
135-
if (!response.ok) return null
136-
const data = (await response.json()) as UserInfo
137-
return data
138-
} catch {
139-
return null
140-
}
141-
}
142-
143122
public async getSessions(): Promise<AuthenticationSession[]> {
144123
const authUri = this.getAuthUri()
145124
log(
@@ -176,7 +155,7 @@ export class CloudAuthenticationProvider
176155

177156
// Fetch user info for account label (cached after first successful fetch)
178157
if (!this.cachedLabel) {
179-
const info = await this.fetchUserInfo(token)
158+
const info = await ApiService.fetchUserInfo(token)
180159
if (info?.email) {
181160
this.cachedLabel = info.email
182161
}

src/cloud/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ export interface Config {
4242
team_id: string
4343
}
4444

45+
export interface UserInfo {
46+
email: string
47+
full_name: string
48+
}
49+
4550
export interface ListResponse<T> {
4651
data: T[]
4752
count: number

src/test/cloud/api.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as assert from "node:assert"
22
import sinon from "sinon"
33
import * as vscode from "vscode"
4-
import { ApiService } from "../../cloud/api"
4+
import { ApiService, BASE_URL } from "../../cloud/api"
55
import { mockResponse } from "../testUtils"
66

77
suite("cloud/api", () => {
@@ -44,10 +44,7 @@ suite("cloud/api", () => {
4444

4545
// Verify fetch was called with correct params
4646
const [url, options] = fetchStub.firstCall.args
47-
assert.strictEqual(
48-
url,
49-
`${ApiService.BASE_URL}/login/device/authorization`,
50-
)
47+
assert.strictEqual(url, `${BASE_URL}/login/device/authorization`)
5148
assert.strictEqual(options?.method, "POST")
5249
assert.ok(
5350
(options?.headers as Record<string, string>)["Content-Type"]?.includes(

0 commit comments

Comments
 (0)