From 62493e54c1b63f94c72ae9940eb163d937ade166 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 10:42:17 +0200 Subject: [PATCH] relations wip --- dev-components.yaml | 38 +++++++++ .../ingestion/HigherOrderOperations.test.ts | 15 +++- .../src/ingestion/LocationReaders.ts | 10 ++- .../processors/GroupPopulatorProcessor.ts | 82 +++++++++++++++++++ .../src/ingestion/processors/index.ts | 1 + .../src/ingestion/processors/results.ts | 10 ++- .../src/ingestion/processors/types.ts | 12 ++- .../catalog-backend/src/ingestion/types.ts | 8 +- .../src/service/CatalogBuilder.ts | 2 + 9 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 dev-components.yaml create mode 100644 plugins/catalog-backend/src/ingestion/processors/GroupPopulatorProcessor.ts diff --git a/dev-components.yaml b/dev-components.yaml new file mode 100644 index 0000000000..1671f6057f --- /dev/null +++ b/dev-components.yaml @@ -0,0 +1,38 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: root +spec: + type: org +--- +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: platform +spec: + type: mission + parent: root +# --- +# apiVersion: backstage.io/v1alpha1 +# kind: Group +# metadata: +# name: pdx +# spec: +# type: tribe +# parent: platform +# --- +# apiVersion: backstage.io/v1alpha1 +# kind: Group +# metadata: +# name: tools +# spec: +# type: squad +# parent: pdx +# --- +# apiVersion: backstage.io/v1alpha1 +# kind: Group +# metadata: +# name: pulp-fiction +# spec: +# type: squad +# parent: pdx diff --git a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.test.ts b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.test.ts index 34320fe18f..c7c8e738c8 100644 --- a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.test.ts +++ b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.test.ts @@ -69,7 +69,11 @@ describe('HigherOrderOperations', () => { }; locationsCatalog.addLocation.mockImplementation(x => Promise.resolve(x)); locationsCatalog.locations.mockResolvedValue([]); - locationReader.read.mockResolvedValue({ entities: [], errors: [] }); + locationReader.read.mockResolvedValue({ + entities: [], + errors: [], + relations: [], + }); const result = await higherOrderOperation.addLocation(spec); @@ -109,7 +113,11 @@ describe('HigherOrderOperations', () => { data: location, }, ]); - locationReader.read.mockResolvedValue({ entities: [], errors: [] }); + locationReader.read.mockResolvedValue({ + entities: [], + errors: [], + relations: [], + }); const result = await higherOrderOperation.addLocation(spec); @@ -138,6 +146,7 @@ describe('HigherOrderOperations', () => { locationReader.read.mockResolvedValue({ entities: [{ entity, location }], errors: [{ error: new Error('abcd'), location }], + relations: [], }); await expect(higherOrderOperation.addLocation(spec)).rejects.toThrow( @@ -186,6 +195,7 @@ describe('HigherOrderOperations', () => { locationReader.read.mockResolvedValue({ entities: [{ entity: desc, location }], errors: [], + relations: [], }); entitiesCatalog.batchAddOrUpdateEntities.mockResolvedValue(undefined); @@ -231,6 +241,7 @@ describe('HigherOrderOperations', () => { locationReader.read.mockResolvedValue({ entities: [{ entity: desc, location }], errors: [], + relations: [], }); entitiesCatalog.entities.mockResolvedValue([]); entitiesCatalog.addEntities.mockResolvedValue(undefined); diff --git a/plugins/catalog-backend/src/ingestion/LocationReaders.ts b/plugins/catalog-backend/src/ingestion/LocationReaders.ts index e23a4524b6..597a8ad6a3 100644 --- a/plugins/catalog-backend/src/ingestion/LocationReaders.ts +++ b/plugins/catalog-backend/src/ingestion/LocationReaders.ts @@ -61,7 +61,11 @@ export class LocationReaders implements LocationReader { async read(location: LocationSpec): Promise { const { rulesEnforcer, logger } = this.options; - const output: ReadLocationResult = { entities: [], errors: [] }; + const output: ReadLocationResult = { + entities: [], + errors: [], + relations: [], + }; let items: CatalogProcessorResult[] = [result.location(location, false)]; for (let depth = 0; depth < MAX_DEPTH; ++depth) { @@ -96,6 +100,10 @@ export class LocationReaders implements LocationReader { location: item.location, error: item.error, }); + } else if (item.type === 'relation') { + output.relations.push({ + relation: item.relation, + }); } } diff --git a/plugins/catalog-backend/src/ingestion/processors/GroupPopulatorProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/GroupPopulatorProcessor.ts new file mode 100644 index 0000000000..a1b0c6f15e --- /dev/null +++ b/plugins/catalog-backend/src/ingestion/processors/GroupPopulatorProcessor.ts @@ -0,0 +1,82 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { + Entity, + ENTITY_DEFAULT_NAMESPACE, + GroupEntity, + LocationSpec, +} from '@backstage/catalog-model'; +import { CatalogProcessor, CatalogProcessorEmit } from './types'; +import * as result from './results'; + +export class GroupPopulatorProcessor implements CatalogProcessor { + async postProcessEntity( + entity: Entity, + _location: LocationSpec, + emit: CatalogProcessorEmit, + ): Promise { + if (entity.kind.toLowerCase() !== 'group') { + return entity; + } + const spec = entity.spec as GroupEntity['spec']; + + const { parent, children } = spec; + const self = { + kind: entity.kind, + name: entity.metadata.name, + namespace: entity.metadata.namespace ?? ENTITY_DEFAULT_NAMESPACE, + }; + + if (parent) { + emit( + result.relation({ + type: 'parent', + source: self, + target: { ...self, name: parent }, + }), + ); + emit( + result.relation({ + type: 'child', + source: { ...self, name: parent }, + target: self, + }), + ); + } + + if (children) { + for (const child of children) { + emit( + result.relation({ + type: 'child', + source: self, + target: { ...self, name: child }, + }), + ); + emit( + result.relation({ + type: 'parent', + source: { ...self, name: child }, + target: self, + }), + ); + } + } + + return entity; + } +} diff --git a/plugins/catalog-backend/src/ingestion/processors/index.ts b/plugins/catalog-backend/src/ingestion/processors/index.ts index 59a87af555..2a09247e45 100644 --- a/plugins/catalog-backend/src/ingestion/processors/index.ts +++ b/plugins/catalog-backend/src/ingestion/processors/index.ts @@ -28,6 +28,7 @@ export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor'; export { GithubReaderProcessor } from './GithubReaderProcessor'; export { GitlabApiReaderProcessor } from './GitlabApiReaderProcessor'; export { GitlabReaderProcessor } from './GitlabReaderProcessor'; +export { GroupPopulatorProcessor } from './GroupPopulatorProcessor'; export { LocationRefProcessor } from './LocationEntityProcessor'; export { PlaceholderProcessor } from './PlaceholderProcessor'; export type { PlaceholderResolver } from './PlaceholderProcessor'; diff --git a/plugins/catalog-backend/src/ingestion/processors/results.ts b/plugins/catalog-backend/src/ingestion/processors/results.ts index 2718d871dd..158bc3d950 100644 --- a/plugins/catalog-backend/src/ingestion/processors/results.ts +++ b/plugins/catalog-backend/src/ingestion/processors/results.ts @@ -15,7 +15,11 @@ */ import { InputError, NotFoundError } from '@backstage/backend-common'; -import { Entity, LocationSpec } from '@backstage/catalog-model'; +import { + Entity, + EntityRelationSpec, + LocationSpec, +} from '@backstage/catalog-model'; import { CatalogProcessorResult } from './types'; export function notFoundError( @@ -67,3 +71,7 @@ export function entity( ): CatalogProcessorResult { return { type: 'entity', location: atLocation, entity: newEntity }; } + +export function relation(spec: EntityRelationSpec): CatalogProcessorResult { + return { type: 'relation', relation: spec }; +} diff --git a/plugins/catalog-backend/src/ingestion/processors/types.ts b/plugins/catalog-backend/src/ingestion/processors/types.ts index c9adf07eff..9ed5e6e92d 100644 --- a/plugins/catalog-backend/src/ingestion/processors/types.ts +++ b/plugins/catalog-backend/src/ingestion/processors/types.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { Entity, LocationSpec } from '@backstage/catalog-model'; +import { + Entity, + EntityRelationSpec, + LocationSpec, +} from '@backstage/catalog-model'; export type CatalogProcessor = { /** @@ -113,6 +117,11 @@ export type CatalogProcessorEntityResult = { location: LocationSpec; }; +export type CatalogProcessorRelationResult = { + type: 'relation'; + relation: EntityRelationSpec; +}; + export type CatalogProcessorErrorResult = { type: 'error'; error: Error; @@ -123,4 +132,5 @@ export type CatalogProcessorResult = | CatalogProcessorLocationResult | CatalogProcessorDataResult | CatalogProcessorEntityResult + | CatalogProcessorRelationResult | CatalogProcessorErrorResult; diff --git a/plugins/catalog-backend/src/ingestion/types.ts b/plugins/catalog-backend/src/ingestion/types.ts index 5bbec01e7e..acaae61cc9 100644 --- a/plugins/catalog-backend/src/ingestion/types.ts +++ b/plugins/catalog-backend/src/ingestion/types.ts @@ -14,7 +14,12 @@ * limitations under the License. */ -import type { Entity, Location, LocationSpec } from '@backstage/catalog-model'; +import type { + Entity, + EntityRelationSpec, + Location, + LocationSpec, +} from '@backstage/catalog-model'; // // HigherOrderOperation @@ -47,6 +52,7 @@ export type LocationReader = { export type ReadLocationResult = { entities: ReadLocationEntity[]; + relations: { relation: EntityRelationSpec }[]; errors: ReadLocationError[]; }; diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index e6fb540f16..52f10232ce 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -52,6 +52,7 @@ import { GithubReaderProcessor, GitlabApiReaderProcessor, GitlabReaderProcessor, + GroupPopulatorProcessor, HigherOrderOperation, HigherOrderOperations, LocationReaders, @@ -334,6 +335,7 @@ export class CatalogBuilder { new YamlProcessor(), new CodeOwnersProcessor({ reader }), new LocationRefProcessor(), + new GroupPopulatorProcessor(), new AnnotateLocationEntityProcessor(), ];