Merge pull request #17125 from dawngerpony/dawngerpony/parent-domain-v1
Allow a domain entity to have an optional domain
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/catalog-model': minor
|
||||
---
|
||||
|
||||
Introduce a domain attribute to the domain entity to allow a hierarchy of domains to exist.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
Emit well known relationships for the Domain entity kind.
|
||||
@@ -1265,6 +1265,7 @@ metadata:
|
||||
description: Everything about artists
|
||||
spec:
|
||||
owner: artist-relations-team
|
||||
subdomainOf: audio-domain
|
||||
```
|
||||
|
||||
In addition to the [common envelope metadata](#common-to-all-kinds-the-metadata)
|
||||
@@ -1293,6 +1294,15 @@ but there will always be one ultimate owner.
|
||||
| ------------------------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------- |
|
||||
| [`Group`](#kind-group) (default), [`User`](#kind-user) | Same as this entity, typically `default` | [`ownerOf`, and reverse `ownedBy`](well-known-relations.md#ownedby-and-ownerof) |
|
||||
|
||||
### `spec.subdomainOf` [optional]
|
||||
|
||||
An [entity reference](references.md#string-references) to another domain of
|
||||
which the domain is a part, e.g. `audio`. This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| [`Domain`](#kind-domain) (default) | Same as this entity, typically `default` | [`partOf`, and reverse `hasPart`](well-known-relations.md#partof-and-haspart) |
|
||||
|
||||
## Kind: Location
|
||||
|
||||
Describes the following entity kind:
|
||||
|
||||
@@ -99,11 +99,12 @@ A relation with a [Domain](descriptor-format.md#kind-domain),
|
||||
[System](descriptor-format.md#kind-system) or
|
||||
[Component](descriptor-format.md#kind-component) entity, typically from a
|
||||
[Component](descriptor-format.md#kind-component),
|
||||
[API](descriptor-format.md#kind-api), or
|
||||
[System](descriptor-format.md#kind-system).
|
||||
[API](descriptor-format.md#kind-api),
|
||||
[System](descriptor-format.md#kind-system), or
|
||||
[Domain](descriptor-format.md#kind-domain),
|
||||
|
||||
These relations express that a component belongs to a larger component; a
|
||||
component, API or resource belongs to a system; or that a system is grouped
|
||||
under a domain.
|
||||
component, API or resource belongs to a system; that a system is grouped
|
||||
under a domain; or that a domain belongs to a larger domain.
|
||||
|
||||
This relation is commonly based on `spec.system` or `spec.domain`.
|
||||
|
||||
@@ -122,6 +122,7 @@ interface DomainEntityV1alpha1 extends Entity {
|
||||
// (undocumented)
|
||||
spec: {
|
||||
owner: string;
|
||||
subdomainOf?: string;
|
||||
};
|
||||
}
|
||||
export { DomainEntityV1alpha1 as DomainEntity };
|
||||
|
||||
@@ -5,5 +5,6 @@ metadata:
|
||||
description: A collection of all Backstage example domains
|
||||
spec:
|
||||
targets:
|
||||
- ./domains/audio-domain.yaml
|
||||
- ./domains/artists-domain.yaml
|
||||
- ./domains/playback-domain.yaml
|
||||
|
||||
@@ -12,3 +12,4 @@ metadata:
|
||||
icon: dashboard
|
||||
spec:
|
||||
owner: team-a
|
||||
subdomainOf: audio
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Domain
|
||||
metadata:
|
||||
name: audio
|
||||
description: Everything related to audio
|
||||
links:
|
||||
- url: http://example.com/domain/audio/
|
||||
title: Domain Readme
|
||||
- url: http://example.com/domains/audio/dashboard
|
||||
title: Domain Metrics Dashboard
|
||||
icon: dashboard
|
||||
spec:
|
||||
owner: acme-corp
|
||||
@@ -5,3 +5,4 @@ metadata:
|
||||
description: Everything related to audio playback
|
||||
spec:
|
||||
owner: user:frank.tiernan
|
||||
subdomainOf: audio
|
||||
|
||||
@@ -31,6 +31,7 @@ describe('DomainV1alpha1Validator', () => {
|
||||
},
|
||||
spec: {
|
||||
owner: 'me',
|
||||
subdomainOf: 'parent-domain',
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -68,4 +69,19 @@ describe('DomainV1alpha1Validator', () => {
|
||||
(entity as any).spec.owner = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('accepts missing subdomainOf', async () => {
|
||||
delete (entity as any).spec.subdomainOf;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong subdomainOf', async () => {
|
||||
(entity as any).spec.subdomainOf = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/subdomainOf/);
|
||||
});
|
||||
|
||||
it('rejects empty subdomainOf', async () => {
|
||||
(entity as any).spec.subdomainOf = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/subdomainOf/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface DomainEntityV1alpha1 extends Entity {
|
||||
kind: 'Domain';
|
||||
spec: {
|
||||
owner: string;
|
||||
subdomainOf?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"description": "Everything about artists"
|
||||
},
|
||||
"spec": {
|
||||
"owner": "artist-relations-team"
|
||||
"owner": "artist-relations-team",
|
||||
"subdomainOf": "audio"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -38,6 +39,12 @@
|
||||
"description": "An entity reference to the owner of the component.",
|
||||
"examples": ["artist-relations-team", "user:john.johnson"],
|
||||
"minLength": 1
|
||||
},
|
||||
"subdomainOf": {
|
||||
"type": "string",
|
||||
"description": "An entity reference to another domain of which the domain is a part.",
|
||||
"examples": ["audio"],
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,12 +418,13 @@ describe('BuiltinKindsEntityProcessor', () => {
|
||||
metadata: { name: 'n' },
|
||||
spec: {
|
||||
owner: 'o',
|
||||
subdomainOf: 'p',
|
||||
},
|
||||
};
|
||||
|
||||
await processor.postProcessEntity(entity, location, emit);
|
||||
|
||||
expect(emit).toHaveBeenCalledTimes(2);
|
||||
expect(emit).toHaveBeenCalledTimes(4);
|
||||
expect(emit).toHaveBeenCalledWith({
|
||||
type: 'relation',
|
||||
relation: {
|
||||
@@ -440,6 +441,22 @@ describe('BuiltinKindsEntityProcessor', () => {
|
||||
target: { kind: 'Group', namespace: 'default', name: 'o' },
|
||||
},
|
||||
});
|
||||
expect(emit).toHaveBeenCalledWith({
|
||||
type: 'relation',
|
||||
relation: {
|
||||
source: { kind: 'Domain', namespace: 'default', name: 'n' },
|
||||
type: 'partOf',
|
||||
target: { kind: 'Domain', namespace: 'default', name: 'p' },
|
||||
},
|
||||
});
|
||||
expect(emit).toHaveBeenCalledWith({
|
||||
type: 'relation',
|
||||
relation: {
|
||||
source: { kind: 'Domain', namespace: 'default', name: 'p' },
|
||||
type: 'hasPart',
|
||||
target: { kind: 'Domain', namespace: 'default', name: 'n' },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('generates relations for user entities', async () => {
|
||||
|
||||
@@ -298,6 +298,12 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
|
||||
RELATION_OWNED_BY,
|
||||
RELATION_OWNER_OF,
|
||||
);
|
||||
doEmit(
|
||||
domain.spec.subdomainOf,
|
||||
{ defaultKind: 'Domain', defaultNamespace: selfRef.namespace },
|
||||
RELATION_PART_OF,
|
||||
RELATION_HAS_PART,
|
||||
);
|
||||
}
|
||||
|
||||
return entity;
|
||||
|
||||
Reference in New Issue
Block a user