Merge pull request #12325 from sblausten/tech-insights-context

Allow FactRetrieverRegistry to be injected into builder for flexibility
This commit is contained in:
Fredrik Adelöw
2022-07-01 08:50:01 +02:00
committed by GitHub
6 changed files with 159 additions and 7 deletions
+19 -1
View File
@@ -11,6 +11,7 @@ import { FactCheckerFactory } from '@backstage/plugin-tech-insights-node';
import { FactLifecycle } from '@backstage/plugin-tech-insights-node';
import { FactRetriever } from '@backstage/plugin-tech-insights-node';
import { FactRetrieverRegistration } from '@backstage/plugin-tech-insights-node';
import { FactSchema } from '@backstage/plugin-tech-insights-node';
import { Logger } from 'winston';
import { PluginDatabaseManager } from '@backstage/backend-common';
import { PluginEndpointDiscovery } from '@backstage/backend-common';
@@ -51,6 +52,22 @@ export type FactRetrieverRegistrationOptions = {
lifecycle?: FactLifecycle;
};
// @public (undocumented)
export interface FactRetrieverRegistry {
// (undocumented)
get(retrieverReference: string): FactRetrieverRegistration;
// (undocumented)
getSchemas(): FactSchema[];
// (undocumented)
listRegistrations(): FactRetrieverRegistration[];
// (undocumented)
listRetrievers(): FactRetriever[];
// (undocumented)
register(registration: FactRetrieverRegistration): void;
// (undocumented)
readonly retrievers: Map<string, FactRetrieverRegistration>;
}
// @public
export type PersistenceContext = {
techInsightsStore: TechInsightsStore;
@@ -91,7 +108,8 @@ export interface TechInsightsOptions<
// (undocumented)
discovery: PluginEndpointDiscovery;
factCheckerFactory?: FactCheckerFactory<CheckType, CheckResultType>;
factRetrievers: FactRetrieverRegistration[];
factRetrieverRegistry?: FactRetrieverRegistry;
factRetrievers?: FactRetrieverRegistration[];
// (undocumented)
logger: Logger;
// (undocumented)
@@ -25,5 +25,6 @@ export type {
export type { PersistenceContext } from './service/persistence/persistenceContext';
export { createFactRetrieverRegistration } from './service/fact/createFactRetriever';
export type { FactRetrieverRegistry } from './service/fact/FactRetrieverRegistry';
export type { FactRetrieverRegistrationOptions } from './service/fact/createFactRetriever';
export * from './service/fact/factRetrievers';
@@ -21,8 +21,21 @@ import {
} from '@backstage/plugin-tech-insights-node';
import { ConflictError, NotFoundError } from '@backstage/errors';
export class FactRetrieverRegistry {
private readonly retrievers = new Map<string, FactRetrieverRegistration>();
/**
* @public
*
*/
export interface FactRetrieverRegistry {
readonly retrievers: Map<string, FactRetrieverRegistration>;
register(registration: FactRetrieverRegistration): void;
get(retrieverReference: string): FactRetrieverRegistration;
listRetrievers(): FactRetriever[];
listRegistrations(): FactRetrieverRegistration[];
getSchemas(): FactSchema[];
}
export class DefaultFactRetrieverRegistry implements FactRetrieverRegistry {
readonly retrievers = new Map<string, FactRetrieverRegistration>();
constructor(retrievers: FactRetrieverRegistration[]) {
retrievers.forEach(it => {
@@ -0,0 +1,91 @@
/*
* 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 { buildTechInsightsContext } from './techInsightsContextBuilder';
import {
DatabaseManager,
getVoidLogger,
PluginDatabaseManager,
ServerTokenManager,
} from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { TaskScheduler } from '@backstage/backend-tasks';
import { DefaultFactRetrieverRegistry } from './fact/FactRetrieverRegistry';
import { Knex } from 'knex';
jest.mock('./fact/FactRetrieverRegistry');
jest.mock('./fact/FactRetrieverEngine', () => ({
FactRetrieverEngine: {
create: jest.fn().mockResolvedValue({
schedule: jest.fn(),
}),
},
}));
describe('buildTechInsightsContext', () => {
const pluginDatabase: PluginDatabaseManager = {
getClient: () => {
return Promise.resolve({
migrate: {
latest: () => {},
},
}) as unknown as Promise<Knex>;
},
};
const databaseManager: Partial<DatabaseManager> = {
forPlugin: () => pluginDatabase,
};
const manager = databaseManager as DatabaseManager;
const discoveryMock = {
getBaseUrl: (_: string) => Promise.resolve('http://mock.url'),
getExternalBaseUrl: (_: string) => Promise.resolve('http://mock.url'),
};
const scheduler = new TaskScheduler(manager, getVoidLogger()).forPlugin(
'tech-insights',
);
beforeEach(() => {
jest.clearAllMocks();
});
it('constructs the default FactRetrieverRegistry if factRetrievers but no factRetrieverRegistry are passed', () => {
buildTechInsightsContext({
database: pluginDatabase,
logger: getVoidLogger(),
factRetrievers: [],
scheduler: scheduler,
config: ConfigReader.fromConfigs([]),
discovery: discoveryMock,
tokenManager: ServerTokenManager.noop(),
});
expect(DefaultFactRetrieverRegistry).toHaveBeenCalledTimes(1);
});
it('uses factRetrieverRegistry implementation instead of the default FactRetrieverRegistry if it is passed in', () => {
const factRetrieverRegistryMock = {} as DefaultFactRetrieverRegistry;
buildTechInsightsContext({
database: pluginDatabase,
logger: getVoidLogger(),
factRetrievers: [],
factRetrieverRegistry: factRetrieverRegistryMock,
scheduler: scheduler,
config: ConfigReader.fromConfigs([]),
discovery: discoveryMock,
tokenManager: ServerTokenManager.noop(),
});
expect(DefaultFactRetrieverRegistry).not.toHaveBeenCalled();
});
});
@@ -16,7 +16,10 @@
import { FactRetrieverEngine } from './fact/FactRetrieverEngine';
import { Logger } from 'winston';
import { FactRetrieverRegistry } from './fact/FactRetrieverRegistry';
import {
DefaultFactRetrieverRegistry,
FactRetrieverRegistry,
} from './fact/FactRetrieverRegistry';
import { Config } from '@backstage/config';
import {
PluginDatabaseManager,
@@ -49,16 +52,25 @@ export interface TechInsightsOptions<
CheckResultType extends CheckResult,
> {
/**
* A collection of FactRetrieverRegistrations.
* Optional collection of FactRetrieverRegistrations (required if no factRetrieverRegistry passed in).
* Used to register FactRetrievers and their schemas and schedule an execution loop for them.
*
* Not needed if passing in your own FactRetrieverRegistry implementation. Required otherwise.
*/
factRetrievers: FactRetrieverRegistration[];
factRetrievers?: FactRetrieverRegistration[];
/**
* Optional factory exposing a `construct` method to initialize a FactChecker implementation
*/
factCheckerFactory?: FactCheckerFactory<CheckType, CheckResultType>;
/**
* Optional FactRetrieverRegistry implementation that replaces the default one.
*
* If passing this in you don't need to pass in factRetrievers also.
*/
factRetrieverRegistry?: FactRetrieverRegistry;
logger: Logger;
config: Config;
discovery: PluginEndpointDiscovery;
@@ -109,7 +121,19 @@ export const buildTechInsightsContext = async <
tokenManager,
} = options;
const factRetrieverRegistry = new FactRetrieverRegistry(factRetrievers);
const buildFactRetrieverRegistry = (): FactRetrieverRegistry => {
if (!options.factRetrieverRegistry) {
if (!factRetrievers) {
throw new Error(
'Failed to build FactRetrieverRegistry because no factRetrievers found',
);
}
return new DefaultFactRetrieverRegistry(factRetrievers);
}
return options.factRetrieverRegistry;
};
const factRetrieverRegistry = buildFactRetrieverRegistry();
const persistenceContext = await initializePersistenceContext(
await database.getClient(),