Merge pull request #1209 from spotify/freben/model-more-fields
Add description, owner, and lifecycle to the catalog model
This commit is contained in:
@@ -23,8 +23,8 @@ import {
|
||||
SchemaValidEntityPolicy,
|
||||
} from './entity';
|
||||
import {
|
||||
ComponentEntityV1beta1Policy,
|
||||
LocationEntityV1beta1Policy,
|
||||
ComponentEntityV1alpha1Policy,
|
||||
LocationEntityV1alpha1Policy,
|
||||
} from './kinds';
|
||||
import { EntityPolicy } from './types';
|
||||
|
||||
@@ -72,8 +72,8 @@ export class EntityPolicies implements EntityPolicy {
|
||||
new ReservedFieldsEntityPolicy(),
|
||||
]),
|
||||
EntityPolicies.anyOf([
|
||||
new ComponentEntityV1beta1Policy(),
|
||||
new LocationEntityV1beta1Policy(),
|
||||
new ComponentEntityV1alpha1Policy(),
|
||||
new LocationEntityV1alpha1Policy(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -95,6 +95,12 @@ export type EntityMeta = {
|
||||
*/
|
||||
namespace?: string;
|
||||
|
||||
/**
|
||||
* A short (typically relatively few words, on one line) description of the
|
||||
* entity.
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* Key/value pairs of identifying information attached to the entity.
|
||||
*/
|
||||
|
||||
@@ -24,13 +24,13 @@ describe('DefaultNamespaceEntityPolicy', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
withoutNamespace = yaml.parse(`
|
||||
apiVersion: backstage.io/v1beta1
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-component-yay
|
||||
`);
|
||||
withNamespace = yaml.parse(`
|
||||
apiVersion: backstage.io/v1beta1
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-component-yay
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('FieldFormatEntityPolicy', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
data = yaml.parse(`
|
||||
apiVersion: backstage.io/v1beta1
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
uid: e01199ab-08cc-44c2-8e19-5c29ded82521
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('NoForeignRootFieldsEntityPolicy', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
data = yaml.parse(`
|
||||
apiVersion: backstage.io/v1beta1
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
uid: e01199ab-08cc-44c2-8e19-5c29ded82521
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('ReservedFieldsEntityPolicy', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
data = yaml.parse(`
|
||||
apiVersion: backstage.io/v1beta1
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
uid: e01199ab-08cc-44c2-8e19-5c29ded82521
|
||||
@@ -61,4 +61,9 @@ describe('ReservedFieldsEntityPolicy', () => {
|
||||
/annotation.*apiVersion/i,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects core fields mistakenly placed in metadata', async () => {
|
||||
data.metadata.owner = 'emma';
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/owner/i);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,17 +17,23 @@
|
||||
import { EntityPolicy } from '../../types';
|
||||
import { Entity } from '../Entity';
|
||||
|
||||
const DEFAULT_RESERVED_ENTITY_FIELDS = [
|
||||
const DEFAULT_RESERVED_ENTITY_FIELDS: string[] = [
|
||||
'apiVersion',
|
||||
'kind',
|
||||
'uid',
|
||||
'etag',
|
||||
'generation',
|
||||
'name',
|
||||
'namespace',
|
||||
'labels',
|
||||
'annotations',
|
||||
'spec',
|
||||
'metadata.uid',
|
||||
'metadata.etag',
|
||||
'metadata.generation',
|
||||
'metadata.name',
|
||||
'metadata.namespace',
|
||||
'metadata.description',
|
||||
'metadata.labels',
|
||||
'metadata.annotations',
|
||||
// The below items are known to appear in core kinds, and therefore should
|
||||
// not be appearing in metadata (which would indicate that the user made a
|
||||
// mistake in where to place them).
|
||||
'spec.lifecycle',
|
||||
'spec.owner',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -44,20 +50,32 @@ export class ReservedFieldsEntityPolicy implements EntityPolicy {
|
||||
}
|
||||
|
||||
async enforce(entity: Entity): Promise<Entity> {
|
||||
for (const field of this.reservedFields) {
|
||||
if (entity.spec?.hasOwnProperty(field)) {
|
||||
for (const path of this.reservedFields) {
|
||||
const [where, name] = path.includes('.')
|
||||
? path.split('.')
|
||||
: [undefined, path];
|
||||
|
||||
if (where !== 'metadata' && entity.metadata.hasOwnProperty(name)) {
|
||||
throw new Error(
|
||||
`The spec may not contain the field ${field}, because it has reserved meaning`,
|
||||
`The metadata may not contain the field ${name}, because it has reserved meaning`,
|
||||
);
|
||||
}
|
||||
if (entity.metadata.labels?.hasOwnProperty(field)) {
|
||||
if (where !== 'spec' && entity.spec?.hasOwnProperty(name)) {
|
||||
throw new Error(
|
||||
`A label may not have the field ${field}, because it has reserved meaning`,
|
||||
`The spec may not contain the field ${name}, because it has reserved meaning`,
|
||||
);
|
||||
}
|
||||
if (entity.metadata.annotations?.hasOwnProperty(field)) {
|
||||
if (where !== 'labels' && entity.metadata.labels?.hasOwnProperty(name)) {
|
||||
throw new Error(
|
||||
`An annotation may not have the field ${field}, because it has reserved meaning`,
|
||||
`A label may not have the field ${name}, because it has reserved meaning`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
where !== 'annotations' &&
|
||||
entity.metadata.annotations?.hasOwnProperty(name)
|
||||
) {
|
||||
throw new Error(
|
||||
`An annotation may not have the field ${name}, because it has reserved meaning`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('SchemaValidEntityPolicy', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
data = yaml.parse(`
|
||||
apiVersion: backstage.io/v1beta1
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
uid: e01199ab-08cc-44c2-8e19-5c29ded82521
|
||||
@@ -100,6 +100,11 @@ describe('SchemaValidEntityPolicy', () => {
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/uid/);
|
||||
});
|
||||
|
||||
it('rejects empty uid', async () => {
|
||||
data.metadata.uid = '';
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/uid/);
|
||||
});
|
||||
|
||||
it('accepts missing etag', async () => {
|
||||
delete data.metadata.etag;
|
||||
await expect(policy.enforce(data)).resolves.toBe(data);
|
||||
@@ -110,6 +115,11 @@ describe('SchemaValidEntityPolicy', () => {
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/etag/);
|
||||
});
|
||||
|
||||
it('rejects empty etag', async () => {
|
||||
data.metadata.etag = '';
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/etag/);
|
||||
});
|
||||
|
||||
it('accepts missing generation', async () => {
|
||||
delete data.metadata.generation;
|
||||
await expect(policy.enforce(data)).resolves.toBe(data);
|
||||
@@ -120,6 +130,16 @@ describe('SchemaValidEntityPolicy', () => {
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/generation/);
|
||||
});
|
||||
|
||||
it('rejects zero generation', async () => {
|
||||
data.metadata.generation = 0;
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/generation/);
|
||||
});
|
||||
|
||||
it('rejects non-integer generation', async () => {
|
||||
data.metadata.generation = 1.5;
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/generation/);
|
||||
});
|
||||
|
||||
it('rejects missing name', async () => {
|
||||
delete data.metadata.name;
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/name/);
|
||||
@@ -140,6 +160,16 @@ describe('SchemaValidEntityPolicy', () => {
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/namespace/);
|
||||
});
|
||||
|
||||
it('accepts missing description', async () => {
|
||||
delete data.metadata.description;
|
||||
await expect(policy.enforce(data)).resolves.toBe(data);
|
||||
});
|
||||
|
||||
it('rejects bad description type', async () => {
|
||||
data.metadata.description = 7;
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/description/);
|
||||
});
|
||||
|
||||
it('accepts missing labels', async () => {
|
||||
delete data.metadata.labels;
|
||||
await expect(policy.enforce(data)).resolves.toBe(data);
|
||||
|
||||
@@ -23,32 +23,12 @@ const DEFAULT_ENTITY_SCHEMA = yup.object({
|
||||
kind: yup.string().required(),
|
||||
metadata: yup
|
||||
.object({
|
||||
uid: yup
|
||||
.string()
|
||||
.notRequired()
|
||||
.test(
|
||||
'metadata.uid',
|
||||
'The uid must not be empty',
|
||||
value => value === undefined || value.length > 0,
|
||||
),
|
||||
etag: yup
|
||||
.string()
|
||||
.notRequired()
|
||||
.test(
|
||||
'metadata.etag',
|
||||
'The etag must not be empty',
|
||||
value => value === undefined || value.length > 0,
|
||||
),
|
||||
generation: yup
|
||||
.number()
|
||||
.notRequired()
|
||||
.test(
|
||||
'metadata.generation',
|
||||
'The generation must be an integer greater than zero',
|
||||
value => value === undefined || (value === (value | 0) && value > 0),
|
||||
),
|
||||
uid: yup.string().notRequired().min(1),
|
||||
etag: yup.string().notRequired().min(1),
|
||||
generation: yup.number().notRequired().integer().min(1),
|
||||
name: yup.string().required(),
|
||||
namespace: yup.string().notRequired(),
|
||||
description: yup.string().notRequired(),
|
||||
labels: yup.object<Record<string, string>>().notRequired(),
|
||||
annotations: yup.object<Record<string, string>>().notRequired(),
|
||||
})
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { EntityPolicy } from '../types';
|
||||
import {
|
||||
ComponentEntityV1alpha1,
|
||||
ComponentEntityV1alpha1Policy,
|
||||
} from './ComponentEntityV1alpha1';
|
||||
|
||||
describe('ComponentV1alpha1Policy', () => {
|
||||
let entity: ComponentEntityV1alpha1;
|
||||
let policy: EntityPolicy;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
owner: 'me',
|
||||
},
|
||||
};
|
||||
policy = new ComponentEntityV1alpha1Policy();
|
||||
});
|
||||
|
||||
it('happy path: accepts valid data', async () => {
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('silently accepts v1beta1 as well', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta1';
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('rejects unknown apiVersion', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta0';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/apiVersion/);
|
||||
});
|
||||
|
||||
it('rejects unknown kind', async () => {
|
||||
(entity as any).kind = 'Wizard';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/kind/);
|
||||
});
|
||||
|
||||
it('rejects missing type', async () => {
|
||||
delete (entity as any).spec.type;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects wrong type', async () => {
|
||||
(entity as any).spec.type = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects empty type', async () => {
|
||||
(entity as any).spec.type = '';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects missing lifecycle', async () => {
|
||||
delete (entity as any).spec.lifecycle;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects wrong lifecycle', async () => {
|
||||
(entity as any).spec.lifecycle = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects empty lifecycle', async () => {
|
||||
(entity as any).spec.lifecycle = '';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects wrong owner', async () => {
|
||||
(entity as any).spec.owner = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects empty owner', async () => {
|
||||
(entity as any).spec.owner = '';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
});
|
||||
+13
-11
@@ -18,35 +18,37 @@ import * as yup from 'yup';
|
||||
import type { Entity } from '../entity/Entity';
|
||||
import type { EntityPolicy } from '../types';
|
||||
|
||||
const API_VERSION = 'backstage.io/v1beta1';
|
||||
const KIND = 'Component';
|
||||
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
|
||||
const KIND = 'Component' as const;
|
||||
|
||||
export interface ComponentEntityV1beta1 extends Entity {
|
||||
apiVersion: typeof API_VERSION;
|
||||
export interface ComponentEntityV1alpha1 extends Entity {
|
||||
apiVersion: typeof API_VERSION[number];
|
||||
kind: typeof KIND;
|
||||
spec: {
|
||||
type: string;
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class ComponentEntityV1beta1Policy implements EntityPolicy {
|
||||
export class ComponentEntityV1alpha1Policy implements EntityPolicy {
|
||||
private schema: yup.Schema<any>;
|
||||
|
||||
constructor() {
|
||||
this.schema = yup.object<Partial<ComponentEntityV1beta1>>({
|
||||
this.schema = yup.object<Partial<ComponentEntityV1alpha1>>({
|
||||
apiVersion: yup.string().required().oneOf(API_VERSION),
|
||||
kind: yup.string().required().equals([KIND]),
|
||||
spec: yup
|
||||
.object({
|
||||
type: yup.string().required(),
|
||||
type: yup.string().required().min(1),
|
||||
lifecycle: yup.string().required().min(1),
|
||||
owner: yup.string().required().min(1),
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
}
|
||||
|
||||
async enforce(envelope: Entity): Promise<Entity> {
|
||||
if (envelope.apiVersion !== API_VERSION || envelope.kind !== KIND) {
|
||||
throw new Error('Unsupported apiVersion / kind');
|
||||
}
|
||||
|
||||
return await this.schema.validate(envelope, { strict: true });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { EntityPolicy } from '../types';
|
||||
import {
|
||||
LocationEntityV1alpha1,
|
||||
LocationEntityV1alpha1Policy,
|
||||
} from './LocationEntityV1alpha1';
|
||||
|
||||
describe('LocationV1alpha1Policy', () => {
|
||||
let entity: LocationEntityV1alpha1;
|
||||
let policy: EntityPolicy;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Location',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
},
|
||||
spec: {
|
||||
type: 'github',
|
||||
},
|
||||
};
|
||||
policy = new LocationEntityV1alpha1Policy();
|
||||
});
|
||||
|
||||
it('happy path: accepts valid data', async () => {
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('silently accepts v1beta1 as well', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta1';
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('rejects unknown apiVersion', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta0';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/apiVersion/);
|
||||
});
|
||||
|
||||
it('rejects unknown kind', async () => {
|
||||
(entity as any).kind = 'Wizard';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/kind/);
|
||||
});
|
||||
|
||||
it('rejects missing type', async () => {
|
||||
delete (entity as any).spec.type;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects wrong type', async () => {
|
||||
(entity as any).spec.type = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects empty type', async () => {
|
||||
(entity as any).spec.type = '';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('accepts good target', async () => {
|
||||
(entity as any).spec.target =
|
||||
'https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/examples/artist-lookup-component.yaml';
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('rejects wrong target', async () => {
|
||||
(entity as any).spec.target = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/target/);
|
||||
});
|
||||
|
||||
it('rejects empty target', async () => {
|
||||
(entity as any).spec.target = '';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/target/);
|
||||
});
|
||||
|
||||
it('accepts good targets', async () => {
|
||||
(entity as any).spec.targets = [
|
||||
'https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/examples/artist-lookup-component.yaml',
|
||||
'https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/examples/playback-order-component.yaml',
|
||||
];
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('rejects wrong targets', async () => {
|
||||
(entity as any).spec.targets = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/targets/);
|
||||
});
|
||||
});
|
||||
+10
-12
@@ -18,11 +18,11 @@ import * as yup from 'yup';
|
||||
import type { Entity } from '../entity/Entity';
|
||||
import type { EntityPolicy } from '../types';
|
||||
|
||||
const API_VERSION = 'backstage.io/v1beta1';
|
||||
const KIND = 'Location';
|
||||
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
|
||||
const KIND = 'Location' as const;
|
||||
|
||||
export interface LocationEntityV1beta1 extends Entity {
|
||||
apiVersion: typeof API_VERSION;
|
||||
export interface LocationEntityV1alpha1 extends Entity {
|
||||
apiVersion: typeof API_VERSION[number];
|
||||
kind: typeof KIND;
|
||||
spec: {
|
||||
type: string;
|
||||
@@ -31,15 +31,17 @@ export interface LocationEntityV1beta1 extends Entity {
|
||||
};
|
||||
}
|
||||
|
||||
export class LocationEntityV1beta1Policy implements EntityPolicy {
|
||||
export class LocationEntityV1alpha1Policy implements EntityPolicy {
|
||||
private schema: yup.Schema<any>;
|
||||
|
||||
constructor() {
|
||||
this.schema = yup.object<Partial<LocationEntityV1beta1>>({
|
||||
this.schema = yup.object<Partial<LocationEntityV1alpha1>>({
|
||||
apiVersion: yup.string().required().oneOf(API_VERSION),
|
||||
kind: yup.string().required().equals([KIND]),
|
||||
spec: yup
|
||||
.object({
|
||||
type: yup.string().required(),
|
||||
target: yup.string().notRequired(),
|
||||
type: yup.string().required().min(1),
|
||||
target: yup.string().notRequired().min(1),
|
||||
targets: yup.array(yup.string()).notRequired(),
|
||||
})
|
||||
.required(),
|
||||
@@ -47,10 +49,6 @@ export class LocationEntityV1beta1Policy implements EntityPolicy {
|
||||
}
|
||||
|
||||
async enforce(envelope: Entity): Promise<Entity> {
|
||||
if (envelope.apiVersion !== API_VERSION || envelope.kind !== KIND) {
|
||||
throw new Error('Unsupported apiVersion / kind');
|
||||
}
|
||||
|
||||
return await this.schema.validate(envelope, { strict: true });
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { ComponentEntityV1beta1Policy } from './ComponentEntityV1beta1';
|
||||
export { ComponentEntityV1alpha1Policy } from './ComponentEntityV1alpha1';
|
||||
export type {
|
||||
ComponentEntityV1beta1 as ComponentEntity,
|
||||
ComponentEntityV1beta1,
|
||||
} from './ComponentEntityV1beta1';
|
||||
export { LocationEntityV1beta1Policy } from './LocationEntityV1beta1';
|
||||
ComponentEntityV1alpha1 as ComponentEntity,
|
||||
ComponentEntityV1alpha1,
|
||||
} from './ComponentEntityV1alpha1';
|
||||
export { LocationEntityV1alpha1Policy } from './LocationEntityV1alpha1';
|
||||
export type {
|
||||
LocationEntityV1beta1 as LocationEntity,
|
||||
LocationEntityV1beta1,
|
||||
} from './LocationEntityV1beta1';
|
||||
LocationEntityV1alpha1 as LocationEntity,
|
||||
LocationEntityV1alpha1,
|
||||
} from './LocationEntityV1alpha1';
|
||||
|
||||
Reference in New Issue
Block a user