Modifications after PR review
* Change entity filter to be an actual entity filter instead of a list of kinds or types * Modify/simplify types a little bit * Split common type libs to one for node and one isomorphic * Remove unnecessary items from FactChecker interface to simplify execution loop. Needs still matching README.md changes. Signed-off-by: Jussi Hallila <jussi@hallila.com>
This commit is contained in:
@@ -77,16 +77,18 @@ you will not have any fact retrievers present in your application. To have the i
|
||||
To create factRetrieverRegistration you need to implement `FactRetriever` interface defined in `@backstage/plugin-tech-insights-common` package. After you have implemented this interface you can wrap that into a registration object like follows:
|
||||
|
||||
```ts
|
||||
const myFactRetriever: FactRetriever = {
|
||||
import { createFactRetrieverRegistration } from './createFactRetriever';
|
||||
|
||||
const myFactRetriever = {
|
||||
/**
|
||||
* snip
|
||||
*/
|
||||
};
|
||||
|
||||
const myFactRetrieverRegistration = {
|
||||
cadence: '1 * 3 * * ', // On the first minute of the third day of the month
|
||||
factRetriever: myFactRetriever,
|
||||
};
|
||||
const myFactRetrieverRegistration = createFactRetrieverRegistration(
|
||||
'1 * 3 * * ', // On the first minute of the third day of the month
|
||||
myFactRetriever,
|
||||
);
|
||||
```
|
||||
|
||||
Then you can modify the example `techInsights.ts` file shown above like this:
|
||||
@@ -104,28 +106,25 @@ discovery,
|
||||
|
||||
### Creating Fact Retrievers
|
||||
|
||||
A Fact Retriever consist of three parts:
|
||||
A Fact Retriever consist of four parts:
|
||||
|
||||
1. `ref` - unique identifier of a fact retriever
|
||||
2. `schema` - A versioned schema defining the shape of data a fact retriever returns
|
||||
3. `handler` - An asynchronous function handling the logic of retrieving and returning facts for an entity
|
||||
1. `id` - unique identifier of a fact retriever
|
||||
2. `version`: A semver string indicating the current version of the schema and the handler
|
||||
3. `schema` - A versioned schema defining the shape of data a fact retriever returns
|
||||
4. `handler` - An asynchronous function handling the logic of retrieving and returning facts for an entity
|
||||
|
||||
An example implementation of a FactRetriever could for example be as follows:
|
||||
|
||||
```ts
|
||||
const myFactRetriever: FactRetriever = {
|
||||
ref: 'documentation-number-factretriever', // unique ref, identifier of the fact retriever
|
||||
id: 'documentation-number-factretriever', // unique identifier of the fact retriever
|
||||
version: '0.1.1', // SemVer version number of this fact retriever schema. This should be incremented if the implementation changes
|
||||
schema: {
|
||||
version: '0.1.1', // SemVer version number of this fact retriever schema. This should be incremented if the implementation changes
|
||||
|
||||
// the actual schema
|
||||
schema: {
|
||||
// Name/identifier of an individual fact that this retriever returns
|
||||
examplenumberfact: {
|
||||
type: 'integer', // Type of the fact
|
||||
description: 'A fact of a number', // Description of the fact
|
||||
entityTypes: ['component'], // An array of entity kinds that this fact is applicable to
|
||||
},
|
||||
// Name/identifier of an individual fact that this retriever returns
|
||||
examplenumberfact: {
|
||||
type: 'integer', // Type of the fact
|
||||
description: 'A fact of a number', // Description of the fact
|
||||
entityTypes: ['component'], // An array of entity kinds that this fact is applicable to
|
||||
},
|
||||
},
|
||||
handler: async ctx => {
|
||||
|
||||
@@ -6,17 +6,16 @@
|
||||
import { CheckResult } from '@backstage/plugin-tech-insights-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import express from 'express';
|
||||
import { FactChecker } from '@backstage/plugin-tech-insights-common';
|
||||
import { FactCheckerFactory } from '@backstage/plugin-tech-insights-common';
|
||||
import { FactRetrieverRegistration } from '@backstage/plugin-tech-insights-common';
|
||||
import { FactChecker } from '@backstage/plugin-tech-insights-node';
|
||||
import { FactCheckerFactory } from '@backstage/plugin-tech-insights-node';
|
||||
import { FactRetriever } from '@backstage/plugin-tech-insights-node';
|
||||
import { FactRetrieverRegistration } from '@backstage/plugin-tech-insights-node';
|
||||
import { Logger as Logger_2 } from 'winston';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { TechInsightCheck } from '@backstage/plugin-tech-insights-common';
|
||||
import { TechInsightsStore } from '@backstage/plugin-tech-insights-common';
|
||||
import { TechInsightCheck } from '@backstage/plugin-tech-insights-node';
|
||||
import { TechInsightsStore } from '@backstage/plugin-tech-insights-node';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "buildTechInsightsContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export const buildTechInsightsContext: <
|
||||
CheckType extends TechInsightCheck,
|
||||
@@ -25,6 +24,12 @@ export const buildTechInsightsContext: <
|
||||
options: TechInsightsOptions<CheckType, CheckResultType>,
|
||||
) => Promise<TechInsightsContext<CheckType, CheckResultType>>;
|
||||
|
||||
// @public
|
||||
export function createFactRetrieverRegistration(
|
||||
cadence: string,
|
||||
factRetriever: FactRetriever,
|
||||
): FactRetrieverRegistration;
|
||||
|
||||
// @public
|
||||
export function createRouter<
|
||||
CheckType extends TechInsightCheck,
|
||||
|
||||
@@ -33,10 +33,10 @@ exports.up = async function up(knex) {
|
||||
.notNullable()
|
||||
.comment('SemVer string defining the version of schema.');
|
||||
table
|
||||
.string('entityTypes')
|
||||
.string('entityFilter')
|
||||
.nullable()
|
||||
.comment(
|
||||
'A comma separated collection of entity kinds the fact retriever providing this schema affects. Defaults to null, which means all entity kinds.',
|
||||
'A serialized entity filter object used to determine which entities this schema is applicable to.',
|
||||
);
|
||||
table
|
||||
.text('schema')
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"@backstage/config": "^0.1.8",
|
||||
"@backstage/errors": "^0.1.1",
|
||||
"@backstage/plugin-tech-insights-common": "^0.1.0",
|
||||
"@backstage/plugin-tech-insights-node": "^0.1.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"cross-fetch": "^3.0.6",
|
||||
"express": "^4.17.1",
|
||||
|
||||
@@ -24,3 +24,4 @@ export type {
|
||||
} from './service/techInsightsContextBuilder';
|
||||
|
||||
export type { PersistenceContext } from './service/persistence/DatabaseManager';
|
||||
export { createFactRetrieverRegistration } from './service/fact/createFactRetriever';
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
FactSchemaDefinition,
|
||||
TechInsightFact,
|
||||
TechInsightsStore,
|
||||
} from '@backstage/plugin-tech-insights-common';
|
||||
} from '@backstage/plugin-tech-insights-node';
|
||||
import { FactRetrieverRegistry } from './FactRetrieverRegistry';
|
||||
import { FactRetrieverEngine } from './FactRetrieverEngine';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
@@ -37,7 +37,7 @@ jest.mock('node-cron', () => {
|
||||
const testFactRetriever: FactRetriever = {
|
||||
id: 'test-factretriever',
|
||||
version: '0.0.1',
|
||||
entityTypes: ['component'],
|
||||
entityFilter: [{ kind: 'component' }],
|
||||
schema: {
|
||||
testnumberfact: {
|
||||
type: 'integer',
|
||||
@@ -101,10 +101,10 @@ describe('FactRetrieverEngine', () => {
|
||||
};
|
||||
|
||||
it('Should update fact retriever schemas on initialization', async () => {
|
||||
factSchemaAssertionCallback = ({ id, schema, version, entityTypes }) => {
|
||||
factSchemaAssertionCallback = ({ id, schema, version, entityFilter }) => {
|
||||
expect(id).toEqual('test-factretriever');
|
||||
expect(version).toEqual('0.0.1');
|
||||
expect(entityTypes).toEqual(['component']);
|
||||
expect(entityFilter).toEqual([{ kind: 'component' }]);
|
||||
expect(schema).toEqual({
|
||||
testnumberfact: {
|
||||
type: 'integer',
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
FactRetriever,
|
||||
FactRetrieverContext,
|
||||
TechInsightsStore,
|
||||
} from '@backstage/plugin-tech-insights-common';
|
||||
} from '@backstage/plugin-tech-insights-node';
|
||||
import { FactRetrieverRegistry } from './FactRetrieverRegistry';
|
||||
import { schedule, validate, ScheduledTask } from 'node-cron';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
FactRetriever,
|
||||
FactRetrieverRegistration,
|
||||
FactSchema,
|
||||
} from '@backstage/plugin-tech-insights-common';
|
||||
} from '@backstage/plugin-tech-insights-node';
|
||||
import { ConflictError, NotFoundError } from '@backstage/errors';
|
||||
|
||||
export class FactRetrieverRegistry {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2021 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 {
|
||||
FactRetriever,
|
||||
FactRetrieverRegistration,
|
||||
} from '@backstage/plugin-tech-insights-node';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* A helper function to construct fact retriever registrations.
|
||||
*
|
||||
* Cron expressions help:
|
||||
* ┌────────────── second (optional)
|
||||
# │ ┌──────────── minute
|
||||
# │ │ ┌────────── hour
|
||||
# │ │ │ ┌──────── day of month
|
||||
# │ │ │ │ ┌────── month
|
||||
# │ │ │ │ │ ┌──── day of week
|
||||
# │ │ │ │ │ │
|
||||
# │ │ │ │ │ │
|
||||
# * * * * * *
|
||||
*
|
||||
*
|
||||
* @param cadence - cron expression to indicate when the fact retriever should be triggered
|
||||
* @param factRetriever - Implementation of fact retriever consisting of at least id, version, schema and handler
|
||||
*/
|
||||
export function createFactRetrieverRegistration(
|
||||
cadence: string,
|
||||
factRetriever: FactRetriever,
|
||||
): FactRetrieverRegistration {
|
||||
return {
|
||||
cadence,
|
||||
factRetriever,
|
||||
};
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import knexFactory, { Knex } from 'knex';
|
||||
import { Logger } from 'winston';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { TechInsightsDatabase } from './TechInsightsDatabase';
|
||||
import { TechInsightsStore } from '@backstage/plugin-tech-insights-common';
|
||||
import { TechInsightsStore } from '@backstage/plugin-tech-insights-node';
|
||||
|
||||
const migrationsDir = resolvePackagePath(
|
||||
'@backstage/plugin-tech-insights-backend',
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
import { DatabaseManager } from './DatabaseManager';
|
||||
import { DateTime, Duration } from 'luxon';
|
||||
import { TechInsightsStore } from '@backstage/plugin-tech-insights-common';
|
||||
import { TechInsightsStore } from '@backstage/plugin-tech-insights-node';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
const factSchemas = [
|
||||
{
|
||||
id: 'test-fact',
|
||||
version: '0.0.1-test',
|
||||
entityTypes: ['component'],
|
||||
entityFilter: JSON.stringify([{ kind: 'component' }]),
|
||||
schema: JSON.stringify({
|
||||
testNumberFact: {
|
||||
type: 'integer',
|
||||
@@ -35,7 +35,7 @@ const additionalFactSchemas = [
|
||||
{
|
||||
id: 'test-fact',
|
||||
version: '1.2.1-test',
|
||||
entityTypes: ['component'],
|
||||
entityFilter: JSON.stringify([{ kind: 'component' }]),
|
||||
schema: JSON.stringify({
|
||||
testNumberFact: {
|
||||
type: 'integer',
|
||||
@@ -50,7 +50,7 @@ const additionalFactSchemas = [
|
||||
{
|
||||
id: 'test-fact',
|
||||
version: '1.1.1-test',
|
||||
entityTypes: ['component'],
|
||||
entityFilter: JSON.stringify([{ kind: 'component' }]),
|
||||
schema: JSON.stringify({
|
||||
testStringFact: {
|
||||
type: 'string',
|
||||
@@ -63,7 +63,7 @@ const additionalFactSchemas = [
|
||||
const secondSchema = {
|
||||
id: 'second-test-fact',
|
||||
version: '0.0.1-test',
|
||||
entityTypes: ['service'],
|
||||
entityFilter: JSON.stringify([{ kind: 'service' }]),
|
||||
schema: JSON.stringify({
|
||||
testStringFact: {
|
||||
type: 'string',
|
||||
@@ -137,7 +137,7 @@ describe('Tech Insights database', () => {
|
||||
expect(schemas[0]).toMatchObject({
|
||||
id: 'test-fact',
|
||||
version: '0.0.1-test',
|
||||
entityTypes: ['component'],
|
||||
entityFilter: [{ kind: 'component' }],
|
||||
testNumberFact: {
|
||||
type: 'integer',
|
||||
description: 'Test fact with a number type',
|
||||
@@ -152,7 +152,7 @@ describe('Tech Insights database', () => {
|
||||
expect(schemas[0]).toMatchObject({
|
||||
id: 'test-fact',
|
||||
version: '1.2.1-test',
|
||||
entityTypes: ['component'],
|
||||
entityFilter: [{ kind: 'component' }],
|
||||
testNumberFact: {
|
||||
type: 'integer',
|
||||
description: 'Test fact with a number type',
|
||||
@@ -177,7 +177,7 @@ describe('Tech Insights database', () => {
|
||||
expect(schemas[0]).toMatchObject({
|
||||
id: 'test-fact',
|
||||
version: '1.2.1-test',
|
||||
entityTypes: ['component'],
|
||||
entityFilter: [{ kind: 'component' }],
|
||||
testNumberFact: {
|
||||
type: 'integer',
|
||||
description: 'Test fact with a number type',
|
||||
@@ -190,7 +190,7 @@ describe('Tech Insights database', () => {
|
||||
expect(schemas[1]).toMatchObject({
|
||||
id: 'second',
|
||||
version: '0.0.1-test',
|
||||
entityTypes: ['service'],
|
||||
entityFilter: [{ kind: 'service' }],
|
||||
testStringFact: {
|
||||
type: 'string',
|
||||
description: 'Test fact with a string type',
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
FlatTechInsightFact,
|
||||
TechInsightsStore,
|
||||
FactSchemaDefinition,
|
||||
} from '@backstage/plugin-tech-insights-common';
|
||||
} from '@backstage/plugin-tech-insights-node';
|
||||
import { rsort } from 'semver';
|
||||
import { groupBy, omit } from 'lodash';
|
||||
import { DateTime } from 'luxon';
|
||||
@@ -39,7 +39,7 @@ type RawDbFactSchemaRow = {
|
||||
id: string;
|
||||
version: string;
|
||||
schema: string;
|
||||
entityTypes?: string;
|
||||
entityFilter?: string;
|
||||
};
|
||||
|
||||
export class TechInsightsDatabase implements TechInsightsStore {
|
||||
@@ -63,12 +63,12 @@ export class TechInsightsDatabase implements TechInsightsStore {
|
||||
.map((it: RawDbFactSchemaRow) => ({
|
||||
...omit(it, 'schema'),
|
||||
...JSON.parse(it.schema),
|
||||
entityTypes: it.entityTypes ? it.entityTypes.split(',') : [],
|
||||
entityFilter: it.entityFilter ? JSON.parse(it.entityFilter) : null,
|
||||
}));
|
||||
}
|
||||
|
||||
async insertFactSchema(schemaDefinition: FactSchemaDefinition) {
|
||||
const { id, version, schema, entityTypes } = schemaDefinition;
|
||||
const { id, version, schema, entityFilter } = schemaDefinition;
|
||||
const existingSchemas = await this.db<RawDbFactSchemaRow>('fact_schemas')
|
||||
.where({ id })
|
||||
.and.where({ version })
|
||||
@@ -78,7 +78,7 @@ export class TechInsightsDatabase implements TechInsightsStore {
|
||||
await this.db<RawDbFactSchemaRow>('fact_schemas').insert({
|
||||
id,
|
||||
version,
|
||||
entityTypes: entityTypes && entityTypes.join(','),
|
||||
entityFilter: entityFilter ? JSON.stringify(entityFilter) : undefined,
|
||||
schema: JSON.stringify(schema),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { ConfigReader } from '@backstage/config';
|
||||
import request from 'supertest';
|
||||
import express from 'express';
|
||||
import { PersistenceContext } from './persistence/DatabaseManager';
|
||||
import { TechInsightsStore } from '@backstage/plugin-tech-insights-common';
|
||||
import { TechInsightsStore } from '@backstage/plugin-tech-insights-node';
|
||||
import { DateTime } from 'luxon';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@ import { Config } from '@backstage/config';
|
||||
import {
|
||||
FactChecker,
|
||||
TechInsightCheck,
|
||||
CheckResult,
|
||||
} from '@backstage/plugin-tech-insights-common';
|
||||
} 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/DatabaseManager';
|
||||
|
||||
@@ -23,16 +23,16 @@ import {
|
||||
PluginEndpointDiscovery,
|
||||
} from '@backstage/backend-common';
|
||||
import {
|
||||
CheckResult,
|
||||
FactChecker,
|
||||
FactCheckerFactory,
|
||||
FactRetrieverRegistration,
|
||||
TechInsightCheck,
|
||||
} from '@backstage/plugin-tech-insights-common';
|
||||
} from '@backstage/plugin-tech-insights-node';
|
||||
import {
|
||||
DatabaseManager,
|
||||
PersistenceContext,
|
||||
} from './persistence/DatabaseManager';
|
||||
import { CheckResult } from '@backstage/plugin-tech-insights-common';
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -80,6 +80,8 @@ export type TechInsightsContext<
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* Constructs needed persistence context, fact retriever engine
|
||||
* and optionally fact checker implementations to be used in the tech insights module.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user