diff --git a/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx b/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx index ff6782c512..c10b4e081e 100644 --- a/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx +++ b/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx @@ -23,17 +23,12 @@ import { import { stringifyEntityRef } from '@backstage/catalog-model'; import { makeStyles } from '@material-ui/core'; import { BackstageTheme } from '@backstage/theme'; -import { - errorApiRef, - useApi, - featureFlagsApiRef, -} from '@backstage/core-plugin-api'; +import { errorApiRef, useApi } from '@backstage/core-plugin-api'; import { useTemplateParameterSchema } from '../../hooks/useTemplateParameterSchema'; import { Stepper, type StepperProps } from '../Stepper/Stepper'; import { SecretsContextProvider } from '../../../secrets/SecretsContext'; import { ReviewStepProps } from '@backstage/plugin-scaffolder-react'; -import cloneDeep from 'lodash/cloneDeep'; -import { useMemo, useCallback } from 'react'; +import { useFeatureFlaggedProperties } from '../../hooks/useFeatureFlaggedProperties'; const useStyles = makeStyles(() => ({ markdown: { @@ -87,63 +82,7 @@ export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => { const { loading, manifest, error } = useTemplateParameterSchema(templateRef); - const featureFlagKey = 'backstage:featureFlag'; - const featureFlagApi = useApi(featureFlagsApiRef); - - // return manifest with updates steps - const filterOutStepProperties = useCallback(() => { - if (manifest) { - const filteredSteps = manifest.steps - .filter(step => { - const featureFlag = step.schema[featureFlagKey]; - return ( - typeof featureFlag !== 'string' || - featureFlagApi.isActive(featureFlag) - ); - }) - .map(step => { - const filteredStep = cloneDeep(step); - const removedPropertyKeys: Array = []; - if (filteredStep.schema.properties) { - filteredStep.schema.properties = Object.fromEntries( - Object.entries(filteredStep.schema.properties).filter( - ([key, value]) => { - if (value[featureFlagKey]) { - if (featureFlagApi.isActive(value[featureFlagKey])) { - return true; - } - - removedPropertyKeys.push(key); - return false; - } - return true; - }, - ), - ); - - // remove the feature flag property key from required if they are not active - filteredStep.schema.required = Array.isArray( - filteredStep.schema.required, - ) - ? filteredStep.schema.required?.filter( - r => !removedPropertyKeys.includes(r as string), - ) - : filteredStep.schema.required; - } - - return filteredStep; - }); - - return { ...manifest, steps: filteredSteps }; - } - - return manifest; - }, [manifest, featureFlagKey, featureFlagApi]); - - const sortedManifest = useMemo( - () => filterOutStepProperties(), - [filterOutStepProperties], - ); + const sortedManifest = useFeatureFlaggedProperties(manifest); useEffect(() => { if (error) { diff --git a/plugins/scaffolder-react/src/next/hooks/index.ts b/plugins/scaffolder-react/src/next/hooks/index.ts index ca8ce10eff..9e192c143f 100644 --- a/plugins/scaffolder-react/src/next/hooks/index.ts +++ b/plugins/scaffolder-react/src/next/hooks/index.ts @@ -19,3 +19,4 @@ export { type ParsedTemplateSchema, } from './useTemplateSchema'; export { useTemplateParameterSchema } from './useTemplateParameterSchema'; +export { useFeatureFlaggedProperties } from './useFeatureFlaggedProperties'; diff --git a/plugins/scaffolder-react/src/next/hooks/useFeatureFlaggedProperties.ts b/plugins/scaffolder-react/src/next/hooks/useFeatureFlaggedProperties.ts new file mode 100644 index 0000000000..ae1a6d9c67 --- /dev/null +++ b/plugins/scaffolder-react/src/next/hooks/useFeatureFlaggedProperties.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2023 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 cloneDeep from 'lodash/cloneDeep'; +import { useApi, featureFlagsApiRef } from '@backstage/core-plugin-api'; +import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react'; + +/** + * Returns manifest of software templates with steps without a featureFlag tag. + * @alpha + */ + +export const useFeatureFlaggedProperties = ( + manifest: TemplateParameterSchema | undefined, +) => { + const featureFlagKey = 'backstage:featureFlag'; + const featureFlagApi = useApi(featureFlagsApiRef); + + if (!manifest) { + return null; + } + + const filteredSteps = manifest?.steps + .filter(step => { + const featureFlag = step.schema[featureFlagKey]; + return ( + typeof featureFlag !== 'string' || featureFlagApi.isActive(featureFlag) + ); + }) + .map(step => { + const filteredStep = cloneDeep(step); + const removedPropertyKeys: Array = []; + if (filteredStep.schema.properties) { + filteredStep.schema.properties = Object.fromEntries( + Object.entries(filteredStep.schema.properties).filter( + ([key, value]) => { + if (value[featureFlagKey]) { + if (featureFlagApi.isActive(value[featureFlagKey])) { + return true; + } + + removedPropertyKeys.push(key); + return false; + } + return true; + }, + ), + ); + + // remove the feature flag property key from required if they are not active + filteredStep.schema.required = Array.isArray( + filteredStep.schema.required, + ) + ? filteredStep.schema.required?.filter( + r => !removedPropertyKeys.includes(r as string), + ) + : filteredStep.schema.required; + } + + return filteredStep; + }); + + return { ...manifest, steps: filteredSteps }; +};