Skip to content

Commit

Permalink
Splits url handling into UrlProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
eamodio committed Feb 27, 2025
1 parent 4aa7344 commit dee9185
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 135 deletions.
38 changes: 10 additions & 28 deletions src/container.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ConfigurationChangeEvent, Disposable, Event, ExtensionContext } from 'vscode';
import { EventEmitter, ExtensionMode, Uri } from 'vscode';
import { EventEmitter, ExtensionMode } from 'vscode';
import {
getSharedGKStorageLocationProvider,
getSupportedGitProviders,
Expand Down Expand Up @@ -30,6 +30,7 @@ import { OrganizationService } from './plus/gk/organizationService';
import { ProductConfigProvider } from './plus/gk/productConfigProvider';
import { ServerConnection } from './plus/gk/serverConnection';
import { SubscriptionService } from './plus/gk/subscriptionService';
import { UrlsProvider } from './plus/gk/urlsProvider';
import { GraphStatusBarController } from './plus/graph/statusbar';
import type { CloudIntegrationService } from './plus/integrations/authentication/cloudIntegrationService';
import { ConfiguredIntegrationService } from './plus/integrations/authentication/configuredIntegrationService';
Expand Down Expand Up @@ -193,7 +194,8 @@ export class Container {
configuration.onDidChangeAny(this.onAnyConfigurationChanged, this),
];

this._disposables.push((this._connection = new ServerConnection(this)));
this._urls = new UrlsProvider(this.env);
this._disposables.push((this._connection = new ServerConnection(this, this._urls)));

this._disposables.push(
(this._accountAuthentication = new AccountAuthenticationProvider(this, this._connection)),
Expand Down Expand Up @@ -429,7 +431,7 @@ export class Container {
private _enrichments: EnrichmentService | undefined;
get enrichments(): EnrichmentService {
if (this._enrichments == null) {
this._disposables.push((this._enrichments = new EnrichmentService(this, new ServerConnection(this))));
this._disposables.push((this._enrichments = new EnrichmentService(this, this._connection)));
}

return this._enrichments;
Expand Down Expand Up @@ -686,6 +688,11 @@ export class Container {
return this._uri;
}

private readonly _urls: UrlsProvider;
get urls(): UrlsProvider {
return this._urls;
}

private readonly _usage: UsageTracker;
get usage(): UsageTracker {
return this._usage;
Expand Down Expand Up @@ -827,31 +834,6 @@ export class Container {
},
});
}

@memoize()
private get baseGkDevUri(): Uri {
if (this.env === 'staging') {
return Uri.parse('https://staging.gitkraken.dev');
}

if (this.env === 'dev') {
return Uri.parse('https://dev.gitkraken.dev');
}

return Uri.parse('https://gitkraken.dev');
}

getGkDevUri(path?: string, query?: string): Uri {
let uri = path != null ? Uri.joinPath(this.baseGkDevUri, path) : this.baseGkDevUri;
if (query != null) {
uri = uri.with({ query: query });
}
return uri;
}

generateWebGkDevUrl(path?: string): string {
return this.getGkDevUri(path, '?source=gitlens').toString();
}
}

export function isContainer(container: any): container is Container {
Expand Down
24 changes: 6 additions & 18 deletions src/plus/drafts/draftsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,20 +453,13 @@ export class DraftService implements Disposable {

let headers;
if (options?.providerAuth) {
headers = {
'Provider-Auth': Buffer.from(JSON.stringify(options.providerAuth)).toString('base64'),
};
headers = { 'Provider-Auth': Buffer.from(JSON.stringify(options.providerAuth)).toString('base64') };
}

const rsp = await this.connection.fetchGkApi(
'/v1/drafts',
{
method: 'GET',
headers: headers,
},
{
query: queryStrings.length ? queryStrings.join('&') : undefined,
},
{ method: 'GET', headers: headers },
{ query: queryStrings.length ? queryStrings.join('&') : undefined },
);

if (!rsp.ok) {
Expand Down Expand Up @@ -870,13 +863,8 @@ export class DraftService implements Disposable {
try {
const rsp = await this.connection.fetchGkApi(
'v1/drafts/counts',
{
method: 'POST',
body: body,
},
{
query: 'type=suggested_pr_change',
},
{ method: 'POST', body: body },
{ query: 'type=suggested_pr_change' },
);

if (!rsp.ok) {
Expand All @@ -896,7 +884,7 @@ export class DraftService implements Disposable {
generateWebUrl(draft: Draft): string;
generateWebUrl(draftOrDraftId: Draft | string): string {
const id = typeof draftOrDraftId === 'string' ? draftOrDraftId : draftOrDraftId.id;
return this.container.generateWebGkDevUrl(`/drafts/${id}`);
return this.container.urls.getGkDevUrl(['drafts', id]);
}
}

Expand Down
8 changes: 3 additions & 5 deletions src/plus/gk/authenticationConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ export class AuthenticationConnection implements Disposable {
Uri.parse(`${env.uriScheme}://${this.container.context.extension.id}/${AuthenticationUriPathPrefix}`),
);

const uri = this.container.getGkDevUri(
const url = this.container.urls.getGkDevUrl(
signUp ? 'register' : 'login',
`${scopes.includes('gitlens') ? 'source=gitlens&' : ''}${
context != null ? `context=${context}&` : ''
}state=${encodeURIComponent(gkstate)}&redirect_uri=${encodeURIComponent(callbackUri.toString(true))}`,
);

if (!(await openUrl(uri.toString(true)))) {
if (!(await openUrl(url))) {
Logger.error(undefined, scope, 'Opening login URL failed');

this._pendingStates.delete(scopeKey);
Expand Down Expand Up @@ -206,9 +206,7 @@ export class AuthenticationConnection implements Disposable {
state: state ?? '',
}),
},
{
unAuthenticated: true,
},
{ token: false },
);

if (!rsp.ok) {
Expand Down
112 changes: 44 additions & 68 deletions src/plus/gk/serverConnection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RequestError } from '@octokit/request-error';
import type { CancellationToken } from 'vscode';
import { version as codeVersion, env, Uri, window } from 'vscode';
import type { HeadersInit, RequestInfo, RequestInit, Response } from '@env/fetch';
import type { RequestInfo, RequestInit, Response } from '@env/fetch';
import { fetch as _fetch, getProxyAgent } from '@env/fetch';
import { getPlatform } from '@env/platform';
import type { Disposable } from '../../api/gitlens';
Expand All @@ -27,45 +27,27 @@ import { memoize } from '../../system/decorators/-webview/memoize';
import { Logger } from '../../system/logger';
import type { LogScope } from '../../system/logger.scope';
import { getLogScope } from '../../system/logger.scope';
import type { UrlsProvider } from './urlsProvider';

interface FetchOptions {
cancellation?: CancellationToken;
timeout?: number;
}

interface GKFetchOptions extends FetchOptions {
token?: string;
unAuthenticated?: boolean;
query?: string;
token?: string | false;
organizationId?: string | false;
query?: string;
}

export class ServerConnection implements Disposable {
constructor(private readonly container: Container) {}
constructor(
private readonly container: Container,
public readonly urls: UrlsProvider,
) {}

dispose(): void {}

@memoize()
private get baseGkApiUri(): Uri {
if (this.container.env === 'staging') {
return Uri.parse('https://staging-api.gitkraken.dev');
}

if (this.container.env === 'dev') {
return Uri.parse('https://dev-api.gitkraken.dev');
}

return Uri.parse('https://api.gitkraken.dev');
}

getGkApiUrl(...pathSegments: string[]): string {
return Uri.joinPath(this.baseGkApiUri, ...pathSegments).toString();
}

getGkConfigUrl(...pathSegments: string[]): string {
return Uri.joinPath(Uri.parse('https://configs.gitkraken.dev'), 'gitlens', ...pathSegments).toString();
}

@memoize()
get userAgent(): string {
// TODO@eamodio figure out standardized format/structure for our user agents
Expand Down Expand Up @@ -121,11 +103,11 @@ export class ServerConnection implements Disposable {
}

async fetchGkApi(path: string, init?: RequestInit, options?: GKFetchOptions): Promise<Response> {
return this.gkFetch(this.getGkApiUrl(path), init, options);
return this.gkFetch(this.urls.getGkApiUrl(path), init, options);
}

async fetchGkConfig(path: string, init?: RequestInit, options?: FetchOptions): Promise<Response> {
return this.fetch(this.getGkConfigUrl(path), init, options);
return this.fetch(this.urls.getGkConfigUrl(path), init, options);
}

async fetchGkApiGraphQL(
Expand All @@ -134,15 +116,34 @@ export class ServerConnection implements Disposable {
init?: RequestInit,
options?: GKFetchOptions,
): Promise<Response> {
return this.fetchGkApi(
path,
{
method: 'POST',
...init,
body: JSON.stringify(request),
},
options,
);
return this.fetchGkApi(path, { method: 'POST', ...init, body: JSON.stringify(request) }, options);
}

async getGkHeaders(
token?: string | false,
organizationId?: string | false,
init?: Record<string, string>,
): Promise<Record<string, string>> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'Client-Name': this.clientName,
'Client-Version': this.container.version,
'User-Agent': this.userAgent,
...init,
};

token ??= await this.getAccessToken();
if (token) {
headers.Authorization = `Bearer ${token}`;
}

// only check for cached subscription or we'll get into an infinite loop
organizationId ??= (await this.container.subscription.getSubscription(true)).activeOrganization?.id;
if (organizationId) {
headers['gk-org-id'] = organizationId;
}

return headers;
}

private async gkFetch(url: RequestInfo, init?: RequestInit, options?: GKFetchOptions): Promise<Response> {
Expand All @@ -152,29 +153,11 @@ export class ServerConnection implements Disposable {
const scope = getLogScope();

try {
let token;
({ token, ...options } = options ?? {});
if (!options?.unAuthenticated) {
token ??= await this.getAccessToken();
}

const headers: Record<string, unknown> = {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
'Client-Name': this.clientName,
'Client-Version': this.container.version,
...init?.headers,
};

// only check for cached subscription or we'll get into an infinite loop
let organizationId = options?.organizationId;
if (organizationId === undefined) {
organizationId = (await this.container.subscription.getSubscription(true)).activeOrganization?.id;
}

if (organizationId) {
headers['gk-org-id'] = organizationId;
}
const headers = await this.getGkHeaders(
options?.token,
options?.organizationId,
init?.headers ? { ...(init?.headers as Record<string, string>) } : undefined,
);

if (options?.query != null) {
if (url instanceof URL) {
Expand All @@ -184,14 +167,7 @@ export class ServerConnection implements Disposable {
}
}

const rsp = await this.fetch(
url,
{
...init,
headers: headers as HeadersInit,
},
options,
);
const rsp = await this.fetch(url, { ...init, headers: headers }, options);
if (!rsp.ok) {
await this.handleGkUnsuccessfulResponse(rsp, scope);
} else {
Expand Down
6 changes: 3 additions & 3 deletions src/plus/gk/subscriptionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,10 +635,10 @@ export class SubscriptionService implements Disposable {

try {
const exchangeToken = await this.container.accountAuthentication.getExchangeToken();
await openUrl(this.container.getGkDevUri('account', `token=${exchangeToken}`).toString(true));
await openUrl(this.container.urls.getGkDevUrl('account', `token=${exchangeToken}`));
} catch (ex) {
Logger.error(ex, scope);
await openUrl(this.container.getGkDevUri('account').toString(true));
await openUrl(this.container.urls.getGkDevUrl('account'));
}
}

Expand Down Expand Up @@ -950,7 +950,7 @@ export class SubscriptionService implements Disposable {
Logger.error(ex, scope);
}

aborted = !(await openUrl(this.container.getGkDevUri('purchase/checkout', query.toString()).toString(true)));
aborted = !(await openUrl(this.container.urls.getGkDevUrl('purchase/checkout', query)));

if (aborted) {
return;
Expand Down
Loading

0 comments on commit dee9185

Please sign in to comment.