Add profile section to group entity
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
---
|
||||
|
||||
Introduce a `profile` section for group entities that can optional include a
|
||||
`displayName`, `email` and `picture`.
|
||||
@@ -723,6 +723,10 @@ metadata:
|
||||
description: The infra business unit
|
||||
spec:
|
||||
type: business-unit
|
||||
profile:
|
||||
displayName: Infrastructure
|
||||
email: infrastructure@example.com
|
||||
picture: https://example.com/groups/bu-infrastructure.jpeg
|
||||
parent: ops
|
||||
children: [backstage, other]
|
||||
```
|
||||
@@ -747,6 +751,14 @@ Some common values for this field could be:
|
||||
- `product-area`
|
||||
- `root` - as a common virtual root of the hierarchy, if desired
|
||||
|
||||
### `spec.profile` [optional]
|
||||
|
||||
Optional profile information about the group, mainly for display purposes. All
|
||||
fields of this structure are also optional. The email would be a group email of
|
||||
some form, that the group may wish to be used for contacting them. The picture
|
||||
is expected to be a URL pointing to an image that's representative of the group,
|
||||
and that a browser could fetch and render on a group page or similar.
|
||||
|
||||
### `spec.parent` [optional]
|
||||
|
||||
The immediate parent group in the hierarchy, if any. Not all groups must have a
|
||||
|
||||
@@ -28,11 +28,15 @@ describe('GroupV1alpha1Validator', () => {
|
||||
kind: 'Group',
|
||||
metadata: {
|
||||
name: 'doe-squad',
|
||||
title: 'Doe Squad',
|
||||
description: 'A squad for John and Jane',
|
||||
},
|
||||
spec: {
|
||||
type: 'squad',
|
||||
profile: {
|
||||
displayName: 'Doe Squad',
|
||||
email: 'doe@doe.org',
|
||||
picture: 'https://doe.org/doe',
|
||||
},
|
||||
parent: 'group-a',
|
||||
children: ['child-a', 'child-b'],
|
||||
},
|
||||
@@ -73,6 +77,70 @@ describe('GroupV1alpha1Validator', () => {
|
||||
await expect(validator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
// profile
|
||||
|
||||
it('accepts missing profile', async () => {
|
||||
delete (entity as any).spec.profile;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong profile', async () => {
|
||||
(entity as any).spec.profile = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/profile/);
|
||||
});
|
||||
|
||||
it('profile accepts missing displayName', async () => {
|
||||
delete (entity as any).spec.profile.displayName;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('profile rejects wrong displayName', async () => {
|
||||
(entity as any).spec.profile.displayName = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/displayName/);
|
||||
});
|
||||
|
||||
it('profile rejects empty displayName', async () => {
|
||||
(entity as any).spec.profile.displayName = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/displayName/);
|
||||
});
|
||||
|
||||
it('profile accepts missing email', async () => {
|
||||
delete (entity as any).spec.profile.email;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('profile rejects wrong email', async () => {
|
||||
(entity as any).spec.profile.email = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/email/);
|
||||
});
|
||||
|
||||
it('profile rejects empty email', async () => {
|
||||
(entity as any).spec.profile.email = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/email/);
|
||||
});
|
||||
|
||||
it('profile accepts missing picture', async () => {
|
||||
delete (entity as any).spec.profile.picture;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('profile rejects wrong picture', async () => {
|
||||
(entity as any).spec.profile.picture = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/picture/);
|
||||
});
|
||||
|
||||
it('profile rejects empty picture', async () => {
|
||||
(entity as any).spec.profile.picture = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/picture/);
|
||||
});
|
||||
|
||||
it('profile accepts unknown additional fields', async () => {
|
||||
(entity as any).spec.profile.foo = 'data';
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
// parent
|
||||
|
||||
it('accepts missing parent', async () => {
|
||||
delete (entity as any).spec.parent;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
@@ -83,6 +151,8 @@ describe('GroupV1alpha1Validator', () => {
|
||||
await expect(validator.check(entity)).rejects.toThrow(/parent/);
|
||||
});
|
||||
|
||||
// children
|
||||
|
||||
it('rejects missing children', async () => {
|
||||
delete (entity as any).spec.children;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/children/);
|
||||
|
||||
@@ -27,6 +27,13 @@ const schema = yup.object<Partial<GroupEntityV1alpha1>>({
|
||||
spec: yup
|
||||
.object({
|
||||
type: yup.string().required().min(1),
|
||||
profile: yup
|
||||
.object({
|
||||
displayName: yup.string().min(1).notRequired(),
|
||||
email: yup.string().min(1).notRequired(),
|
||||
picture: yup.string().min(1).notRequired(),
|
||||
})
|
||||
.notRequired(),
|
||||
parent: yup.string().notRequired().min(1),
|
||||
// Use these manual tests because yup .required() requires at least
|
||||
// one element and there is no simple workaround -_-
|
||||
@@ -46,6 +53,11 @@ export interface GroupEntityV1alpha1 extends Entity {
|
||||
kind: typeof KIND;
|
||||
spec: {
|
||||
type: string;
|
||||
profile?: {
|
||||
displayName?: string;
|
||||
email?: string;
|
||||
picture?: string;
|
||||
};
|
||||
parent?: string;
|
||||
children: string[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user