Merge pull request #22145 from Bonial-International-GmbH/pjungermann/new-backend/tech-insights

new backend system: tech-insights
This commit is contained in:
Fredrik Adelöw
2024-01-31 15:27:24 +01:00
committed by GitHub
45 changed files with 1601 additions and 100 deletions
@@ -13,7 +13,17 @@ To add this FactChecker into your Tech Insights you need to install the module i
yarn add --cwd packages/backend @backstage/plugin-tech-insights-backend-module-jsonfc
```
and modify the `techInsights.ts` file to contain a reference to the FactCheckers implementation.
### Add to the backend
```ts title="packages/backend/src/index.ts"
backend.add(import('@backstage/plugin-tech-insights-backend-module-jsonfc'));
```
This setup requires checks to be provided using the config.
### Add to the backend (old)
Modify the `techInsights.ts` file to contain a reference to the FactCheckers implementation.
```diff
+import { JsonRulesEngineFactCheckerFactory } from '@backstage/plugin-tech-insights-backend-module-jsonfc';
@@ -34,7 +44,7 @@ and modify the `techInsights.ts` file to contain a reference to the FactCheckers
});
```
By default this implementation comes with an in-memory storage to store checks. You can inject an additional data store by adding an implementation of `TechInsightCheckRegistry` into the constructor options when creating a `JsonRulesEngineFactCheckerFactory`. That can be done as follows
By default, this implementation comes with an in-memory storage to store checks. You can inject an additional data store by adding an implementation of `TechInsightCheckRegistry` into the constructor options when creating a `JsonRulesEngineFactCheckerFactory`. That can be done as follows
```diff
const myTechInsightCheckRegistry: TechInsightCheckRegistry<MyCheckType> = // snip
@@ -46,7 +56,7 @@ By default this implementation comes with an in-memory storage to store checks.
```
## Adding checks
## Adding checks in code
Checks for this FactChecker are constructed as [`json-rules-engine` compatible JSON rules](https://github.com/CacheControl/json-rules-engine/blob/master/docs/rules.md#conditions). A check could look like the following for example:
@@ -86,6 +96,28 @@ export const exampleCheck: TechInsightJsonRuleCheck = {
};
```
## Adding checks in config
Example:
```yaml title="app-config.yaml"
techInsights:
factChecker:
checks:
groupOwnerCheck:
type: json-rules-engine
name: Group Owner Check
description: Verifies that a group has been set as the spec.owner for this entity
factIds:
- entityOwnershipFactRetriever
rule:
conditions:
all:
- fact: hasGroupOwner
operator: equal
value: true
```
### More than one `factIds` for a check.
When more than one is supplied, the requested fact **MUST** be present in at least one of the fact retrievers.
@@ -3,9 +3,11 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { BackendFeature } from '@backstage/backend-plugin-api';
import { BooleanCheckResult } from '@backstage/plugin-tech-insights-common';
import { CheckResponse } from '@backstage/plugin-tech-insights-common';
import { CheckValidationResponse } from '@backstage/plugin-tech-insights-node';
import { Config } from '@backstage/config';
import { FactChecker } from '@backstage/plugin-tech-insights-node';
import { Logger } from 'winston';
import { Operator } from 'json-rules-engine';
@@ -63,6 +65,11 @@ export class JsonRulesEngineFactCheckerFactory {
constructor(options: JsonRulesEngineFactCheckerFactoryOptions);
// (undocumented)
construct(repository: TechInsightsStore): JsonRulesEngineFactChecker;
// (undocumented)
static fromConfig(
config: Config,
options: Omit<JsonRulesEngineFactCheckerFactoryOptions, 'checks'>,
): JsonRulesEngineFactCheckerFactory;
}
// @public
@@ -104,5 +111,9 @@ export interface TechInsightJsonRuleCheck extends TechInsightCheck {
rule: Rule;
}
// @public
const techInsightsModuleJsonRulesEngineFactCheckerFactory: () => BackendFeature;
export default techInsightsModuleJsonRulesEngineFactCheckerFactory;
// (No @packageDocumentation comment for this package)
```
@@ -0,0 +1,183 @@
{
"$schema": "https://backstage.io/schema/config-v1",
"type": "object",
"properties": {
"techInsights": {
"type": "object",
"properties": {
"factChecker": {
"type": "object",
"properties": {
"checks": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/check"
}
}
}
}
}
}
},
"$defs": {
"check": {
"type": "object",
"required": ["type", "name", "description", "factIds", "rule"],
"properties": {
"type": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"factIds": {
"type": "array",
"items": {
"type": "string"
}
},
"rule": {
"$ref": "#/$defs/rule"
}
}
},
"conditionProperties": {
"type": "object",
"required": ["fact", "operator", "value"],
"properties": {
"fact": {
"type": "string"
},
"operator": {
"type": "string"
},
"value": {
"oneOf": [
{
"type": "object",
"required": ["fact"],
"properties": {
"fact": {
"type": "string"
}
}
},
{}
]
},
"path": {
"type": "string"
},
"name": {
"type": "string"
},
"priority": {
"type": "number"
},
"params": {
"type": "object",
"additionalProperties": true
}
}
},
"conditionReference": {
"type": "object",
"required": ["condition"],
"properties": {
"condition": {
"type": "string"
},
"name": {
"type": "string"
},
"priority": {
"type": "number"
}
}
},
"nestedCondition": {
"oneOf": [
{
"$ref": "#/$defs/conditionProperties"
},
{
"$ref": "#/$defs/topLevelCondition"
}
]
},
"rule": {
"type": "string",
"required": ["conditions"],
"properties": {
"conditions": {
"$ref": "#/$defs/topLevelCondition"
},
"name": {
"type": "string"
},
"priority": {
"type": "number"
},
"successMetadata": {
"type": "object",
"additionalProperties": true
},
"failureMetadata": {
"type": "object",
"additionalProperties": true
}
}
},
"topLevelCondition": {
"oneOf": [
{
"type": "object",
"required": ["all"],
"properties": {
"all": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/$defs/nestedCondition"
}
]
}
}
}
},
{
"type": "object",
"required": ["any"],
"properties": {
"any": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/$defs/nestedCondition"
}
]
}
}
}
},
{
"type": "object",
"required": ["not"],
"properties": {
"not": {
"$ref": "#/$defs/nestedCondition"
}
}
},
{
"$ref": "#/$defs/conditionReference"
}
]
}
}
}
@@ -34,9 +34,12 @@
},
"dependencies": {
"@backstage/backend-common": "workspace:^",
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/config": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/plugin-tech-insights-common": "workspace:^",
"@backstage/plugin-tech-insights-node": "workspace:^",
"@backstage/types": "workspace:^",
"ajv": "^8.10.0",
"json-rules-engine": "^6.1.2",
"lodash": "^4.17.21",
@@ -44,9 +47,12 @@
"winston": "^3.2.1"
},
"devDependencies": {
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^"
},
"files": [
"config.json",
"dist"
]
],
"configSchema": "config.json"
}
@@ -13,15 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
JsonRulesEngineFactCheckerFactory,
JsonRulesEngineFactChecker,
} from './service/JsonRulesEngineFactChecker';
export { JSON_RULE_ENGINE_CHECK_TYPE } from './constants';
export type {
JsonRulesEngineFactCheckerFactoryOptions,
JsonRulesEngineFactCheckerOptions,
} from './service/JsonRulesEngineFactChecker';
export { techInsightsModuleJsonRulesEngineFactCheckerFactory as default } from './module';
export * from './service';
export type {
JsonRuleCheckResponse,
JsonRuleBooleanCheckResult,
@@ -0,0 +1,17 @@
/*
* Copyright 2024 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.
*/
export * from './techInsightsModuleJsonRulesEngineFactCheckerFactory';
@@ -0,0 +1,68 @@
/*
* Copyright 2024 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 { mockServices, startTestBackend } from '@backstage/backend-test-utils';
import { techInsightsFactCheckerFactoryExtensionPoint } from '@backstage/plugin-tech-insights-node';
import { JSON_RULE_ENGINE_CHECK_TYPE } from '../constants';
import { techInsightsModuleJsonRulesEngineFactCheckerFactory } from './techInsightsModuleJsonRulesEngineFactCheckerFactory';
describe('techInsightsModuleJsonRulesEngineFactCheckerFactory', () => {
it('should register the factory', async () => {
const extensionPoint = {
setFactCheckerFactory: jest.fn(),
} satisfies Partial<typeof techInsightsFactCheckerFactoryExtensionPoint.T>;
await startTestBackend({
extensionPoints: [
[techInsightsFactCheckerFactoryExtensionPoint, extensionPoint],
],
features: [
techInsightsModuleJsonRulesEngineFactCheckerFactory(),
mockServices.logger.factory(),
mockServices.rootConfig.factory({
data: {
techInsights: {
factChecker: {
checks: {
groupOwnerCheck: {
type: JSON_RULE_ENGINE_CHECK_TYPE,
name: 'Group Owner Check',
description:
'Verifies that a group has been set as the spec.owner for this entity',
factIds: ['entityOwnershipFactRetriever'],
rule: {
conditions: {
all: [
{
fact: 'hasGroupOwner',
operator: 'equal',
value: true,
},
],
},
},
},
},
},
},
},
}),
],
});
expect(extensionPoint.setFactCheckerFactory).toHaveBeenCalled();
});
});
@@ -0,0 +1,51 @@
/*
* Copyright 2024 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,
} from '@backstage/backend-plugin-api';
import { techInsightsFactCheckerFactoryExtensionPoint } from '@backstage/plugin-tech-insights-node';
import { JsonRulesEngineFactCheckerFactory } from '../service';
/**
* Sets a JsonRulesEngineFactCheckerFactory as FactCheckerFactory
* loading checks from the config.
*
* @public
*/
export const techInsightsModuleJsonRulesEngineFactCheckerFactory =
createBackendModule({
pluginId: 'tech-insights',
moduleId: 'json-rules-engine-fact-checker-factory',
register(env) {
env.registerInit({
deps: {
config: coreServices.rootConfig,
logger: coreServices.logger,
techInsights: techInsightsFactCheckerFactoryExtensionPoint,
},
async init({ config, logger, techInsights }) {
const winstonLogger = loggerToWinstonLogger(logger);
const factory = JsonRulesEngineFactCheckerFactory.fromConfig(config, {
logger: winstonLogger,
});
techInsights.setFactCheckerFactory(factory);
},
});
},
});
@@ -14,7 +14,9 @@
* limitations under the License.
*/
import { JsonRuleBooleanCheckResult, TechInsightJsonRuleCheck } from '../types';
import { Config } from '@backstage/config';
import { isError } from '@backstage/errors';
import { FactResponse } from '@backstage/plugin-tech-insights-common';
import {
FactChecker,
TechInsightCheckRegistry,
@@ -22,20 +24,20 @@ import {
TechInsightsStore,
CheckValidationResponse,
} from '@backstage/plugin-tech-insights-node';
import { FactResponse } from '@backstage/plugin-tech-insights-common';
import Ajv, { SchemaObject } from 'ajv';
import {
Engine,
EngineResult,
Operator,
TopLevelCondition,
} from 'json-rules-engine';
import { DefaultCheckRegistry } from './CheckRegistry';
import { Logger } from 'winston';
import { pick } from 'lodash';
import Ajv, { SchemaObject } from 'ajv';
import * as validationSchema from './validation-schema.json';
import { Logger } from 'winston';
import { JSON_RULE_ENGINE_CHECK_TYPE } from '../constants';
import { isError } from '@backstage/errors';
import { JsonRuleBooleanCheckResult, TechInsightJsonRuleCheck } from '../types';
import { DefaultCheckRegistry } from './CheckRegistry';
import { readChecksFromConfig } from './config';
import * as validationSchema from './validation-schema.json';
const noopEvent = {
type: 'noop',
@@ -340,7 +342,7 @@ export class JsonRulesEngineFactChecker
*
* Implementation of checkRegistry is optional.
* If there is a need to use persistent storage for checks, it is recommended to inject a storage implementation here.
* Otherwise an in-memory option is instantiated and used.
* Otherwise, an in-memory option is instantiated and used.
*/
export type JsonRulesEngineFactCheckerFactoryOptions = {
checks: TechInsightJsonRuleCheck[];
@@ -354,7 +356,7 @@ export type JsonRulesEngineFactCheckerFactoryOptions = {
*
* Factory to construct JsonRulesEngineFactChecker
* Can be constructed with optional implementation of CheckInsightCheckRegistry if needed.
* Otherwise defaults to using in-memory CheckRegistry
* Otherwise, defaults to using in-memory CheckRegistry.
*/
export class JsonRulesEngineFactCheckerFactory {
private readonly checks: TechInsightJsonRuleCheck[];
@@ -362,6 +364,18 @@ export class JsonRulesEngineFactCheckerFactory {
private readonly checkRegistry?: TechInsightCheckRegistry<TechInsightJsonRuleCheck>;
private readonly operators?: Operator[];
static fromConfig(
config: Config,
options: Omit<JsonRulesEngineFactCheckerFactoryOptions, 'checks'>,
): JsonRulesEngineFactCheckerFactory {
const checks = readChecksFromConfig(config);
return new JsonRulesEngineFactCheckerFactory({
...options,
checks,
});
}
constructor(options: JsonRulesEngineFactCheckerFactoryOptions) {
this.logger = options.logger;
this.checks = options.checks;
@@ -0,0 +1,164 @@
/*
* Copyright 2024 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 { ConfigReader } from '@backstage/config';
import { JSON_RULE_ENGINE_CHECK_TYPE } from '../constants';
import { readChecksFromConfig } from './config';
describe('config', () => {
describe('readChecksFromConfig', () => {
it('no config return empty checks array', () => {
const config = new ConfigReader({});
const checks = readChecksFromConfig(config);
expect(checks).toHaveLength(0);
});
it('empty checks config return empty checks array', () => {
const config = new ConfigReader({
techInsights: {
factChecker: {},
},
});
const checks = readChecksFromConfig(config);
expect(checks).toHaveLength(0);
});
it('with checks return parsed checks', () => {
const config = new ConfigReader({
techInsights: {
factChecker: {
checks: {
fooCheck: {
type: JSON_RULE_ENGINE_CHECK_TYPE,
name: 'Foo Check',
description: 'Verifies foo',
factIds: ['fooFactRetriever'],
rule: {
conditions: {
all: [
{
fact: 'numFoo',
operator: 'greaterThanInclusive',
value: 1,
},
{
fact: 'hasFoo',
operator: 'equal',
value: true,
},
],
},
},
},
barCheck: {
type: JSON_RULE_ENGINE_CHECK_TYPE,
name: 'Bar Check',
description: 'Verifies bar',
factIds: ['barFactRetriever'],
rule: {
conditions: {
any: [
{
fact: 'barEnabled',
operator: 'equal',
value: false,
},
{
fact: 'hasBar',
operator: 'equal',
value: true,
},
],
},
},
},
bazCheck: {
type: JSON_RULE_ENGINE_CHECK_TYPE,
name: 'Baz Check',
description: 'Verifies baz',
factIds: ['bazFactRetriever'],
rule: {
conditions: {
not: {
fact: 'bazConfig',
operator: 'equal',
value: { invalid: true },
},
},
},
},
},
},
},
});
const checks = readChecksFromConfig(config);
expect(checks).toHaveLength(3);
expect(checks.map(check => check.id)).toEqual([
'fooCheck',
'barCheck',
'bazCheck',
]);
const fooCheck = checks.find(check => check.id === 'fooCheck')!;
expect(fooCheck.name).toEqual('Foo Check');
expect(fooCheck.rule.conditions).toEqual({
all: [
{
fact: 'numFoo',
operator: 'greaterThanInclusive',
value: 1,
},
{
fact: 'hasFoo',
operator: 'equal',
value: true,
},
],
});
const barCheck = checks.find(check => check.id === 'barCheck')!;
expect(barCheck.name).toEqual('Bar Check');
expect(barCheck.rule.conditions).toEqual({
any: [
{
fact: 'barEnabled',
operator: 'equal',
value: false,
},
{
fact: 'hasBar',
operator: 'equal',
value: true,
},
],
});
const bazCheck = checks.find(check => check.id === 'bazCheck')!;
expect(bazCheck.name).toEqual('Baz Check');
expect(bazCheck.rule.conditions).toEqual({
not: {
fact: 'bazConfig',
operator: 'equal',
value: { invalid: true },
},
});
});
});
});
@@ -0,0 +1,162 @@
/*
* Copyright 2024 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 { Config } from '@backstage/config';
import { TopLevelCondition } from 'json-rules-engine';
import { Rule, TechInsightJsonRuleCheck } from '../types';
// copy of non-exported `ConditionProperties` from 'json-rules-engine'
interface ConditionProperties {
fact: string;
operator: string;
value: { fact: string } | any;
path?: string;
priority?: number;
params?: Record<string, any>;
name?: string;
}
// copy of non-exported `NestedCondition` from 'json-rules-engine'
type NestedCondition = ConditionProperties | TopLevelCondition;
function readRuleConditionProperties(config: Config): ConditionProperties {
const fact = config.getString('fact');
const name = config.getOptionalString('name');
const operator = config.getString('operator');
const params = config.getOptionalConfig('params')?.get<Record<string, any>>();
const path = config.getOptionalString('path');
const priority = config.getOptionalNumber('priority');
const value: { fact: string } | any = config.get('value');
return {
fact,
name,
operator,
params,
path,
priority,
value,
};
}
function readRuleNestedCondition(config: Config): NestedCondition {
if (config.has('fact')) {
return readRuleConditionProperties(config);
}
return readRuleTopLevelCondition(config);
}
function readRuleTopLevelCondition(config: Config): TopLevelCondition {
const base = {
name: config.getOptionalString('name'),
priority: config.getOptionalNumber('priority'),
};
if (config.has('all')) {
const all = config
.getConfigArray('all')
.map(conditionConfig => readRuleNestedCondition(conditionConfig));
return {
...base,
all,
};
}
if (config.has('any')) {
const any = config
.getConfigArray('any')
.map(conditionConfig => readRuleNestedCondition(conditionConfig));
return {
...base,
any,
};
}
if (config.has('not')) {
const not = readRuleNestedCondition(config.getConfig('not'));
return {
...base,
not,
};
}
const condition = config.getString('condition');
return {
...base,
condition,
};
}
function readRuleFromRuleConfig(config: Config): Rule {
const conditions = readRuleTopLevelCondition(config.getConfig('conditions'));
const name = config.getOptionalString('name');
const priority = config.getOptionalNumber('priority');
return {
conditions,
name,
priority,
};
}
function readCheckFromCheckConfig(
id: string,
config: Config,
): TechInsightJsonRuleCheck {
const type = config.getString('type');
const name = config.getString('name');
const description = config.getString('description');
const factIds = config.getStringArray('factIds');
const successMetadata = config
.getOptionalConfig('successMetadata')
?.get<Record<string, any>>();
const failureMetadata = config
.getOptionalConfig('failureMetadata')
?.get<Record<string, any>>();
const rule = readRuleFromRuleConfig(config.getConfig('rule'));
return {
description,
factIds,
failureMetadata,
id,
name,
rule,
successMetadata,
type,
};
}
export function readChecksFromConfig(
config: Config,
): TechInsightJsonRuleCheck[] {
const key = 'techInsights.factChecker.checks';
if (!config.has(key)) {
return [];
}
const checksConfig = config.getConfig(key);
const checks: TechInsightJsonRuleCheck[] = [];
checksConfig.keys().forEach(checkId => {
const checkConfig = checksConfig.getConfig(checkId);
checks.push(readCheckFromCheckConfig(checkId, checkConfig));
});
return checks;
}
@@ -0,0 +1,25 @@
/*
* Copyright 2024 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.
*/
export {
JsonRulesEngineFactCheckerFactory,
JsonRulesEngineFactChecker,
} from './JsonRulesEngineFactChecker';
export type {
JsonRulesEngineFactCheckerFactoryOptions,
JsonRulesEngineFactCheckerOptions,
} from './JsonRulesEngineFactChecker';
+25
View File
@@ -15,6 +15,31 @@ yarn add --cwd packages/backend @backstage/plugin-tech-insights-backend
### Adding the plugin to your `packages/backend`
```ts title="packages/backend/src/index.ts"
backend.add(import('@backstage/plugin-tech-insights-backend'));
```
You can use the extension points [@backstage/plugin-tech-insights-node](../tech-insights-node)
to add your `FactRetriever` or set a `FactCheckerFactory`.
The built-in `FactRetrievers`:
- `entityMetadataFactRetriever`
- `entityOwnershipFactRetriever`
- `techdocsFactRetriever`
`FactRetrievers` only get registered if they get configured:
```yaml title="app-config.yaml"
techInsights:
factRetrievers:
entityOwnershipFactRetriever:
cadence: '*/15 * * * *'
lifecycle: { timeToLive: { weeks: 2 } }
```
### Adding the plugin to your `packages/backend` (old)
You'll need to add the plugin to the router in your `backend` package. You can
do this by creating a file called `packages/backend/src/plugins/techInsights.ts`. An example content for `techInsights.ts` could be something like this.
+16 -24
View File
@@ -3,6 +3,7 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { BackendFeature } from '@backstage/backend-plugin-api';
import { CheckResult } from '@backstage/plugin-tech-insights-common';
import { Config } from '@backstage/config';
import { Duration } from 'luxon';
@@ -12,14 +13,14 @@ 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-common';
import { FactRetrieverRegistry as FactRetrieverRegistry_2 } from '@backstage/plugin-tech-insights-node';
import { HumanDuration } from '@backstage/types';
import { Logger } from 'winston';
import { PersistenceContext as PersistenceContext_2 } from '@backstage/plugin-tech-insights-node';
import { PluginDatabaseManager } from '@backstage/backend-common';
import { PluginEndpointDiscovery } from '@backstage/backend-common';
import { PluginTaskScheduler } from '@backstage/backend-tasks';
import { TechInsightCheck } from '@backstage/plugin-tech-insights-node';
import { TechInsightsStore } from '@backstage/plugin-tech-insights-node';
import { TokenManager } from '@backstage/backend-common';
// @public
@@ -63,30 +64,17 @@ export type FactRetrieverRegistrationOptions = {
initialDelay?: Duration | HumanDuration;
};
// @public (undocumented)
export interface FactRetrieverRegistry {
// (undocumented)
get(retrieverReference: string): Promise<FactRetrieverRegistration>;
// (undocumented)
getSchemas(): Promise<FactSchema[]>;
// (undocumented)
listRegistrations(): Promise<FactRetrieverRegistration[]>;
// (undocumented)
listRetrievers(): Promise<FactRetriever[]>;
// (undocumented)
register(registration: FactRetrieverRegistration): Promise<void>;
}
// @public @deprecated (undocumented)
export type FactRetrieverRegistry = FactRetrieverRegistry_2;
// @public
export const initializePersistenceContext: (
database: PluginDatabaseManager,
options?: PersistenceContextOptions,
) => Promise<PersistenceContext>;
) => Promise<PersistenceContext_2>;
// @public
export type PersistenceContext = {
techInsightsStore: TechInsightsStore;
};
// @public @deprecated (undocumented)
export type PersistenceContext = PersistenceContext_2;
// @public
export type PersistenceContextOptions = {
@@ -101,7 +89,7 @@ export interface RouterOptions<
config: Config;
factChecker?: FactChecker<CheckType, CheckResultType>;
logger: Logger;
persistenceContext: PersistenceContext;
persistenceContext: PersistenceContext_2;
}
// @public
@@ -113,7 +101,7 @@ export type TechInsightsContext<
CheckResultType extends CheckResult,
> = {
factChecker?: FactChecker<CheckType, CheckResultType>;
persistenceContext: PersistenceContext;
persistenceContext: PersistenceContext_2;
factRetrieverEngine: FactRetrieverEngine;
};
@@ -129,16 +117,20 @@ export interface TechInsightsOptions<
// (undocumented)
discovery: PluginEndpointDiscovery;
factCheckerFactory?: FactCheckerFactory<CheckType, CheckResultType>;
factRetrieverRegistry?: FactRetrieverRegistry;
factRetrieverRegistry?: FactRetrieverRegistry_2;
factRetrievers?: FactRetrieverRegistration[];
// (undocumented)
logger: Logger;
persistenceContext?: PersistenceContext;
persistenceContext?: PersistenceContext_2;
// (undocumented)
scheduler: PluginTaskScheduler;
// (undocumented)
tokenManager: TokenManager;
}
// @public
const techInsightsPlugin: () => BackendFeature;
export default techInsightsPlugin;
// (No @packageDocumentation comment for this package)
```
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright 2024 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 { HumanDuration } from '@backstage/types';
export interface Config {
/** Configuration options for the tech-insights plugin */
techInsights?: {
/** Configuration options for fact retrievers */
factRetrievers?: {
/** Configuration for a fact retriever and its registration identified by its name. */
[name: string]: {
/** A cron expression to indicate when the fact retriever should be triggered. */
cadence: string;
/** Optional duration of the initial delay. */
initialDelay?: HumanDuration;
/** Optional lifecycle definition indicating the cleanup logic of facts when this retriever is run. */
lifecycle?: { timeToLive: HumanDuration } | { maxItems: number };
/** Optional duration to determine how long the fact retriever should be allowed to run, defaults to 5 minutes. */
timeout?: HumanDuration;
};
};
};
}
+4 -1
View File
@@ -34,6 +34,7 @@
},
"dependencies": {
"@backstage/backend-common": "workspace:^",
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/backend-tasks": "workspace:^",
"@backstage/catalog-client": "workspace:^",
"@backstage/catalog-model": "workspace:^",
@@ -63,7 +64,9 @@
"wait-for-expect": "^3.0.2"
},
"files": [
"config.d.ts",
"dist",
"migrations/**/*.{js,d.ts}"
]
],
"configSchema": "config.d.ts"
}
@@ -0,0 +1,32 @@
/*
* Copyright 2024 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 {
FactRetrieverRegistry as FactRetrieverRegistry_,
PersistenceContext as PersistenceContext_,
} from '@backstage/plugin-tech-insights-node';
/**
* @public
* @deprecated Use FactRetrieverRegistry from `@backstage/plugin-tech-insights-node` instead.
*/
export type FactRetrieverRegistry = FactRetrieverRegistry_;
/**
* @public
* @deprecated Use PersistenceContext from `@backstage/plugin-tech-insights-node` instead.
*/
export type PersistenceContext = PersistenceContext_;
+3 -18
View File
@@ -14,21 +14,6 @@
* limitations under the License.
*/
export * from './service/router';
export type { RouterOptions } from './service/router';
export { buildTechInsightsContext } from './service/techInsightsContextBuilder';
export { initializePersistenceContext } from './service/persistence/persistenceContext';
export type {
TechInsightsOptions,
TechInsightsContext,
} from './service/techInsightsContextBuilder';
export type { FactRetrieverEngine } from './service/fact/FactRetrieverEngine';
export type {
PersistenceContext,
PersistenceContextOptions,
} 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';
export { techInsightsPlugin as default } from './plugin';
export * from './deprecated';
export * from './service';
@@ -0,0 +1,76 @@
/*
* Copyright 2024 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 { ConfigReader } from '@backstage/config';
import { FactRetriever, TTL } from '@backstage/plugin-tech-insights-node';
import { createFactRetrieverRegistrationFromConfig } from './config';
describe('config', () => {
const mockFactRetriever = jest.fn() as unknown as FactRetriever;
describe('createFactRetrieverRegistrationFromConfig', () => {
it('no config return undefined', () => {
const config = new ConfigReader({});
const registration = createFactRetrieverRegistrationFromConfig(
config,
'any',
mockFactRetriever,
);
expect(registration).toBeUndefined();
});
it('no entry for fact retriever return undefined', () => {
const config = new ConfigReader({
techInsights: {
factRetrievers: {},
},
});
const registration = createFactRetrieverRegistrationFromConfig(
config,
'any',
mockFactRetriever,
);
expect(registration).toBeUndefined();
});
it('with config for fact retriever return registration', () => {
const config = new ConfigReader({
techInsights: {
factRetrievers: {
any: {
cadence: '*/15 * * * *',
lifecycle: { timeToLive: { weeks: 2 } },
},
},
},
});
const registration = createFactRetrieverRegistrationFromConfig(
config,
'any',
mockFactRetriever,
);
expect(registration).toBeDefined();
expect(registration!.cadence).toEqual('*/15 * * * *');
expect((registration!.lifecycle! as TTL).timeToLive).toEqual({
weeks: 2,
});
});
});
});
@@ -0,0 +1,94 @@
/*
* Copyright 2024 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 { Config, readDurationFromConfig } from '@backstage/config';
import {
FactLifecycle,
FactRetriever,
FactRetrieverRegistration,
} from '@backstage/plugin-tech-insights-node';
import {
createFactRetrieverRegistration,
FactRetrieverRegistrationOptions,
} from '../service';
type FactRetrieverConfig = Omit<
FactRetrieverRegistrationOptions,
'factRetriever'
>;
function readLifecycleConfig(
config: Config | undefined,
): FactLifecycle | undefined {
if (!config) {
return undefined;
}
if (config.has('maxItems')) {
return {
maxItems: config.getNumber('maxItems'),
};
}
return {
timeToLive: readDurationFromConfig(config.getConfig('timeToLive')),
};
}
function readFactRetrieverConfig(
config: Config,
name: string,
): FactRetrieverConfig | undefined {
const factRetrieverConfig = config.getOptionalConfig(
`techInsights.factRetrievers.${name}`,
);
if (!factRetrieverConfig) {
return undefined;
}
const cadence = factRetrieverConfig.getString('cadence');
const initialDelay = factRetrieverConfig.has('initialDelay')
? readDurationFromConfig(factRetrieverConfig.getConfig('initialDelay'))
: undefined;
const lifecycle = readLifecycleConfig(
factRetrieverConfig.getOptionalConfig('lifecycle'),
);
const timeout = factRetrieverConfig.has('timeout')
? readDurationFromConfig(factRetrieverConfig.getConfig('timeout'))
: undefined;
return {
cadence,
initialDelay,
lifecycle,
timeout,
};
}
export function createFactRetrieverRegistrationFromConfig(
config: Config,
name: string,
factRetriever: FactRetriever,
): FactRetrieverRegistration | undefined {
const factRetrieverConfig = readFactRetrieverConfig(config, name);
return factRetrieverConfig
? createFactRetrieverRegistration({
...factRetrieverConfig,
factRetriever,
})
: undefined;
}
@@ -0,0 +1,17 @@
/*
* Copyright 2024 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.
*/
export * from './plugin';
@@ -0,0 +1,50 @@
/*
* Copyright 2024 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 { mockServices, startTestBackend } from '@backstage/backend-test-utils';
import { techInsightsPlugin } from './plugin';
describe('techInsightsPlugin', () => {
it('should register tech-insights plugin and its router', async () => {
const httpRouterMock = mockServices.httpRouter.mock();
await startTestBackend({
extensionPoints: [],
features: [
techInsightsPlugin(),
httpRouterMock.factory,
mockServices.database.factory(),
mockServices.logger.factory(),
mockServices.rootConfig.factory({
data: {
techInsights: {
factRetrievers: {
entityOwnershipFactRetriever: {
cadence: '*/15 * * * *',
lifecycle: { timeToLive: { weeks: 2 } },
},
},
},
},
}),
mockServices.scheduler.factory(),
mockServices.tokenManager.factory(),
],
});
expect(httpRouterMock.use).toHaveBeenCalledTimes(1);
});
});
@@ -0,0 +1,151 @@
/*
* Copyright 2024 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,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { CheckResult } from '@backstage/plugin-tech-insights-common';
import {
FactCheckerFactory,
FactRetriever,
FactRetrieverRegistration,
FactRetrieverRegistry,
PersistenceContext,
TechInsightCheck,
techInsightsFactCheckerFactoryExtensionPoint,
techInsightsFactRetrieverRegistryExtensionPoint,
techInsightsFactRetrieversExtensionPoint,
techInsightsPersistenceContextExtensionPoint,
} from '@backstage/plugin-tech-insights-node';
import {
buildTechInsightsContext,
createRouter,
entityMetadataFactRetriever,
entityOwnershipFactRetriever,
techdocsFactRetriever,
} from '../service';
import { createFactRetrieverRegistrationFromConfig } from './config';
/**
* The tech-insights backend plugin.
*
* @public
*/
export const techInsightsPlugin = createBackendPlugin({
pluginId: 'tech-insights',
register(env) {
let factCheckerFactory:
| FactCheckerFactory<TechInsightCheck, CheckResult>
| undefined = undefined;
env.registerExtensionPoint(techInsightsFactCheckerFactoryExtensionPoint, {
setFactCheckerFactory<
CheckType extends TechInsightCheck,
CheckResultType extends CheckResult,
>(factory: FactCheckerFactory<CheckType, CheckResultType>): void {
factCheckerFactory = factory;
},
});
let factRetrieverRegistry: FactRetrieverRegistry | undefined = undefined;
env.registerExtensionPoint(
techInsightsFactRetrieverRegistryExtensionPoint,
{
setFactRetrieverRegistry(registry: FactRetrieverRegistry): void {
factRetrieverRegistry = registry;
},
},
);
// initialized with built-in fact retrievers
// only added as registration if there is config for them
const addedFactRetrievers: Record<string, FactRetriever> = {
entityMetadataFactRetriever,
entityOwnershipFactRetriever,
techdocsFactRetriever,
};
env.registerExtensionPoint(techInsightsFactRetrieversExtensionPoint, {
addFactRetrievers(factRetrievers: Record<string, FactRetriever>): void {
Object.entries(factRetrievers).forEach(([key, value]) => {
addedFactRetrievers[key] = value;
});
},
});
let persistenceContext: PersistenceContext | undefined = undefined;
env.registerExtensionPoint(techInsightsPersistenceContextExtensionPoint, {
setPersistenceContext(context: PersistenceContext): void {
persistenceContext = context;
},
});
env.registerInit({
deps: {
config: coreServices.rootConfig,
database: coreServices.database,
discovery: coreServices.discovery,
httpRouter: coreServices.httpRouter,
logger: coreServices.logger,
scheduler: coreServices.scheduler,
tokenManager: coreServices.tokenManager,
},
async init({
config,
database,
discovery,
httpRouter,
logger,
scheduler,
tokenManager,
}) {
const winstonLogger = loggerToWinstonLogger(logger);
const factRetrievers: FactRetrieverRegistration[] = Object.entries(
addedFactRetrievers,
)
.map(([name, factRetriever]) =>
createFactRetrieverRegistrationFromConfig(
config,
name,
factRetriever,
),
)
.filter(registration => registration) as FactRetrieverRegistration[];
const context = await buildTechInsightsContext({
config,
database,
discovery,
factCheckerFactory,
factRetrieverRegistry,
factRetrievers,
logger: winstonLogger,
persistenceContext,
scheduler,
tokenManager,
});
httpRouter.use(
await createRouter({
...context,
config,
logger: winstonLogger,
}),
);
},
});
},
});
@@ -17,11 +17,11 @@
import {
FactRetriever,
FactRetrieverRegistration,
FactRetrieverRegistry,
FactSchemaDefinition,
TechInsightFact,
TechInsightsStore,
} from '@backstage/plugin-tech-insights-node';
import { FactRetrieverRegistry } from './FactRetrieverRegistry';
import {
DefaultFactRetrieverEngine,
FactRetrieverEngine,
@@ -18,10 +18,10 @@ import {
FactRetriever,
FactRetrieverContext,
FactRetrieverRegistration,
FactRetrieverRegistry,
TechInsightFact,
TechInsightsStore,
} from '@backstage/plugin-tech-insights-node';
import { FactRetrieverRegistry } from './FactRetrieverRegistry';
import { Logger } from 'winston';
import { PluginTaskScheduler } from '@backstage/backend-tasks';
import { Duration } from 'luxon';
@@ -17,22 +17,11 @@
import {
FactRetriever,
FactRetrieverRegistration,
FactRetrieverRegistry,
} from '@backstage/plugin-tech-insights-node';
import { FactSchema } from '@backstage/plugin-tech-insights-common';
import { ConflictError, NotFoundError } from '@backstage/errors';
/**
* @public
*
*/
export interface FactRetrieverRegistry {
register(registration: FactRetrieverRegistration): Promise<void>;
get(retrieverReference: string): Promise<FactRetrieverRegistration>;
listRetrievers(): Promise<FactRetriever[]>;
listRegistrations(): Promise<FactRetrieverRegistration[]>;
getSchemas(): Promise<FactSchema[]>;
}
/**
* A basic in memory fact retriever registry.
*
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './entityOwnershipFactRetriever';
export * from './entityMetadataFactRetriever';
export * from './entityOwnershipFactRetriever';
export * from './techdocsFactRetriever';
@@ -0,0 +1,20 @@
/*
* Copyright 2024 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.
*/
export { createFactRetrieverRegistration } from './createFactRetriever';
export type { FactRetrieverRegistrationOptions } from './createFactRetriever';
export type { FactRetrieverEngine } from './FactRetrieverEngine';
export * from './factRetrievers';
@@ -0,0 +1,26 @@
/*
* Copyright 2024 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.
*/
export * from './fact';
export * from './persistence';
export * from './router';
export type { RouterOptions } from './router';
export { buildTechInsightsContext } from './techInsightsContextBuilder';
export type {
TechInsightsOptions,
TechInsightsContext,
} from './techInsightsContextBuilder';
@@ -0,0 +1,18 @@
/*
* Copyright 2024 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.
*/
export { initializePersistenceContext } from './persistenceContext';
export type { PersistenceContextOptions } from './persistenceContext';
@@ -20,22 +20,13 @@ import {
} from '@backstage/backend-common';
import { Logger } from 'winston';
import { TechInsightsDatabase } from './TechInsightsDatabase';
import { TechInsightsStore } from '@backstage/plugin-tech-insights-node';
import { PersistenceContext } from '@backstage/plugin-tech-insights-node';
const migrationsDir = resolvePackagePath(
'@backstage/plugin-tech-insights-backend',
'migrations',
);
/**
* A Container for persistence related components in TechInsights
*
* @public
*/
export type PersistenceContext = {
techInsightsStore: TechInsightsStore;
};
/**
* A Container for persistence context initialization options
*
@@ -24,8 +24,10 @@ import {
import { ConfigReader } from '@backstage/config';
import request from 'supertest';
import express from 'express';
import { PersistenceContext } from './persistence/persistenceContext';
import { TechInsightsStore } from '@backstage/plugin-tech-insights-node';
import {
PersistenceContext,
TechInsightsStore,
} from '@backstage/plugin-tech-insights-node';
import { DateTime } from 'luxon';
import { Knex } from 'knex';
import { TaskScheduler } from '@backstage/backend-tasks';
@@ -19,13 +19,13 @@ import Router from 'express-promise-router';
import { Config } from '@backstage/config';
import {
FactChecker,
PersistenceContext,
TechInsightCheck,
} from '@backstage/plugin-tech-insights-node';
import { CheckResult } from '@backstage/plugin-tech-insights-common';
import { Logger } from 'winston';
import { DateTime } from 'luxon';
import { PersistenceContext } from './persistence/persistenceContext';
import {
CompoundEntityRef,
parseEntityRef,
@@ -19,10 +19,7 @@ import {
FactRetrieverEngine,
} from './fact/FactRetrieverEngine';
import { Logger } from 'winston';
import {
DefaultFactRetrieverRegistry,
FactRetrieverRegistry,
} from './fact/FactRetrieverRegistry';
import { DefaultFactRetrieverRegistry } from './fact/FactRetrieverRegistry';
import { Config } from '@backstage/config';
import {
PluginDatabaseManager,
@@ -33,12 +30,11 @@ import {
FactChecker,
FactCheckerFactory,
FactRetrieverRegistration,
FactRetrieverRegistry,
PersistenceContext,
TechInsightCheck,
} from '@backstage/plugin-tech-insights-node';
import {
initializePersistenceContext,
PersistenceContext,
} from './persistence/persistenceContext';
import { initializePersistenceContext } from './persistence';
import { CheckResult } from '@backstage/plugin-tech-insights-common';
import { PluginTaskScheduler } from '@backstage/backend-tasks';
+61
View File
@@ -8,6 +8,7 @@ import { Config } from '@backstage/config';
import { DateTime } from 'luxon';
import { Duration } from 'luxon';
import { DurationLike } from 'luxon';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
import { FactSchema } from '@backstage/plugin-tech-insights-common';
import { HumanDuration } from '@backstage/types';
import { JsonValue } from '@backstage/types';
@@ -79,6 +80,20 @@ export type FactRetrieverRegistration = {
initialDelay?: Duration | HumanDuration;
};
// @public (undocumented)
export interface FactRetrieverRegistry {
// (undocumented)
get(retrieverReference: string): Promise<FactRetrieverRegistration>;
// (undocumented)
getSchemas(): Promise<FactSchema[]>;
// (undocumented)
listRegistrations(): Promise<FactRetrieverRegistration[]>;
// (undocumented)
listRetrievers(): Promise<FactRetriever[]>;
// (undocumented)
register(registration: FactRetrieverRegistration): Promise<void>;
}
// @public
export type FactSchemaDefinition = Omit<FactRetriever, 'handler'>;
@@ -92,6 +107,11 @@ export type MaxItems = {
maxItems: number;
};
// @public
export type PersistenceContext = {
techInsightsStore: TechInsightsStore;
};
// @public
export interface TechInsightCheck {
description: string;
@@ -137,6 +157,47 @@ export type TechInsightFact = {
timestamp?: DateTime;
};
// @public (undocumented)
export interface TechInsightsFactCheckerFactoryExtensionPoint {
// (undocumented)
setFactCheckerFactory<
CheckType extends TechInsightCheck,
CheckResultType extends CheckResult,
>(
factory: FactCheckerFactory<CheckType, CheckResultType>,
): void;
}
// @public
export const techInsightsFactCheckerFactoryExtensionPoint: ExtensionPoint<TechInsightsFactCheckerFactoryExtensionPoint>;
// @public (undocumented)
export interface TechInsightsFactRetrieverRegistryExtensionPoint {
// (undocumented)
setFactRetrieverRegistry(registry: FactRetrieverRegistry): void;
}
// @public
export const techInsightsFactRetrieverRegistryExtensionPoint: ExtensionPoint<TechInsightsFactRetrieverRegistryExtensionPoint>;
// @public (undocumented)
export interface TechInsightsFactRetrieversExtensionPoint {
// (undocumented)
addFactRetrievers(factRetrievers: Record<string, FactRetriever>): void;
}
// @public
export const techInsightsFactRetrieversExtensionPoint: ExtensionPoint<TechInsightsFactRetrieversExtensionPoint>;
// @public (undocumented)
export interface TechInsightsPersistenceContextExtensionPoint {
// (undocumented)
setPersistenceContext(context: PersistenceContext): void;
}
// @public
export const techInsightsPersistenceContextExtensionPoint: ExtensionPoint<TechInsightsPersistenceContextExtensionPoint>;
// @public
export interface TechInsightsStore {
getFactsBetweenTimestampsByIds(
+1
View File
@@ -33,6 +33,7 @@
},
"dependencies": {
"@backstage/backend-common": "workspace:^",
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/config": "workspace:^",
"@backstage/plugin-tech-insights-common": "workspace:^",
"@backstage/types": "workspace:^",
@@ -0,0 +1,94 @@
/*
* Copyright 2024 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 { createExtensionPoint } from '@backstage/backend-plugin-api';
import { CheckResult } from '@backstage/plugin-tech-insights-common';
import { FactCheckerFactory, TechInsightCheck } from './checks';
import { FactRetriever, FactRetrieverRegistry } from './facts';
import { PersistenceContext } from './persistence';
/**
* @public
*/
export interface TechInsightsFactRetrieversExtensionPoint {
addFactRetrievers(factRetrievers: Record<string, FactRetriever>): void;
}
/**
* An extension point that allows other plugins or modules to add fact retrievers.
*
* @public
*/
export const techInsightsFactRetrieversExtensionPoint =
createExtensionPoint<TechInsightsFactRetrieversExtensionPoint>({
id: 'tech-insights.fact-retrievers',
});
/**
* @public
*/
export interface TechInsightsFactCheckerFactoryExtensionPoint {
setFactCheckerFactory<
CheckType extends TechInsightCheck,
CheckResultType extends CheckResult,
>(
factory: FactCheckerFactory<CheckType, CheckResultType>,
): void;
}
/**
* An extension point that allows other plugins or modules to set a FactCheckerFactory.
*
* @public
*/
export const techInsightsFactCheckerFactoryExtensionPoint =
createExtensionPoint<TechInsightsFactCheckerFactoryExtensionPoint>({
id: 'tech-insights.fact-checker-factory',
});
/**
* @public
*/
export interface TechInsightsFactRetrieverRegistryExtensionPoint {
setFactRetrieverRegistry(registry: FactRetrieverRegistry): void;
}
/**
* An extension point that allows other plugins or modules to set a custom FactRetrieverRegistry.
*
* @public
*/
export const techInsightsFactRetrieverRegistryExtensionPoint =
createExtensionPoint<TechInsightsFactRetrieverRegistryExtensionPoint>({
id: 'tech-insights.fact-retriever-registry',
});
/**
* @public
*/
export interface TechInsightsPersistenceContextExtensionPoint {
setPersistenceContext(context: PersistenceContext): void;
}
/**
* An extension point that allows other plugins or modules to set a custom PersistenceContext.
*
* @public
*/
export const techInsightsPersistenceContextExtensionPoint =
createExtensionPoint<TechInsightsPersistenceContextExtensionPoint>({
id: 'tech-insights.persistence-context',
});
+11
View File
@@ -230,3 +230,14 @@ export type FactRetrieverRegistration = {
*/
initialDelay?: Duration | HumanDuration;
};
/**
* @public
*/
export interface FactRetrieverRegistry {
register(registration: FactRetrieverRegistration): Promise<void>;
get(retrieverReference: string): Promise<FactRetrieverRegistration>;
listRetrievers(): Promise<FactRetriever[]>;
listRegistrations(): Promise<FactRetrieverRegistration[]>;
getSchemas(): Promise<FactSchema[]>;
}
+1
View File
@@ -15,5 +15,6 @@
*/
export * from './checks';
export * from './extensionPoints';
export * from './facts';
export * from './persistence';
@@ -22,6 +22,15 @@ import {
import { DateTime } from 'luxon';
import { FactSchema } from '@backstage/plugin-tech-insights-common';
/**
* A Container for persistence related components in TechInsights
*
* @public
*/
export type PersistenceContext = {
techInsightsStore: TechInsightsStore;
};
/**
* TechInsights Database
*