Merge pull request #3449 from SDA-SE/feat/provides-consumse-api
Replace implementsApis with providesApis and add consumesApis
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-api-docs': patch
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Replace usage of implementsApis with relations
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Add `providesApis` and `consumesApis` to the component entity spec.
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
---
|
||||
|
||||
Marked the field `spec.implementsApis` on `Component` entities for deprecation on Dec 14th, 2020.
|
||||
|
||||
Code that consumes these fields should remove those usages as soon as possible and migrate to using
|
||||
relations instead. Producers should fill the field `spec.providesApis` instead, which has the same
|
||||
semantic.
|
||||
|
||||
After Dec 14th, the fields will be removed from types and classes of the Backstage repository. At
|
||||
the first release after that, they will not be present in released packages either.
|
||||
|
||||
If your catalog-info.yaml files still contain this field after the deletion, they will still be
|
||||
valid and your ingestion will not break, but they won't be visible in the types for consuming code, and the expected relations will not be generated based on them either.```
|
||||
@@ -381,7 +381,7 @@ spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: artist-relations@example.com
|
||||
implementsApis:
|
||||
providesApis:
|
||||
- artist-api
|
||||
```
|
||||
|
||||
@@ -445,12 +445,35 @@ group of people in an organizational structure.
|
||||
|
||||
### `spec.implementsApis` [optional]
|
||||
|
||||
**NOTE**: This field was marked for deprecation on Nov 25nd, 2020. It will be
|
||||
removed entirely from the model on Dec 14th, 2020 in the repository and will not
|
||||
be present in released packages following the next release after that. Please
|
||||
update your code to not consume this field before the removal date.
|
||||
|
||||
Links APIs that are implemented 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`.
|
||||
|
||||
This field has the same behavior as `spec.providesApis`.
|
||||
|
||||
### `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:
|
||||
|
||||
@@ -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
|
||||
@@ -50,7 +52,14 @@ export interface ComponentEntityV1alpha1 extends Entity {
|
||||
type: string;
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
/**
|
||||
* @deprecated This field will disappear on Dec 14th, 2020. Please remove
|
||||
* any consuming code. The new field providesApis provides the
|
||||
* same functionality like before.
|
||||
*/
|
||||
implementsApis?: string[];
|
||||
providesApis?: string[];
|
||||
consumesApis?: string[];
|
||||
kubernetes?: {
|
||||
selector: {
|
||||
matchLabels: {
|
||||
|
||||
@@ -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 a component provides or consumes an API, see the [`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) properties on the Component kind.
|
||||
|
||||
## Links
|
||||
|
||||
|
||||
+3
-4
@@ -28,7 +28,7 @@ spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: guest
|
||||
implementsApis:
|
||||
providesApis:
|
||||
- example-api
|
||||
`;
|
||||
|
||||
@@ -49,8 +49,7 @@ export const MissingImplementsApisEmptyState = () => {
|
||||
description={
|
||||
<Typography>
|
||||
Components can implement APIs that are displayed on this page. You
|
||||
need to fill the <code>implementsApis</code> field to enable this
|
||||
tool.
|
||||
need to fill the <code>providesApis</code> field to enable this tool.
|
||||
</Typography>
|
||||
}
|
||||
action={
|
||||
@@ -71,7 +70,7 @@ export const MissingImplementsApisEmptyState = () => {
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
href="https://backstage.io/docs/features/software-catalog/descriptor-format#specimplementsapis-optional"
|
||||
href="https://backstage.io/docs/features/software-catalog/descriptor-format#specprovidesapis-optional"
|
||||
>
|
||||
Read more
|
||||
</Button>
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Entity, RELATION_PROVIDES_API } from '@backstage/catalog-model';
|
||||
import { Route, Routes } from 'react-router';
|
||||
import { catalogRoute } from '../routes';
|
||||
import { EntityPageApi } from './EntityPageApi';
|
||||
import { MissingImplementsApisEmptyState } from './MissingImplementsApisEmptyState';
|
||||
|
||||
const isPluginApplicableToEntity = (entity: Entity) => {
|
||||
return ((entity.spec?.implementsApis as string[]) || []).length > 0;
|
||||
// TODO: Also support RELATION_CONSUMES_API
|
||||
return entity.relations?.some(r => r.type === RELATION_PROVIDES_API);
|
||||
};
|
||||
|
||||
export const Router = ({ entity }: { entity: Entity }) =>
|
||||
|
||||
@@ -14,8 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentEntity } from '@backstage/catalog-model';
|
||||
import {
|
||||
ComponentEntity,
|
||||
RELATION_PROVIDES_API,
|
||||
} from '@backstage/catalog-model';
|
||||
|
||||
export const useComponentApiNames = (entity: ComponentEntity) => {
|
||||
return (entity.spec?.implementsApis as string[]) || [];
|
||||
// TODO: This code doesn't handle namespaces and kinds correctly, but will be removed soon
|
||||
return (
|
||||
entity.relations
|
||||
?.filter(r => r.type === RELATION_PROVIDES_API)
|
||||
?.map(r => r.target.name) || []
|
||||
);
|
||||
};
|
||||
|
||||
+35
-1
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
Entity,
|
||||
ENTITY_DEFAULT_NAMESPACE,
|
||||
RELATION_OWNED_BY,
|
||||
RELATION_PROVIDES_API,
|
||||
serializeEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import {
|
||||
@@ -111,6 +112,8 @@ type AboutCardProps = {
|
||||
export function AboutCard({ entity, variant }: AboutCardProps) {
|
||||
const classes = useStyles();
|
||||
const codeLink = getCodeLinkInfo(entity);
|
||||
// TODO: Also support RELATION_CONSUMES_API here
|
||||
const hasApis = entity.relations?.some(r => r.type === RELATION_PROVIDES_API);
|
||||
|
||||
return (
|
||||
<Card className={variant === 'gridItem' ? classes.gridItemCard : ''}>
|
||||
@@ -146,9 +149,9 @@ export function AboutCard({ entity, variant }: AboutCardProps) {
|
||||
}/${entity.kind}/${entity.metadata.name}`}
|
||||
/>
|
||||
<IconLinkVertical
|
||||
disabled={!entity.spec?.implementsApis}
|
||||
disabled={!hasApis}
|
||||
label="View API"
|
||||
title={!entity.spec?.implementsApis ? 'No APIs available' : ''}
|
||||
title={hasApis ? '' : 'No APIs available'}
|
||||
icon={<ExtensionIcon />}
|
||||
href="api"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user