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 <jonah.grimes@gmail.com>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface ResourceEntityV1alpha1 extends Entity {
|
||||
spec: {
|
||||
type: string;
|
||||
owner: string;
|
||||
dependencyOf?: string[];
|
||||
dependsOn?: string[];
|
||||
system?: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
+84
-7
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user