Do not expose Bitbucket internals

Signed-off-by: Mathias Åhsberg <mathias.ahsberg@resurs.se>
This commit is contained in:
Mathias Åhsberg
2021-04-29 14:20:41 +00:00
parent 34a4aa2c1e
commit 6e9d535c69
4 changed files with 93 additions and 129 deletions
@@ -17,21 +17,73 @@ import { getVoidLogger } from '@backstage/backend-common';
import { BitbucketDiscoveryProcessor } from './BitbucketDiscoveryProcessor';
import { ConfigReader } from '@backstage/config';
import { LocationSpec } from '@backstage/catalog-model';
import {
BitbucketClient,
BitbucketRepositoryParser,
PagedResponse,
} from './bitbucket';
import { BitbucketRepositoryParser, PagedResponse } from './bitbucket';
import { results } from './index';
import { RequestHandler, rest } from 'msw';
import { setupServer } from 'msw/node';
function pagedResponse(values: any): PagedResponse<any> {
return {
values: values,
isLastPage: true,
} as PagedResponse<any>;
const server = setupServer();
function setupStubs(projects: any[]) {
function pagedResponse(values: any): PagedResponse<any> {
return {
values: values,
isLastPage: true,
} as PagedResponse<any>;
}
function stubbedProject(
project: string,
repos: string[],
): RequestHandler<any, any> {
return rest.get(
`https://bitbucket.mycompany.com/api/rest/1.0/projects/${project}/repos`,
(_, res, ctx) => {
const response = [];
for (const repo of repos) {
response.push({
slug: repo,
links: {
self: [
{
href: `https://bitbucket.mycompany.com/projects/${project}/repos/${repo}/browse`,
},
],
},
});
}
return res(ctx.json(pagedResponse(response)));
},
);
}
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 };
}),
),
),
);
},
),
);
for (const project of projects) {
server.use(stubbedProject(project.key, project.repos));
}
}
describe('BitbucketDiscoveryProcessor', () => {
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
afterEach(() => jest.resetAllMocks());
describe('reject unrelated entries', () => {
@@ -81,58 +133,29 @@ describe('BitbucketDiscoveryProcessor', () => {
const processor = BitbucketDiscoveryProcessor.fromConfig(
new ConfigReader({
integrations: {
bitbucket: [{ host: 'bitbucket.mycompany.com', token: 'blob' }],
bitbucket: [
{
host: 'bitbucket.mycompany.com',
token: 'blob',
apiBaseUrl: 'https://bitbucket.mycompany.com/api/rest/1.0',
},
],
},
}),
{ logger: getVoidLogger() },
);
it('output all repositories', async () => {
setupStubs([
{ key: 'backstage', repos: ['backstage'] },
{ key: 'demo', repos: ['demo'] },
]);
const location: LocationSpec = {
type: 'bitbucket-discovery',
target:
'https://bitbucket.mycompany.com/projects/*/repos/*/catalog.yaml',
};
jest
.spyOn(BitbucketClient.prototype, 'listProjects')
.mockResolvedValue(
pagedResponse([{ key: 'backstage' }, { key: 'demo' }]),
);
jest
.spyOn(BitbucketClient.prototype, 'listRepositories')
.mockResolvedValueOnce(
pagedResponse([
{
slug: 'backstage',
links: {
self: [
{
href:
'https://bitbucket.mycompany.com/projects/backstage/repos/backstage/browse',
},
],
},
},
]),
);
jest
.spyOn(BitbucketClient.prototype, 'listRepositories')
.mockResolvedValueOnce(
pagedResponse([
{
slug: 'demo',
links: {
self: [
{
href:
'https://bitbucket.mycompany.com/projects/demo/repos/demo/browse',
},
],
},
},
]),
);
const emitter = jest.fn();
await processor.readLocation(location, false, emitter);
@@ -158,44 +181,16 @@ describe('BitbucketDiscoveryProcessor', () => {
});
it('output repositories with wildcards', async () => {
setupStubs([
{ key: 'backstage', repos: ['backstage', 'techdocs-cli'] },
{ key: 'demo', repos: ['demo'] },
]);
const location: LocationSpec = {
type: 'bitbucket-discovery',
target:
'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-*/catalog.yaml',
};
jest
.spyOn(BitbucketClient.prototype, 'listProjects')
.mockResolvedValue(pagedResponse([{ key: 'backstage' }]));
jest
.spyOn(BitbucketClient.prototype, 'listRepositories')
.mockResolvedValueOnce(
pagedResponse([
{ slug: 'backstage' },
{
slug: 'techdocs-cli',
links: {
self: [
{
href:
'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-cli/browse',
},
],
},
},
{
slug: 'techdocs-container',
links: {
self: [
{
href:
'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-container/browse',
},
],
},
},
]),
);
const emitter = jest.fn();
await processor.readLocation(location, false, emitter);
@@ -208,46 +203,15 @@ describe('BitbucketDiscoveryProcessor', () => {
},
optional: true,
});
expect(emitter).toHaveBeenCalledWith({
type: 'location',
location: {
type: 'url',
target:
'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-container/browse/catalog.yaml',
},
optional: true,
});
});
it('filter unrelated repositories', async () => {
setupStubs([{ key: 'backstage', repos: ['test', 'abctest', 'testxyz'] }]);
const location: LocationSpec = {
type: 'bitbucket-discovery',
target:
'https://bitbucket.mycompany.com/projects/backstage/repos/test/catalog.yaml',
};
jest
.spyOn(BitbucketClient.prototype, 'listProjects')
.mockResolvedValue(pagedResponse([{ key: 'backstage' }]));
jest
.spyOn(BitbucketClient.prototype, 'listRepositories')
.mockResolvedValue(
pagedResponse([
{ slug: 'abstest' },
{ slug: 'testxyz' },
{
slug: 'test',
links: {
self: [
{
href:
'https://bitbucket.mycompany.com/projects/backstage/repos/test',
},
],
},
},
]),
);
const emitter = jest.fn();
await processor.readLocation(location, false, emitter);
@@ -256,7 +220,7 @@ describe('BitbucketDiscoveryProcessor', () => {
location: {
type: 'url',
target:
'https://bitbucket.mycompany.com/projects/backstage/repos/test/catalog.yaml',
'https://bitbucket.mycompany.com/projects/backstage/repos/test/browse/catalog.yaml',
},
optional: true,
});
@@ -277,26 +241,27 @@ describe('BitbucketDiscoveryProcessor', () => {
const processor = BitbucketDiscoveryProcessor.fromConfig(
new ConfigReader({
integrations: {
bitbucket: [{ host: 'bitbucket.mycompany.com', token: 'blob' }],
bitbucket: [
{
host: 'bitbucket.mycompany.com',
token: 'blob',
apiBaseUrl: 'https://bitbucket.mycompany.com/api/rest/1.0',
},
],
},
}),
{ parser: customRepositoryParser, logger: getVoidLogger() },
);
it('use custom repository parser', async () => {
setupStubs([{ key: 'backstage', repos: ['test'] }]);
const location: LocationSpec = {
type: 'bitbucket-discovery',
target:
'https://bitbucket.mycompany.com/projects/backstage/repos/test/catalog.yaml',
};
jest
.spyOn(BitbucketClient.prototype, 'listProjects')
.mockResolvedValue(pagedResponse([{ key: 'backstage' }]));
jest
.spyOn(BitbucketClient.prototype, 'listRepositories')
.mockResolvedValue(pagedResponse([{ slug: 'test' }]));
const emitter = jest.fn();
await processor.readLocation(location, false, emitter);
@@ -90,7 +90,7 @@ export class BitbucketDiscoveryProcessor implements CatalogProcessor {
for (const repository of result.matches) {
for await (const entity of this.parser({
integration: integration,
target: `${repository.links.self[0]}${catalogPath}`,
target: `${repository.links.self[0].href}${catalogPath}`,
logger: this.logger,
})) {
emit(entity);
@@ -14,7 +14,7 @@
* limitations under the License.
*/
export { BitbucketClient, paginated } from './client';
export type { PagedResponse } from './client';
export * from './types';
export type { BitbucketRepositoryParser } from './BitbucketRepositoryParser';
export { defaultRepositoryParser } from './BitbucketRepositoryParser';
export type { PagedResponse } from './client';
export type { BitbucketRepository } from './types';
export type { BitbucketRepositoryParser } from './BitbucketRepositoryParser';
@@ -36,5 +36,4 @@ export { UrlReaderProcessor } from './UrlReaderProcessor';
export { parseEntityYaml } from './util/parse';
export { results };
export { BitbucketClient } from './bitbucket';
export type { BitbucketRepositoryParser, Bitbucket } from './bitbucket';
export type { BitbucketRepositoryParser } from './bitbucket';