Merge pull request #22145 from Bonial-International-GmbH/pjungermann/new-backend/tech-insights
new backend system: tech-insights
This commit is contained in:
@@ -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';
|
||||
+68
@@ -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();
|
||||
});
|
||||
});
|
||||
+51
@@ -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);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
+23
-9
@@ -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';
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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_;
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
@@ -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[]>;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user