spec.owner added into template schema
Signed-off-by: Victor Perera <v-vperera@expediagroup.com>
This commit is contained in:
@@ -657,6 +657,25 @@ You can find out more about the `parameters` key
|
||||
You can find out more about the `steps` key
|
||||
[here](../software-templates/writing-templates.md)
|
||||
|
||||
### `spec.owner` [optional]
|
||||
|
||||
An [entity reference](#string-references) to the owner of the component, e.g.
|
||||
`artist-relations-team`. This field is required.
|
||||
|
||||
In Backstage, the owner of an Template is the singular entity (commonly a team)
|
||||
that bears ultimate responsibility for the Template, and has the authority and
|
||||
capability to develop and maintain it. They will be the point of contact if
|
||||
something goes wrong, or if features are to be requested. The main purpose of
|
||||
this field is for display purposes in Backstage, so that people looking at
|
||||
catalog items can get an understanding of to whom this Template belongs. It is
|
||||
not to be used by automated processes to for example assign authorization in
|
||||
runtime systems. There may be others that also develop or otherwise touch the
|
||||
Template, but there will always be one ultimate owner.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| ------------------------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------- |
|
||||
| [`Group`](#kind-group) (default), [`User`](#kind-user) | Same as this entity, typically `default` | [`ownerOf`, and reverse `ownedBy`](well-known-relations.md#ownedby-and-ownerof) |
|
||||
|
||||
## Kind: API
|
||||
|
||||
Describes the following entity kind:
|
||||
|
||||
@@ -91,4 +91,8 @@ describe('templateEntityV1alpha1Validator', () => {
|
||||
(entity as any).spec.templater = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/templater/);
|
||||
});
|
||||
it('accepts missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface TemplateEntityV1alpha1 extends Entity {
|
||||
templater: string;
|
||||
path?: string;
|
||||
schema: JSONSchema;
|
||||
owner: string;
|
||||
owner?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ describe('templateEntityV1beta2Validator', () => {
|
||||
output: {
|
||||
fetchUrl: '{{ steps.fetch.output.targetUrl }}',
|
||||
},
|
||||
owner: 'example@email.com',
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -121,4 +122,8 @@ describe('templateEntityV1beta2Validator', () => {
|
||||
delete (entity as any).spec.steps[0].action;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/action/);
|
||||
});
|
||||
it('accepts missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface TemplateEntityV1beta2 extends Entity {
|
||||
parameters?: JsonObject;
|
||||
}>;
|
||||
output?: { [name: string]: string };
|
||||
owner?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,10 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"description": "The JSONSchema describing the inputs for the template."
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"description": "The user (or group) owner of the template"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +168,10 @@
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"description": "The user (or group) owner of the template"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
GroupEntity,
|
||||
ResourceEntity,
|
||||
SystemEntity,
|
||||
TemplateEntity,
|
||||
UserEntity,
|
||||
} from '@backstage/catalog-model';
|
||||
import { BuiltinKindsEntityProcessor } from './BuiltinKindsEntityProcessor';
|
||||
@@ -520,5 +521,47 @@ describe('BuiltinKindsEntityProcessor', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
it('generates relations for template entities', async () => {
|
||||
const entity: TemplateEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Template',
|
||||
metadata: { name: 'n' },
|
||||
spec: {
|
||||
schema: {
|
||||
properties: {
|
||||
description: {
|
||||
title: 'd',
|
||||
type: 'string',
|
||||
description: 'des',
|
||||
},
|
||||
},
|
||||
},
|
||||
templater: 'cookiecutter',
|
||||
path: '.',
|
||||
type: 'service',
|
||||
owner: 'o',
|
||||
},
|
||||
};
|
||||
|
||||
await processor.postProcessEntity(entity, location, emit);
|
||||
|
||||
expect(emit).toBeCalledTimes(2);
|
||||
expect(emit).toBeCalledWith({
|
||||
type: 'relation',
|
||||
relation: {
|
||||
source: { kind: 'Group', namespace: 'default', name: 'o' },
|
||||
type: 'ownerOf',
|
||||
target: { kind: 'Template', namespace: 'default', name: 'n' },
|
||||
},
|
||||
});
|
||||
expect(emit).toBeCalledWith({
|
||||
type: 'relation',
|
||||
relation: {
|
||||
source: { kind: 'Template', namespace: 'default', name: 'n' },
|
||||
type: 'ownedBy',
|
||||
target: { kind: 'Group', namespace: 'default', name: 'o' },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
resourceEntityV1alpha1Validator,
|
||||
SystemEntity,
|
||||
systemEntityV1alpha1Validator,
|
||||
TemplateEntity,
|
||||
templateEntityV1alpha1Validator,
|
||||
templateEntityV1beta2Validator,
|
||||
UserEntity,
|
||||
@@ -131,6 +132,19 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit relations for the Template kind
|
||||
*/
|
||||
if (entity.kind === 'Template') {
|
||||
const template = entity as TemplateEntity;
|
||||
doEmit(
|
||||
template.spec.owner,
|
||||
{ defaultKind: 'Group', defaultNamespace: selfRef.namespace },
|
||||
RELATION_OWNED_BY,
|
||||
RELATION_OWNER_OF,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit relations for the Component kind
|
||||
*/
|
||||
|
||||
@@ -29,8 +29,16 @@ import {
|
||||
import React from 'react';
|
||||
import { generatePath } from 'react-router';
|
||||
import { rootRouteRef } from '../../routes';
|
||||
import { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
|
||||
import {
|
||||
TemplateEntityV1alpha1,
|
||||
Entity,
|
||||
RELATION_OWNED_BY,
|
||||
} from '@backstage/catalog-model';
|
||||
import { FavouriteTemplate } from '../FavouriteTemplate/FavouriteTemplate';
|
||||
import {
|
||||
getEntityRelations,
|
||||
EntityRefLinks,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
cardHeader: {
|
||||
@@ -69,7 +77,6 @@ type TemplateProps = {
|
||||
title: string;
|
||||
type: string;
|
||||
name: string;
|
||||
owner: string;
|
||||
};
|
||||
|
||||
const getTemplateCardProps = (
|
||||
@@ -81,7 +88,6 @@ const getTemplateCardProps = (
|
||||
title: `${(template.metadata.title || template.metadata.name) ?? ''}`,
|
||||
type: template.spec.type ?? '',
|
||||
description: template.metadata.description ?? '-',
|
||||
owner: template.spec.owner ?? '-',
|
||||
tags: (template.metadata?.tags as string[]) ?? [],
|
||||
};
|
||||
};
|
||||
@@ -90,7 +96,10 @@ export const TemplateCard = ({ template }: TemplateCardProps) => {
|
||||
const backstageTheme = useTheme<BackstageTheme>();
|
||||
const rootLink = useRouteRef(rootRouteRef);
|
||||
const templateProps = getTemplateCardProps(template);
|
||||
|
||||
const ownedByRelations = getEntityRelations(
|
||||
template as Entity,
|
||||
RELATION_OWNED_BY,
|
||||
);
|
||||
const themeId = pageTheme[templateProps.type] ? templateProps.type : 'other';
|
||||
const theme = backstageTheme.getPageTheme({ themeId });
|
||||
const classes = useStyles({ backgroundImage: theme.backgroundImage });
|
||||
@@ -119,7 +128,7 @@ export const TemplateCard = ({ template }: TemplateCardProps) => {
|
||||
<Typography variant="body2" className={classes.label}>
|
||||
Owner
|
||||
</Typography>
|
||||
<Typography variant="inherit">{templateProps.owner}</Typography>
|
||||
<EntityRefLinks entityRefs={ownedByRelations} defaultKind="Group" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="body2" className={classes.label}>
|
||||
|
||||
Reference in New Issue
Block a user