diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index e0fc10ff1e..1bd5528f22 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -100,13 +100,50 @@ metadata: ``` The value of this annotation is the so-called slug that identifies a project on -[GitHub](https://github.com) that is related to this entity. It is on the format +[GitHub](https://github.com) (either the public one, or a private GitHub +Enterprise installation) that is related to this entity. It is on the format `/`, and is the same as can be seen in the URL location bar of the browser when viewing that project. Specifying this annotation will enable GitHub related features in Backstage for that entity. +### github.com/team-slug + +```yaml +# Example: +metadata: + annotations: + github.com/team-slug: spotify/backstage-core +``` + +The value of this annotation is the so-called slug that identifies a team on +[GitHub](https://github.com) (either the public one, or a private GitHub +Enterprise installation) that is related to this entity. It is on the format +`/`, and is the same as can be seen in the URL location bar +of the browser when viewing that team. + +This annotation can be used on a [Group entity](descriptor-format.md#kind-group) +to note that it originated from that team on GitHub. + +### github.com/user-login + +```yaml +# Example: +metadata: + annotations: + github.com/user-login: freben +``` + +The value of this annotation is the so-called login that identifies a user on +[GitHub](https://github.com) (either the public one, or a private GitHub +Enterprise installation) that is related to this entity. It is on the format +``, and is the same as can be seen in the URL location bar of the +browser when viewing that user. + +This annotation can be used on a [User entity](descriptor-format.md#kind-user) +to note that it originated from that user on GitHub. + ### sentry.io/project-slug ```yaml diff --git a/plugins/catalog-backend/src/ingestion/processors/GithubOrgReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/GithubOrgReaderProcessor.test.ts new file mode 100644 index 0000000000..e0d35f78aa --- /dev/null +++ b/plugins/catalog-backend/src/ingestion/processors/GithubOrgReaderProcessor.test.ts @@ -0,0 +1,124 @@ +/* + * 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 { LocationSpec } from '@backstage/catalog-model'; +import { ConfigReader } from '@backstage/config'; +import { + GithubOrgReaderProcessor, + parseUrl, + readConfig, +} from './GithubOrgReaderProcessor'; + +describe('GithubOrgReaderProcessor', () => { + describe('readConfig', () => { + function config( + providers: { target: string; apiBaseUrl?: string; token?: string }[], + ) { + return ConfigReader.fromConfigs([ + { + context: '', + data: { + 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(); + expect(() => parseUrl('https://github.com/org/foo')).toThrow(); + expect(() => parseUrl('https://github.com/org/foo/teams')).toThrow(); + expect(parseUrl('https://github.com/foo%32')).toEqual({ org: 'foo2' }); + }); + }); + + describe('implementation', () => { + it('rejects unknown types', async () => { + const processor = new GithubOrgReaderProcessor([ + { target: 'https://github.com', apiBaseUrl: 'https://api.github.com' }, + ]); + const location: LocationSpec = { + type: 'not-github-org', + target: 'https://github.com', + }; + await expect( + processor.readLocation(location, false, () => {}), + ).resolves.toBeFalsy(); + }); + + it('rejects unknown targets', async () => { + const processor = new GithubOrgReaderProcessor([ + { target: 'https://github.com', apiBaseUrl: 'https://api.github.com' }, + ]); + const location: LocationSpec = { + type: 'github-org', + target: 'https://not.github.com/apa', + }; + await expect( + processor.readLocation(location, false, () => {}), + ).rejects.toThrow( + /There is no GitHub Org provider that matches https:\/\/not.github.com\/apa/, + ); + }); + }); +}); diff --git a/plugins/catalog-backend/src/ingestion/processors/GithubReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/GithubReaderProcessor.test.ts index 3ab24541e7..8eb46db967 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GithubReaderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GithubReaderProcessor.test.ts @@ -243,7 +243,7 @@ describe('GithubReaderProcessor', () => { { target: 'https://github.com', apiBaseUrl: 'https://api.github.com' }, ]); const location: LocationSpec = { - type: 'not-github/api', + type: 'not-github', target: 'https://github.com', }; await expect( @@ -256,7 +256,7 @@ describe('GithubReaderProcessor', () => { { target: 'https://github.com', apiBaseUrl: 'https://api.github.com' }, ]); const location: LocationSpec = { - type: 'github/api', + type: 'github', target: 'https://not.github.com/apa', }; await expect(