Execute workflow form action (#11099)

- create a form filler component
- send the response on submit
- put back a field name. We need it for the step output
- validate a form is well set before activation

TODO:
- we need to refresh to see the form submitted. We need to discuss about
a strategy
- the response is not saved in the step settings. We need a new endpoint
to update workflow run step



https://github.com/user-attachments/assets/0f34a6cd-ed8c-4d9a-a1d4-51455cc83443
This commit is contained in:
Thomas Trompette
2025-03-21 18:38:14 +01:00
committed by GitHub
parent 07bd2486ca
commit c50cdd9510
25 changed files with 582 additions and 70 deletions

View File

@ -7,16 +7,19 @@ describe('generateFakeFormResponse', () => {
const schema = [
{
id: '96939213-49ac-4dee-949d-56e6c7be98e6',
name: 'name',
type: FieldMetadataType.TEXT,
label: 'Name',
},
{
id: '96939213-49ac-4dee-949d-56e6c7be98e7',
name: 'age',
type: FieldMetadataType.NUMBER,
label: 'Age',
},
{
id: '96939213-49ac-4dee-949d-56e6c7be98e8',
name: 'email',
type: FieldMetadataType.EMAILS,
label: 'Email',
},
@ -25,7 +28,7 @@ describe('generateFakeFormResponse', () => {
const result = generateFakeFormResponse(schema);
expect(result).toEqual({
'96939213-49ac-4dee-949d-56e6c7be98e8': {
email: {
isLeaf: false,
label: 'Email',
value: {
@ -44,14 +47,14 @@ describe('generateFakeFormResponse', () => {
},
icon: undefined,
},
'96939213-49ac-4dee-949d-56e6c7be98e6': {
name: {
isLeaf: true,
label: 'Name',
type: FieldMetadataType.TEXT,
value: 'My text',
icon: undefined,
},
'96939213-49ac-4dee-949d-56e6c7be98e7': {
age: {
isLeaf: true,
label: 'Age',
type: FieldMetadataType.NUMBER,

View File

@ -9,7 +9,7 @@ export const generateFakeFormResponse = (
formMetadata: FormFieldMetadata[],
): Record<string, Leaf | Node> => {
return formMetadata.reduce((acc, formFieldMetadata) => {
acc[formFieldMetadata.id] = generateFakeField({
acc[formFieldMetadata.name] = generateFakeField({
type: formFieldMetadata.type,
label: formFieldMetadata.label,
});

View File

@ -506,12 +506,14 @@ export class WorkflowVersionStepWorkspaceService {
input: [
{
id: v4(),
name: 'company',
label: 'Company',
placeholder: 'Select a company',
type: FieldMetadataType.TEXT,
},
{
id: v4(),
name: 'number',
label: 'Number',
placeholder: '1000',
type: FieldMetadataType.NUMBER,

View File

@ -4,8 +4,10 @@ import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-execut
export type FormFieldMetadata = {
id: string;
name: string;
label: string;
type: FieldMetadataType;
value?: any;
placeholder?: string;
settings?: Record<string, any>;
};

View File

@ -1,8 +1,15 @@
import { isNonEmptyString } from '@sniptt/guards';
import {
WorkflowVersionStatus,
WorkflowVersionWorkspaceEntity,
} from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
import { WorkflowFormActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type';
import {
WorkflowAction,
WorkflowActionType,
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import {
WorkflowTriggerException,
WorkflowTriggerExceptionCode,
@ -58,6 +65,10 @@ function assertVersionIsValid(workflowVersion: WorkflowVersionWorkspaceEntity) {
workflowVersion.trigger.type,
workflowVersion.trigger.settings,
);
workflowVersion.steps.forEach((step) => {
assertStepIsValid(step);
});
}
function assertTriggerSettingsAreValid(
@ -188,3 +199,46 @@ function assertDatabaseEventTriggerSettingsAreValid(settings: any) {
);
}
}
function assertStepIsValid(step: WorkflowAction) {
switch (step.type) {
case WorkflowActionType.FORM:
assertFormStepIsValid(step.settings);
break;
default:
break;
}
}
function assertFormStepIsValid(settings: WorkflowFormActionSettings) {
if (!settings.input) {
throw new WorkflowTriggerException(
'No input provided in form step',
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_TRIGGER,
);
}
// Check all fields have unique and defined names
const fieldNames = settings.input.map((fieldMetadata) => fieldMetadata.name);
const uniqueFieldNames = new Set(fieldNames);
if (fieldNames.length !== uniqueFieldNames.size) {
throw new WorkflowTriggerException(
'Form action fields must have unique names',
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION,
);
}
// Check all fields have defined labels and types
settings.input.forEach((fieldMetadata) => {
if (
!isNonEmptyString(fieldMetadata.label) ||
!isNonEmptyString(fieldMetadata.type)
) {
throw new WorkflowTriggerException(
'Form action fields must have a defined label and type',
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION,
);
}
});
}