Add system, domain and resource entity kinds

This commit is contained in:
Oliver Sand
2021-01-12 17:05:49 +01:00
parent 2d44b59b91
commit abbee6fff4
16 changed files with 713 additions and 6 deletions
@@ -70,6 +70,7 @@ components:
items:
$ref: "#/components/schemas/Pet"
`,
system: 'system',
},
};
});
@@ -152,4 +153,19 @@ components:
(entity as any).spec.definition = '';
await expect(validator.check(entity)).rejects.toThrow(/definition/);
});
it('accepts missing system', async () => {
delete (entity as any).spec.system;
await expect(validator.check(entity)).resolves.toBe(true);
});
it('rejects wrong system', async () => {
(entity as any).spec.system = 7;
await expect(validator.check(entity)).rejects.toThrow(/system/);
});
it('rejects empty system', async () => {
(entity as any).spec.system = '';
await expect(validator.check(entity)).rejects.toThrow(/system/);
});
});
@@ -30,6 +30,7 @@ const schema = yup.object<Partial<ApiEntityV1alpha1>>({
lifecycle: yup.string().required().min(1),
owner: yup.string().required().min(1),
definition: yup.string().required().min(1),
system: yup.string().notRequired().min(1),
})
.required(),
});
@@ -42,6 +43,7 @@ export interface ApiEntityV1alpha1 extends Entity {
lifecycle: string;
owner: string;
definition: string;
system?: string;
};
}
@@ -36,6 +36,7 @@ describe('ComponentV1alpha1Validator', () => {
subcomponentOf: 'monolith',
providesApis: ['api-0'],
consumesApis: ['api-0'],
system: 'system',
},
};
});
@@ -158,4 +159,19 @@ describe('ComponentV1alpha1Validator', () => {
(entity as any).spec.consumesApis = [];
await expect(validator.check(entity)).resolves.toBe(true);
});
it('accepts missing system', async () => {
delete (entity as any).spec.system;
await expect(validator.check(entity)).resolves.toBe(true);
});
it('rejects wrong system', async () => {
(entity as any).spec.system = 7;
await expect(validator.check(entity)).rejects.toThrow(/system/);
});
it('rejects empty system', async () => {
(entity as any).spec.system = '';
await expect(validator.check(entity)).rejects.toThrow(/system/);
});
});
@@ -32,6 +32,7 @@ const schema = yup.object<Partial<ComponentEntityV1alpha1>>({
subcomponentOf: yup.string().notRequired().min(1),
providesApis: yup.array(yup.string().required()).notRequired(),
consumesApis: yup.array(yup.string().required()).notRequired(),
system: yup.string().notRequired().min(1),
})
.required(),
});
@@ -46,6 +47,7 @@ export interface ComponentEntityV1alpha1 extends Entity {
subcomponentOf?: string;
providesApis?: string[];
consumesApis?: string[];
system?: string;
};
}
@@ -0,0 +1,71 @@
/*
* 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 {
DomainEntityV1alpha1,
domainEntityV1alpha1Validator as validator,
} from './DomainEntityV1alpha1';
describe('DomainV1alpha1Validator', () => {
let entity: DomainEntityV1alpha1;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Domain',
metadata: {
name: 'test',
},
spec: {
owner: 'me',
},
};
});
it('happy path: accepts valid data', async () => {
await expect(validator.check(entity)).resolves.toBe(true);
});
it('silently accepts v1beta1 as well', async () => {
(entity as any).apiVersion = 'backstage.io/v1beta1';
await expect(validator.check(entity)).resolves.toBe(true);
});
it('ignores unknown apiVersion', async () => {
(entity as any).apiVersion = 'backstage.io/v1beta0';
await expect(validator.check(entity)).resolves.toBe(false);
});
it('ignores unknown kind', async () => {
(entity as any).kind = 'Wizard';
await expect(validator.check(entity)).resolves.toBe(false);
});
it('rejects missing owner', async () => {
delete (entity as any).spec.owner;
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
it('rejects wrong owner', async () => {
(entity as any).spec.owner = 7;
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
it('rejects empty owner', async () => {
(entity as any).spec.owner = '';
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
});
@@ -0,0 +1,46 @@
/*
* 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 * as yup from 'yup';
import type { Entity } from '../entity/Entity';
import { schemaValidator } from './util';
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
const KIND = 'Domain' as const;
const schema = yup.object<Partial<DomainEntityV1alpha1>>({
apiVersion: yup.string().required().oneOf(API_VERSION),
kind: yup.string().required().equals([KIND]),
spec: yup
.object({
owner: yup.string().required().min(1),
})
.required(),
});
export interface DomainEntityV1alpha1 extends Entity {
apiVersion: typeof API_VERSION[number];
kind: typeof KIND;
spec: {
owner: string;
};
}
export const domainEntityV1alpha1Validator = schemaValidator(
KIND,
API_VERSION,
schema,
);
@@ -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 {
ResourceEntityV1alpha1,
resourceEntityV1alpha1Validator as validator,
} from './ResourceEntityV1alpha1';
describe('ResourceV1alpha1Validator', () => {
let entity: ResourceEntityV1alpha1;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Resource',
metadata: {
name: 'test',
},
spec: {
type: 'database',
owner: 'me',
system: 'system',
},
};
});
it('happy path: accepts valid data', async () => {
await expect(validator.check(entity)).resolves.toBe(true);
});
it('silently accepts v1beta1 as well', async () => {
(entity as any).apiVersion = 'backstage.io/v1beta1';
await expect(validator.check(entity)).resolves.toBe(true);
});
it('ignores unknown apiVersion', async () => {
(entity as any).apiVersion = 'backstage.io/v1beta0';
await expect(validator.check(entity)).resolves.toBe(false);
});
it('ignores unknown kind', async () => {
(entity as any).kind = 'Wizard';
await expect(validator.check(entity)).resolves.toBe(false);
});
it('rejects missing type', async () => {
delete (entity as any).spec.type;
await expect(validator.check(entity)).rejects.toThrow(/type/);
});
it('rejects wrong type', async () => {
(entity as any).spec.type = 7;
await expect(validator.check(entity)).rejects.toThrow(/type/);
});
it('rejects empty type', async () => {
(entity as any).spec.type = '';
await expect(validator.check(entity)).rejects.toThrow(/type/);
});
it('rejects missing owner', async () => {
delete (entity as any).spec.owner;
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
it('rejects wrong owner', async () => {
(entity as any).spec.owner = 7;
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
it('rejects empty owner', async () => {
(entity as any).spec.owner = '';
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
it('accepts missing system', async () => {
delete (entity as any).spec.system;
await expect(validator.check(entity)).resolves.toBe(true);
});
it('rejects wrong system', async () => {
(entity as any).spec.system = 7;
await expect(validator.check(entity)).rejects.toThrow(/system/);
});
it('rejects empty system', async () => {
(entity as any).spec.system = '';
await expect(validator.check(entity)).rejects.toThrow(/system/);
});
});
@@ -0,0 +1,50 @@
/*
* 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 * as yup from 'yup';
import type { Entity } from '../entity/Entity';
import { schemaValidator } from './util';
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
const KIND = 'Resource' as const;
const schema = yup.object<Partial<ResourceEntityV1alpha1>>({
apiVersion: yup.string().required().oneOf(API_VERSION),
kind: yup.string().required().equals([KIND]),
spec: yup
.object({
type: yup.string().required().min(1),
owner: yup.string().required().min(1),
system: yup.string().notRequired().min(1),
})
.required(),
});
export interface ResourceEntityV1alpha1 extends Entity {
apiVersion: typeof API_VERSION[number];
kind: typeof KIND;
spec: {
type: string;
owner: string;
system?: string;
};
}
export const resourceEntityV1alpha1Validator = schemaValidator(
KIND,
API_VERSION,
schema,
);
@@ -0,0 +1,87 @@
/*
* 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 {
SystemEntityV1alpha1,
systemEntityV1alpha1Validator as validator,
} from './SystemEntityV1alpha1';
describe('SystemV1alpha1Validator', () => {
let entity: SystemEntityV1alpha1;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'System',
metadata: {
name: 'test',
},
spec: {
owner: 'me',
domain: 'domain',
},
};
});
it('happy path: accepts valid data', async () => {
await expect(validator.check(entity)).resolves.toBe(true);
});
it('silently accepts v1beta1 as well', async () => {
(entity as any).apiVersion = 'backstage.io/v1beta1';
await expect(validator.check(entity)).resolves.toBe(true);
});
it('ignores unknown apiVersion', async () => {
(entity as any).apiVersion = 'backstage.io/v1beta0';
await expect(validator.check(entity)).resolves.toBe(false);
});
it('ignores unknown kind', async () => {
(entity as any).kind = 'Wizard';
await expect(validator.check(entity)).resolves.toBe(false);
});
it('rejects missing owner', async () => {
delete (entity as any).spec.owner;
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
it('rejects wrong owner', async () => {
(entity as any).spec.owner = 7;
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
it('rejects empty owner', async () => {
(entity as any).spec.owner = '';
await expect(validator.check(entity)).rejects.toThrow(/owner/);
});
it('accepts missing domain', async () => {
delete (entity as any).spec.domain;
await expect(validator.check(entity)).resolves.toBe(true);
});
it('rejects wrong domain', async () => {
(entity as any).spec.domain = 7;
await expect(validator.check(entity)).rejects.toThrow(/domain/);
});
it('rejects empty domain', async () => {
(entity as any).spec.domain = '';
await expect(validator.check(entity)).rejects.toThrow(/domain/);
});
});
@@ -0,0 +1,48 @@
/*
* 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 * as yup from 'yup';
import type { Entity } from '../entity/Entity';
import { schemaValidator } from './util';
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
const KIND = 'System' as const;
const schema = yup.object<Partial<SystemEntityV1alpha1>>({
apiVersion: yup.string().required().oneOf(API_VERSION),
kind: yup.string().required().equals([KIND]),
spec: yup
.object({
owner: yup.string().required().min(1),
domain: yup.string().notRequired().min(1),
})
.required(),
});
export interface SystemEntityV1alpha1 extends Entity {
apiVersion: typeof API_VERSION[number];
kind: typeof KIND;
spec: {
owner: string;
domain?: string;
};
}
export const systemEntityV1alpha1Validator = schemaValidator(
KIND,
API_VERSION,
schema,
);
+15
View File
@@ -26,6 +26,11 @@ export type {
ComponentEntityV1alpha1 as ComponentEntity,
ComponentEntityV1alpha1,
} from './ComponentEntityV1alpha1';
export { domainEntityV1alpha1Validator } from './DomainEntityV1alpha1';
export type {
DomainEntityV1alpha1 as DomainEntity,
DomainEntityV1alpha1,
} from './DomainEntityV1alpha1';
export { groupEntityV1alpha1Validator } from './GroupEntityV1alpha1';
export type {
GroupEntityV1alpha1 as GroupEntity,
@@ -37,6 +42,16 @@ export type {
LocationEntityV1alpha1,
} from './LocationEntityV1alpha1';
export * from './relations';
export { resourceEntityV1alpha1Validator } from './ResourceEntityV1alpha1';
export type {
ResourceEntityV1alpha1 as ResourceEntity,
ResourceEntityV1alpha1,
} from './ResourceEntityV1alpha1';
export { systemEntityV1alpha1Validator } from './SystemEntityV1alpha1';
export type {
SystemEntityV1alpha1 as SystemEntity,
SystemEntityV1alpha1,
} from './SystemEntityV1alpha1';
export { templateEntityV1alpha1Validator } from './TemplateEntityV1alpha1';
export type {
TemplateEntityV1alpha1 as TemplateEntity,
@@ -30,7 +30,7 @@ export const RELATION_OWNED_BY = 'ownedBy';
export const RELATION_OWNER_OF = 'ownerOf';
/**
* A relation with an API entity, typically from a component or system
* A relation with an API entity, typically from a component
*/
export const RELATION_CONSUMES_API = 'consumesApi';
export const RELATION_API_CONSUMED_BY = 'apiConsumedBy';
@@ -57,8 +57,13 @@ export const RELATION_MEMBER_OF = 'memberOf';
export const RELATION_HAS_MEMBER = 'hasMember';
/**
<<<<<<< HEAD
* A part/whole relation, typically for components in a system and systems
* in a domain.
=======
* A grouping relation, typically for components, resources or APIs in a
* system, or for systems inside a domain.
>>>>>>> Add system, domain and resource entity kinds
*/
export const RELATION_PART_OF = 'partOf';
export const RELATION_HAS_PART = 'hasPart';