Add providesApis and consumesApis to component spec

This commit is contained in:
Oliver Sand
2020-11-25 16:01:34 +01:00
parent b614e81184
commit fb19e0241d
7 changed files with 118 additions and 5 deletions
@@ -381,7 +381,7 @@ spec:
type: website
lifecycle: production
owner: artist-relations@example.com
implementsApis:
providesApis:
- artist-api
```
@@ -451,6 +451,25 @@ is optional.
The software catalog expects a list of one or more strings that references the
names of other entities of the `kind` `API`.
This field has the same behavior as `spec.providesApis` and will be deprecated
in the future.
### `spec.providesApis` [optional]
Links APIs that are provided by the component, e.g. `artist-api`. This field is
optional.
The software catalog expects a list of one or more strings that references the
names of other entities of the `kind` `API`.
### `spec.consumesApis` [optional]
Links APIs that are consumed by the component, e.g. `artist-api`. This field is
optional.
The software catalog expects a list of one or more strings that references the
names of other entities of the `kind` `API`.
## Kind: Template
Describes the following entity kind:
+2 -2
View File
@@ -51,7 +51,7 @@ spec:
type: service
lifecycle: experimental
owner: group:pet-managers
implementsApis:
providesApis:
- petstore
- internal/streetlights
- hello-world
@@ -66,7 +66,7 @@ catalog that is of kind `Group`, namespace `default` (which, actually, also can
be left out in its own yaml file because that's the default value there too),
and name `pet-managers`.
The entries in `implementsApis` are also references. In this case, none of them
The entries in `providesApis` are also references. In this case, none of them
needs to specify a kind since we know from the context that that's the only kind
that's supported here. The second entry specifies a namespace but the other ones
don't, and in this context, the default is to refer to the same namespace as the
@@ -34,6 +34,8 @@ describe('ComponentV1alpha1Validator', () => {
lifecycle: 'production',
owner: 'me',
implementsApis: ['api-0'],
providesApis: ['api-0'],
consumesApis: ['api-0'],
},
};
});
@@ -121,4 +123,44 @@ describe('ComponentV1alpha1Validator', () => {
(entity as any).spec.implementsApis = [];
await expect(validator.check(entity)).resolves.toBe(true);
});
it('accepts missing providesApis', async () => {
delete (entity as any).spec.providesApis;
await expect(validator.check(entity)).resolves.toBe(true);
});
it('rejects empty providesApis', async () => {
(entity as any).spec.providesApis = [''];
await expect(validator.check(entity)).rejects.toThrow(/providesApis/);
});
it('rejects undefined providesApis', async () => {
(entity as any).spec.providesApis = [undefined];
await expect(validator.check(entity)).rejects.toThrow(/providesApis/);
});
it('accepts no providesApis', async () => {
(entity as any).spec.providesApis = [];
await expect(validator.check(entity)).resolves.toBe(true);
});
it('accepts missing consumesApis', async () => {
delete (entity as any).spec.consumesApis;
await expect(validator.check(entity)).resolves.toBe(true);
});
it('rejects empty consumesApis', async () => {
(entity as any).spec.consumesApis = [''];
await expect(validator.check(entity)).rejects.toThrow(/consumesApis/);
});
it('rejects undefined consumesApis', async () => {
(entity as any).spec.consumesApis = [undefined];
await expect(validator.check(entity)).rejects.toThrow(/consumesApis/);
});
it('accepts no consumesApis', async () => {
(entity as any).spec.consumesApis = [];
await expect(validator.check(entity)).resolves.toBe(true);
});
});
@@ -30,6 +30,8 @@ const schema = yup.object<Partial<ComponentEntityV1alpha1>>({
lifecycle: yup.string().required().min(1),
owner: yup.string().required().min(1),
implementsApis: yup.array(yup.string().required()).notRequired(),
providesApis: yup.array(yup.string().required()).notRequired(),
consumesApis: yup.array(yup.string().required()).notRequired(),
kubernetes: yup
.object<any>({
selector: yup
@@ -51,6 +53,8 @@ export interface ComponentEntityV1alpha1 extends Entity {
lifecycle: string;
owner: string;
implementsApis?: string[];
providesApis?: string[];
consumesApis?: string[];
kubernetes?: {
selector: {
matchLabels: {
+1 -1
View File
@@ -21,7 +21,7 @@ Right now, the following API formats are supported:
Other formats are displayed as plain text, but this can easily be extended.
To fill the catalog with APIs, [provide entities of kind API](https://backstage.io/docs/features/software-catalog/descriptor-format#kind-api).
To link that an component implements an API, see [`implementsApis` property on components](https://backstage.io/docs/features/software-catalog/descriptor-format#specimplementsapis-optional).
To link that an component provides or consumes an API, see [`providesApis`](https://backstage.io/docs/features/software-catalog/descriptor-format#specprovidesapis-optional) and [`consumesApis`](https://backstage.io/docs/features/software-catalog/descriptor-format#specconsumesapis-optional) property on components.
## Links
@@ -68,12 +68,14 @@ describe('BuiltinKindsEntityProcessor', () => {
owner: 'o',
lifecycle: 'l',
implementsApis: ['a'],
providesApis: ['b'],
consumesApis: ['c'],
},
};
await processor.postProcessEntity(entity, location, emit);
expect(emit).toBeCalledTimes(4);
expect(emit).toBeCalledTimes(8);
expect(emit).toBeCalledWith({
type: 'relation',
relation: {
@@ -106,6 +108,38 @@ describe('BuiltinKindsEntityProcessor', () => {
target: { kind: 'API', namespace: 'default', name: 'a' },
},
});
expect(emit).toBeCalledWith({
type: 'relation',
relation: {
source: { kind: 'API', namespace: 'default', name: 'b' },
type: 'apiProvidedBy',
target: { kind: 'Component', namespace: 'default', name: 'n' },
},
});
expect(emit).toBeCalledWith({
type: 'relation',
relation: {
source: { kind: 'Component', namespace: 'default', name: 'n' },
type: 'providesApi',
target: { kind: 'API', namespace: 'default', name: 'b' },
},
});
expect(emit).toBeCalledWith({
type: 'relation',
relation: {
source: { kind: 'API', namespace: 'default', name: 'c' },
type: 'apiConsumedBy',
target: { kind: 'Component', namespace: 'default', name: 'n' },
},
});
expect(emit).toBeCalledWith({
type: 'relation',
relation: {
source: { kind: 'Component', namespace: 'default', name: 'n' },
type: 'consumesApi',
target: { kind: 'API', namespace: 'default', name: 'c' },
},
});
});
it('generates relations for api entities', async () => {
@@ -26,8 +26,10 @@ import {
locationEntityV1alpha1Validator,
LocationSpec,
parseEntityRef,
RELATION_API_CONSUMED_BY,
RELATION_API_PROVIDED_BY,
RELATION_CHILD_OF,
RELATION_CONSUMES_API,
RELATION_HAS_MEMBER,
RELATION_MEMBER_OF,
RELATION_OWNED_BY,
@@ -138,6 +140,18 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
RELATION_PROVIDES_API,
RELATION_API_PROVIDED_BY,
);
doEmit(
component.spec.providesApis,
{ defaultKind: 'API', defaultNamespace: selfRef.namespace },
RELATION_PROVIDES_API,
RELATION_API_PROVIDED_BY,
);
doEmit(
component.spec.consumesApis,
{ defaultKind: 'API', defaultNamespace: selfRef.namespace },
RELATION_CONSUMES_API,
RELATION_API_CONSUMED_BY,
);
}
/*