Implement readTree on BitBucketUrlReader

This commit is contained in:
Kim S. Ly
2020-12-07 21:16:01 -05:00
parent 43e1277796
commit ea6f833167
4 changed files with 105 additions and 20 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': minor
---
Implement readTree on BitBucketUrlReader
@@ -47,7 +47,12 @@ describe('BitbucketUrlReader', () => {
msw.setupDefaultHandlers(worker);
const repoBuffer = fs.readFileSync(
path.resolve('src', 'reading', '__fixtures__', 'repo.zip'),
path.resolve(
'src',
'reading',
'__fixtures__',
'bitbucket-repo-with-commit-hash.zip',
),
);
it('returns the wanted files from an archive', async () => {
@@ -61,10 +66,20 @@ describe('BitbucketUrlReader', () => {
ctx.body(repoBuffer),
),
),
rest.get(
'https://api.bitbucket.org/2.0/repositories/backstage/mock/commits/master',
(_, res, ctx) =>
res(
ctx.status(200),
ctx.json({
values: [{ hash: '12ab34cd56ef78gh90ij12kl34mn56op78qr90st' }],
}),
),
),
);
const processor = new BitbucketUrlReader(
{ host: 'bitbucket.org', apiBaseUrl: 'x' },
{ host: 'bitbucket.org', apiBaseUrl: 'https://api.bitbucket.org/2.0' },
{ treeResponseFactory },
);
@@ -75,17 +90,17 @@ describe('BitbucketUrlReader', () => {
const files = await response.files();
expect(files.length).toBe(2);
const mkDocsFile = await files[0].content();
const indexMarkdownFile = await files[1].content();
const indexMarkdownFile = await files[0].content();
const mkDocsFile = await files[1].content();
expect(mkDocsFile.toString()).toBe('site_name: Test\n');
expect(indexMarkdownFile.toString()).toBe('# Test\n');
expect(mkDocsFile.toString()).toBe('site_name: Test\n');
});
it('uses private bitbucket host', async () => {
worker.use(
rest.get(
'https://bitbucket.mycompany.net/projects/a/repos/b/archive?format=tgz',
'https://bitbucket.mycompany.net/projects/backstage/repos/mock/archive?format=tgz',
(_, res, ctx) =>
res(
ctx.status(200),
@@ -93,15 +108,28 @@ describe('BitbucketUrlReader', () => {
ctx.body(repoBuffer),
),
),
rest.get(
'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/commits/?until=master',
(_, res, ctx) =>
res(
ctx.status(200),
ctx.json({
values: [{ id: '12ab34cd56ef78gh90ij12kl34mn56op78qr90st' }],
}),
),
),
);
const processor = new BitbucketUrlReader(
{ host: 'bitbucket.mycompany.net', apiBaseUrl: 'x' },
{
host: 'bitbucket.mycompany.net',
apiBaseUrl: 'https://api.bitbucket.mycompany.net/rest/api/1.0',
},
{ treeResponseFactory },
);
const response = await processor.readTree(
'https://bitbucket.mycompany.net/projects/a/repos/b/browse/docs',
'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs',
);
const files = await response.files();
@@ -114,7 +142,7 @@ describe('BitbucketUrlReader', () => {
it('must specify a branch', async () => {
const processor = new BitbucketUrlReader(
{ host: 'bitbucket.org', apiBaseUrl: 'x' },
{ host: 'bitbucket.org', apiBaseUrl: 'https://api.bitbucket.org/2.0' },
{ treeResponseFactory },
);
@@ -136,10 +164,20 @@ describe('BitbucketUrlReader', () => {
ctx.body(repoBuffer),
),
),
rest.get(
'https://api.bitbucket.org/2.0/repositories/backstage/mock/commits/master',
(_, res, ctx) =>
res(
ctx.status(200),
ctx.json({
values: [{ hash: '12ab34cd56ef78gh90ij12kl34mn56op78qr90st' }],
}),
),
),
);
const processor = new BitbucketUrlReader(
{ host: 'bitbucket.org', apiBaseUrl: 'x' },
{ host: 'bitbucket.org', apiBaseUrl: 'https://api.bitbucket.org/2.0' },
{ treeResponseFactory },
);
@@ -99,14 +99,8 @@ export class BitbucketUrlReader implements UrlReader {
url: string,
options?: ReadTreeOptions,
): Promise<ReadTreeResponse> {
const {
name: repoName,
owner,
ref,
protocol,
resource,
// filepath,
} = parseGitUri(url);
const gitUrl: parseGitUri.GitUrl = parseGitUri(url);
const { name: repoName, owner, ref, protocol, resource, filepath } = gitUrl;
const isHosted = resource === 'bitbucket.org';
@@ -130,10 +124,11 @@ export class BitbucketUrlReader implements UrlReader {
throw new Error(message);
}
const lastCommitShortHash = await this.getLastCommitShortHash(gitUrl);
return this.treeResponseFactory.fromZipArchive({
stream: (response.body as unknown) as Readable,
// TODO: The zip contains the commit hash, not branch name - may need additional api call to get it
// path: `${owner}-${repoName}-4f9778cd49a4/${filepath}`,
path: `${owner}-${repoName}-${lastCommitShortHash}/${filepath}`,
filter: options?.filter,
});
}
@@ -146,4 +141,51 @@ export class BitbucketUrlReader implements UrlReader {
}
return `bitbucket{host=${host},authed=${authed}}`;
}
private async getLastCommitShortHash(
gitUrl: parseGitUri.GitUrl,
): Promise<String> {
const { name: repoName, owner, ref, protocol, resource } = gitUrl;
const isHosted = resource === 'bitbucket.org';
const branch = ref ? ref : 'master';
const commitsApiUrl = isHosted
? `${protocol}://${this.config.apiBaseUrl}/repositories/${owner}/${repoName}/commits/${branch}`
: `${protocol}://${this.config.apiBaseUrl}/projects/${owner}/repos/${repoName}/commits/?until=${branch}`;
const commitsResponse = await fetch(
commitsApiUrl,
getApiRequestOptions(this.config),
);
if (!commitsResponse.ok) {
const message = `Failed to retrieve commits from ${commitsApiUrl}, ${commitsResponse.status} ${commitsResponse.statusText}`;
if (commitsResponse.status === 404) {
throw new NotFoundError(message);
}
throw new Error(message);
}
const commits = await commitsResponse.json();
if (isHosted) {
if (
commits &&
commits.values &&
commits.values.length > 0 &&
commits.values[0].hash
) {
return commits.values[0].hash.substring(0, 12);
}
} else {
if (
commits &&
commits.values &&
commits.values.length > 0 &&
commits.values[0].id
) {
return commits.values[0].id.substring(0, 12);
}
}
throw new Error(`Failed to read response from ${commitsApiUrl}`);
}
}