integrations: move common integration concerns to a separate package (#3295)
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint')],
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
# Integrations common functionality
|
||||
|
||||
Contains some common functionality of integrations.
|
||||
|
||||
This package will be imported both by the frontend and backend.
|
||||
|
||||
## Links
|
||||
|
||||
- [The Backstage homepage](https://backstage.io)
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@backstage/integration",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"module": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "backstage-cli build",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1",
|
||||
"git-url-parse": "^11.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.2.0",
|
||||
"@types/jest": "^26.0.7"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config, ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
AzureIntegrationConfig,
|
||||
readAzureIntegrationConfig,
|
||||
readAzureIntegrationConfigs,
|
||||
} from './config';
|
||||
|
||||
describe('readAzureIntegrationConfig', () => {
|
||||
function buildConfig(data: Partial<AzureIntegrationConfig>): Config {
|
||||
return ConfigReader.fromConfigs([{ context: '', data }]);
|
||||
}
|
||||
|
||||
it('reads all values', () => {
|
||||
const output = readAzureIntegrationConfig(
|
||||
buildConfig({
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
}),
|
||||
);
|
||||
expect(output).toEqual({
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
});
|
||||
});
|
||||
|
||||
it('inserts the defaults if missing', () => {
|
||||
const output = readAzureIntegrationConfig(buildConfig({}));
|
||||
expect(output).toEqual({ host: 'dev.azure.com' });
|
||||
});
|
||||
|
||||
it('rejects funky configs', () => {
|
||||
const valid: any = {
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
};
|
||||
expect(() =>
|
||||
readAzureIntegrationConfig(buildConfig({ ...valid, host: 7 })),
|
||||
).toThrow(/host/);
|
||||
expect(() =>
|
||||
readAzureIntegrationConfig(buildConfig({ ...valid, token: 7 })),
|
||||
).toThrow(/token/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readAzureIntegrationConfigs', () => {
|
||||
function buildConfig(data: Partial<AzureIntegrationConfig>[]): Config[] {
|
||||
return data.map(item =>
|
||||
ConfigReader.fromConfigs([{ context: '', data: item }]),
|
||||
);
|
||||
}
|
||||
|
||||
it('reads all values', () => {
|
||||
const output = readAzureIntegrationConfigs(
|
||||
buildConfig([
|
||||
{
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(output).toContainEqual({
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds a default entry when missing', () => {
|
||||
const output = readAzureIntegrationConfigs(buildConfig([]));
|
||||
expect(output).toEqual([
|
||||
{
|
||||
host: 'dev.azure.com',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
const AZURE_HOST = 'dev.azure.com';
|
||||
|
||||
/**
|
||||
* The configuration parameters for a single Azure provider.
|
||||
*/
|
||||
export type AzureIntegrationConfig = {
|
||||
/**
|
||||
* The host of the target that this matches on, e.g. "dev.azure.com".
|
||||
*
|
||||
* Currently only "dev.azure.com" is supported.
|
||||
*/
|
||||
host: string;
|
||||
|
||||
/**
|
||||
* The authorization token to use for requests.
|
||||
*
|
||||
* If no token is specified, anonymous access is used.
|
||||
*/
|
||||
token?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a single Azure integration config.
|
||||
*
|
||||
* @param config The config object of a single integration
|
||||
*/
|
||||
export function readAzureIntegrationConfig(
|
||||
config: Config,
|
||||
): AzureIntegrationConfig {
|
||||
const host = config.getOptionalString('host') ?? AZURE_HOST;
|
||||
const token = config.getOptionalString('token');
|
||||
return { host, token };
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a set of Azure integration configs, and inserts some defaults for
|
||||
* public Azure if not specified.
|
||||
*
|
||||
* @param configs All of the integration config objects
|
||||
*/
|
||||
export function readAzureIntegrationConfigs(
|
||||
configs: Config[],
|
||||
): AzureIntegrationConfig[] {
|
||||
// First read all the explicit integrations
|
||||
const result = configs.map(readAzureIntegrationConfig);
|
||||
|
||||
// If no explicit dev.azure.com integration was added, put one in the list as
|
||||
// a convenience
|
||||
if (!result.some(c => c.host === AZURE_HOST)) {
|
||||
result.push({ host: AZURE_HOST });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
readAzureIntegrationConfig,
|
||||
readAzureIntegrationConfigs,
|
||||
} from './config';
|
||||
export type { AzureIntegrationConfig } from './config';
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config, ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
BitbucketIntegrationConfig,
|
||||
readBitbucketIntegrationConfig,
|
||||
readBitbucketIntegrationConfigs,
|
||||
} from './config';
|
||||
|
||||
describe('readBitbucketIntegrationConfig', () => {
|
||||
function buildConfig(data: Partial<BitbucketIntegrationConfig>): Config {
|
||||
return ConfigReader.fromConfigs([{ context: '', data }]);
|
||||
}
|
||||
|
||||
it('reads all values', () => {
|
||||
const output = readBitbucketIntegrationConfig(
|
||||
buildConfig({
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
token: 't',
|
||||
username: 'u',
|
||||
appPassword: 'p',
|
||||
}),
|
||||
);
|
||||
expect(output).toEqual({
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
token: 't',
|
||||
username: 'u',
|
||||
appPassword: 'p',
|
||||
});
|
||||
});
|
||||
|
||||
it('inserts the defaults if missing', () => {
|
||||
const output = readBitbucketIntegrationConfig(buildConfig({}));
|
||||
expect(output).toEqual(
|
||||
expect.objectContaining({
|
||||
host: 'bitbucket.org',
|
||||
apiBaseUrl: 'https://api.bitbucket.org/2.0',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects funky configs', () => {
|
||||
const valid: any = {
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
token: 't',
|
||||
username: 'u',
|
||||
appPassword: 'p',
|
||||
};
|
||||
expect(() =>
|
||||
readBitbucketIntegrationConfig(buildConfig({ ...valid, host: 7 })),
|
||||
).toThrow(/host/);
|
||||
expect(() =>
|
||||
readBitbucketIntegrationConfig(buildConfig({ ...valid, apiBaseUrl: 7 })),
|
||||
).toThrow(/apiBaseUrl/);
|
||||
expect(() =>
|
||||
readBitbucketIntegrationConfig(buildConfig({ ...valid, token: 7 })),
|
||||
).toThrow(/token/);
|
||||
expect(() =>
|
||||
readBitbucketIntegrationConfig(buildConfig({ ...valid, username: 7 })),
|
||||
).toThrow(/username/);
|
||||
expect(() =>
|
||||
readBitbucketIntegrationConfig(buildConfig({ ...valid, appPassword: 7 })),
|
||||
).toThrow(/appPassword/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readBitbucketIntegrationConfigs', () => {
|
||||
function buildConfig(data: Partial<BitbucketIntegrationConfig>[]): Config[] {
|
||||
return data.map(item =>
|
||||
ConfigReader.fromConfigs([{ context: '', data: item }]),
|
||||
);
|
||||
}
|
||||
|
||||
it('reads all values', () => {
|
||||
const output = readBitbucketIntegrationConfigs(
|
||||
buildConfig([
|
||||
{
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
token: 't',
|
||||
username: 'u',
|
||||
appPassword: 'p',
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(output).toContainEqual({
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
token: 't',
|
||||
username: 'u',
|
||||
appPassword: 'p',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds a default Bitbucket Cloud entry when missing', () => {
|
||||
const output = readBitbucketIntegrationConfigs(buildConfig([]));
|
||||
expect(output).toEqual([
|
||||
{
|
||||
host: 'bitbucket.org',
|
||||
apiBaseUrl: 'https://api.bitbucket.org/2.0',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('injects the correct Bitbucket Cloud API base URL when missing', () => {
|
||||
const output = readBitbucketIntegrationConfigs(
|
||||
buildConfig([{ host: 'bitbucket.org' }]),
|
||||
);
|
||||
expect(output).toEqual([
|
||||
{
|
||||
host: 'bitbucket.org',
|
||||
apiBaseUrl: 'https://api.bitbucket.org/2.0',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
const BITBUCKET_HOST = 'bitbucket.org';
|
||||
const BITBUCKET_API_BASE_URL = 'https://api.bitbucket.org/2.0';
|
||||
|
||||
/**
|
||||
* The configuration parameters for a single Bitbucket API provider.
|
||||
*/
|
||||
export type BitbucketIntegrationConfig = {
|
||||
/**
|
||||
* The host of the target that this matches on, e.g. "bitbucket.org"
|
||||
*/
|
||||
host: string;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
apiBaseUrl?: string;
|
||||
|
||||
/**
|
||||
* The authorization token to use for requests to a Bitbucket Server provider.
|
||||
*
|
||||
* See https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html
|
||||
*
|
||||
* If no token is specified, anonymous access is used.
|
||||
*/
|
||||
token?: string;
|
||||
|
||||
/**
|
||||
* The username to use for requests to Bitbucket Cloud (bitbucket.org).
|
||||
*/
|
||||
username?: string;
|
||||
|
||||
/**
|
||||
* Authentication with Bitbucket Cloud (bitbucket.org) is done using app passwords.
|
||||
*
|
||||
* See https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/
|
||||
*/
|
||||
appPassword?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a single Bitbucket integration config.
|
||||
*
|
||||
* @param config The config object of a single integration
|
||||
*/
|
||||
export function readBitbucketIntegrationConfig(
|
||||
config: Config,
|
||||
): BitbucketIntegrationConfig {
|
||||
const host = config.getOptionalString('host') ?? BITBUCKET_HOST;
|
||||
let apiBaseUrl = config.getOptionalString('apiBaseUrl');
|
||||
const token = config.getOptionalString('token');
|
||||
const username = config.getOptionalString('username');
|
||||
const appPassword = config.getOptionalString('appPassword');
|
||||
|
||||
if (apiBaseUrl) {
|
||||
apiBaseUrl = apiBaseUrl.replace(/\/+$/, '');
|
||||
} else if (host === BITBUCKET_HOST) {
|
||||
apiBaseUrl = BITBUCKET_API_BASE_URL;
|
||||
}
|
||||
|
||||
return {
|
||||
host,
|
||||
apiBaseUrl,
|
||||
token,
|
||||
username,
|
||||
appPassword,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a set of Bitbucket integration configs, and inserts some defaults for
|
||||
* public Bitbucket if not specified.
|
||||
*
|
||||
* @param configs All of the integration config objects
|
||||
*/
|
||||
export function readBitbucketIntegrationConfigs(
|
||||
configs: Config[],
|
||||
): BitbucketIntegrationConfig[] {
|
||||
// First read all the explicit integrations
|
||||
const result = configs.map(readBitbucketIntegrationConfig);
|
||||
|
||||
// If no explicit bitbucket.org integration was added, put one in the list as
|
||||
// a convenience
|
||||
if (!result.some(c => c.host === BITBUCKET_HOST)) {
|
||||
result.push({
|
||||
host: BITBUCKET_HOST,
|
||||
apiBaseUrl: BITBUCKET_API_BASE_URL,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
readBitbucketIntegrationConfig,
|
||||
readBitbucketIntegrationConfigs,
|
||||
} from './config';
|
||||
export type { BitbucketIntegrationConfig } from './config';
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config, ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
GitHubIntegrationConfig,
|
||||
readGitHubIntegrationConfig,
|
||||
readGitHubIntegrationConfigs,
|
||||
} from './config';
|
||||
|
||||
describe('readGitHubIntegrationConfig', () => {
|
||||
function buildConfig(provider: Partial<GitHubIntegrationConfig>) {
|
||||
return ConfigReader.fromConfigs([{ context: '', data: provider }]);
|
||||
}
|
||||
|
||||
it('reads all values', () => {
|
||||
const output = readGitHubIntegrationConfig(
|
||||
buildConfig({
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
rawBaseUrl: 'https://a.com/raw',
|
||||
token: 't',
|
||||
}),
|
||||
);
|
||||
expect(output).toEqual({
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
rawBaseUrl: 'https://a.com/raw',
|
||||
token: 't',
|
||||
});
|
||||
});
|
||||
|
||||
it('injects the correct GitHub API base URL when missing', () => {
|
||||
const output = readGitHubIntegrationConfig(
|
||||
buildConfig({ host: 'github.com' }),
|
||||
);
|
||||
expect(output).toEqual({
|
||||
host: 'github.com',
|
||||
apiBaseUrl: 'https://api.github.com',
|
||||
rawBaseUrl: 'https://raw.githubusercontent.com',
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects funky configs', () => {
|
||||
const valid: any = {
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
rawBaseUrl: 'https://a.com/raw',
|
||||
token: 't',
|
||||
};
|
||||
expect(() =>
|
||||
readGitHubIntegrationConfig(buildConfig({ ...valid, host: 7 })),
|
||||
).toThrow(/host/);
|
||||
expect(() =>
|
||||
readGitHubIntegrationConfig(buildConfig({ ...valid, apiBaseUrl: 7 })),
|
||||
).toThrow(/apiBaseUrl/);
|
||||
expect(() =>
|
||||
readGitHubIntegrationConfig(buildConfig({ ...valid, rawBaseUrl: 7 })),
|
||||
).toThrow(/rawBaseUrl/);
|
||||
expect(() =>
|
||||
readGitHubIntegrationConfig(buildConfig({ ...valid, token: 7 })),
|
||||
).toThrow(/token/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readGitHubIntegrationConfigs', () => {
|
||||
function buildConfig(
|
||||
providers: Partial<GitHubIntegrationConfig>[],
|
||||
): Config[] {
|
||||
return providers.map(provider =>
|
||||
ConfigReader.fromConfigs([{ context: '', data: provider }]),
|
||||
);
|
||||
}
|
||||
|
||||
it('reads all values', () => {
|
||||
const output = readGitHubIntegrationConfigs(
|
||||
buildConfig([
|
||||
{
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
rawBaseUrl: 'https://a.com/raw',
|
||||
token: 't',
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(output).toContainEqual({
|
||||
host: 'a.com',
|
||||
apiBaseUrl: 'https://a.com/api',
|
||||
rawBaseUrl: 'https://a.com/raw',
|
||||
token: 't',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds a default GitHub entry when missing', () => {
|
||||
const output = readGitHubIntegrationConfigs(buildConfig([]));
|
||||
expect(output).toEqual([
|
||||
{
|
||||
host: 'github.com',
|
||||
apiBaseUrl: 'https://api.github.com',
|
||||
rawBaseUrl: 'https://raw.githubusercontent.com',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
const GITHUB_HOST = 'github.com';
|
||||
const GITHUB_API_BASE_URL = 'https://api.github.com';
|
||||
const GITHUB_RAW_BASE_URL = 'https://raw.githubusercontent.com';
|
||||
|
||||
/**
|
||||
* The configuration parameters for a single GitHub integration.
|
||||
*/
|
||||
export type GitHubIntegrationConfig = {
|
||||
/**
|
||||
* The host of the target that this matches on, e.g. "github.com"
|
||||
*/
|
||||
host: string;
|
||||
|
||||
/**
|
||||
* The base URL of the API of this provider, e.g. "https://api.github.com",
|
||||
* with no trailing slash.
|
||||
*
|
||||
* May be omitted specifically for GitHub; then it will be deduced.
|
||||
*
|
||||
* The API will always be preferred if both its base URL and a token are
|
||||
* present.
|
||||
*/
|
||||
apiBaseUrl?: string;
|
||||
|
||||
/**
|
||||
* The base URL of the raw fetch endpoint of this provider, e.g.
|
||||
* "https://raw.githubusercontent.com", with no trailing slash.
|
||||
*
|
||||
* May be omitted specifically for GitHub; then it will be deduced.
|
||||
*
|
||||
* The API will always be preferred if both its base URL and a token are
|
||||
* present.
|
||||
*/
|
||||
rawBaseUrl?: string;
|
||||
|
||||
/**
|
||||
* The authorization token to use for requests to this provider.
|
||||
*
|
||||
* If no token is specified, anonymous access is used.
|
||||
*/
|
||||
token?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a single GitHub integration config.
|
||||
*
|
||||
* @param config The config object of a single integration
|
||||
*/
|
||||
export function readGitHubIntegrationConfig(
|
||||
config: Config,
|
||||
): GitHubIntegrationConfig {
|
||||
const host = config.getOptionalString('host') ?? GITHUB_HOST;
|
||||
let apiBaseUrl = config.getOptionalString('apiBaseUrl');
|
||||
let rawBaseUrl = config.getOptionalString('rawBaseUrl');
|
||||
const token = config.getOptionalString('token');
|
||||
|
||||
if (apiBaseUrl) {
|
||||
apiBaseUrl = apiBaseUrl.replace(/\/+$/, '');
|
||||
} else if (host === GITHUB_HOST) {
|
||||
apiBaseUrl = GITHUB_API_BASE_URL;
|
||||
}
|
||||
|
||||
if (rawBaseUrl) {
|
||||
rawBaseUrl = rawBaseUrl.replace(/\/+$/, '');
|
||||
} else if (host === GITHUB_HOST) {
|
||||
rawBaseUrl = GITHUB_RAW_BASE_URL;
|
||||
}
|
||||
|
||||
return { host, apiBaseUrl, rawBaseUrl, token };
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a set of GitHub integration configs, and inserts some defaults for
|
||||
* public GitHub if not specified.
|
||||
*
|
||||
* @param configs All of the integration config objects
|
||||
*/
|
||||
export function readGitHubIntegrationConfigs(
|
||||
configs: Config[],
|
||||
): GitHubIntegrationConfig[] {
|
||||
// First read all the explicit integrations
|
||||
const result = configs.map(readGitHubIntegrationConfig);
|
||||
|
||||
// If no explicit github.com integration was added, put one in the list as
|
||||
// a convenience
|
||||
if (!result.some(c => c.host === GITHUB_HOST)) {
|
||||
result.push({
|
||||
host: GITHUB_HOST,
|
||||
apiBaseUrl: GITHUB_API_BASE_URL,
|
||||
rawBaseUrl: GITHUB_RAW_BASE_URL,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
readGitHubIntegrationConfig,
|
||||
readGitHubIntegrationConfigs,
|
||||
} from './config';
|
||||
export type { GitHubIntegrationConfig } from './config';
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config, ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
GitLabIntegrationConfig,
|
||||
readGitLabIntegrationConfig,
|
||||
readGitLabIntegrationConfigs,
|
||||
} from './config';
|
||||
|
||||
describe('readGitLabIntegrationConfig', () => {
|
||||
function buildConfig(data: Partial<GitLabIntegrationConfig>): Config {
|
||||
return ConfigReader.fromConfigs([{ context: '', data }]);
|
||||
}
|
||||
|
||||
it('reads all values', () => {
|
||||
const output = readGitLabIntegrationConfig(
|
||||
buildConfig({
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
}),
|
||||
);
|
||||
expect(output).toEqual({
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
});
|
||||
});
|
||||
|
||||
it('inserts the defaults if missing', () => {
|
||||
const output = readGitLabIntegrationConfig(buildConfig({}));
|
||||
expect(output).toEqual({ host: 'gitlab.com' });
|
||||
});
|
||||
|
||||
it('rejects funky configs', () => {
|
||||
const valid: any = {
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
};
|
||||
expect(() =>
|
||||
readGitLabIntegrationConfig(buildConfig({ ...valid, host: 7 })),
|
||||
).toThrow(/host/);
|
||||
expect(() =>
|
||||
readGitLabIntegrationConfig(buildConfig({ ...valid, token: 7 })),
|
||||
).toThrow(/token/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readGitLabIntegrationConfigs', () => {
|
||||
function buildConfig(data: Partial<GitLabIntegrationConfig>[]): Config[] {
|
||||
return data.map(item =>
|
||||
ConfigReader.fromConfigs([{ context: '', data: item }]),
|
||||
);
|
||||
}
|
||||
|
||||
it('reads all values', () => {
|
||||
const output = readGitLabIntegrationConfigs(
|
||||
buildConfig([
|
||||
{
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(output).toContainEqual({
|
||||
host: 'a.com',
|
||||
token: 't',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds a default entry when missing', () => {
|
||||
const output = readGitLabIntegrationConfigs(buildConfig([]));
|
||||
expect(output).toEqual([
|
||||
{
|
||||
host: 'gitlab.com',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
const GITLAB_HOST = 'gitlab.com';
|
||||
|
||||
/**
|
||||
* The configuration parameters for a single GitLab integration.
|
||||
*/
|
||||
export type GitLabIntegrationConfig = {
|
||||
/**
|
||||
* The host of the target that this matches on, e.g. "gitlab.com"
|
||||
*/
|
||||
host: string;
|
||||
|
||||
/**
|
||||
* The authorization token to use for requests this provider.
|
||||
*
|
||||
* If no token is specified, anonymous access is used.
|
||||
*/
|
||||
token?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a single GitLab integration config.
|
||||
*
|
||||
* @param config The config object of a single integration
|
||||
*/
|
||||
export function readGitLabIntegrationConfig(
|
||||
config: Config,
|
||||
): GitLabIntegrationConfig {
|
||||
const host = config.getOptionalString('host') ?? GITLAB_HOST;
|
||||
const token = config.getOptionalString('token');
|
||||
return { host, token };
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a set of GitLab integration configs, and inserts some defaults for
|
||||
* public GitLab if not specified.
|
||||
*
|
||||
* @param configs All of the integration config objects
|
||||
*/
|
||||
export function readGitLabIntegrationConfigs(
|
||||
configs: Config[],
|
||||
): GitLabIntegrationConfig[] {
|
||||
// First read all the explicit integrations
|
||||
const result = configs.map(readGitLabIntegrationConfig);
|
||||
|
||||
// As a convenience we always make sure there's at least an unauthenticated
|
||||
// reader for public gitlab repos.
|
||||
if (!result.some(c => c.host === GITLAB_HOST)) {
|
||||
result.push({ host: GITLAB_HOST });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
readGitLabIntegrationConfig,
|
||||
readGitLabIntegrationConfigs,
|
||||
} from './config';
|
||||
export type { GitLabIntegrationConfig } from './config';
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './azure';
|
||||
export * from './bitbucket';
|
||||
export * from './github';
|
||||
export * from './gitlab';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {};
|
||||
Reference in New Issue
Block a user