use alpha versioning in the catalog instead

This commit is contained in:
Fredrik Adelöw
2020-06-16 13:00:31 +02:00
parent 8b1c795840
commit e01fae0a28
13 changed files with 156 additions and 53 deletions
+4 -4
View File
@@ -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(),
]),
]);
}
@@ -16,17 +16,17 @@
import { EntityPolicy } from '../types';
import {
ComponentEntityV1beta1,
ComponentEntityV1beta1Policy,
} from './ComponentEntityV1beta1';
ComponentEntityV1alpha1,
ComponentEntityV1alpha1Policy,
} from './ComponentEntityV1alpha1';
describe('ComponentV1beta1Policy', () => {
let entity: ComponentEntityV1beta1;
describe('ComponentV1alpha1Policy', () => {
let entity: ComponentEntityV1alpha1;
let policy: EntityPolicy;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1beta1',
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'test',
@@ -37,15 +37,20 @@ describe('ComponentV1beta1Policy', () => {
owner: 'me',
},
};
policy = new ComponentEntityV1beta1Policy();
policy = new ComponentEntityV1alpha1Policy();
});
it('happy path: accepts valid data', async () => {
await expect(policy.enforce(entity)).resolves.toBe(entity);
});
it('happy path: 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/v1beta2';
(entity as any).apiVersion = 'backstage.io/v1beta0';
await expect(policy.enforce(entity)).rejects.toThrow(/apiVersion/);
});
@@ -64,7 +69,7 @@ describe('ComponentV1beta1Policy', () => {
await expect(policy.enforce(entity)).rejects.toThrow(/type/);
});
it('rejects empty wrong type', async () => {
it('rejects empty type', async () => {
(entity as any).spec.type = '';
await expect(policy.enforce(entity)).rejects.toThrow(/type/);
});
@@ -79,7 +84,7 @@ describe('ComponentV1beta1Policy', () => {
await expect(policy.enforce(entity)).rejects.toThrow(/lifecycle/);
});
it('rejects wrong empty', async () => {
it('rejects empty lifecycle', async () => {
(entity as any).spec.lifecycle = '';
await expect(policy.enforce(entity)).rejects.toThrow(/lifecycle/);
});
@@ -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 = '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;
@@ -31,11 +31,13 @@ export interface ComponentEntityV1beta1 extends Entity {
};
}
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().min(1),
@@ -47,10 +49,6 @@ export class ComponentEntityV1beta1Policy 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 });
}
}
@@ -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('happy path: 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/);
});
});
@@ -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 });
}
}
+8 -8
View File
@@ -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';
@@ -1,4 +1,4 @@
apiVersion: backstage.io/v1beta1
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: artist-lookup
@@ -1,4 +1,4 @@
apiVersion: backstage.io/v1beta1
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: playback-order
@@ -1,4 +1,4 @@
apiVersion: backstage.io/v1beta1
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: podcast-api
@@ -1,4 +1,4 @@
apiVersion: backstage.io/v1beta1
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: queue-proxy
@@ -1,4 +1,4 @@
apiVersion: backstage.io/v1beta1
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: searcher
@@ -1,4 +1,4 @@
apiVersion: backstage.io/v1beta1
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: shuffle-api
@@ -14,10 +14,9 @@
* limitations under the License.
*/
import type { Entity } from '@backstage/catalog-model';
import { LOCATION_ANNOTATION } from '@backstage/catalog-model';
import { NotFoundError } from '@backstage/backend-common';
import { LOCATION_ANNOTATION } from '@backstage/catalog-model';
import type { Entity } from '@backstage/catalog-model';
import type { Database, DbEntityResponse, EntityFilters } from '../database';
import type { EntitiesCatalog } from './types';