diff --git a/packages/backend-common/src/reading/GitlabUrlReader.test.ts b/packages/backend-common/src/reading/GitlabUrlReader.test.ts index 60a8d19db3..09da9c4e0a 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.test.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.test.ts @@ -14,139 +14,109 @@ * limitations under the License. */ -import { GitlabUrlReader, readConfig } from './GitlabUrlReader'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '../logging'; +import { GitlabUrlReader } from './GitlabUrlReader'; + +const logger = getVoidLogger(); describe('GitlabUrlReader', () => { - const createConfig = (token: string | undefined) => - ConfigReader.fromConfigs([ - { - context: '', - data: { - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: token, - }, - ], - }, - }, - }, - ]); + const worker = setupServer(); - it('should build project urls', () => { - const processor = new GitlabUrlReader( - readConfig(createConfig(undefined))[0], + beforeAll(() => worker.listen({ onUnhandledRequest: 'error' })); + afterAll(() => worker.close()); + + beforeEach(() => { + worker.use( + rest.get('*/api/v4/projects/:name', (_, res, ctx) => + res(ctx.status(200), ctx.json({ id: 12345 })), + ), + rest.get('*', (req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + url: req.url.toString(), + headers: req.headers.getAllHeaders(), + }), + ), + ), + ); + }); + afterEach(() => worker.resetHandlers()); + + const createConfig = (token?: string) => + new ConfigReader( + { + integrations: { gitlab: [{ host: 'gitlab.com', token }] }, + }, + 'test-config', ); - const tests = [ - { - target: - 'https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml', - url: new URL( + it.each([ + // Project URLs + { + url: + 'https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml', + config: createConfig(), + response: expect.objectContaining({ + url: 'https://gitlab.com/api/v4/projects/12345/repository/files/my%2Fpath%2Fto%2Ffile.yaml/raw?ref=branch', - ), - err: undefined, - }, - { - target: - 'https://gitlab.example.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml', - url: new URL( + headers: expect.objectContaining({ + 'private-token': '', + }), + }), + }, + { + url: + 'https://gitlab.example.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml', + config: createConfig('0123456789'), + response: expect.objectContaining({ + url: 'https://gitlab.example.com/api/v4/projects/12345/repository/files/my%2Fpath%2Fto%2Ffile.yaml/raw?ref=branch', - ), - err: undefined, - }, - { - target: - 'https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/to/file.yaml', // Repo not in subgroup - url: new URL( + headers: expect.objectContaining({ + 'private-token': '0123456789', + }), + }), + }, + { + url: + 'https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/to/file.yaml', // Repo not in subgroup + config: createConfig(), + response: expect.objectContaining({ + url: 'https://gitlab.com/api/v4/projects/12345/repository/files/my%2Fpath%2Fto%2Ffile.yaml/raw?ref=branch', - ), - err: undefined, - }, - ]; + }), + }, - for (const test of tests) { - if (test.url) { - expect( - processor.buildProjectUrl(test.target, 12345).toString(), - ).toEqual(test.url.toString()); - } else { - throw new Error( - 'This should not have happened. Either err or url should have matched.', - ); - } - } + // Raw URLs + { + url: 'https://gitlab.example.com/a/b/blob/master/c.yaml', + config: createConfig(), + response: expect.objectContaining({ + url: 'https://gitlab.example.com/a/b/raw/master/c.yaml', + }), + }, + ])('should handle happy path %#', async ({ url, config, response }) => { + const [{ reader }] = GitlabUrlReader.factory({ config, logger }); + + const data = await reader.read(url); + const res = await JSON.parse(data.toString('utf-8')); + expect(res).toEqual(response); }); - it('should build raw urls', () => { - const processor = new GitlabUrlReader( - readConfig(createConfig(undefined))[0], - ); - - const tests = [ - { - target: 'https://gitlab.example.com/a/b/blob/master/c.yaml', - url: new URL('https://gitlab.example.com/a/b/raw/master/c.yaml'), - err: undefined, - }, - ]; - - for (const test of tests) { - if (test.url) { - expect(processor.buildRawUrl(test.target).toString()).toEqual( - test.url.toString(), - ); - } else { - throw new Error( - 'This should not have happened. Either err or url should have matched.', - ); - } - } - }); - - it('should return request options', () => { - const tests = [ - { - token: '0123456789', - expect: { - headers: { - 'PRIVATE-TOKEN': '0123456789', - }, - }, - }, - { - token: '', - err: - "Invalid type in config for key 'integrations.gitlab[0].token' in '', got empty-string, wanted string", - expect: { - headers: { - 'PRIVATE-TOKEN': '', - }, - }, - }, - { - token: undefined, - expect: { - headers: { - 'PRIVATE-TOKEN': '', - }, - }, - }, - ]; - - for (const test of tests) { - if (test.err) { - expect( - () => new GitlabUrlReader(readConfig(createConfig(test.token))[0]), - ).toThrowError(test.err); - } else { - const processor = new GitlabUrlReader( - readConfig(createConfig(test.token))[0], - ); - expect(processor.getRequestOptions()).toEqual(test.expect); - } - } + it.each([ + { + url: '', + config: createConfig(''), + error: + "Invalid type in config for key 'integrations.gitlab[0].token' in 'test-config', got empty-string, wanted string", + }, + ])('should handle error path %#', async ({ url, config, error }) => { + await expect(async () => { + const [{ reader }] = GitlabUrlReader.factory({ config, logger }); + await reader.read(url); + }).rejects.toThrow(error); }); }); diff --git a/packages/backend-common/src/reading/GitlabUrlReader.ts b/packages/backend-common/src/reading/GitlabUrlReader.ts index 5b4708a188..a6ffef0a34 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.ts @@ -26,7 +26,7 @@ type Options = { token?: string; }; -export function readConfig(config: Config): Options[] { +function readConfig(config: Config): Options[] { const optionsArr = Array(); const providerConfigs = @@ -59,14 +59,6 @@ export class GitlabUrlReader implements UrlReader { constructor(private readonly options: Options) {} - getRequestOptions(): RequestInit { - return { - headers: { - ['PRIVATE-TOKEN']: this.options.token ?? '', - }, - }; - } - async read(url: string): Promise { // TODO(Rugvip): merged the old GitlabReaderProcessor in here and used // the existence of /~/blob/ to switch the logic. Don't know if this @@ -100,19 +92,23 @@ export class GitlabUrlReader implements UrlReader { // Converts // from: https://gitlab.example.com/a/b/blob/master/c.yaml // to: https://gitlab.example.com/a/b/raw/master/c.yaml - buildRawUrl(target: string): URL { + private buildRawUrl(target: string): URL { try { const url = new URL(target); - const [empty, userOrOrg, repoName, ...restOfPath] = url.pathname - .split('/') - // for the common case https://gitlab.example.com/a/b/-/blob/master/c.yaml - .filter(path => path !== '-'); + const [ + empty, + userOrOrg, + repoName, + blobKeyword, + ...restOfPath + ] = url.pathname.split('/'); if ( empty !== '' || userOrOrg === '' || repoName === '' || + blobKeyword !== 'blob' || !restOfPath.join('/').match(/\.yaml$/) ) { throw new Error('Wrong GitLab URL'); @@ -131,7 +127,7 @@ export class GitlabUrlReader implements UrlReader { // convert https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/filepath // to https://gitlab.com/api/v4/projects//repository/files/filepath?ref=branch - buildProjectUrl(target: string, projectID: Number): URL { + private buildProjectUrl(target: string, projectID: Number): URL { try { const url = new URL(target); @@ -154,7 +150,7 @@ export class GitlabUrlReader implements UrlReader { } } - async getProjectID(target: string): Promise { + private async getProjectID(target: string): Promise { const url = new URL(target); if ( @@ -188,6 +184,14 @@ export class GitlabUrlReader implements UrlReader { } } + private getRequestOptions(): RequestInit { + return { + headers: { + ['PRIVATE-TOKEN']: this.options.token ?? '', + }, + }; + } + toString() { const { host, token } = this.options; return `gitlab{host=${host},authed=${Boolean(token)}}`;