Skip to content

Commit

Permalink
catalog-backend: break out github org config for consistency
Browse files Browse the repository at this point in the history
  • Loading branch information
freben committed Dec 16, 2020
1 parent d62be2f commit e7496dc
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 130 deletions.
5 changes: 5 additions & 0 deletions .changeset/good-news-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---

Break out GithubOrgReaderProcessor config into its own file for consistency with the other org processors.
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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),
});
}

Expand Down Expand Up @@ -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('/');

Expand Down
Original file line number Diff line number Diff line change
@@ -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/);
});
});
});
84 changes: 84 additions & 0 deletions plugins/catalog-backend/src/ingestion/processors/github/config.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@
* limitations under the License.
*/

export { readGithubConfig } from './config';
export type { ProviderConfig } from './config';
export { getOrganizationTeams, getOrganizationUsers } from './github';

0 comments on commit e7496dc

Please sign in to comment.