From 958d97c4dd6b66590372d5175e353fe2600409d6 Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Fri, 26 Mar 2021 23:21:12 -0400 Subject: [PATCH 01/13] Added catalog-model support for dependsOn to Component and dependencyOf to Resource. Updated relevant relationship generator entity processors, and system diagram support. Signed-off-by: Jonah Grimes --- .../components/artist-lookup-component.yaml | 2 + .../src/kinds/ComponentEntityV1alpha1.test.ts | 21 ++++++++++ .../src/kinds/ComponentEntityV1alpha1.ts | 1 + .../src/kinds/ResourceEntityV1alpha1.test.ts | 21 ++++++++++ .../src/kinds/ResourceEntityV1alpha1.ts | 1 + .../kinds/Component.v1alpha1.schema.json | 8 ++++ .../kinds/Resource.v1alpha1.schema.json | 8 ++++ .../BuiltinKindsEntityProcessor.test.ts | 38 ++++++++++++++++++- .../processors/BuiltinKindsEntityProcessor.ts | 14 +++++++ .../SystemDiagramCard/SystemDiagramCard.tsx | 14 ++++++- 10 files changed, 125 insertions(+), 3 deletions(-) diff --git a/packages/catalog-model/examples/components/artist-lookup-component.yaml b/packages/catalog-model/examples/components/artist-lookup-component.yaml index edd9b8fcf9..713fd8fadb 100644 --- a/packages/catalog-model/examples/components/artist-lookup-component.yaml +++ b/packages/catalog-model/examples/components/artist-lookup-component.yaml @@ -32,4 +32,6 @@ spec: type: service lifecycle: experimental owner: team-a + dependsOn: + - artists-db system: artist-engagement-portal diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts index 9284a5d5b1..19bc168223 100644 --- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts @@ -36,6 +36,7 @@ describe('ComponentV1alpha1Validator', () => { subcomponentOf: 'monolith', providesApis: ['api-0'], consumesApis: ['api-0'], + dependsOn: ['resource-0'], system: 'system', }, }; @@ -160,6 +161,26 @@ describe('ComponentV1alpha1Validator', () => { await expect(validator.check(entity)).resolves.toBe(true); }); + it('accepts missing dependsOn', async () => { + delete (entity as any).spec.dependsOn; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('rejects empty dependsOn', async () => { + (entity as any).spec.dependsOn = ['']; + await expect(validator.check(entity)).rejects.toThrow(/dependsOn/); + }); + + it('rejects undefined dependsOn', async () => { + (entity as any).spec.dependsOn = [undefined]; + await expect(validator.check(entity)).rejects.toThrow(/dependsOn/); + }); + + it('accepts no dependsOn', async () => { + (entity as any).spec.dependsOn = []; + await expect(validator.check(entity)).resolves.toBe(true); + }); + it('accepts missing system', async () => { delete (entity as any).spec.system; await expect(validator.check(entity)).resolves.toBe(true); diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts index 3006a4288c..489aa8b1bd 100644 --- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts @@ -34,6 +34,7 @@ export interface ComponentEntityV1alpha1 extends Entity { subcomponentOf?: string; providesApis?: string[]; consumesApis?: string[]; + dependsOn?: string[]; system?: string; }; } diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts index ad8ea5cdf3..076288628a 100644 --- a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts @@ -32,6 +32,7 @@ describe('ResourceV1alpha1Validator', () => { spec: { type: 'database', owner: 'me', + dependencyOf: ['component-0'], system: 'system', }, }; @@ -86,6 +87,26 @@ describe('ResourceV1alpha1Validator', () => { await expect(validator.check(entity)).rejects.toThrow(/owner/); }); + it('accepts missing dependencyOf', async () => { + delete (entity as any).spec.dependencyOf; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('rejects empty dependencyOf', async () => { + (entity as any).spec.dependencyOf = ['']; + await expect(validator.check(entity)).rejects.toThrow(/dependencyOf/); + }); + + it('rejects undefined dependencyOf', async () => { + (entity as any).spec.dependencyOf = [undefined]; + await expect(validator.check(entity)).rejects.toThrow(/dependencyOf/); + }); + + it('accepts no dependencyOf', async () => { + (entity as any).spec.dependencyOf = []; + await expect(validator.check(entity)).resolves.toBe(true); + }); + it('accepts missing system', async () => { delete (entity as any).spec.system; await expect(validator.check(entity)).resolves.toBe(true); diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts index 520a39f02c..0b3662b91f 100644 --- a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts @@ -30,6 +30,7 @@ export interface ResourceEntityV1alpha1 extends Entity { spec: { type: string; owner: string; + dependencyOf?: string[]; system?: string; }; } diff --git a/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json b/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json index 822059a0ea..e2b98d5c3b 100644 --- a/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json +++ b/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json @@ -84,6 +84,14 @@ "type": "string", "minLength": 1 } + }, + "dependsOn": { + "type": "array", + "description": "An array of entity references to the resources that the component depends on.", + "items": { + "type": "string", + "minLength": 1 + } } } } diff --git a/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json b/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json index b426dd7f94..dbe9c969f3 100644 --- a/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json +++ b/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json @@ -47,6 +47,14 @@ "examples": ["artist-relations-team", "user:john.johnson"], "minLength": 1 }, + "dependencyOf": { + "type": "array", + "description": "An array of entity references to the components that the resource is a dependency of.", + "items": { + "type": "string", + "minLength": 1 + } + }, "system": { "type": "string", "description": "An entity reference to the system that the resource belongs to.", diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts index feb4791477..2d61191aa8 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts @@ -45,13 +45,14 @@ describe('BuiltinKindsEntityProcessor', () => { lifecycle: 'l', providesApis: ['b'], consumesApis: ['c'], + dependsOn: ['r'], system: 's', }, }; await processor.postProcessEntity(entity, location, emit); - expect(emit).toBeCalledTimes(10); + expect(emit).toBeCalledTimes(12); expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -100,6 +101,22 @@ describe('BuiltinKindsEntityProcessor', () => { target: { kind: 'API', namespace: 'default', name: 'c' }, }, }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Resource', namespace: 'default', name: 'r' }, + type: 'dependencyOf', + target: { kind: 'Component', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Component', namespace: 'default', name: 'n' }, + type: 'dependsOn', + target: { kind: 'Resource', namespace: 'default', name: 'r' }, + }, + }); expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -193,13 +210,14 @@ describe('BuiltinKindsEntityProcessor', () => { spec: { type: 'database', owner: 'o', + dependencyOf: ['c'], system: 's', }, }; await processor.postProcessEntity(entity, location, emit); - expect(emit).toBeCalledTimes(4); + expect(emit).toBeCalledTimes(6); expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -216,6 +234,22 @@ describe('BuiltinKindsEntityProcessor', () => { target: { kind: 'Group', namespace: 'default', name: 'o' }, }, }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Resource', namespace: 'default', name: 'n' }, + type: 'dependencyOf', + target: { kind: 'Component', namespace: 'default', name: 'c' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Component', namespace: 'default', name: 'c' }, + type: 'dependsOn', + target: { kind: 'Resource', namespace: 'default', name: 'n' }, + }, + }); expect(emit).toBeCalledWith({ type: 'relation', relation: { diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts index 26a8538707..d1de992a81 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts @@ -32,6 +32,8 @@ import { RELATION_API_PROVIDED_BY, RELATION_CHILD_OF, RELATION_CONSUMES_API, + RELATION_DEPENDENCY_OF, + RELATION_DEPENDS_ON, RELATION_HAS_MEMBER, RELATION_HAS_PART, RELATION_MEMBER_OF, @@ -146,6 +148,12 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { RELATION_CONSUMES_API, RELATION_API_CONSUMED_BY, ); + doEmit( + component.spec.dependsOn, + { defaultKind: 'Resource', defaultNamespace: selfRef.namespace }, + RELATION_DEPENDS_ON, + RELATION_DEPENDENCY_OF, + ); doEmit( component.spec.system, { defaultKind: 'System', defaultNamespace: selfRef.namespace }, @@ -186,6 +194,12 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { RELATION_OWNED_BY, RELATION_OWNER_OF, ); + doEmit( + resource.spec.dependencyOf, + { defaultKind: 'Component', defaultNamespace: selfRef.namespace }, + RELATION_DEPENDENCY_OF, + RELATION_DEPENDS_ON, + ); doEmit( resource.spec.system, { defaultKind: 'System', defaultNamespace: selfRef.namespace }, diff --git a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx index df20e910aa..78288c46e9 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx +++ b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx @@ -16,6 +16,7 @@ import { Entity, + RELATION_DEPENDS_ON, RELATION_PROVIDES_API, RELATION_PART_OF, serializeEntityRef, @@ -124,7 +125,6 @@ export function SystemDiagramCard() { catalogItem, RELATION_PROVIDES_API, ); - catalogItemRelations_providesApi.forEach(foundRelation => systemEdges.push({ from: simplifiedEntityName(catalogItem), @@ -132,6 +132,18 @@ export function SystemDiagramCard() { label: 'provides API', }), ); + + const catalogItemRelations_dependsOn = getEntityRelations( + catalogItem, + RELATION_DEPENDS_ON, + ); + catalogItemRelations_dependsOn.forEach(foundRelation => + systemEdges.push({ + from: simplifiedEntityName(catalogItem), + to: simplifiedEntityName(foundRelation), + label: 'depends on', + }), + ); } } From 372f9e80c6f6d742c4125d3a0fbc218ecbf35bdc Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Fri, 26 Mar 2021 23:47:01 -0400 Subject: [PATCH 02/13] Rolled back changes to examples in catalog-model to maintain compatibility with demo Signed-off-by: Jonah Grimes --- .../examples/components/artist-lookup-component.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/catalog-model/examples/components/artist-lookup-component.yaml b/packages/catalog-model/examples/components/artist-lookup-component.yaml index 713fd8fadb..edd9b8fcf9 100644 --- a/packages/catalog-model/examples/components/artist-lookup-component.yaml +++ b/packages/catalog-model/examples/components/artist-lookup-component.yaml @@ -32,6 +32,4 @@ spec: type: service lifecycle: experimental owner: team-a - dependsOn: - - artists-db system: artist-engagement-portal From ba4beaca821de91e89807ffae76c6377ca892f0b Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Fri, 26 Mar 2021 23:21:12 -0400 Subject: [PATCH 03/13] Added catalog-model support for dependsOn to Component and dependencyOf to Resource. Updated relevant relationship generator entity processors, and system diagram support. Signed-off-by: Jonah Grimes --- .../examples/components/artist-lookup-component.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/catalog-model/examples/components/artist-lookup-component.yaml b/packages/catalog-model/examples/components/artist-lookup-component.yaml index edd9b8fcf9..713fd8fadb 100644 --- a/packages/catalog-model/examples/components/artist-lookup-component.yaml +++ b/packages/catalog-model/examples/components/artist-lookup-component.yaml @@ -32,4 +32,6 @@ spec: type: service lifecycle: experimental owner: team-a + dependsOn: + - artists-db system: artist-engagement-portal From 9b18984cab80bb5b4cbecc6a8b526fae501d3aa6 Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Fri, 26 Mar 2021 23:47:01 -0400 Subject: [PATCH 04/13] Rolled back changes to examples in catalog-model to maintain compatibility with demo Signed-off-by: Jonah Grimes --- .../examples/components/artist-lookup-component.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/catalog-model/examples/components/artist-lookup-component.yaml b/packages/catalog-model/examples/components/artist-lookup-component.yaml index 713fd8fadb..edd9b8fcf9 100644 --- a/packages/catalog-model/examples/components/artist-lookup-component.yaml +++ b/packages/catalog-model/examples/components/artist-lookup-component.yaml @@ -32,6 +32,4 @@ spec: type: service lifecycle: experimental owner: team-a - dependsOn: - - artists-db system: artist-engagement-portal From 5d074056394413a9976a55d4f4ea4e35fa272e17 Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Sat, 27 Mar 2021 00:06:27 -0400 Subject: [PATCH 05/13] added changeset for dependsOn/dependencyOf Signed-off-by: Jonah Grimes --- .changeset/red-planets-hammer.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/red-planets-hammer.md diff --git a/.changeset/red-planets-hammer.md b/.changeset/red-planets-hammer.md new file mode 100644 index 0000000000..19938e0d3d --- /dev/null +++ b/.changeset/red-planets-hammer.md @@ -0,0 +1,7 @@ +--- +'@backstage/catalog-model': minor +'@backstage/plugin-catalog-backend': minor +'@backstage/plugin-catalog': patch +--- + +Implemented missing support for the dependsOn/dependencyOf relationships between Component and Resource catalog model objects. Added support for generating the relevant relationships to the BuiltinKindsEntityProcessor, and added simple support for fetching relationships between Components and Resources for rendering in the system diagram. All catalog-model changes backwards compatible. From 098cfb74b97961b9a953b84703b02b364d8afc95 Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Sat, 27 Mar 2021 00:25:06 -0400 Subject: [PATCH 06/13] updated documentation to account for new relationship specifications in the catalog model Signed-off-by: Jonah Grimes --- .../software-catalog/descriptor-format.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index e4b046d76f..668b139d6a 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -519,6 +519,15 @@ consumed by the component, e.g. `artist-api`. This field is optional. | --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------- | | [`API`](#kind-api) (default) | Same as this entity, typically `default` | [`consumesApi`, and reverse `apiConsumedBy`](well-known-relations.md#consumesapi-and-apiconsumedby) | +### `spec.dependsOn` [optional] + +An array of [entity references](#string-references) to the Resources that the +component depends on, e.g. `artists-db`. This field is optional. + +| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type | +| --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- | +| [`Resource`](#kind-resource) (default) | Same as this entity, typically `default` | [`dependsOn`, and reverse `dependencyOf`](well-known-relations.md#dependson-and-dependencyof) | + ## Kind: Template The following describes the following entity kind: @@ -1001,6 +1010,15 @@ belongs to, e.g. `artist-engagement-portal`. This field is optional. | --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- | | [`System`](#kind-system) (default) | Same as this entity, typically `default` | [`partOf`, and reverse `hasPart`](well-known-relations.md#partof-and-haspart) | +### `spec.dependencyOf` [optional] + +An array of [entity references](#string-references) to the Components that the +resource is a dependency of, e.g. `artist-lookup`. This field is optional. + +| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type | +| ---------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- | +| [`Component`](#kind-component) (default) | Same as this entity, typically `default` | [`dependencyOf`, and reverse `dependsOn`](well-known-relations.md#dependson-and-dependencyof) | + ## Kind: System Describes the following entity kind: From b886314e2897bcf4e3a33e2eee7c805bd70eee2f Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Sat, 27 Mar 2021 09:05:08 -0400 Subject: [PATCH 07/13] updated changeset to patch for all affected packages/plugins Signed-off-by: Jonah Grimes --- .changeset/red-planets-hammer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/red-planets-hammer.md b/.changeset/red-planets-hammer.md index 19938e0d3d..780ef533f1 100644 --- a/.changeset/red-planets-hammer.md +++ b/.changeset/red-planets-hammer.md @@ -1,6 +1,6 @@ --- -'@backstage/catalog-model': minor -'@backstage/plugin-catalog-backend': minor +'@backstage/catalog-model': patch +'@backstage/plugin-catalog-backend': patch '@backstage/plugin-catalog': patch --- From c8b60c4b4d39df860b429a08d0454912ff029772 Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Mon, 29 Mar 2021 12:57:53 -0400 Subject: [PATCH 08/13] reformatted changeset Signed-off-by: Jonah Grimes --- .changeset/red-planets-hammer.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.changeset/red-planets-hammer.md b/.changeset/red-planets-hammer.md index 780ef533f1..eb72314556 100644 --- a/.changeset/red-planets-hammer.md +++ b/.changeset/red-planets-hammer.md @@ -4,4 +4,10 @@ '@backstage/plugin-catalog': patch --- -Implemented missing support for the dependsOn/dependencyOf relationships between Component and Resource catalog model objects. Added support for generating the relevant relationships to the BuiltinKindsEntityProcessor, and added simple support for fetching relationships between Components and Resources for rendering in the system diagram. All catalog-model changes backwards compatible. +Implemented missing support for the dependsOn/dependencyOf relationships +between `Component` and `Resource` catalog model objects. + +Added support for generating the relevant relationships to the +`BuiltinKindsEntityProcessor`, and added simple support for fetching +relationships between `Components` and `Resources` for rendering in the +system diagram. All catalog-model changes backwards compatible. From d5e059a3c89ee5a43987717cd29cdc6ca5723784 Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Wed, 31 Mar 2021 10:01:39 -0400 Subject: [PATCH 09/13] removed dependencyOf reverse specification of dependencies, added dependsOn support at the Resource entity level, removed default entity reference kind for dependsOn, updated documentation Signed-off-by: Jonah Grimes --- .../software-catalog/descriptor-format.md | 22 +++-- .../src/kinds/ComponentEntityV1alpha1.test.ts | 2 +- .../src/kinds/ResourceEntityV1alpha1.test.ts | 22 ++--- .../src/kinds/ResourceEntityV1alpha1.ts | 2 +- .../kinds/Resource.v1alpha1.schema.json | 2 +- .../BuiltinKindsEntityProcessor.test.ts | 91 +++++++++++++++++-- .../processors/BuiltinKindsEntityProcessor.ts | 27 ++++-- 7 files changed, 131 insertions(+), 37 deletions(-) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 668b139d6a..16829b6716 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -521,12 +521,14 @@ consumed by the component, e.g. `artist-api`. This field is optional. ### `spec.dependsOn` [optional] -An array of [entity references](#string-references) to the Resources that the -component depends on, e.g. `artists-db`. This field is optional. +An array of [entity references](#string-references) to the components and +resources that the component depends on, e.g. `artists-db`. This field is +optional. | [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type | | --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- | -| [`Resource`](#kind-resource) (default) | Same as this entity, typically `default` | [`dependsOn`, and reverse `dependencyOf`](well-known-relations.md#dependson-and-dependencyof) | +| [`Component`](#kind-component) | Same as this entity, typically `default` | [`dependsOn`, and reverse `dependencyOf`](well-known-relations.md#dependson-and-dependencyof) | +| [`Resource`](#kind-resource) | Same as this entity, typically `default` | [`dependsOn`, and reverse `dependencyOf`](well-known-relations.md#dependson-and-dependencyof) | ## Kind: Template @@ -1010,14 +1012,16 @@ belongs to, e.g. `artist-engagement-portal`. This field is optional. | --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- | | [`System`](#kind-system) (default) | Same as this entity, typically `default` | [`partOf`, and reverse `hasPart`](well-known-relations.md#partof-and-haspart) | -### `spec.dependencyOf` [optional] +### `spec.dependsOn` [optional] -An array of [entity references](#string-references) to the Components that the -resource is a dependency of, e.g. `artist-lookup`. This field is optional. +An array of [entity references](#string-references) to the components and +resources that the resource depends on, e.g. `artist-lookup`. This field is +optional. -| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type | -| ---------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- | -| [`Component`](#kind-component) (default) | Same as this entity, typically `default` | [`dependencyOf`, and reverse `dependsOn`](well-known-relations.md#dependson-and-dependencyof) | +| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type | +| --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- | +| [`Component`](#kind-component) | Same as this entity, typically `default` | [`dependsOn`, and reverse `dependencyOf`](well-known-relations.md#dependson-and-dependencyof) | +| [`Resource`](#kind-resource) | Same as this entity, typically `default` | [`dependsOn`, and reverse `dependencyOf`](well-known-relations.md#dependson-and-dependencyof) | ## Kind: System diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts index 19bc168223..358e7b6526 100644 --- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts @@ -36,7 +36,7 @@ describe('ComponentV1alpha1Validator', () => { subcomponentOf: 'monolith', providesApis: ['api-0'], consumesApis: ['api-0'], - dependsOn: ['resource-0'], + dependsOn: ['resource:resource-0', 'component:component-0'], system: 'system', }, }; diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts index 076288628a..953ec5889a 100644 --- a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts @@ -32,7 +32,7 @@ describe('ResourceV1alpha1Validator', () => { spec: { type: 'database', owner: 'me', - dependencyOf: ['component-0'], + dependsOn: ['component:component-0', 'resource:resource-0'], system: 'system', }, }; @@ -87,23 +87,23 @@ describe('ResourceV1alpha1Validator', () => { await expect(validator.check(entity)).rejects.toThrow(/owner/); }); - it('accepts missing dependencyOf', async () => { - delete (entity as any).spec.dependencyOf; + it('accepts missing dependsOn', async () => { + delete (entity as any).spec.dependsOn; await expect(validator.check(entity)).resolves.toBe(true); }); - it('rejects empty dependencyOf', async () => { - (entity as any).spec.dependencyOf = ['']; - await expect(validator.check(entity)).rejects.toThrow(/dependencyOf/); + it('rejects empty dependsOn', async () => { + (entity as any).spec.dependsOn = ['']; + await expect(validator.check(entity)).rejects.toThrow(/dependsOn/); }); - it('rejects undefined dependencyOf', async () => { - (entity as any).spec.dependencyOf = [undefined]; - await expect(validator.check(entity)).rejects.toThrow(/dependencyOf/); + it('rejects undefined dependsOn', async () => { + (entity as any).spec.dependsOn = [undefined]; + await expect(validator.check(entity)).rejects.toThrow(/dependsOn/); }); - it('accepts no dependencyOf', async () => { - (entity as any).spec.dependencyOf = []; + it('accepts no dependsOn', async () => { + (entity as any).spec.dependsOn = []; await expect(validator.check(entity)).resolves.toBe(true); }); diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts index 0b3662b91f..fd71500f40 100644 --- a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts @@ -30,7 +30,7 @@ export interface ResourceEntityV1alpha1 extends Entity { spec: { type: string; owner: string; - dependencyOf?: string[]; + dependsOn?: string[]; system?: string; }; } diff --git a/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json b/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json index dbe9c969f3..2dcf6f4eb2 100644 --- a/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json +++ b/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json @@ -47,7 +47,7 @@ "examples": ["artist-relations-team", "user:john.johnson"], "minLength": 1 }, - "dependencyOf": { + "dependsOn": { "type": "array", "description": "An array of entity references to the components that the resource is a dependency of.", "items": { diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts index 2d61191aa8..ad446c6a7a 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts @@ -45,14 +45,14 @@ describe('BuiltinKindsEntityProcessor', () => { lifecycle: 'l', providesApis: ['b'], consumesApis: ['c'], - dependsOn: ['r'], + dependsOn: ['Resource:r', 'Component:d'], system: 's', }, }; await processor.postProcessEntity(entity, location, emit); - expect(emit).toBeCalledTimes(12); + expect(emit).toBeCalledTimes(14); expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -101,6 +101,14 @@ describe('BuiltinKindsEntityProcessor', () => { target: { kind: 'API', namespace: 'default', name: 'c' }, }, }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Component', namespace: 'default', name: 'n' }, + type: 'dependsOn', + target: { kind: 'Resource', namespace: 'default', name: 'r' }, + }, + }); expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -114,7 +122,15 @@ describe('BuiltinKindsEntityProcessor', () => { relation: { source: { kind: 'Component', namespace: 'default', name: 'n' }, type: 'dependsOn', - target: { kind: 'Resource', namespace: 'default', name: 'r' }, + target: { kind: 'Component', namespace: 'default', name: 'd' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Component', namespace: 'default', name: 'd' }, + type: 'dependencyOf', + target: { kind: 'Component', namespace: 'default', name: 'n' }, }, }); expect(emit).toBeCalledWith({ @@ -151,6 +167,29 @@ describe('BuiltinKindsEntityProcessor', () => { }); }); + it('generates an error for component entities with unspecified dependsOn entity reference kinds', async () => { + const entity: ComponentEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { name: 'n' }, + spec: { + type: 'service', + owner: 'o', + subcomponentOf: 's', + lifecycle: 'l', + providesApis: ['b'], + consumesApis: ['c'], + dependsOn: ['r'], + system: 's', + }, + }; + await expect( + processor.postProcessEntity(entity, location, emit), + ).rejects.toThrowError( + 'Entity reference kind is undefined and has no default', + ); + }); + it('generates relations for api entities', async () => { const entity: ApiEntity = { apiVersion: 'backstage.io/v1alpha1', @@ -210,14 +249,14 @@ describe('BuiltinKindsEntityProcessor', () => { spec: { type: 'database', owner: 'o', - dependencyOf: ['c'], + dependsOn: ['Component:c', 'Resource:r'], system: 's', }, }; await processor.postProcessEntity(entity, location, emit); - expect(emit).toBeCalledTimes(6); + expect(emit).toBeCalledTimes(8); expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -234,11 +273,12 @@ describe('BuiltinKindsEntityProcessor', () => { target: { kind: 'Group', namespace: 'default', name: 'o' }, }, }); + expect(emit).toBeCalledWith({ type: 'relation', relation: { source: { kind: 'Resource', namespace: 'default', name: 'n' }, - type: 'dependencyOf', + type: 'dependsOn', target: { kind: 'Component', namespace: 'default', name: 'c' }, }, }); @@ -246,10 +286,28 @@ describe('BuiltinKindsEntityProcessor', () => { type: 'relation', relation: { source: { kind: 'Component', namespace: 'default', name: 'c' }, - type: 'dependsOn', + type: 'dependencyOf', target: { kind: 'Resource', namespace: 'default', name: 'n' }, }, }); + + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Resource', namespace: 'default', name: 'n' }, + type: 'dependsOn', + target: { kind: 'Resource', namespace: 'default', name: 'r' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Resource', namespace: 'default', name: 'r' }, + type: 'dependencyOf', + target: { kind: 'Resource', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -268,6 +326,25 @@ describe('BuiltinKindsEntityProcessor', () => { }); }); + it('generates an error for resource entities with unspecified dependsOn entity reference kinds', async () => { + const entity: ResourceEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Resource', + metadata: { name: 'n' }, + spec: { + type: 'database', + owner: 'o', + dependsOn: ['r'], + system: 's', + }, + }; + await expect( + processor.postProcessEntity(entity, location, emit), + ).rejects.toThrowError( + 'Entity reference kind is undefined and has no default', + ); + }); + it('generates relations for system entities', async () => { const entity: SystemEntity = { apiVersion: 'backstage.io/v1alpha1', diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts index d1de992a81..27313ba014 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts @@ -92,7 +92,7 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { function doEmit( targets: string | string[] | undefined, - context: { defaultKind: string; defaultNamespace: string }, + context: { defaultKind?: string; defaultNamespace: string }, outgoingRelation: string, incomingRelation: string, ): void { @@ -101,16 +101,29 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { } for (const target of [targets].flat()) { const targetRef = parseEntityRef(target, context); + if (targetRef.kind === undefined) { + throw new Error( + 'Entity reference kind is undefined and has no default', + ); + } emit( result.relation({ source: selfRef, type: outgoingRelation, - target: targetRef, + target: { + kind: targetRef.kind, + namespace: targetRef.namespace, + name: targetRef.name, + }, }), ); emit( result.relation({ - source: targetRef, + source: { + kind: targetRef.kind, + namespace: targetRef.namespace, + name: targetRef.name, + }, type: incomingRelation, target: selfRef, }), @@ -150,7 +163,7 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { ); doEmit( component.spec.dependsOn, - { defaultKind: 'Resource', defaultNamespace: selfRef.namespace }, + { defaultNamespace: selfRef.namespace }, RELATION_DEPENDS_ON, RELATION_DEPENDENCY_OF, ); @@ -195,10 +208,10 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { RELATION_OWNER_OF, ); doEmit( - resource.spec.dependencyOf, - { defaultKind: 'Component', defaultNamespace: selfRef.namespace }, - RELATION_DEPENDENCY_OF, + resource.spec.dependsOn, + { defaultNamespace: selfRef.namespace }, RELATION_DEPENDS_ON, + RELATION_DEPENDENCY_OF, ); doEmit( resource.spec.system, From 8c999911279599aa8b069e16e515a84ecfe7f8a6 Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Wed, 31 Mar 2021 13:46:07 -0400 Subject: [PATCH 10/13] Update packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Jonah Grimes --- .../src/schema/kinds/Component.v1alpha1.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json b/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json index e2b98d5c3b..34fc48d285 100644 --- a/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json +++ b/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json @@ -87,7 +87,7 @@ }, "dependsOn": { "type": "array", - "description": "An array of entity references to the resources that the component depends on.", + "description": "An array of references to other entities that the component depends on to function.", "items": { "type": "string", "minLength": 1 From 3b0c1338f6a39292cec57f6f57cd9d8a40d2d1ab Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Wed, 31 Mar 2021 13:46:19 -0400 Subject: [PATCH 11/13] Update packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Jonah Grimes --- .../src/schema/kinds/Resource.v1alpha1.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json b/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json index 2dcf6f4eb2..4958afc18c 100644 --- a/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json +++ b/packages/catalog-model/src/schema/kinds/Resource.v1alpha1.schema.json @@ -49,7 +49,7 @@ }, "dependsOn": { "type": "array", - "description": "An array of entity references to the components that the resource is a dependency of.", + "description": "An array of references to other entities that the resource depends on to function.", "items": { "type": "string", "minLength": 1 From 6e936c417c707618ac2cb0b94b69642cc23dec62 Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Wed, 31 Mar 2021 13:46:39 -0400 Subject: [PATCH 12/13] Update plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Jonah Grimes --- .../src/ingestion/processors/BuiltinKindsEntityProcessor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts index 27313ba014..0f9522ca27 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts @@ -103,7 +103,7 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { const targetRef = parseEntityRef(target, context); if (targetRef.kind === undefined) { throw new Error( - 'Entity reference kind is undefined and has no default', + `Entity reference "${target}" did not specify a kind (e.g. starting with "Component:"), and has no default`, ); } emit( From a9eff0c1b4b63ceff04769d965e0d0df6816442a Mon Sep 17 00:00:00 2001 From: Jonah Grimes Date: Wed, 31 Mar 2021 15:42:01 -0400 Subject: [PATCH 13/13] fixed failing tests Signed-off-by: Jonah Grimes --- .../processors/BuiltinKindsEntityProcessor.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts index ad446c6a7a..a86acc3138 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts @@ -186,7 +186,7 @@ describe('BuiltinKindsEntityProcessor', () => { await expect( processor.postProcessEntity(entity, location, emit), ).rejects.toThrowError( - 'Entity reference kind is undefined and has no default', + 'Entity reference "r" did not specify a kind (e.g. starting with "Component:"), and has no default', ); }); @@ -334,14 +334,14 @@ describe('BuiltinKindsEntityProcessor', () => { spec: { type: 'database', owner: 'o', - dependsOn: ['r'], + dependsOn: ['c'], system: 's', }, }; await expect( processor.postProcessEntity(entity, location, emit), ).rejects.toThrowError( - 'Entity reference kind is undefined and has no default', + 'Entity reference "c" did not specify a kind (e.g. starting with "Component:"), and has no default', ); });