feat: implemented useFormHooks hook for calling hooks on create
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -26,12 +26,16 @@ import { makeStyles } from '@material-ui/core/styles';
|
||||
import { errorApiRef, useAnalytics, useApi } from '@backstage/core-plugin-api';
|
||||
import { useTemplateParameterSchema } from '../../hooks/useTemplateParameterSchema';
|
||||
import { Stepper, type StepperProps } from '../Stepper/Stepper';
|
||||
import { SecretsContextProvider } from '../../../secrets/SecretsContext';
|
||||
import {
|
||||
SecretsContextProvider,
|
||||
useTemplateSecrets,
|
||||
} from '../../../secrets/SecretsContext';
|
||||
import { useFilteredSchemaProperties } from '../../hooks/useFilteredSchemaProperties';
|
||||
import { ReviewStepProps } from '@backstage/plugin-scaffolder-react';
|
||||
import { useTemplateTimeSavedMinutes } from '../../hooks/useTemplateTimeSaved';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { ScaffolderFormHook } from '../../extensions';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { useFormHooks } from '../../../../../scaffolder/src/alpha/hooks/useFormHooks';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
markdown: {
|
||||
@@ -53,7 +57,6 @@ export type WorkflowProps = {
|
||||
description?: string;
|
||||
namespace: string;
|
||||
templateName: string;
|
||||
formHooks?: ScaffolderFormHook[];
|
||||
components?: {
|
||||
ReviewStepComponent?: React.ComponentType<ReviewStepProps>;
|
||||
};
|
||||
@@ -74,7 +77,6 @@ export type WorkflowProps = {
|
||||
export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => {
|
||||
const { title, description, namespace, templateName, onCreate, ...props } =
|
||||
workflowProps;
|
||||
|
||||
const analytics = useAnalytics();
|
||||
const styles = useStyles();
|
||||
const templateRef = stringifyEntityRef({
|
||||
@@ -82,17 +84,34 @@ export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => {
|
||||
namespace: namespace,
|
||||
name: templateName,
|
||||
});
|
||||
|
||||
const errorApi = useApi(errorApiRef);
|
||||
|
||||
const { loading, manifest, error } = useTemplateParameterSchema(templateRef);
|
||||
|
||||
const sortedManifest = useFilteredSchemaProperties(manifest);
|
||||
|
||||
const minutesSaved = useTemplateTimeSavedMinutes(templateRef);
|
||||
const { setSecrets } = useTemplateSecrets();
|
||||
const formHooks = useFormHooks();
|
||||
|
||||
const workflowOnCreate = useCallback(
|
||||
async (formState: Record<string, JsonValue>) => {
|
||||
if (manifest?.EXPERIMENTAL_formHooks && formHooks?.size) {
|
||||
// for each of the form hooks, go and call the hook with the context
|
||||
await Promise.all(
|
||||
manifest.EXPERIMENTAL_formHooks.map(async hook => {
|
||||
const formHook = formHooks.get(hook.id);
|
||||
if (!formHook) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to find form hook', hook.id);
|
||||
return;
|
||||
}
|
||||
|
||||
await formHook.fn({
|
||||
setSecrets,
|
||||
input: hook.input,
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
onCreate(formState);
|
||||
|
||||
const name =
|
||||
@@ -101,7 +120,15 @@ export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => {
|
||||
value: minutesSaved,
|
||||
});
|
||||
},
|
||||
[onCreate, analytics, templateName, minutesSaved],
|
||||
[
|
||||
manifest?.EXPERIMENTAL_formHooks,
|
||||
formHooks,
|
||||
onCreate,
|
||||
analytics,
|
||||
templateName,
|
||||
minutesSaved,
|
||||
setSecrets,
|
||||
],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -14,18 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { AnyApiRef } from '@backstage/core-plugin-api';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { z } from 'zod';
|
||||
|
||||
export type ScaffolderFormHookContext<TInput> = {
|
||||
input: TInput;
|
||||
setSecret: (key: string, value: JsonValue) => void;
|
||||
setSecrets: (input: Record<string, string>) => void;
|
||||
};
|
||||
|
||||
export type ScaffolderInitialFormHook<
|
||||
export type ScaffolderFormHook<
|
||||
TInputSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType } = {},
|
||||
TDeps extends { [key in string]: AnyApiRef } = { [key in string]: AnyApiRef },
|
||||
TInput = {
|
||||
TInput extends {} = {
|
||||
[key in keyof TInputSchema]: z.infer<ReturnType<TInputSchema[key]>>;
|
||||
},
|
||||
> = {
|
||||
@@ -43,11 +42,6 @@ export type ScaffolderInitialFormHook<
|
||||
) => Promise<void>;
|
||||
};
|
||||
|
||||
export type ScaffolderFormHook<TInput = any> = {
|
||||
id: string;
|
||||
fn: (ctx: ScaffolderFormHookContext<TInput>) => Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method for creating hooks which can be used to collect
|
||||
* secrets from the user before submitting to the backend.
|
||||
@@ -56,7 +50,7 @@ export type ScaffolderFormHook<TInput = any> = {
|
||||
export function createScaffolderFormHook<
|
||||
TDeps extends { [key in string]: AnyApiRef },
|
||||
TInputSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType },
|
||||
TInput = {
|
||||
TInput extends {} = {
|
||||
[key in keyof TInputSchema]: z.infer<ReturnType<TInputSchema[key]>>;
|
||||
},
|
||||
>(options: {
|
||||
@@ -71,7 +65,7 @@ export function createScaffolderFormHook<
|
||||
? { [key in keyof TDeps]: TDeps[key]['T'] }
|
||||
: never,
|
||||
) => Promise<void>;
|
||||
}): ScaffolderInitialFormHook<TInputSchema, TDeps, TInput> {
|
||||
}): ScaffolderFormHook<TInputSchema, TDeps, TInput> {
|
||||
return {
|
||||
...options,
|
||||
version: 'v1',
|
||||
|
||||
@@ -17,20 +17,23 @@
|
||||
import useAsync from 'react-use/esm/useAsync';
|
||||
import { scaffolderApiRef } from '../../api/ref';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export const useTemplateParameterSchema = (templateRef: string) => {
|
||||
const scaffolderApi = useApi(scaffolderApiRef);
|
||||
const { value, loading, error } = useAsync(
|
||||
const {
|
||||
value: manifest,
|
||||
loading,
|
||||
error,
|
||||
} = useAsync(
|
||||
() => scaffolderApi.getTemplateParameterSchema(templateRef),
|
||||
[scaffolderApi, templateRef],
|
||||
);
|
||||
|
||||
return {
|
||||
manifest: value as TemplateParameterSchema | undefined,
|
||||
manifest,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
|
||||
@@ -33,4 +33,5 @@ export type TemplateParameterSchema = {
|
||||
description?: string;
|
||||
schema: JsonObject;
|
||||
}>;
|
||||
EXPERIMENTAL_formHooks?: { id: string; input?: JsonObject }[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user