Skip to content

Commit

Permalink
Add system, domain and resource entity kinds
Browse files Browse the repository at this point in the history
  • Loading branch information
Fox32 committed Jan 12, 2021
1 parent 2d44b59 commit abbee6f
Show file tree
Hide file tree
Showing 16 changed files with 713 additions and 6 deletions.
6 changes: 6 additions & 0 deletions .changeset/thin-icons-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@backstage/catalog-model': patch
'@backstage/plugin-catalog-backend': patch
---

Implement System, Domain and Resource entity kinds.
2 changes: 1 addition & 1 deletion app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ integrations:

catalog:
rules:
- allow: [Component, API, Group, User, Template, Location]
- allow: [Component, API, Resource, Group, User, Template, System, Domain, Location]

processors:
githubOrg:
Expand Down
16 changes: 16 additions & 0 deletions packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ components:
items:
$ref: "#/components/schemas/Pet"
`,
system: 'system',
},
};
});
Expand Down Expand Up @@ -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/);
});
});
2 changes: 2 additions & 0 deletions packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
});
Expand All @@ -42,6 +43,7 @@ export interface ApiEntityV1alpha1 extends Entity {
lifecycle: string;
owner: string;
definition: string;
system?: string;
};
}

Expand Down
16 changes: 16 additions & 0 deletions packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('ComponentV1alpha1Validator', () => {
subcomponentOf: 'monolith',
providesApis: ['api-0'],
consumesApis: ['api-0'],
system: 'system',
},
};
});
Expand Down Expand Up @@ -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/);
});
});
2 changes: 2 additions & 0 deletions packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
});
Expand All @@ -46,6 +47,7 @@ export interface ComponentEntityV1alpha1 extends Entity {
subcomponentOf?: string;
providesApis?: string[];
consumesApis?: string[];
system?: string;
};
}

Expand Down
71 changes: 71 additions & 0 deletions packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts
Original file line number Diff line number Diff line change
@@ -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/);
});
});
46 changes: 46 additions & 0 deletions packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts
Original file line number Diff line number Diff line change
@@ -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,
);
103 changes: 103 additions & 0 deletions packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts
Original file line number Diff line number Diff line change
@@ -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/);
});
});
50 changes: 50 additions & 0 deletions packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts
Original file line number Diff line number Diff line change
@@ -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,
);
Loading

0 comments on commit abbee6f

Please sign in to comment.