diff --git a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts index 57f73b1876b01..46a1433be2c31 100644 --- a/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts +++ b/plugins/scaffolder-backend/src/lib/catalog/CatalogEntityClient.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { TemplateEntity } from '@backstage/catalog-model'; +import { + TemplateEntityV1alpha1, + TemplateEntityV1beta2, +} from '@backstage/catalog-model'; import { CatalogApi } from '@backstage/catalog-client'; import { ConflictError, NotFoundError } from '@backstage/backend-common'; @@ -32,7 +35,7 @@ export class CatalogEntityClient { async findTemplate( templateName: string, options?: { token?: string }, - ): Promise { + ): Promise { const { items: templates } = (await this.catalogClient.getEntities( { filter: { @@ -41,7 +44,7 @@ export class CatalogEntityClient { }, }, options, - )) as { items: TemplateEntity[] }; + )) as { items: (TemplateEntityV1alpha1 | TemplateEntityV1beta2)[] }; if (templates.length !== 1) { if (templates.length > 1) { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index 7fc35d9e46824..27a1b62d04c2d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -48,7 +48,7 @@ export type TaskSpec = { id: string; name: string; action: string; - parameters?: { [name: string]: JsonValue }; + parameters?: JsonObject; }>; output: { [name: string]: string }; }; diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 2cee30fc87d77..21d9efc641286 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -50,6 +50,11 @@ import { PluginDatabaseManager, } from '@backstage/backend-common'; import { CatalogApi } from '@backstage/catalog-client'; +import { + TemplateEntityV1alpha1, + TemplateEntityV1beta2, + Entity, +} from '@backstage/catalog-model'; export interface RouterOptions { preparers: PreparerBuilder; @@ -63,6 +68,21 @@ export interface RouterOptions { catalogClient: CatalogApi; } +function isAlpha1Template( + entity: TemplateEntityV1alpha1 | TemplateEntityV1beta2, +): entity is TemplateEntityV1alpha1 { + return ( + entity.apiVersion === 'backstage.io/v1alpha1' || + entity.apiVersion === 'backstage.io/v1beta1' + ); +} + +function isBeta2Template( + entity: TemplateEntityV1alpha1 | TemplateEntityV1beta2, +): entity is TemplateEntityV1beta2 { + return entity.apiVersion === 'backstage.io/v1beta2'; +} + export async function createRouter( options: RouterOptions, ): Promise { @@ -144,6 +164,11 @@ export async function createRouter( const template = await entityClient.findTemplate(templateName, { token: getBearerToken(req.headers.authorization), }); + if (!isAlpha1Template(template)) { + throw new InputError( + `This endpoint does not support templates with version ${template.apiVersion}`, + ); + } const validationResult: ValidatorResult = validate( values, @@ -249,18 +274,16 @@ export async function createRouter( } const template = await entityClient.findTemplate(name); - if (template.apiVersion === 'backstage.io/v1beta1') { - const betaTemplate = template as any; // TODO: proper type - const parameters = betaTemplate.spec.schema; - const steps = Array.isArray(parameters) ? parameters : [parameters]; + if (isBeta2Template(template)) { + const parameters = [template.spec.parameters ?? []].flat(); res.json({ title: template.metadata.title ?? template.metadata.name, - steps: steps.map(schema => ({ + steps: parameters.map(schema => ({ title: schema.title ?? 'Fill in template parameters', schema, })), }); - } else if (template.apiVersion === 'backstage.io/v1alpha1') { + } else if (isAlpha1Template(template)) { res.json({ title: template.metadata.title ?? template.metadata.name, steps: [ @@ -298,7 +321,9 @@ export async function createRouter( }); } else { throw new InputError( - `Unsupported apiVersion field in schema entity, ${template.apiVersion}`, + `Unsupported apiVersion field in schema entity, ${ + (template as Entity).apiVersion + }`, ); } }, @@ -313,31 +338,36 @@ export async function createRouter( }; const template = await entityClient.findTemplate(templateName); - const validationResult: ValidatorResult = validate( - values, - template.spec.schema, - ); + let taskSpec; + if (isAlpha1Template(template)) { + const result = validate(values, template.spec.schema); - if (!validationResult.valid) { - res.status(400).json({ errors: validationResult.errors }); - return; - } + if (!result.valid) { + res.status(400).json({ errors: result.errors }); + return; + } - let taskSpec; - if (template.apiVersion === 'backstage.io/v1alpha1') { taskSpec = templateEntityToSpec(template, values); - } else if (template.apiVersion === 'backstage.io/v1beta1') { - // TODO: add v1beta1 type - // const betaTemplate = template as TemplateEntityV1beta1 - const betaTemplate = template as any; + } else if (isBeta2Template(template)) { + for (const parameters of [template.spec.parameters ?? []].flat()) { + const result = validate(values, parameters); + + if (!result.valid) { + res.status(400).json({ errors: result.errors }); + return; + } + } + taskSpec = { values, - steps: betaTemplate.spec.steps, - output: betaTemplate.spec.output, + steps: template.spec.steps, + output: template.spec.output ?? {}, }; } else { throw new InputError( - `Unsupported apiVersion field in schema entity, ${template.apiVersion}`, + `Unsupported apiVersion field in schema entity, ${ + (template as Entity).apiVersion + }`, ); }