From 65742aa801f0dc62b1668c0eb05aaf2f8a2fca12 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Oct 2021 17:37:10 +0200 Subject: [PATCH] scaffolder-backend: exported new TemplateEntityProcessor Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- plugins/scaffolder-backend/package.json | 2 + plugins/scaffolder-backend/src/index.ts | 1 + .../processor/TemplateEntityProcessor.test.ts | 80 +++++++++++++++ .../src/processor/TemplateEntityProcessor.ts | 97 +++++++++++++++++++ .../scaffolder-backend/src/processor/index.ts | 17 ++++ 5 files changed, 197 insertions(+) create mode 100644 plugins/scaffolder-backend/src/processor/TemplateEntityProcessor.test.ts create mode 100644 plugins/scaffolder-backend/src/processor/TemplateEntityProcessor.ts create mode 100644 plugins/scaffolder-backend/src/processor/index.ts diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index ffe8a1e1b8..4de879d41b 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -36,6 +36,8 @@ "@backstage/config": "^0.1.10", "@backstage/errors": "^0.1.2", "@backstage/integration": "^0.6.7", + "@backstage/plugin-catalog-backend": "^0.15.0", + "@backstage/plugin-scaffolder-common": "^0.1.0", "@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.1.2", "@gitbeaker/core": "^30.2.0", "@gitbeaker/node": "^30.2.0", diff --git a/plugins/scaffolder-backend/src/index.ts b/plugins/scaffolder-backend/src/index.ts index a7ae821463..76cda63eef 100644 --- a/plugins/scaffolder-backend/src/index.ts +++ b/plugins/scaffolder-backend/src/index.ts @@ -23,3 +23,4 @@ export * from './scaffolder'; export * from './service/router'; export * from './lib/catalog'; +export * from './processor'; diff --git a/plugins/scaffolder-backend/src/processor/TemplateEntityProcessor.test.ts b/plugins/scaffolder-backend/src/processor/TemplateEntityProcessor.test.ts new file mode 100644 index 0000000000..6b9576695a --- /dev/null +++ b/plugins/scaffolder-backend/src/processor/TemplateEntityProcessor.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright 2020 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 { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; +import { TemplateEntityProcessor } from './TemplateEntityProcessor'; + +const mockLocation = { type: 'a', target: 'b' }; +const mockEntity: TemplateEntityV1beta3 = { + apiVersion: 'templates.backstage.io/v1beta3', + kind: 'Template', + metadata: { name: 'n' }, + spec: { + parameters: {}, + steps: [], + type: 'service', + owner: 'o', + }, +}; + +describe('TemplateEntityProcessor', () => { + describe('validateEntityKind', () => { + it('validates the entity kind', async () => { + const processor = new TemplateEntityProcessor(); + + await expect(processor.validateEntityKind(mockEntity)).resolves.toBe( + true, + ); + await expect( + processor.validateEntityKind({ + ...mockEntity, + apiVersion: 'backstage.io/v1beta3', + }), + ).resolves.toBe(false); + await expect( + processor.validateEntityKind({ ...mockEntity, kind: 'Component' }), + ).resolves.toBe(false); + }); + }); + + describe('postProcessEntity', () => { + it('generates relations for component entities', async () => { + const processor = new TemplateEntityProcessor(); + + const emit = jest.fn(); + + await processor.postProcessEntity(mockEntity, mockLocation, emit); + + expect(emit).toBeCalledTimes(2); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Group', namespace: 'default', name: 'o' }, + type: 'ownerOf', + target: { kind: 'Template', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Template', namespace: 'default', name: 'n' }, + type: 'ownedBy', + target: { kind: 'Group', namespace: 'default', name: 'o' }, + }, + }); + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/processor/TemplateEntityProcessor.ts b/plugins/scaffolder-backend/src/processor/TemplateEntityProcessor.ts new file mode 100644 index 0000000000..dc11b6d86d --- /dev/null +++ b/plugins/scaffolder-backend/src/processor/TemplateEntityProcessor.ts @@ -0,0 +1,97 @@ +/* + * Copyright 2020 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 { + Entity, + getEntityName, + LocationSpec, + parseEntityRef, + RELATION_OWNED_BY, + RELATION_OWNER_OF, + entityKindSchemaValidator, +} from '@backstage/catalog-model'; +import { + CatalogProcessor, + CatalogProcessorEmit, + results, +} from '@backstage/plugin-catalog-backend'; +import { + TemplateEntityV1beta3, + templateEntityV1beta3Schema, +} from '@backstage/plugin-scaffolder-common'; + +export class TemplateEntityProcessor implements CatalogProcessor { + private readonly validators = [ + entityKindSchemaValidator(templateEntityV1beta3Schema), + ]; + + async validateEntityKind(entity: Entity): Promise { + for (const validator of this.validators) { + if (validator(entity)) { + return true; + } + } + + return false; + } + + async postProcessEntity( + entity: Entity, + _location: LocationSpec, + emit: CatalogProcessorEmit, + ): Promise { + const selfRef = getEntityName(entity); + + if ( + entity.apiVersion === 'templates.backstage.io/v1beta3' && + entity.kind === 'Template' + ) { + const template = entity as TemplateEntityV1beta3; + + const target = template.spec.owner; + if (target) { + const targetRef = parseEntityRef(target, { + defaultKind: 'Group', + defaultNamespace: selfRef.namespace, + }); + emit( + results.relation({ + source: selfRef, + type: RELATION_OWNED_BY, + target: { + kind: targetRef.kind, + namespace: targetRef.namespace, + name: targetRef.name, + }, + }), + ); + emit( + results.relation({ + source: { + kind: targetRef.kind, + namespace: targetRef.namespace, + name: targetRef.name, + }, + type: RELATION_OWNER_OF, + target: selfRef, + }), + ); + } + } + + return entity; + } +} diff --git a/plugins/scaffolder-backend/src/processor/index.ts b/plugins/scaffolder-backend/src/processor/index.ts new file mode 100644 index 0000000000..621ac186a4 --- /dev/null +++ b/plugins/scaffolder-backend/src/processor/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { TemplateEntityProcessor } from './TemplateEntityProcessor';