Merge pull request #3751 from backstage/freben/github-config

catalog-backend: break out github org config for consistency
This commit is contained in:
Fredrik Adelöw
2020-12-16 16:52:28 +01:00
committed by GitHub
6 changed files with 178 additions and 130 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Break out GithubOrgReaderProcessor config into its own file for consistency with the other org processors.
@@ -16,69 +16,9 @@
import { getVoidLogger } from '@backstage/backend-common';
import { LocationSpec } from '@backstage/catalog-model';
import { ConfigReader } from '@backstage/config';
import {
GithubOrgReaderProcessor,
parseUrl,
readConfig,
} from './GithubOrgReaderProcessor';
import { GithubOrgReaderProcessor, parseUrl } from './GithubOrgReaderProcessor';
describe('GithubOrgReaderProcessor', () => {
describe('readConfig', () => {
function config(
providers: { target: string; apiBaseUrl?: string; token?: string }[],
) {
return new ConfigReader({
catalog: { processors: { githubOrg: { providers } } },
});
}
it('adds a default GitHub entry when missing', () => {
const output = readConfig(config([]));
expect(output).toEqual([
{
target: 'https://github.com',
apiBaseUrl: 'https://api.github.com',
},
]);
});
it('injects the correct GitHub API base URL when missing', () => {
const output = readConfig(config([{ target: 'https://github.com' }]));
expect(output).toEqual([
{
target: 'https://github.com',
apiBaseUrl: 'https://api.github.com',
},
]);
});
it('rejects custom targets with no base URLs', () => {
expect(() =>
readConfig(config([{ target: 'https://ghe.company.com' }])),
).toThrow(
'Provider at https://ghe.company.com must configure an explicit apiBaseUrl',
);
});
it('rejects funky configs', () => {
expect(() => readConfig(config([{ target: 7 } as any]))).toThrow(
/target/,
);
expect(() => readConfig(config([{ noTarget: '7' } as any]))).toThrow(
/target/,
);
expect(() =>
readConfig(
config([{ target: 'https://github.com', apiBaseUrl: 7 } as any]),
),
).toThrow(/apiBaseUrl/);
expect(() =>
readConfig(config([{ target: 'https://github.com', token: 7 } as any])),
).toThrow(/token/);
});
});
describe('parseUrl', () => {
it('only supports clean org urls, and decodes them', () => {
expect(() => parseUrl('https://github.com')).toThrow();
@@ -18,7 +18,12 @@ import { LocationSpec } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import { graphql } from '@octokit/graphql';
import { Logger } from 'winston';
import { getOrganizationTeams, getOrganizationUsers } from './github';
import {
getOrganizationTeams,
getOrganizationUsers,
ProviderConfig,
readGithubConfig,
} from './github';
import * as results from './results';
import { CatalogProcessor, CatalogProcessorEmit } from './types';
import { buildOrgHierarchy } from './util/org';
@@ -33,7 +38,7 @@ export class GithubOrgReaderProcessor implements CatalogProcessor {
static fromConfig(config: Config, options: { logger: Logger }) {
return new GithubOrgReaderProcessor({
...options,
providers: readConfig(config),
providers: readGithubConfig(config),
});
}
@@ -113,73 +118,6 @@ export class GithubOrgReaderProcessor implements CatalogProcessor {
* Helpers
*/
/**
* The configuration parameters for a single GitHub API provider.
*/
type ProviderConfig = {
/**
* The prefix of the target that this matches on, e.g. "https://github.com",
* with no trailing slash.
*/
target: 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.
*/
apiBaseUrl?: string;
/**
* The authorization token to use for requests to this provider.
*
* If no token is specified, anonymous access is used.
*/
token?: string;
};
// TODO(freben): Break out common code and config from here and GithubReaderProcessor
export function readConfig(config: Config): ProviderConfig[] {
const providers: ProviderConfig[] = [];
const providerConfigs =
config.getOptionalConfigArray('catalog.processors.githubOrg.providers') ??
[];
// First read all the explicit providers
for (const providerConfig of providerConfigs) {
const target = providerConfig.getString('target').replace(/\/+$/, '');
let apiBaseUrl = providerConfig.getOptionalString('apiBaseUrl');
const token = providerConfig.getOptionalString('token');
if (apiBaseUrl) {
apiBaseUrl = apiBaseUrl.replace(/\/+$/, '');
} else if (target === 'https://github.com') {
apiBaseUrl = 'https://api.github.com';
}
if (!apiBaseUrl) {
throw new Error(
`Provider at ${target} must configure an explicit apiBaseUrl`,
);
}
providers.push({ target, apiBaseUrl, token });
}
// If no explicit github.com provider was added, put one in the list as
// a convenience
if (!providers.some(p => p.target === 'https://github.com')) {
providers.push({
target: 'https://github.com',
apiBaseUrl: 'https://api.github.com',
});
}
return providers;
}
export function parseUrl(urlString: string): { org: string } {
const path = new URL(urlString).pathname.substr(1).split('/');
@@ -0,0 +1,79 @@
/*
* 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 { ConfigReader } from '@backstage/config';
import { readGithubConfig } from './config';
describe('config', () => {
describe('readGithubConfig', () => {
function config(
providers: { target: string; apiBaseUrl?: string; token?: string }[],
) {
return new ConfigReader({
catalog: { processors: { githubOrg: { providers } } },
});
}
it('adds a default GitHub entry when missing', () => {
const output = readGithubConfig(config([]));
expect(output).toEqual([
{
target: 'https://github.com',
apiBaseUrl: 'https://api.github.com',
},
]);
});
it('injects the correct GitHub API base URL when missing', () => {
const output = readGithubConfig(
config([{ target: 'https://github.com' }]),
);
expect(output).toEqual([
{
target: 'https://github.com',
apiBaseUrl: 'https://api.github.com',
},
]);
});
it('rejects custom targets with no base URLs', () => {
expect(() =>
readGithubConfig(config([{ target: 'https://ghe.company.com' }])),
).toThrow(
'Provider at https://ghe.company.com must configure an explicit apiBaseUrl',
);
});
it('rejects funky configs', () => {
expect(() => readGithubConfig(config([{ target: 7 } as any]))).toThrow(
/target/,
);
expect(() =>
readGithubConfig(config([{ noTarget: '7' } as any])),
).toThrow(/target/);
expect(() =>
readGithubConfig(
config([{ target: 'https://github.com', apiBaseUrl: 7 } as any]),
),
).toThrow(/apiBaseUrl/);
expect(() =>
readGithubConfig(
config([{ target: 'https://github.com', token: 7 } as any]),
),
).toThrow(/token/);
});
});
});
@@ -0,0 +1,84 @@
/*
* 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';
/**
* The configuration parameters for a single GitHub API provider.
*/
export type ProviderConfig = {
/**
* The prefix of the target that this matches on, e.g. "https://github.com",
* with no trailing slash.
*/
target: 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.
*/
apiBaseUrl?: string;
/**
* The authorization token to use for requests to this provider.
*
* If no token is specified, anonymous access is used.
*/
token?: string;
};
// TODO(freben): Break out common code and config from here and GithubReaderProcessor
export function readGithubConfig(config: Config): ProviderConfig[] {
const providers: ProviderConfig[] = [];
const providerConfigs =
config.getOptionalConfigArray('catalog.processors.githubOrg.providers') ??
[];
// First read all the explicit providers
for (const providerConfig of providerConfigs) {
const target = providerConfig.getString('target').replace(/\/+$/, '');
let apiBaseUrl = providerConfig.getOptionalString('apiBaseUrl');
const token = providerConfig.getOptionalString('token');
if (apiBaseUrl) {
apiBaseUrl = apiBaseUrl.replace(/\/+$/, '');
} else if (target === 'https://github.com') {
apiBaseUrl = 'https://api.github.com';
}
if (!apiBaseUrl) {
throw new Error(
`Provider at ${target} must configure an explicit apiBaseUrl`,
);
}
providers.push({ target, apiBaseUrl, token });
}
// If no explicit github.com provider was added, put one in the list as
// a convenience
if (!providers.some(p => p.target === 'https://github.com')) {
providers.push({
target: 'https://github.com',
apiBaseUrl: 'https://api.github.com',
});
}
return providers;
}
@@ -14,4 +14,6 @@
* limitations under the License.
*/
export { readGithubConfig } from './config';
export type { ProviderConfig } from './config';
export { getOrganizationTeams, getOrganizationUsers } from './github';