Add a DefaultParentEntityPolicy.
It's useful to have an entity policy to set a parent for unparented groups. This can be used to build a groups hierarchy with a single root parent group without having to make changes to every group entity in the catalog. Signed-off-by: James Peach <jpeach@cloudflare.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
---
|
||||
|
||||
Introduced DefaultParentEntityPolicy to set a default group entity parent.
|
||||
@@ -108,6 +108,13 @@ export class DefaultNamespaceEntityPolicy implements EntityPolicy {
|
||||
enforce(entity: Entity): Promise<Entity>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class DefaultParentEntityPolicy implements EntityPolicy {
|
||||
constructor(parent: string);
|
||||
// (undocumented)
|
||||
enforce(entity: Entity): Promise<Entity>;
|
||||
}
|
||||
|
||||
// @public
|
||||
interface DomainEntityV1alpha1 extends Entity {
|
||||
// (undocumented)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { UserEntity, GroupEntity } from '../../kinds';
|
||||
import { DefaultParentEntityPolicy } from './DefaultParentEntityPolicy';
|
||||
|
||||
describe('DefaultParentEntityPolicy', () => {
|
||||
it('should ignore non-group entities', async () => {
|
||||
const p = new DefaultParentEntityPolicy('name');
|
||||
const u: UserEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'User',
|
||||
metadata: { name: 'n' },
|
||||
spec: { profile: {}, memberOf: ['c'] },
|
||||
};
|
||||
const result = await p.enforce(u);
|
||||
expect(result).toEqual({
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'User',
|
||||
metadata: { name: 'n' },
|
||||
spec: { profile: {}, memberOf: ['c'] },
|
||||
});
|
||||
});
|
||||
|
||||
it('should parent group entities', async () => {
|
||||
const p = new DefaultParentEntityPolicy('name');
|
||||
const g: GroupEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: { name: 'n' },
|
||||
spec: { type: 'foo', children: [] },
|
||||
};
|
||||
const result = await p.enforce(g);
|
||||
expect(result).toEqual({
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: { name: 'n' },
|
||||
spec: { type: 'foo', parent: 'group:default/name', children: [] },
|
||||
});
|
||||
});
|
||||
|
||||
it('should not replace existing parents', async () => {
|
||||
const p = new DefaultParentEntityPolicy('namespace/name');
|
||||
const g: GroupEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: { name: 'n' },
|
||||
spec: { type: 'foo', parent: 'group:something/else', children: [] },
|
||||
};
|
||||
const result = await p.enforce(g);
|
||||
expect(result).toEqual({
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Group',
|
||||
metadata: { name: 'n' },
|
||||
spec: { type: 'foo', parent: 'group:something/else', children: [] },
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '../Entity';
|
||||
import { GroupEntity } from '../../kinds';
|
||||
import { EntityPolicy } from './types';
|
||||
import { DEFAULT_NAMESPACE } from '../constants';
|
||||
import { parseEntityRef, stringifyEntityRef } from '../ref';
|
||||
|
||||
/**
|
||||
* DefaultParentPolicy is an EntityPolicy that updates group entities
|
||||
* with a parent of last resort. This ensures that, while we preserve
|
||||
* any existing group hierarchies, we can guarantee that there is a
|
||||
* single global root of the group hierarchy.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class DefaultParentEntityPolicy implements EntityPolicy {
|
||||
private readonly parentRef: string;
|
||||
|
||||
constructor(parentEntityRef: string) {
|
||||
const { kind, namespace, name } = parseEntityRef(parentEntityRef, {
|
||||
defaultKind: 'Group',
|
||||
defaultNamespace: DEFAULT_NAMESPACE,
|
||||
});
|
||||
|
||||
if (kind.toLocaleUpperCase('en-US') !== 'GROUP') {
|
||||
throw new TypeError('group parent must be a group');
|
||||
}
|
||||
|
||||
this.parentRef = stringifyEntityRef({
|
||||
kind: kind,
|
||||
namespace: namespace,
|
||||
name: name,
|
||||
});
|
||||
}
|
||||
|
||||
async enforce(entity: Entity): Promise<Entity> {
|
||||
if (entity.kind !== 'Group') {
|
||||
return entity;
|
||||
}
|
||||
|
||||
const group = entity as GroupEntity;
|
||||
if (group.spec.parent) {
|
||||
return group;
|
||||
}
|
||||
|
||||
// Avoid making the parent entity it's own parent.
|
||||
if (stringifyEntityRef(group) !== this.parentRef) {
|
||||
group.spec.parent = this.parentRef;
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
export { DefaultNamespaceEntityPolicy } from './DefaultNamespaceEntityPolicy';
|
||||
export { DefaultParentEntityPolicy } from './DefaultParentEntityPolicy';
|
||||
export { FieldFormatEntityPolicy } from './FieldFormatEntityPolicy';
|
||||
export { NoForeignRootFieldsEntityPolicy } from './NoForeignRootFieldsEntityPolicy';
|
||||
export { SchemaValidEntityPolicy } from './SchemaValidEntityPolicy';
|
||||
|
||||
Reference in New Issue
Block a user