Merge pull request #8815 from dhruvaldarji/dhruvaldarji/add-baseurl-support-for-bitbucket-discovery-processor
fix(catalog-backend): support bitbucket servers with custom baseUrls
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Provide support for Bitbucket servers with custom BaseURLs.
|
||||
+211
-16
@@ -29,7 +29,10 @@ import { setupServer } from 'msw/node';
|
||||
|
||||
const server = setupServer();
|
||||
|
||||
function setupStubs(projects: any[]) {
|
||||
function setupStubs(
|
||||
projects: any[],
|
||||
bitbucketBaseUrl = `https://bitbucket.mycompany.com`,
|
||||
) {
|
||||
function pagedResponse(values: any): PagedResponse<any> {
|
||||
return {
|
||||
values: values,
|
||||
@@ -42,7 +45,7 @@ function setupStubs(projects: any[]) {
|
||||
repos: string[],
|
||||
): RequestHandler<any, any> {
|
||||
return rest.get(
|
||||
`https://bitbucket.mycompany.com/api/rest/1.0/projects/${project}/repos`,
|
||||
`${bitbucketBaseUrl}/api/rest/1.0/projects/${project}/repos`,
|
||||
(_, res, ctx) => {
|
||||
const response = [];
|
||||
for (const repo of repos) {
|
||||
@@ -51,7 +54,7 @@ function setupStubs(projects: any[]) {
|
||||
links: {
|
||||
self: [
|
||||
{
|
||||
href: `https://bitbucket.mycompany.com/projects/${project}/repos/${repo}/browse`,
|
||||
href: `${bitbucketBaseUrl}/projects/${project}/repos/${repo}/browse`,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -63,20 +66,17 @@ function setupStubs(projects: any[]) {
|
||||
}
|
||||
|
||||
server.use(
|
||||
rest.get(
|
||||
`https://bitbucket.mycompany.com/api/rest/1.0/projects`,
|
||||
(_, res, ctx) => {
|
||||
return res(
|
||||
ctx.json(
|
||||
pagedResponse(
|
||||
projects.map(p => {
|
||||
return { key: p.key };
|
||||
}),
|
||||
),
|
||||
rest.get(`${bitbucketBaseUrl}/api/rest/1.0/projects`, (_, res, ctx) => {
|
||||
return res(
|
||||
ctx.json(
|
||||
pagedResponse(
|
||||
projects.map(p => {
|
||||
return { key: p.key };
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
for (const project of projects) {
|
||||
@@ -249,6 +249,30 @@ describe('BitbucketDiscoveryProcessor', () => {
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('output repositories by target search ref', async () => {
|
||||
setupStubs([{ key: 'demo', repos: ['demo'] }]);
|
||||
const location: LocationSpec = {
|
||||
type: 'bitbucket-discovery',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/projects/demo/repos/demo/catalog.yaml?ref=branch-name',
|
||||
};
|
||||
|
||||
const emitter = jest.fn();
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/projects/demo/repos/demo/browse/catalog.yaml?ref=branch-name',
|
||||
presence: 'optional',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('filter unrelated repositories', async () => {
|
||||
setupStubs([{ key: 'backstage', repos: ['test', 'abctest', 'testxyz'] }]);
|
||||
const location: LocationSpec = {
|
||||
@@ -302,6 +326,177 @@ describe('BitbucketDiscoveryProcessor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('handles organisation repositories with a custom baseURL', () => {
|
||||
const processor = BitbucketDiscoveryProcessor.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
bitbucket: [
|
||||
{
|
||||
host: 'bitbucket.mycompany.com',
|
||||
token: 'blob',
|
||||
apiBaseUrl:
|
||||
'https://bitbucket.mycompany.com/custom-path/api/rest/1.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
{ logger: getVoidLogger() },
|
||||
);
|
||||
|
||||
it('output all repositories', async () => {
|
||||
setupStubs(
|
||||
[
|
||||
{ key: 'backstage', repos: ['backstage'] },
|
||||
{ key: 'demo', repos: ['demo'] },
|
||||
],
|
||||
'https://bitbucket.mycompany.com/custom-path',
|
||||
);
|
||||
const location: LocationSpec = {
|
||||
type: 'bitbucket-discovery',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/*/repos/*/catalog.yaml',
|
||||
};
|
||||
|
||||
const emitter = jest.fn();
|
||||
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/backstage/browse/catalog.yaml',
|
||||
presence: 'optional',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/demo/repos/demo/browse/catalog.yaml',
|
||||
presence: 'optional',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('output repositories with wildcards', async () => {
|
||||
setupStubs(
|
||||
[
|
||||
{ key: 'backstage', repos: ['backstage', 'techdocs-cli'] },
|
||||
{ key: 'demo', repos: ['demo'] },
|
||||
],
|
||||
'https://bitbucket.mycompany.com/custom-path',
|
||||
);
|
||||
const location: LocationSpec = {
|
||||
type: 'bitbucket-discovery',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/techdocs-*/catalog.yaml',
|
||||
};
|
||||
|
||||
const emitter = jest.fn();
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/techdocs-cli/browse/catalog.yaml',
|
||||
presence: 'optional',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('output repositories by target search ref', async () => {
|
||||
setupStubs(
|
||||
[{ key: 'demo', repos: ['demo'] }],
|
||||
'https://bitbucket.mycompany.com/custom-path',
|
||||
);
|
||||
const location: LocationSpec = {
|
||||
type: 'bitbucket-discovery',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/demo/repos/demo/catalog.yaml?ref=branch-name',
|
||||
};
|
||||
|
||||
const emitter = jest.fn();
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/demo/repos/demo/browse/catalog.yaml?ref=branch-name',
|
||||
presence: 'optional',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('filter unrelated repositories', async () => {
|
||||
setupStubs(
|
||||
[{ key: 'backstage', repos: ['test', 'abctest', 'testxyz'] }],
|
||||
'https://bitbucket.mycompany.com/custom-path',
|
||||
);
|
||||
const location: LocationSpec = {
|
||||
type: 'bitbucket-discovery',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/test/catalog.yaml',
|
||||
};
|
||||
|
||||
const emitter = jest.fn();
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/test/browse/catalog.yaml',
|
||||
presence: 'optional',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it.each`
|
||||
target
|
||||
${'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/*'}
|
||||
${'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/*/'}
|
||||
${'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/techdocs-*/'}
|
||||
`("target '$target' adds default path to catalog", async ({ target }) => {
|
||||
setupStubs(
|
||||
[{ key: 'backstage', repos: ['techdocs-cli'] }],
|
||||
'https://bitbucket.mycompany.com/custom-path',
|
||||
);
|
||||
|
||||
const location: LocationSpec = {
|
||||
type: 'bitbucket-discovery',
|
||||
target: target,
|
||||
};
|
||||
|
||||
const emitter = jest.fn();
|
||||
await processor.readLocation(location, false, emitter);
|
||||
|
||||
expect(emitter).toHaveBeenCalledTimes(1);
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
type: 'location',
|
||||
location: {
|
||||
type: 'url',
|
||||
target:
|
||||
'https://bitbucket.mycompany.com/custom-path/projects/backstage/repos/techdocs-cli/browse/catalog-info.yaml',
|
||||
presence: 'optional',
|
||||
},
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handles cloud repositories', () => {
|
||||
const processor = BitbucketDiscoveryProcessor.fromConfig(
|
||||
new ConfigReader({
|
||||
|
||||
@@ -230,14 +230,18 @@ function parseUrl(urlString: string): {
|
||||
catalogPath: string;
|
||||
} {
|
||||
const url = new URL(urlString);
|
||||
const path = url.pathname.substr(1).split('/');
|
||||
const indexOfProjectSegment =
|
||||
url.pathname.toLowerCase().indexOf('/projects/') + 1;
|
||||
const path = url.pathname.substr(indexOfProjectSegment).split('/');
|
||||
|
||||
// /projects/backstage/repos/techdocs-*/catalog-info.yaml
|
||||
if (path.length > 3 && path[1].length && path[3].length) {
|
||||
return {
|
||||
projectSearchPath: escapeRegExp(decodeURIComponent(path[1])),
|
||||
repoSearchPath: escapeRegExp(decodeURIComponent(path[3])),
|
||||
catalogPath: `/${decodeURIComponent(path.slice(4).join('/'))}`,
|
||||
catalogPath: `/${decodeURIComponent(
|
||||
path.slice(4).join('/') + url.search,
|
||||
)}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user