From 7e3ca8be717d91c30c083c908dda1cc1aefa393e Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 28 Oct 2024 16:22:34 +0100 Subject: [PATCH] feat: more work on form decorators Signed-off-by: blam --- packages/app/src/apis.ts | 12 +++++ .../src/components/scaffolder/decorators.ts | 33 +++++++++++++ .../src/Template.v1beta3.schema.json | 21 +++++---- .../src/next/components/Workflow/Workflow.tsx | 4 +- .../createScaffolderFormDecorator.ts | 6 +++ plugins/scaffolder/src/alpha/api.tsx | 46 ------------------- .../src/alpha/api/FormDecoratorsApi.ts | 9 ++-- plugins/scaffolder/src/alpha/api/index.ts | 8 +++- plugins/scaffolder/src/alpha/api/types.ts | 4 +- plugins/scaffolder/src/alpha/extensions.tsx | 29 ++++++++++++ .../alpha/hooks/useFormDecorators.test.tsx | 2 - plugins/scaffolder/src/alpha/index.ts | 1 + plugins/scaffolder/src/alpha/plugin.tsx | 2 +- 13 files changed, 109 insertions(+), 68 deletions(-) create mode 100644 packages/app/src/components/scaffolder/decorators.ts delete mode 100644 plugins/scaffolder/src/alpha/api.tsx diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 0c2ccbad22..e39122337f 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -26,6 +26,9 @@ import { discoveryApiRef, } from '@backstage/core-plugin-api'; import { AuthProxyDiscoveryApi } from './AuthProxyDiscoveryApi'; +import { formDecoratorsApiRef } from '@backstage/plugin-scaffolder/alpha'; +import { DefaultScaffolderFormDecoratorsApi } from '@backstage/plugin-scaffolder/alpha'; +import { mockDecorator } from './components/scaffolder/decorators'; export const apis: AnyApiFactory[] = [ createApiFactory({ @@ -39,5 +42,14 @@ export const apis: AnyApiFactory[] = [ factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi), }), + createApiFactory({ + api: formDecoratorsApiRef, + deps: {}, + factory: () => + DefaultScaffolderFormDecoratorsApi.create({ + decorators: [mockDecorator], + }), + }), + ScmAuth.createDefaultApiFactory(), ]; diff --git a/packages/app/src/components/scaffolder/decorators.ts b/packages/app/src/components/scaffolder/decorators.ts new file mode 100644 index 0000000000..6cce3a3fe0 --- /dev/null +++ b/packages/app/src/components/scaffolder/decorators.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2024 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 { githubAuthApiRef } from '@backstage/core-plugin-api'; +import { createScaffolderFormDecorator } from '@backstage/plugin-scaffolder-react/alpha'; + +export const mockDecorator = createScaffolderFormDecorator({ + id: 'githubOauth', + schema: { + input: { + test: z => z.string(), + }, + }, + deps: { + githubApi: githubAuthApiRef, + }, + fn: async ({ setSecrets }, { githubApi }) => { + const token = await githubApi.getAccessToken(); + setSecrets(state => ({ ...state, GITHUB_TOKEN: token })); + }, +}); diff --git a/plugins/scaffolder-common/src/Template.v1beta3.schema.json b/plugins/scaffolder-common/src/Template.v1beta3.schema.json index 9ab9c0ab5a..58f8ef240a 100644 --- a/plugins/scaffolder-common/src/Template.v1beta3.schema.json +++ b/plugins/scaffolder-common/src/Template.v1beta3.schema.json @@ -174,16 +174,19 @@ } }, "EXPERIMENTAL_formDecorators": { - "type": "object", + "type": "array", "description": "A list of decorators and their inputs that the form should trigger before submitting the job", - "properties": { - "id": { - "type": "string", - "description": "The form hook ID" - }, - "input": { - "type": "object", - "description": "A object describing the inputs to the form hook." + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The form hook ID" + }, + "input": { + "type": "object", + "description": "A object describing the inputs to the form hook." + } } } }, diff --git a/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx b/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx index 1b0a10c780..91bc98dad3 100644 --- a/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx +++ b/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx @@ -95,11 +95,11 @@ export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => { async (originalFormState: Record) => { let formState: Record = { ...originalFormState }; - if (manifest?.EXPERIMENTAL_formDecorators && formDecorators?.size) { + if (manifest?.EXPERIMENTAL_formDecorators) { // for each of the form decorators, go and call the decorator with the context await Promise.all( manifest.EXPERIMENTAL_formDecorators.map(async decorator => { - const formDecorator = formDecorators.get(decorator.id); + const formDecorator = formDecorators?.get(decorator.id); if (!formDecorator) { // eslint-disable-next-line no-console console.error('Failed to find form decorator', decorator.id); diff --git a/plugins/scaffolder-react/src/next/extensions/createScaffolderFormDecorator.ts b/plugins/scaffolder-react/src/next/extensions/createScaffolderFormDecorator.ts index 965cfa6107..20da932c05 100644 --- a/plugins/scaffolder-react/src/next/extensions/createScaffolderFormDecorator.ts +++ b/plugins/scaffolder-react/src/next/extensions/createScaffolderFormDecorator.ts @@ -52,6 +52,12 @@ export type ScaffolderFormDecorator< ) => Promise; }; +/** @alpha */ +export type AnyScaffolderFormDecorator = ScaffolderFormDecorator< + { [key in string]: (zImpl: typeof z) => z.ZodType }, + { [key in string]: AnyApiRef }, + any +>; /** * Method for creating decorators which can be used to collect * secrets from the user before submitting to the backend. diff --git a/plugins/scaffolder/src/alpha/api.tsx b/plugins/scaffolder/src/alpha/api.tsx deleted file mode 100644 index 06364cd6e9..0000000000 --- a/plugins/scaffolder/src/alpha/api.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 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 { - ApiBlueprint, - createApiFactory, - discoveryApiRef, - fetchApiRef, - identityApiRef, -} from '@backstage/frontend-plugin-api'; -import { scmIntegrationsApiRef } from '@backstage/integration-react'; -import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; -import { ScaffolderClient } from '../api'; - -export const scaffolderApi = ApiBlueprint.make({ - params: { - factory: createApiFactory({ - api: scaffolderApiRef, - deps: { - discoveryApi: discoveryApiRef, - scmIntegrationsApi: scmIntegrationsApiRef, - fetchApi: fetchApiRef, - identityApi: identityApiRef, - }, - factory: ({ discoveryApi, scmIntegrationsApi, fetchApi, identityApi }) => - new ScaffolderClient({ - discoveryApi, - scmIntegrationsApi, - fetchApi, - identityApi, - }), - }), - }, -}); diff --git a/plugins/scaffolder/src/alpha/api/FormDecoratorsApi.ts b/plugins/scaffolder/src/alpha/api/FormDecoratorsApi.ts index 7638986eab..79ec618ce2 100644 --- a/plugins/scaffolder/src/alpha/api/FormDecoratorsApi.ts +++ b/plugins/scaffolder/src/alpha/api/FormDecoratorsApi.ts @@ -15,24 +15,25 @@ */ import { ScaffolderFormDecoratorsApi } from './types'; -import { ScaffolderFormDecorator } from '@backstage/plugin-scaffolder-react/alpha'; +import { AnyScaffolderFormDecorator } from '@backstage/plugin-scaffolder-react/alpha'; +/** @alpha */ export class DefaultScaffolderFormDecoratorsApi implements ScaffolderFormDecoratorsApi { private constructor( private readonly options: { - decorators: Array; + decorators: Array; }, ) {} - static create(options?: { decorators: ScaffolderFormDecorator[] }) { + static create(options?: { decorators: AnyScaffolderFormDecorator[] }) { return new DefaultScaffolderFormDecoratorsApi( options ?? { decorators: [] }, ); } - async getFormDecorators(): Promise { + async getFormDecorators(): Promise { return this.options.decorators; } } diff --git a/plugins/scaffolder/src/alpha/api/index.ts b/plugins/scaffolder/src/alpha/api/index.ts index bbc30ef50b..0afefe17a5 100644 --- a/plugins/scaffolder/src/alpha/api/index.ts +++ b/plugins/scaffolder/src/alpha/api/index.ts @@ -14,5 +14,9 @@ * limitations under the License. */ -export { formFieldsApiRef } from './ref'; -export type { ScaffolderFormFieldsApi } from './types'; +export { formFieldsApiRef, formDecoratorsApiRef } from './ref'; +export type { + ScaffolderFormFieldsApi, + ScaffolderFormDecoratorsApi, +} from './types'; +export { DefaultScaffolderFormDecoratorsApi } from './FormDecoratorsApi'; diff --git a/plugins/scaffolder/src/alpha/api/types.ts b/plugins/scaffolder/src/alpha/api/types.ts index 28ce8dab3d..d52d102d6d 100644 --- a/plugins/scaffolder/src/alpha/api/types.ts +++ b/plugins/scaffolder/src/alpha/api/types.ts @@ -16,7 +16,7 @@ import { FormFieldExtensionData, - ScaffolderFormDecorator, + AnyScaffolderFormDecorator, } from '@backstage/plugin-scaffolder-react/alpha'; export interface ScaffolderFormFieldsApi { @@ -24,5 +24,5 @@ export interface ScaffolderFormFieldsApi { } export interface ScaffolderFormDecoratorsApi { - getFormDecorators(): Promise; + getFormDecorators(): Promise; } diff --git a/plugins/scaffolder/src/alpha/extensions.tsx b/plugins/scaffolder/src/alpha/extensions.tsx index 191dcf407c..493b0d7114 100644 --- a/plugins/scaffolder/src/alpha/extensions.tsx +++ b/plugins/scaffolder/src/alpha/extensions.tsx @@ -21,11 +21,19 @@ import { import { NavItemBlueprint, PageBlueprint, + ApiBlueprint, + createApiFactory, + discoveryApiRef, + fetchApiRef, + identityApiRef, } from '@backstage/frontend-plugin-api'; import React from 'react'; import { rootRouteRef } from '../routes'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import { FormFieldBlueprint } from '@backstage/plugin-scaffolder-react/alpha'; +import { scmIntegrationsApiRef } from '@backstage/integration-react'; +import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; +import { ScaffolderClient } from '../api'; export const scaffolderPage = PageBlueprint.make({ params: { @@ -50,3 +58,24 @@ export const repoUrlPickerFormField = FormFieldBlueprint.make({ field: () => import('./fields/RepoUrlPicker').then(m => m.RepoUrlPicker), }, }); + +export const scaffolderApi = ApiBlueprint.make({ + params: { + factory: createApiFactory({ + api: scaffolderApiRef, + deps: { + discoveryApi: discoveryApiRef, + scmIntegrationsApi: scmIntegrationsApiRef, + fetchApi: fetchApiRef, + identityApi: identityApiRef, + }, + factory: ({ discoveryApi, scmIntegrationsApi, fetchApi, identityApi }) => + new ScaffolderClient({ + discoveryApi, + scmIntegrationsApi, + fetchApi, + identityApi, + }), + }), + }, +}); diff --git a/plugins/scaffolder/src/alpha/hooks/useFormDecorators.test.tsx b/plugins/scaffolder/src/alpha/hooks/useFormDecorators.test.tsx index d070894206..091e2fbc42 100644 --- a/plugins/scaffolder/src/alpha/hooks/useFormDecorators.test.tsx +++ b/plugins/scaffolder/src/alpha/hooks/useFormDecorators.test.tsx @@ -52,7 +52,6 @@ describe('useFormDecorators', () => { [ formDecoratorsApiRef, DefaultScaffolderFormDecoratorsApi.create({ - // @ts-expect-error - todo decorators: [mockDecorator], }), ], @@ -90,7 +89,6 @@ describe('useFormDecorators', () => { [ formDecoratorsApiRef, DefaultScaffolderFormDecoratorsApi.create({ - // @ts-expect-error - todo decorators: [mockDecorator], }), ], diff --git a/plugins/scaffolder/src/alpha/index.ts b/plugins/scaffolder/src/alpha/index.ts index bd3104a274..53e2b8f0bf 100644 --- a/plugins/scaffolder/src/alpha/index.ts +++ b/plugins/scaffolder/src/alpha/index.ts @@ -23,5 +23,6 @@ export { } from './components'; export { scaffolderTranslationRef } from '../translation'; +export * from './api'; export { default } from './plugin'; diff --git a/plugins/scaffolder/src/alpha/plugin.tsx b/plugins/scaffolder/src/alpha/plugin.tsx index 54e375e875..f3f0e9ed9e 100644 --- a/plugins/scaffolder/src/alpha/plugin.tsx +++ b/plugins/scaffolder/src/alpha/plugin.tsx @@ -26,11 +26,11 @@ import { selectedTemplateRouteRef, viewTechDocRouteRef, } from '../routes'; -import { scaffolderApi } from './api'; import { repoUrlPickerFormField, scaffolderNavItem, scaffolderPage, + scaffolderApi, } from './extensions'; import { formFieldsApi } from './api/FormFieldsApi';