Merge pull request #12232 from jpeach/default-parent-entity-policy

Add a DefaultParentEntityPolicy.
This commit is contained in:
Ben Lambert
2022-06-29 11:14:09 +02:00
committed by GitHub
5 changed files with 152 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/catalog-model': minor
---
Introduced `GroupDefaultParentEntityPolicy` to set a default group entity parent.
+7
View File
@@ -230,6 +230,13 @@ export function getEntitySourceLocation(entity: Entity): {
target: string;
};
// @public
export class GroupDefaultParentEntityPolicy implements EntityPolicy {
constructor(parentEntityRef: string);
// (undocumented)
enforce(entity: Entity): Promise<Entity>;
}
// @public
interface GroupEntityV1alpha1 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 { GroupDefaultParentEntityPolicy } from './GroupDefaultParentEntityPolicy';
describe('GroupDefaultParentEntityPolicy', () => {
it('should ignore non-group entities', async () => {
const p = new GroupDefaultParentEntityPolicy('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 GroupDefaultParentEntityPolicy('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 GroupDefaultParentEntityPolicy('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 GroupDefaultParentEntityPolicy 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 { GroupDefaultParentEntityPolicy } from './GroupDefaultParentEntityPolicy';
export { FieldFormatEntityPolicy } from './FieldFormatEntityPolicy';
export { NoForeignRootFieldsEntityPolicy } from './NoForeignRootFieldsEntityPolicy';
export { SchemaValidEntityPolicy } from './SchemaValidEntityPolicy';