diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx index dda4b6fe28..234f837973 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx @@ -55,6 +55,7 @@ export const Stepper = (props: StepperProps) => { const handleNext = () => { setActiveStep(prevActiveStep => prevActiveStep + 1); }; + return ( <> diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.test.ts b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.test.ts index 5c56c861b1..01e6cea3cc 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.test.ts +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.test.ts @@ -15,7 +15,6 @@ */ import { JsonObject } from '@backstage/types'; -import { TemplateParameterSchema } from '../../../types'; import { extractSchemaFromStep } from './schema'; describe('extractSchemaFromStep', () => { diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.ts b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.ts index d255e56e27..bb4c56f7b0 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.ts +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.ts @@ -17,8 +17,6 @@ import { JsonObject } from '@backstage/types'; import { UiSchema } from '@rjsf/core'; import { JSONSchema7 } from 'json-schema'; -import { TemplateParameterSchema } from '../../../types'; - function isObject(value: unknown): value is JsonObject { return typeof value === 'object' && value !== null && !Array.isArray(value); } @@ -101,6 +99,10 @@ function extractUiSchema(schema: JsonObject, uiSchema: JsonObject) { } } +/** + * @alpha + * Takes a step from a Backstage Template Manifest and converts it to a JSON Schema and UI Schema for rjsf + */ export const extractSchemaFromStep = ( inputStep: JSONSchema7, ): { uiSchema: UiSchema; schema: JSONSchema7 } => { diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.test.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.test.tsx new file mode 100644 index 0000000000..18cdc97264 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.test.tsx @@ -0,0 +1,120 @@ +/* + * Copyright 2022 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 { TemplateParameterSchema } from '../../../types'; +import { useTemplateSchema } from './useTemplateSchema'; +import { renderHook } from '@testing-library/react-hooks'; +import { TestApiProvider } from '@backstage/test-utils'; +import React from 'react'; +import { featureFlagsApiRef } from '@backstage/core-plugin-api'; + +describe('useTemplateSchema', () => { + it('should generate the correct schema', () => { + const manifest: TemplateParameterSchema = { + title: 'Test Template', + description: 'Test Template Description', + steps: [ + { + title: 'Step 1', + description: 'Step 1 Description', + schema: { + type: 'object', + properties: { + field1: { type: 'string', 'ui:field': 'MyCoolComponent' }, + }, + }, + }, + { + title: 'Step 2', + description: 'Step 2 Description', + schema: { + type: 'object', + properties: { + field2: { type: 'string', 'ui:field': 'MyCoolerComponent' }, + }, + }, + }, + ], + }; + + const { + steps: [first, second], + } = useTemplateSchema(manifest); + + expect(first.uiSchema).toEqual({ + field1: { 'ui:field': 'MyCoolComponent' }, + }); + + expect(first.schema).toEqual({ + type: 'object', + properties: { + field1: { type: 'string' }, + }, + }); + + expect(second.uiSchema).toEqual({ + field2: { 'ui:field': 'MyCoolerComponent' }, + }); + + expect(second.schema).toEqual({ + type: 'object', + properties: { + field2: { type: 'string' }, + }, + }); + }); + + it('should use featureFlags property to skip a step if the whole step is disabled', () => { + const manifest: TemplateParameterSchema = { + title: 'Test Template', + description: 'Test Template Description', + steps: [ + { + title: 'Step 1', + description: 'Step 1 Description', + schema: { + type: 'object', + 'ui:backstage': { + featureFlag: 'my-feature-flag', + }, + properties: { + field1: { type: 'string', 'ui:field': 'MyCoolComponent' }, + }, + }, + }, + { + title: 'Step 2', + description: 'Step 2 Description', + schema: { + type: 'object', + properties: { + field2: { type: 'string', 'ui:field': 'MyCoolerComponent' }, + }, + }, + }, + ], + }; + + const result = renderHook(() => useTemplateSchema(manifest), { + wrapper: ({ children }) => ( + false }]]} + > + {children} + + ), + }); + }); +}); diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.ts b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.ts new file mode 100644 index 0000000000..38ea34048e --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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 { UiSchema } from '@rjsf/core'; +import { JSONSchema7 } from 'json-schema'; +import { TemplateParameterSchema } from '../../../types'; +import { extractSchemaFromStep } from './schema'; + +export const useTemplateSchema = ( + manifest: TemplateParameterSchema, +): { steps: { uiSchema: UiSchema; schema: JSONSchema7 }[] } => { + return { + steps: manifest.steps.map(({ schema }) => extractSchemaFromStep(schema)), + }; +};