Added a for the new backend system

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2023-09-06 09:51:03 +02:00
parent 8fcaba7f50
commit fa7004d972
7 changed files with 311 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-github': minor
---
Added a `catalogModuleGithubOrgEntityProvider` for the new backend system
@@ -4,9 +4,24 @@
```ts
import { BackendFeature } from '@backstage/backend-plugin-api';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
import { TeamTransformer } from '@backstage/plugin-catalog-backend-module-github';
import { UserTransformer } from '@backstage/plugin-catalog-backend-module-github';
// @alpha
export const catalogModuleGithubEntityProvider: () => BackendFeature;
// @alpha
export const catalogModuleGithubOrgEntityProvider: () => BackendFeature;
// @alpha
export interface GithubOrgEntityProviderTransformsExtensionPoint {
setTeamTransformer(transformer: TeamTransformer): void;
setUserTransformer(transformer: UserTransformer): void;
}
// @alpha
export const githubOrgEntityProviderTransformsExtensionPoint: ExtensionPoint<GithubOrgEntityProviderTransformsExtensionPoint>;
// (No @packageDocumentation comment for this package)
```
+63
View File
@@ -183,6 +183,69 @@ export interface Config {
schedule?: TaskScheduleDefinitionConfig;
};
};
/**
* Configuration for catalogModuleGithubOrgEntityProvider
*/
githubOrg?:
| {
/**
* A stable id for this provider. Entities from this provider will
* be associated with this ID, so you should take care not to change
* it over time since that may lead to orphaned entities and/or
* conflicts.
*
* @example "ghe"
*/
id: string;
/**
* The target that this provider should consume.
*
* @example "https://mycompany.github.com"
*/
githubUrl: string;
/**
* The list of the GitHub orgs to consume. By default will consume all accessible
* orgs on the given GitHub instance (support for GitHub App integration only).
*/
orgs?: string[];
/**
* The refresh schedule to use.
*/
schedule: TaskScheduleDefinitionConfig;
}
| Array<{
/**
* A stable id for this provider. Entities from this provider will
* be associated with this ID, so you should take care not to change
* it over time since that may lead to orphaned entities and/or
* conflicts.
*
* @example "ghe"
*/
id: string;
/**
* The target that this provider should consume.
*
* @example "https://mycompany.github.com"
*/
githubUrl: string;
/**
* The list of the GitHub orgs to consume. By default will consume all accessible
* orgs on the given GitHub instance (support for GitHub App integration only).
*/
orgs?: string[];
/**
* The refresh schedule to use.
*/
schedule: TaskScheduleDefinitionConfig;
}>;
};
};
}
@@ -16,14 +16,14 @@
import { TaskScheduleDefinition } from '@backstage/backend-tasks';
import { mockServices, startTestBackend } from '@backstage/backend-test-utils';
import { EntityProvider } from '@backstage/plugin-catalog-node';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
import { Duration } from 'luxon';
import { catalogModuleGithubEntityProvider } from './catalogModuleGithubEntityProvider';
import { GithubEntityProvider } from '../providers/GithubEntityProvider';
describe('catalogModuleGithubEntityProvider', () => {
it('should register provider at the catalog extension point', async () => {
let addedProviders: Array<GithubEntityProvider> | undefined;
let addedProviders: Array<EntityProvider> | undefined;
let usedSchedule: TaskScheduleDefinition | undefined;
const extensionPoint = {
@@ -0,0 +1,77 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { TaskScheduleDefinition } from '@backstage/backend-tasks';
import { mockServices, startTestBackend } from '@backstage/backend-test-utils';
import { EntityProvider } from '@backstage/plugin-catalog-node';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
import { Duration } from 'luxon';
import { catalogModuleGithubOrgEntityProvider } from './catalogModuleGithubOrgEntityProvider';
describe('catalogModuleGithubOrgEntityProvider', () => {
it('should register provider at the catalog extension point', async () => {
let addedProviders: Array<EntityProvider> | undefined;
let usedSchedule: TaskScheduleDefinition | undefined;
const extensionPoint = {
addEntityProvider: (...providers: any) => {
addedProviders = providers;
},
};
const runner = jest.fn();
const scheduler = mockServices.scheduler.mock({
createScheduledTaskRunner(schedule) {
usedSchedule = schedule;
return { run: runner };
},
});
const config = {
catalog: {
providers: {
githubOrg: [
{
id: 'default',
githubUrl: 'https://github.com',
orgs: ['backstage'],
schedule: {
frequency: 'P1M',
timeout: 'PT3M',
},
},
],
},
},
};
await startTestBackend({
extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]],
features: [
catalogModuleGithubOrgEntityProvider(),
mockServices.rootConfig.factory({ data: config }),
scheduler.factory,
],
});
expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M'));
expect(usedSchedule?.timeout).toEqual(Duration.fromISO('PT3M'));
expect(addedProviders?.length).toEqual(1);
expect(addedProviders![0].getProviderName()).toEqual(
'GithubMultiOrgEntityProvider:default',
);
expect(runner).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,144 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { loggerToWinstonLogger } from '@backstage/backend-common';
import {
coreServices,
createBackendModule,
createExtensionPoint,
} from '@backstage/backend-plugin-api';
import {
readTaskScheduleDefinitionFromConfig,
TaskScheduleDefinition,
} from '@backstage/backend-tasks';
import { Config } from '@backstage/config';
import {
GithubMultiOrgEntityProvider,
TeamTransformer,
UserTransformer,
} from '@backstage/plugin-catalog-backend-module-github';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
/**
* Interface for {@link githubOrgEntityProviderTransformsExtensionPoint}.
*
* @alpha
*/
export interface GithubOrgEntityProviderTransformsExtensionPoint {
/**
* Set a custom transformer for transforming from GitHub users to catalog
* entities.
*/
setUserTransformer(transformer: UserTransformer): void;
/**
* Set a custom transformer for transforming from GitHub teams to catalog
* entities.
*/
setTeamTransformer(transformer: TeamTransformer): void;
}
/**
* Extension point for runtime configuration of {@link catalogModuleGithubOrgEntityProvider}.
*
* @alpha
*/
export const githubOrgEntityProviderTransformsExtensionPoint =
createExtensionPoint<GithubOrgEntityProviderTransformsExtensionPoint>({
id: 'catalog.githubOrgEntityProvider',
});
/**
* Registers the `GithubMultiOrgEntityProvider` with the catalog processing extension point.
*
* @alpha
*/
export const catalogModuleGithubOrgEntityProvider = createBackendModule({
pluginId: 'catalog',
moduleId: 'githubOrgEntityProvider',
register(env) {
let userTransformer: UserTransformer | undefined;
let teamTransformer: TeamTransformer | undefined;
env.registerExtensionPoint(
githubOrgEntityProviderTransformsExtensionPoint,
{
setUserTransformer(transformer) {
if (userTransformer) {
throw new Error('User transformer may only be set once');
}
userTransformer = transformer;
},
setTeamTransformer(transformer) {
if (teamTransformer) {
throw new Error('Team transformer may only be set once');
}
teamTransformer = transformer;
},
},
);
env.registerInit({
deps: {
catalog: catalogProcessingExtensionPoint,
config: coreServices.rootConfig,
logger: coreServices.logger,
scheduler: coreServices.scheduler,
},
async init({ catalog, config, logger, scheduler }) {
for (const definition of readDefinitionsFromConfig(config)) {
catalog.addEntityProvider(
GithubMultiOrgEntityProvider.fromConfig(config, {
id: definition.id,
githubUrl: definition.githubUrl,
orgs: definition.orgs,
schedule: scheduler.createScheduledTaskRunner(
definition.schedule,
),
logger: loggerToWinstonLogger(logger),
userTransformer,
teamTransformer,
}),
);
}
},
});
},
});
function readDefinitionsFromConfig(rootConfig: Config): Array<{
id: string;
githubUrl: string;
orgs?: string[];
schedule: TaskScheduleDefinition;
}> {
const baseKey = 'catalog.providers.githubOrg';
const baseConfig = rootConfig.getOptional(baseKey);
if (!baseConfig) {
return [];
}
const configs = Array.isArray(baseConfig)
? rootConfig.getConfigArray(baseKey)
: [rootConfig.getConfig(baseKey)];
return configs.map(c => ({
id: c.getString('id'),
githubUrl: c.getString('githubUrl'),
orgs: c.getOptionalStringArray('orgs'),
schedule: readTaskScheduleDefinitionFromConfig(c.getConfig('schedule')),
}));
}
@@ -15,3 +15,8 @@
*/
export { catalogModuleGithubEntityProvider } from './catalogModuleGithubEntityProvider';
export {
catalogModuleGithubOrgEntityProvider,
githubOrgEntityProviderTransformsExtensionPoint,
type GithubOrgEntityProviderTransformsExtensionPoint,
} from './catalogModuleGithubOrgEntityProvider';