feat(catalog): add tags to entity metadata

This add a field tags to the entity metadata to specifiy a user defined classifications.

Closes #1997
This commit is contained in:
Oliver Sand
2020-08-18 18:35:45 +02:00
parent 044a2e2eab
commit 86eac5b458
10 changed files with 52 additions and 0 deletions
@@ -42,6 +42,7 @@ software catalog API.
"labels": {
"system": "public-websites"
},
"tags": ["java"],
"name": "artist-web",
"uid": "2152f463-549d-4d8d-a94d-ce2b7676c6e2"
},
@@ -66,6 +67,8 @@ metadata:
annotations:
example.com/service-discovery: artistweb
circleci.com/project-slug: gh/example-org/artist-website
tags:
- java
spec:
type: website
lifecycle: production
@@ -232,6 +235,20 @@ The `backstage.io/` prefix is reserved for use by Backstage core components.
Values can be of any length, but are limited to being strings.
### `tags` [optional]
A list of single-valued strings, for example to classify catalog entities in
various ways. This is different to the labels in metadata, as labels are
key-value pairs.
The values are user defined, for example the programming language used for the
component, like `java` or `go`.
This field is optional, and currently has no special semantics.
Each tag must be sequences of `[a-zA-Z0-9]` separated by `-`, at most 63
characters in total.
## Kind: Component
Describes the following entity kind:
@@ -113,6 +113,12 @@ export type EntityMeta = JsonObject & {
* entity.
*/
annotations?: Record<string, string>;
/**
* A list of single-valued strings, to for example classify catalog entities in
* various ways.
*/
tags?: string[];
};
/**
@@ -35,6 +35,9 @@ describe('FieldFormatEntityPolicy', () => {
backstage.io/custom: ValueStuff
annotations:
example.com/bindings: are-secret
tags:
- java
- data-service
spec:
custom: stuff
`);
@@ -102,4 +105,9 @@ describe('FieldFormatEntityPolicy', () => {
data.metadata.annotations.a = 7;
await expect(policy.enforce(data)).rejects.toThrow(/annotation.*7/i);
});
it('rejects bad tag value', async () => {
data.metadata.tags.push('Hello World');
await expect(policy.enforce(data)).rejects.toThrow(/tags.*"Hello World"/i);
});
});
@@ -83,6 +83,12 @@ export class FieldFormatEntityPolicy implements EntityPolicy {
require(`annotations.${k}`, v, this.validators.isValidAnnotationValue);
}
const tags = entity.metadata.tags ?? [];
for (let i = 0; i < tags.length; ++i) {
require(`tags.${i}`, tags[i], this.validators.isValidTag);
}
return entity;
}
}
@@ -35,6 +35,9 @@ describe('ReservedFieldsEntityPolicy', () => {
backstage.io/custom: ValueStuff
annotations:
example.com/bindings: are-secret
tags:
- java
- data
spec:
custom: stuff
`);
@@ -29,6 +29,7 @@ const DEFAULT_RESERVED_ENTITY_FIELDS: string[] = [
'metadata.description',
'metadata.labels',
'metadata.annotations',
'metadata.tags',
// The below items are known to appear in core kinds, and therefore should
// not be appearing in metadata (which would indicate that the user made a
// mistake in where to place them).
@@ -36,6 +36,9 @@ describe('SchemaValidEntityPolicy', () => {
backstage.io/custom: ValueStuff
annotations:
example.com/bindings: are-secret
tags:
- java
- data
spec:
custom: stuff
`);
@@ -190,6 +193,11 @@ describe('SchemaValidEntityPolicy', () => {
await expect(policy.enforce(data)).rejects.toThrow(/annotations/);
});
it('rejects bad tags type', async () => {
data.metadata.tags = 7;
await expect(policy.enforce(data)).rejects.toThrow(/tags/);
});
//
// spec
//
@@ -31,6 +31,7 @@ const DEFAULT_ENTITY_SCHEMA = yup.object({
description: yup.string().notRequired(),
labels: yup.object<Record<string, string>>().notRequired(),
annotations: yup.object<Record<string, string>>().notRequired(),
tags: yup.array<string>().notRequired(),
})
.required(),
spec: yup.object({}).notRequired(),
@@ -28,6 +28,7 @@ const defaultValidators: Validators = {
isValidLabelValue: KubernetesValidatorFunctions.isValidLabelValue,
isValidAnnotationKey: KubernetesValidatorFunctions.isValidAnnotationKey,
isValidAnnotationValue: KubernetesValidatorFunctions.isValidAnnotationValue,
isValidTag: CommonValidatorFunctions.isValidDnsLabel,
};
export function makeValidator(overrides: Partial<Validators> = {}): Validators {
@@ -24,4 +24,5 @@ export type Validators = {
isValidLabelValue(value: any): boolean;
isValidAnnotationKey(value: any): boolean;
isValidAnnotationValue(value: any): boolean;
isValidTag(value: any): boolean;
};