integration: add the resolveUrl method
This commit is contained in:
@@ -73,4 +73,19 @@ describe('ScmIntegrations', () => {
|
||||
expect(i.byHost('github.local')).toBe(github);
|
||||
expect(i.byHost('gitlab.local')).toBe(gitlab);
|
||||
});
|
||||
|
||||
it('can resolveUrl using fallback', () => {
|
||||
expect(
|
||||
i.resolveUrl({
|
||||
url: '../b.yaml',
|
||||
base: 'https://no-matching-integration.com/x/a.yaml',
|
||||
}),
|
||||
).toBe('https://no-matching-integration.com/b.yaml');
|
||||
expect(
|
||||
i.resolveUrl({
|
||||
url: 'https://absolute.com/path',
|
||||
base: 'https://no-matching-integration.com/x/a.yaml',
|
||||
}),
|
||||
).toBe('https://absolute.com/path');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,4 +81,13 @@ export class ScmIntegrations implements ScmIntegrationRegistry {
|
||||
.map(i => i.byHost(host))
|
||||
.find(Boolean);
|
||||
}
|
||||
|
||||
resolveUrl(options: { url: string; base: string }): string {
|
||||
const resolve = this.byUrl(options.base)?.resolveUrl;
|
||||
if (!resolve) {
|
||||
return new URL(options.url, options.base).toString();
|
||||
}
|
||||
|
||||
return resolve(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,52 @@ describe('AzureIntegration', () => {
|
||||
expect(integration.type).toBe('azure');
|
||||
expect(integration.title).toBe('h.com');
|
||||
});
|
||||
|
||||
describe('resolveUrl', () => {
|
||||
it('works for valid urls', () => {
|
||||
const integration = new AzureIntegration({
|
||||
host: 'dev.azure.com',
|
||||
} as any);
|
||||
|
||||
expect(
|
||||
integration.resolveUrl({
|
||||
url: '../a.yaml',
|
||||
base:
|
||||
'https://dev.azure.com/organization/project/_git/repository?path=%2Ffolder%2Fcatalog-info.yaml',
|
||||
}),
|
||||
).toBe(
|
||||
'https://dev.azure.com/organization/project/_git/repository?path=%2Fa.yaml',
|
||||
);
|
||||
|
||||
expect(
|
||||
integration.resolveUrl({
|
||||
url: './a.yaml',
|
||||
base: 'https://dev.azure.com/organization/project/_git/repository',
|
||||
}),
|
||||
).toBe(
|
||||
'https://dev.azure.com/organization/project/_git/repository?path=%2Fa.yaml',
|
||||
);
|
||||
|
||||
expect(
|
||||
integration.resolveUrl({
|
||||
url: 'https://absolute.com/path',
|
||||
base:
|
||||
'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml',
|
||||
}),
|
||||
).toBe('https://absolute.com/path');
|
||||
});
|
||||
|
||||
it('falls back to regular URL resolution if not in a repo', () => {
|
||||
const integration = new AzureIntegration({
|
||||
host: 'dev.azure.com',
|
||||
} as any);
|
||||
|
||||
expect(
|
||||
integration.resolveUrl({
|
||||
url: './test',
|
||||
base: 'https://dev.azure.com/organization/project/_git',
|
||||
}),
|
||||
).toBe('https://dev.azure.com/organization/project/test');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import { basicIntegrations } from '../helpers';
|
||||
import { ScmIntegration, ScmIntegrationsFactory } from '../types';
|
||||
import { AzureIntegrationConfig, readAzureIntegrationConfigs } from './config';
|
||||
@@ -42,4 +43,39 @@ export class AzureIntegration implements ScmIntegration {
|
||||
get config(): AzureIntegrationConfig {
|
||||
return this.integrationConfig;
|
||||
}
|
||||
|
||||
/*
|
||||
* Azure repo URLs on the form with a `path` query param are treated specially.
|
||||
*
|
||||
* Example base URL: https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml
|
||||
*/
|
||||
resolveUrl(options: { url: string; base: string }): string {
|
||||
const { url, base } = options;
|
||||
|
||||
// If we can parse the url, it is absolute - then return it verbatim
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new URL(url);
|
||||
return url;
|
||||
} catch {
|
||||
// Ignore intentionally - looks like a relative path
|
||||
}
|
||||
|
||||
const parsed = parseGitUrl(base);
|
||||
const { organization, owner, name, filepath } = parsed;
|
||||
|
||||
// If not an actual file path within a repo, treat the URL as raw
|
||||
if (!organization || !owner || !name) {
|
||||
return new URL(url, base).toString();
|
||||
}
|
||||
|
||||
const path = filepath?.replace(/^\//, '') || '';
|
||||
const mockBaseUrl = new URL(`https://a.com/${path}`);
|
||||
const updatedPath = new URL(url, mockBaseUrl).pathname;
|
||||
|
||||
const newUrl = new URL(base);
|
||||
newUrl.searchParams.set('path', updatedPath);
|
||||
|
||||
return newUrl.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,18 @@ export interface ScmIntegration {
|
||||
* differentiate between different integrations.
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Works like the two-argument form of the URL constructor, resolving an
|
||||
* absolute or relative URL in relation to a base URL.
|
||||
*
|
||||
* If this method is not implemented, the URL constructor is used instead for
|
||||
* URLs that match this integration.
|
||||
*
|
||||
* @param options.url The (absolute or relative) URL or path to resolve
|
||||
* @param options.base The base URL onto which this resolution happens
|
||||
*/
|
||||
resolveUrl?(options: { url: string; base: string }): string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,6 +81,15 @@ export interface ScmIntegrationRegistry
|
||||
bitbucket: ScmIntegrationsGroup<BitbucketIntegration>;
|
||||
github: ScmIntegrationsGroup<GitHubIntegration>;
|
||||
gitlab: ScmIntegrationsGroup<GitLabIntegration>;
|
||||
|
||||
/**
|
||||
* Works like the two-argument form of the URL constructor, resolving an
|
||||
* absolute or relative URL in relation to a base URL.
|
||||
*
|
||||
* @param options.url The (absolute or relative) URL or path to resolve
|
||||
* @param options.base The base URL onto which this resolution happens
|
||||
*/
|
||||
resolveUrl(options: { url: string; base: string }): string;
|
||||
}
|
||||
|
||||
export type ScmIntegrationsFactory<T extends ScmIntegration> = (options: {
|
||||
|
||||
Reference in New Issue
Block a user