diff --git a/.changeset/swift-dolls-bow.md b/.changeset/swift-dolls-bow.md new file mode 100644 index 0000000000..0e7a261055 --- /dev/null +++ b/.changeset/swift-dolls-bow.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +**BREAKING** - Removed the `CatalogEntityClient` export. This is no longer provider by this package, +but you can implement one pretty simply yourself using the `CatalogApi` and applying filters to fetch templates. diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index a28db34c0c..ba16b5d4f0 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -29,7 +29,6 @@ import { SpawnOptionsWithoutStdio } from 'child_process'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskSpecV1beta2 } from '@backstage/plugin-scaffolder-common'; import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common'; -import { TemplateEntityV1beta2 } from '@backstage/plugin-scaffolder-common'; import { TemplateMetadata } from '@backstage/plugin-scaffolder-common'; import { UrlReader } from '@backstage/backend-common'; import { Writable } from 'stream'; @@ -49,19 +48,6 @@ export type ActionContext = { metadata?: TemplateMetadata; }; -// Warning: (ae-missing-release-tag) "CatalogEntityClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public -export class CatalogEntityClient { - constructor(catalogClient: CatalogApi); - findTemplate( - templateName: string, - options?: { - token?: string; - }, - ): Promise; -} - // @public export type CompletedTaskState = 'failed' | 'completed'; diff --git a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts deleted file mode 100644 index e9af165b73..0000000000 --- a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * 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 { TemplateEntityV1beta2 } from '@backstage/plugin-scaffolder-common'; -import { CatalogApi } from '@backstage/catalog-client'; -import { ConflictError, NotFoundError } from '@backstage/errors'; - -/** - * A catalog client tailored for reading out entity data from the catalog. - */ -export class CatalogEntityClient { - constructor(private readonly catalogClient: CatalogApi) {} - - /** - * Looks up a single template using a template name. - * - * Throws a NotFoundError or ConflictError if 0 or multiple templates are found. - */ - async findTemplate( - templateName: string, - options?: { token?: string }, - ): Promise { - const { items: templates } = (await this.catalogClient.getEntities( - { - filter: { - kind: 'template', - 'metadata.name': templateName, - }, - }, - options, - )) as { items: TemplateEntityV1beta2[] }; - - if (templates.length !== 1) { - if (templates.length > 1) { - throw new ConflictError( - 'Templates lookup resulted in multiple matches', - ); - } else { - throw new NotFoundError('Template not found'); - } - } - - return templates[0]; - } -} diff --git a/plugins/scaffolder-backend/src/lib/catalog/index.ts b/plugins/scaffolder-backend/src/lib/catalog/index.ts deleted file mode 100644 index ea693b00b3..0000000000 --- a/plugins/scaffolder-backend/src/lib/catalog/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * 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. - */ -export { CatalogEntityClient } from './CatalogEntityClient'; diff --git a/plugins/scaffolder-backend/src/lib/index.ts b/plugins/scaffolder-backend/src/lib/index.ts index dbb4348a2c..20096f9c03 100644 --- a/plugins/scaffolder-backend/src/lib/index.ts +++ b/plugins/scaffolder-backend/src/lib/index.ts @@ -14,5 +14,4 @@ * limitations under the License. */ -export * from './catalog'; export * from './templating'; diff --git a/plugins/scaffolder-backend/src/service/helpers.ts b/plugins/scaffolder-backend/src/service/helpers.ts index f86b1663a7..5cfc58d586 100644 --- a/plugins/scaffolder-backend/src/service/helpers.ts +++ b/plugins/scaffolder-backend/src/service/helpers.ts @@ -14,14 +14,21 @@ * limitations under the License. */ +import { CatalogApi } from '@backstage/catalog-client'; import { Entity, ANNOTATION_LOCATION, parseLocationRef, ANNOTATION_SOURCE_LOCATION, + EntityRef, + parseEntityRef, } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; -import { assertError } from '@backstage/errors'; +import { assertError, ConflictError, NotFoundError } from '@backstage/errors'; +import { + TemplateEntityV1beta2, + TemplateEntityV1beta3, +} from '@backstage/plugin-scaffolder-common'; import fs from 'fs-extra'; import os from 'os'; import { Logger } from 'winston'; @@ -78,3 +85,49 @@ export function getEntityBaseUrl(entity: Entity): string | undefined { // what the url is pointing to makes sense to use as a baseUrl return undefined; } + +/** + * Will use the provided CatalogApi to go find the template entity ref that is provided with and additional token + * Returns the first matching template, throws a NotFoundError or ConflictError if 0 or multiple templates are found. + */ +export async function findTemplate({ + entityRef, + token, + catalogApi, +}: { + entityRef: EntityRef; + token?: string; + catalogApi: CatalogApi; +}): Promise { + const parsedEntityRef = parseEntityRef(entityRef, { + defaultKind: 'template', + defaultNamespace: 'default', + }); + const { items } = await catalogApi.getEntities( + { + filter: { + kind: 'template', + 'metadata.name': parsedEntityRef.name, + 'metadata.namespace': parsedEntityRef.namespace, + }, + }, + { + token, + }, + ); + + const templates = items.filter( + (entity): entity is TemplateEntityV1beta3 | TemplateEntityV1beta2 => + entity.kind === 'Template', + ); + + if (templates.length !== 1) { + if (templates.length > 1) { + throw new ConflictError('Templates lookup resulted in multiple matches'); + } else { + throw new NotFoundError('Template not found'); + } + } + + return templates[0]; +} diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 440f74d6f6..b2978441bd 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -33,7 +33,7 @@ import express from 'express'; import Router from 'express-promise-router'; import { validate } from 'jsonschema'; import { Logger } from 'winston'; -import { CatalogEntityClient, TemplateFilter } from '../lib'; +import { TemplateFilter } from '../lib'; import { createBuiltinActions, DatabaseTaskStore, @@ -44,7 +44,7 @@ import { TemplateActionRegistry, } from '../scaffolder'; import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; -import { getEntityBaseUrl, getWorkingDirectory } from './helpers'; +import { getEntityBaseUrl, getWorkingDirectory, findTemplate } from './helpers'; /** * RouterOptions @@ -93,7 +93,6 @@ export async function createRouter( const logger = parentLogger.child({ plugin: 'scaffolder' }); const workingDirectory = await getWorkingDirectory(config, logger); - const entityClient = new CatalogEntityClient(catalogClient); const integrations = ScmIntegrations.fromConfig(config); let taskBroker: TaskBroker; @@ -152,7 +151,9 @@ export async function createRouter( ); } - const template = await entityClient.findTemplate(name, { + const template = await findTemplate({ + catalogApi: catalogClient, + entityRef: { kind, namespace, name }, token: getBearerToken(req.headers.authorization), }); if (isSupportedTemplate(template)) { @@ -187,8 +188,12 @@ export async function createRouter( const templateName: string = req.body.templateName; const values = req.body.values; const token = getBearerToken(req.headers.authorization); - const template = await entityClient.findTemplate(templateName, { - token, + const template = await findTemplate({ + catalogApi: catalogClient, + entityRef: { + name: templateName, + }, + token: getBearerToken(req.headers.authorization), }); let taskSpec: TaskSpec;