integration: add the basics of cross-integration concerns

This commit is contained in:
Fredrik Adelöw
2020-11-28 23:32:21 +01:00
parent 040f453c36
commit b8ecf6f482
12 changed files with 481 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/integration': minor
---
Add the basics of cross-integration concerns
@@ -0,0 +1,51 @@
/*
* 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';
import { AzureIntegration } from './azure/AzureIntegration';
import { BitbucketIntegration } from './bitbucket/BitbucketIntegration';
import { GitHubIntegration } from './github/GitHubIntegration';
import { GitLabIntegration } from './gitlab/GitLabIntegration';
import {
ScmIntegration,
ScmIntegrationPredicateTuple,
ScmIntegrations,
} from './types';
export class ScmIntegrationsImpl implements ScmIntegrations {
static fromConfig(config: Config): ScmIntegrationsImpl {
return new ScmIntegrationsImpl([
...AzureIntegration.factory({ config }),
...BitbucketIntegration.factory({ config }),
...GitHubIntegration.factory({ config }),
...GitLabIntegration.factory({ config }),
]);
}
constructor(private readonly integrations: ScmIntegrationPredicateTuple[]) {}
list(): ScmIntegration[] {
return this.integrations.map(i => i.integration);
}
byName(name: string): ScmIntegration | undefined {
return this.integrations.map(i => i.integration).find(i => i.name === name);
}
byUrl(url: string): ScmIntegration | undefined {
return this.integrations.find(i => i.predicate(new URL(url)))?.integration;
}
}
@@ -0,0 +1,48 @@
/*
* 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 { AzureIntegration } from './AzureIntegration';
describe('AzureIntegration', () => {
it('has a working factory', () => {
const integrations = AzureIntegration.factory({
config: ConfigReader.fromConfigs([
{
context: '',
data: {
integrations: {
azure: [
{
host: 'h.com',
token: 'token',
},
],
},
},
},
]),
});
expect(integrations.length).toBe(2); // including default
expect(integrations[0].predicate(new URL('https://h.com/a'))).toBe(true);
});
it('returns the basics', () => {
const integration = new AzureIntegration({ host: 'h.com' } as any);
expect(integration.type).toBe('azure');
expect(integration.name).toBe('h.com');
});
});
@@ -0,0 +1,40 @@
/*
* 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 { ScmIntegration, ScmIntegrationFactory } from '../types';
import { AzureIntegrationConfig, readAzureIntegrationConfigs } from './config';
export class AzureIntegration implements ScmIntegration {
static factory: ScmIntegrationFactory = ({ config }) => {
const configs = readAzureIntegrationConfigs(
config.getOptionalConfigArray('integrations.azure') ?? [],
);
return configs.map(integration => ({
predicate: (url: URL) => url.host === integration.host,
integration: new AzureIntegration(integration),
}));
};
constructor(private readonly config: AzureIntegrationConfig) {}
get type(): string {
return 'azure';
}
get name(): string {
return this.config.host;
}
}
@@ -0,0 +1,51 @@
/*
* 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 { BitbucketIntegration } from './BitbucketIntegration';
describe('BitbucketIntegration', () => {
it('has a working factory', () => {
const integrations = BitbucketIntegration.factory({
config: ConfigReader.fromConfigs([
{
context: '',
data: {
integrations: {
bitbucket: [
{
host: 'h.com',
apiBaseUrl: 'a',
token: 't',
username: 'u',
appPassword: 'p',
},
],
},
},
},
]),
});
expect(integrations.length).toBe(2); // including default
expect(integrations[0].predicate(new URL('https://h.com/a'))).toBe(true);
});
it('returns the basics', () => {
const integration = new BitbucketIntegration({ host: 'h.com' } as any);
expect(integration.type).toBe('bitbucket');
expect(integration.name).toBe('h.com');
});
});
@@ -0,0 +1,43 @@
/*
* 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 { ScmIntegration, ScmIntegrationFactory } from '../types';
import {
BitbucketIntegrationConfig,
readBitbucketIntegrationConfigs,
} from './config';
export class BitbucketIntegration implements ScmIntegration {
static factory: ScmIntegrationFactory = ({ config }) => {
const configs = readBitbucketIntegrationConfigs(
config.getOptionalConfigArray('integrations.bitbucket') ?? [],
);
return configs.map(integration => ({
predicate: (url: URL) => url.host === integration.host,
integration: new BitbucketIntegration(integration),
}));
};
constructor(private readonly config: BitbucketIntegrationConfig) {}
get type(): string {
return 'bitbucket';
}
get name(): string {
return this.config.host;
}
}
@@ -0,0 +1,50 @@
/*
* 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 { GitHubIntegration } from './GitHubIntegration';
describe('GitHubIntegration', () => {
it('has a working factory', () => {
const integrations = GitHubIntegration.factory({
config: ConfigReader.fromConfigs([
{
context: '',
data: {
integrations: {
github: [
{
host: 'h.com',
apiBaseUrl: 'a',
rawBaseUrl: 'r',
token: 't',
},
],
},
},
},
]),
});
expect(integrations.length).toBe(2); // including default
expect(integrations[0].predicate(new URL('https://h.com/a'))).toBe(true);
});
it('returns the basics', () => {
const integration = new GitHubIntegration({ host: 'h.com' } as any);
expect(integration.type).toBe('github');
expect(integration.name).toBe('h.com');
});
});
@@ -0,0 +1,43 @@
/*
* 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 { ScmIntegration, ScmIntegrationFactory } from '../types';
import {
GitHubIntegrationConfig,
readGitHubIntegrationConfigs,
} from './config';
export class GitHubIntegration implements ScmIntegration {
static factory: ScmIntegrationFactory = ({ config }) => {
const configs = readGitHubIntegrationConfigs(
config.getOptionalConfigArray('integrations.github') ?? [],
);
return configs.map(integration => ({
predicate: (url: URL) => url.host === integration.host,
integration: new GitHubIntegration(integration),
}));
};
constructor(private readonly config: GitHubIntegrationConfig) {}
get type(): string {
return 'github';
}
get name(): string {
return this.config.host;
}
}
@@ -0,0 +1,48 @@
/*
* 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 { GitLabIntegration } from './GitLabIntegration';
describe('GitLabIntegration', () => {
it('has a working factory', () => {
const integrations = GitLabIntegration.factory({
config: ConfigReader.fromConfigs([
{
context: '',
data: {
integrations: {
gitlab: [
{
host: 'h.com',
token: 't',
},
],
},
},
},
]),
});
expect(integrations.length).toBe(2); // including default
expect(integrations[0].predicate(new URL('https://h.com/a'))).toBe(true);
});
it('returns the basics', () => {
const integration = new GitLabIntegration({ host: 'h.com' } as any);
expect(integration.type).toBe('gitlab');
expect(integration.name).toBe('h.com');
});
});
@@ -0,0 +1,43 @@
/*
* 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 { ScmIntegration, ScmIntegrationFactory } from '../types';
import {
GitLabIntegrationConfig,
readGitLabIntegrationConfigs,
} from './config';
export class GitLabIntegration implements ScmIntegration {
static factory: ScmIntegrationFactory = ({ config }) => {
const configs = readGitLabIntegrationConfigs(
config.getOptionalConfigArray('integrations.gitlab') ?? [],
);
return configs.map(integration => ({
predicate: (url: URL) => url.host === integration.host,
integration: new GitLabIntegration(integration),
}));
};
constructor(private readonly config: GitLabIntegrationConfig) {}
get type(): string {
return 'gitlab';
}
get name(): string {
return this.config.host;
}
}
+1
View File
@@ -18,3 +18,4 @@ export * from './azure';
export * from './bitbucket';
export * from './github';
export * from './gitlab';
export type { ScmIntegration, ScmIntegrations } from './types';
+58
View File
@@ -0,0 +1,58 @@
/*
* 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';
/**
* Encapsulates a single SCM integration.
*/
export type ScmIntegration = {
type: string;
name: string;
};
/**
* Holds all registered SCM integrations.
*/
export type ScmIntegrations = {
/**
* Lists all registered integrations.
*/
list(): ScmIntegration[];
/**
* Fetches a named integration
*
* @param name The name of a registered integration
*/
byName(name: string): ScmIntegration | undefined;
/**
* Fetches an integration by URL
*
* @param name A URL that matches a registered integration
*/
byUrl(url: string): ScmIntegration | undefined;
};
export type ScmIntegrationPredicateTuple = {
predicate: (url: URL) => boolean;
integration: ScmIntegration;
};
export type ScmIntegrationFactory = (options: {
config: Config;
}) => ScmIntegrationPredicateTuple[];