diff --git a/.changeset/dry-scissors-dream.md b/.changeset/dry-scissors-dream.md new file mode 100644 index 0000000000..09965f0f59 --- /dev/null +++ b/.changeset/dry-scissors-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': minor +--- + +added the possibility to handle raw Gitlab URLs with nested namespaces diff --git a/packages/integration/package.json b/packages/integration/package.json index fbb5076ea5..3f7ce5d219 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -34,6 +34,7 @@ }, "dependencies": { "@backstage/config": "^1.0.1-next.0", + "@backstage/errors": "^1.0.0", "cross-fetch": "^3.1.5", "git-url-parse": "^11.6.0", "@octokit/rest": "^18.5.3", diff --git a/packages/integration/src/gitlab/core.test.ts b/packages/integration/src/gitlab/core.test.ts index 4f39fb2fe7..d01406fe1c 100644 --- a/packages/integration/src/gitlab/core.test.ts +++ b/packages/integration/src/gitlab/core.test.ts @@ -87,6 +87,26 @@ describe('gitlab core', () => { url: 'https://gitlab.example.com/a/b/blob/master/c.yaml', result: 'https://gitlab.example.com/a/b/raw/master/c.yaml', }, + { + config: configWithNoToken, + url: 'https://gitlab.example.com/a/b/repo/blob/master/c.yaml', + result: 'https://gitlab.example.com/a/b/repo/raw/master/c.yaml', + }, + { + config: configWithNoToken, + url: 'https://gitlab.example.com/a/blob/blob/master/c.yaml', + result: 'https://gitlab.example.com/a/blob/raw/master/c.yaml', + }, + { + config: configWithNoToken, + url: 'https://gitlab.example.com/a/b/blob/blob/c.yaml', + result: 'https://gitlab.example.com/a/b/raw/blob/c.yaml', + }, + { + config: configWithNoToken, + url: 'https://gitlab.example.com/a//blob/blob/c.yaml', + result: 'https://gitlab.example.com/a/blob/raw/c.yaml', + }, ])('should handle happy path %#', async ({ config, url, result }) => { await expect(getGitLabFileFetchUrl(url, config)).resolves.toBe(result); }); diff --git a/packages/integration/src/gitlab/core.ts b/packages/integration/src/gitlab/core.ts index b379d3d129..ba0f9c83de 100644 --- a/packages/integration/src/gitlab/core.ts +++ b/packages/integration/src/gitlab/core.ts @@ -16,6 +16,7 @@ import { GitLabIntegrationConfig } from './config'; import fetch from 'cross-fetch'; +import { InputError } from '@backstage/errors'; /** * Given a URL pointing to a file on a provider, returns a URL that is suitable @@ -66,31 +67,34 @@ export function getGitLabRequestOptions(config: GitLabIntegrationConfig): { } // Converts -// from: https://gitlab.example.com/a/b/blob/master/c.yaml -// to: https://gitlab.example.com/a/b/raw/master/c.yaml +// from: https://gitlab.example.com/groupA/teams/repoA/blob/master/c.yaml +// to: https://gitlab.example.com/groupA/teams/repoA/raw/master/c.yaml export function buildRawUrl(target: string): URL { try { const url = new URL(target); - const [empty, userOrOrg, repoName, blobKeyword, ...restOfPath] = - url.pathname.split('/'); + const splitPath = url.pathname.split('/').filter(Boolean); - if ( - empty !== '' || - userOrOrg === '' || - repoName === '' || - blobKeyword !== 'blob' || - !restOfPath.join('/').match(/\.(yaml|yml)$/) - ) { - throw new Error('Wrong GitLab URL'); + // Check blob existence + const blobIndex = splitPath.indexOf('blob', 2); + if (blobIndex < 2 || blobIndex === splitPath.length - 1) { + throw new InputError('Wrong GitLab URL'); + } + + // Take repo path + const repoPath = splitPath.slice(0, blobIndex); + const restOfPath = splitPath.slice(blobIndex + 1); + + if (!restOfPath.join('/').match(/\.(yaml|yml)$/)) { + throw new InputError('Wrong GitLab URL'); } // Replace 'blob' with 'raw' - url.pathname = [empty, userOrOrg, repoName, 'raw', ...restOfPath].join('/'); + url.pathname = [...repoPath, 'raw', ...restOfPath].join('/'); return url; } catch (e) { - throw new Error(`Incorrect url: ${target}, ${e}`); + throw new InputError(`Incorrect url: ${target}, ${e}`); } }