use alpha versioning in the catalog instead
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(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
+15
-10
@@ -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/);
|
||||
});
|
||||
+8
-10
@@ -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/);
|
||||
});
|
||||
});
|
||||
+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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user