feat(bitbucket): ensure apiBaseUrl, replace hardcoded cases

Ensure presence of apiBaseUrl for bitbucket
integrations for both cases Bitbucket Cloud and
Bitbucket Server by setting the default for
Bitbucket Server at the integration config, too.

Replace hardcoded uses of the default apiBaseUrl
with the use of the integration config's value.

Signed-off-by: Patrick Jungermann <Patrick.Jungermann@gmail.com>
This commit is contained in:
Patrick Jungermann
2022-02-24 12:45:04 +01:00
parent dd2e1eb59a
commit 34af86517c
7 changed files with 33 additions and 39 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/integration': minor
'@backstage/backend-common': patch
'@backstage/plugin-scaffolder-backend': patch
---
ensure `apiBaseUrl` being set for Bitbucket integrations, replace hardcoded defaults
@@ -270,22 +270,6 @@ describe('BitbucketUrlReader', () => {
expect(response.etag).toBe('12ab34cd56ef');
});
it('should throw error when apiBaseUrl is missing', () => {
expect(() => {
/* eslint-disable no-new */
new BitbucketUrlReader(
new BitbucketIntegration(
readBitbucketIntegrationConfig(
new ConfigReader({
host: 'bitbucket.mycompany.net',
}),
),
),
{ treeResponseFactory },
);
}).toThrowError('must configure an explicit apiBaseUrl');
});
});
describe('search hosted', () => {
@@ -62,14 +62,9 @@ export class BitbucketUrlReader implements UrlReader {
private readonly integration: BitbucketIntegration,
private readonly deps: { treeResponseFactory: ReadTreeResponseFactory },
) {
const { host, apiBaseUrl, token, username, appPassword } =
integration.config;
const { host, token, username, appPassword } = integration.config;
if (!apiBaseUrl) {
throw new Error(
`Bitbucket integration for '${host}' must configure an explicit apiBaseUrl`,
);
} else if (!token && username && !appPassword) {
if (!token && username && !appPassword) {
throw new Error(
`Bitbucket integration for '${host}' has configured a username but is missing a required appPassword.`,
);
+1 -1
View File
@@ -88,7 +88,7 @@ export class BitbucketIntegration implements ScmIntegration {
// @public
export type BitbucketIntegrationConfig = {
host: string;
apiBaseUrl?: string;
apiBaseUrl: string;
token?: string;
username?: string;
appPassword?: string;
+5 -5
View File
@@ -36,12 +36,10 @@ export type BitbucketIntegrationConfig = {
* The base URL of the API of this provider, e.g. "https://api.bitbucket.org/2.0",
* with no trailing slash.
*
* May be omitted specifically for Bitbucket Cloud; then it will be deduced.
*
* The API will always be preferred if both its base URL and a token are
* present.
* Values omitted at the optional property at the app-config will be deduced
* from the "host" value.
*/
apiBaseUrl?: string;
apiBaseUrl: string;
/**
* The authorization token to use for requests to a Bitbucket Server provider.
@@ -90,6 +88,8 @@ export function readBitbucketIntegrationConfig(
apiBaseUrl = trimEnd(apiBaseUrl, '/');
} else if (host === BITBUCKET_HOST) {
apiBaseUrl = BITBUCKET_API_BASE_URL;
} else {
apiBaseUrl = `https://${host}/rest/api/1.0`;
}
return {
+8 -2
View File
@@ -24,7 +24,10 @@ import {
describe('basicIntegrations', () => {
describe('byUrl', () => {
it('handles hosts without a port', () => {
const integration = new BitbucketIntegration({ host: 'host.com' });
const integration = new BitbucketIntegration({
host: 'host.com',
apiBaseUrl: 'a',
});
const integrations = basicIntegrations<BitbucketIntegration>(
[integration],
i => i.config.host,
@@ -33,7 +36,10 @@ describe('basicIntegrations', () => {
expect(integrations.byUrl('https://host.com:8080/a')).toBeUndefined();
});
it('handles hosts with a port', () => {
const integration = new BitbucketIntegration({ host: 'host.com:8080' });
const integration = new BitbucketIntegration({
host: 'host.com:8080',
apiBaseUrl: 'a',
});
const integrations = basicIntegrations<BitbucketIntegration>(
[integration],
i => i.config.host,
@@ -32,6 +32,7 @@ const createBitbucketCloudRepository = async (opts: {
description?: string;
repoVisibility: 'private' | 'public';
authorization: string;
apiBaseUrl: string;
}) => {
const {
workspace,
@@ -40,6 +41,7 @@ const createBitbucketCloudRepository = async (opts: {
description,
repoVisibility,
authorization,
apiBaseUrl,
} = opts;
const options: RequestInit = {
@@ -59,7 +61,7 @@ const createBitbucketCloudRepository = async (opts: {
let response: Response;
try {
response = await fetch(
`https://api.bitbucket.org/2.0/repositories/${workspace}/${repo}`,
`${apiBaseUrl}/repositories/${workspace}/${repo}`,
options,
);
} catch (e) {
@@ -88,16 +90,14 @@ const createBitbucketCloudRepository = async (opts: {
};
const createBitbucketServerRepository = async (opts: {
host: string;
project: string;
repo: string;
description?: string;
repoVisibility: 'private' | 'public';
authorization: string;
apiBaseUrl?: string;
apiBaseUrl: string;
}) => {
const {
host,
project,
repo,
description,
@@ -121,8 +121,7 @@ const createBitbucketServerRepository = async (opts: {
};
try {
const baseUrl = apiBaseUrl ? apiBaseUrl : `https://${host}/rest/api/1.0`;
response = await fetch(`${baseUrl}/projects/${project}/repos`, options);
response = await fetch(`${apiBaseUrl}/projects/${project}/repos`, options);
} catch (e) {
throw new Error(`Unable to create repository, ${e}`);
}
@@ -306,7 +305,11 @@ export function createPublishBitbucketAction(options: {
const authorization = getAuthorizationHeader(
ctx.input.token
? { host: integrationConfig.config.host, token: ctx.input.token }
? {
host: integrationConfig.config.host,
apiBaseUrl: integrationConfig.config.apiBaseUrl,
token: ctx.input.token,
}
: integrationConfig.config,
);
@@ -319,7 +322,6 @@ export function createPublishBitbucketAction(options: {
const { remoteUrl, repoContentsUrl } = await createMethod({
authorization,
host,
workspace: workspace || '',
project,
repo,