diff --git a/packages/catalog-model/src/EntityPolicies.ts b/packages/catalog-model/src/EntityPolicies.ts index ca189c042e..cf23bcdc88 100644 --- a/packages/catalog-model/src/EntityPolicies.ts +++ b/packages/catalog-model/src/EntityPolicies.ts @@ -15,15 +15,18 @@ */ import { + DefaultNamespaceEntityPolicy, Entity, FieldFormatEntityPolicy, NoForeignRootFieldsEntityPolicy, ReservedFieldsEntityPolicy, SchemaValidEntityPolicy, } from './entity'; -import { ComponentV1beta1Policy } from './kinds'; +import { + ComponentEntityV1beta1Policy, + LocationEntityV1beta1Policy, +} from './kinds'; import { EntityPolicy } from './types'; -import { DefaultNamespaceEntityPolicy } from './entity/policies/DefaultNamespaceEntityPolicy'; // Helper that requires that all of a set of policies can be successfully // applied @@ -68,7 +71,10 @@ export class EntityPolicies implements EntityPolicy { new FieldFormatEntityPolicy(), new ReservedFieldsEntityPolicy(), ]), - EntityPolicies.anyOf([new ComponentV1beta1Policy()]), + EntityPolicies.anyOf([ + new ComponentEntityV1beta1Policy(), + new LocationEntityV1beta1Policy(), + ]), ]); } diff --git a/packages/catalog-model/src/entity/policies/index.ts b/packages/catalog-model/src/entity/policies/index.ts index d64053f7cb..eb7c0e8378 100644 --- a/packages/catalog-model/src/entity/policies/index.ts +++ b/packages/catalog-model/src/entity/policies/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export { DefaultNamespaceEntityPolicy } from './DefaultNamespaceEntityPolicy'; export { FieldFormatEntityPolicy } from './FieldFormatEntityPolicy'; export { NoForeignRootFieldsEntityPolicy } from './NoForeignRootFieldsEntityPolicy'; export { ReservedFieldsEntityPolicy } from './ReservedFieldsEntityPolicy'; diff --git a/packages/catalog-model/src/kinds/ComponentV1beta1.ts b/packages/catalog-model/src/kinds/ComponentEntityV1beta1.ts similarity index 70% rename from packages/catalog-model/src/kinds/ComponentV1beta1.ts rename to packages/catalog-model/src/kinds/ComponentEntityV1beta1.ts index b041bf7967..2f5e8e079c 100644 --- a/packages/catalog-model/src/kinds/ComponentV1beta1.ts +++ b/packages/catalog-model/src/kinds/ComponentEntityV1beta1.ts @@ -15,33 +15,25 @@ */ import * as yup from 'yup'; -import type { Entity, EntityMeta } from '../entity/Entity'; +import type { Entity } from '../entity/Entity'; import type { EntityPolicy } from '../types'; const API_VERSION = 'backstage.io/v1beta1'; const KIND = 'Component'; -export interface ComponentV1beta1 extends Entity { +export interface ComponentEntityV1beta1 extends Entity { apiVersion: typeof API_VERSION; kind: typeof KIND; - metadata: EntityMeta & { - name: string; - }; spec: { type: string; }; } -export class ComponentV1beta1Policy implements EntityPolicy { +export class ComponentEntityV1beta1Policy implements EntityPolicy { private schema: yup.Schema; constructor() { - this.schema = yup.object>({ - metadata: yup - .object({ - name: yup.string().required(), - }) - .required(), + this.schema = yup.object>({ spec: yup .object({ type: yup.string().required(), @@ -51,10 +43,7 @@ export class ComponentV1beta1Policy implements EntityPolicy { } async enforce(envelope: Entity): Promise { - if ( - envelope.apiVersion !== 'backstage.io/v1beta1' || - envelope.kind !== 'Component' - ) { + if (envelope.apiVersion !== API_VERSION || envelope.kind !== KIND) { throw new Error('Unsupported apiVersion / kind'); } diff --git a/packages/catalog-model/src/kinds/LocationEntityV1beta1.ts b/packages/catalog-model/src/kinds/LocationEntityV1beta1.ts new file mode 100644 index 0000000000..e68bb00aa0 --- /dev/null +++ b/packages/catalog-model/src/kinds/LocationEntityV1beta1.ts @@ -0,0 +1,56 @@ +/* + * 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 type { EntityPolicy } from '../types'; + +const API_VERSION = 'backstage.io/v1beta1'; +const KIND = 'Location'; + +export interface LocationEntityV1beta1 extends Entity { + apiVersion: typeof API_VERSION; + kind: typeof KIND; + spec: { + type: string; + target?: string; + targets?: string[]; + }; +} + +export class LocationEntityV1beta1Policy implements EntityPolicy { + private schema: yup.Schema; + + constructor() { + this.schema = yup.object>({ + spec: yup + .object({ + type: yup.string().required(), + target: yup.string().notRequired(), + targets: yup.array(yup.string()).notRequired(), + }) + .required(), + }); + } + + async enforce(envelope: Entity): Promise { + if (envelope.apiVersion !== API_VERSION || envelope.kind !== KIND) { + throw new Error('Unsupported apiVersion / kind'); + } + + return await this.schema.validate(envelope, { strict: true }); + } +} diff --git a/packages/catalog-model/src/kinds/index.ts b/packages/catalog-model/src/kinds/index.ts index ed79fed61d..2d2a0744c5 100644 --- a/packages/catalog-model/src/kinds/index.ts +++ b/packages/catalog-model/src/kinds/index.ts @@ -14,8 +14,13 @@ * limitations under the License. */ +export { ComponentEntityV1beta1Policy } from './ComponentEntityV1beta1'; export type { - ComponentV1beta1, - ComponentV1beta1 as Component, -} from './ComponentV1beta1'; -export { ComponentV1beta1Policy } from './ComponentV1beta1'; + ComponentEntityV1beta1 as ComponentEntity, + ComponentEntityV1beta1, +} from './ComponentEntityV1beta1'; +export { LocationEntityV1beta1Policy } from './LocationEntityV1beta1'; +export type { + LocationEntityV1beta1 as LocationEntity, + LocationEntityV1beta1, +} from './LocationEntityV1beta1'; diff --git a/plugins/catalog-backend/examples/example-location.yaml b/plugins/catalog-backend/examples/example-location.yaml new file mode 100644 index 0000000000..5c507b29b5 --- /dev/null +++ b/plugins/catalog-backend/examples/example-location.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: backstage.io/v1beta1 +kind: Location +metadata: + name: location-1 +spec: + type: github + target: https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/examples/example-components.yaml diff --git a/plugins/catalog-backend/src/ingestion/LocationReaders.ts b/plugins/catalog-backend/src/ingestion/LocationReaders.ts index e806ecfbdb..eee16ea0de 100644 --- a/plugins/catalog-backend/src/ingestion/LocationReaders.ts +++ b/plugins/catalog-backend/src/ingestion/LocationReaders.ts @@ -26,6 +26,7 @@ import { AnnotateLocationEntityProcessor } from './processors/AnnotateLocationEn import { EntityPolicyProcessor } from './processors/EntityPolicyProcessor'; import { FileReaderProcessor } from './processors/FileReaderProcessor'; import { GithubReaderProcessor } from './processors/GithubReaderProcessor'; +import { LocationRefProcessor } from './processors/LocationEntityProcessor'; import * as result from './processors/results'; import { LocationProcessor, @@ -57,6 +58,7 @@ export class LocationReaders implements LocationReader { new GithubReaderProcessor(), new YamlProcessor(), new EntityPolicyProcessor(entityPolicy), + new LocationRefProcessor(), new AnnotateLocationEntityProcessor(), ]; } diff --git a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts new file mode 100644 index 0000000000..a5c9718574 --- /dev/null +++ b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts @@ -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 { Entity, LocationEntity, LocationSpec } from '@backstage/catalog-model'; +import * as result from './results'; +import { LocationProcessor, LocationProcessorEmit } from './types'; + +export class LocationRefProcessor implements LocationProcessor { + async processEntity( + entity: Entity, + _location: LocationSpec, + emit: LocationProcessorEmit, + ): Promise { + if (entity.kind === 'Location') { + const location = entity as LocationEntity; + if (location.spec.target) { + emit( + result.location( + { type: location.spec.type, target: location.spec.target }, + false, + ), + ); + } + if (location.spec.targets) { + for (const target of location.spec.targets) { + emit(result.location({ type: location.spec.type, target }, false)); + } + } + } + + return entity; + } +}