Dynamic annotations

Signed-off-by: Iain Billett <iain@roadie.io>
This commit is contained in:
Iain Billett
2021-11-12 10:20:25 +00:00
parent 9d2d1bd00c
commit 2d4593254f
2 changed files with 72 additions and 11 deletions
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { entityFactRetriever } from './entityFactRetriever';
import { createEntityFactRetriever } from './entityFactRetriever';
import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';
import {
PluginEndpointDiscovery,
@@ -44,6 +44,9 @@ const defaultEntityListResponse: CatalogListResponse<Entity> = {
name: 'service-with-owner',
description: 'service with an owner',
tags: ['a-tag'],
annotations: {
'backstage.io/techdocs-ref': 'dir:.',
},
},
kind: 'Component',
spec: {
@@ -103,6 +106,10 @@ const handlerContext = {
config: ConfigReader.fromConfigs([]),
};
const entityFactRetriever = createEntityFactRetriever({
annotations: ['backstage.io/techdocs-ref'],
});
describe('entityFactRetriever', () => {
beforeEach(() => {
getEntitiesMock.mockResolvedValue(defaultEntityListResponse);
@@ -248,4 +255,33 @@ describe('entityFactRetriever', () => {
});
});
});
describe('dynamic annotation facts', () => {
describe('where the retriever is configured to check for the techdocs annotation', () => {
describe('where the entity has the techdocs annotation', () => {
it('returns true for hasAnnotationBackstageIoTechdocsRef', async () => {
const facts = await entityFactRetriever.handler(handlerContext);
expect(
facts.find(it => it.entity.name === 'service-with-owner'),
).toMatchObject({
facts: {
hasAnnotationBackstageIoTechdocsRef: true,
},
});
});
});
describe('where the entity is missing the techdocs annotation', () => {
it('returns false for hasAnnotationBackstageIoTechdocsRef', async () => {
const facts = await entityFactRetriever.handler(handlerContext);
expect(
facts.find(it => it.entity.name === 'service-with-incomplete-data'),
).toMatchObject({
facts: {
hasAnnotationBackstageIoTechdocsRef: false,
},
});
});
});
});
});
});
@@ -18,8 +18,14 @@ import { FactRetrieverContext } from '@backstage/plugin-tech-insights-node';
import { CatalogClient } from '@backstage/catalog-client';
import { Entity } from '@backstage/catalog-model';
import isEmpty from 'lodash/isEmpty';
import camelCase from 'lodash/camelCase';
import { get } from 'lodash';
export const entityFactRetriever = {
export const createEntityFactRetriever = ({
annotations = [],
}: {
annotations?: string[];
}) => ({
id: 'entityRetriever',
version: '0.0.1',
schema: {
@@ -35,6 +41,15 @@ export const entityFactRetriever = {
type: 'boolean',
description: 'The entity has an owned_by relation',
},
...annotations.reduce((acc: object, it: string) => {
return {
...acc,
[camelCase(`hasAnnotation-${it}`)]: {
type: 'boolean',
description: `The entity has the annotation: ${it} `,
},
};
}, {}),
},
handler: async ({ discovery }: FactRetrieverContext) => {
const catalogClient = new CatalogClient({
@@ -42,22 +57,32 @@ export const entityFactRetriever = {
});
const entities = await catalogClient.getEntities();
return entities.items.map((it: Entity) => {
return entities.items.map((entity: Entity) => {
return {
entity: {
namespace: it.metadata.namespace!!,
kind: it.kind,
name: it.metadata.name,
namespace: entity.metadata.namespace!!,
kind: entity.kind,
name: entity.metadata.name,
},
facts: {
hasOwner: Boolean(it.spec?.owner),
hasOwner: Boolean(entity.spec?.owner),
hasGroupOwner: Boolean(
it.spec?.owner && (it.spec?.owner as string).startsWith('group:'),
entity.spec?.owner &&
(entity.spec?.owner as string).startsWith('group:'),
),
hasDescription: Boolean(entity.metadata?.description),
hasTags: !isEmpty(entity.metadata?.tags),
...annotations.reduce(
(acc: object, annotation: string) => ({
...acc,
[camelCase(`hasAnnotation-${annotation}`)]: Boolean(
get(entity, ['metadata', 'annotations', annotation]),
),
}),
{},
),
hasDescription: Boolean(it.metadata?.description),
hasTags: !isEmpty(it.metadata?.tags),
},
};
});
},
};
});