Files
twenty/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts
Baptiste Devessier cb5f4820d7 Visualize workflow run step input (#10677)
- Compute the context the selected step had access to during its
execution and display it with the `<JsonNestedNode />` component
- Ensure several steps with the same name can be displayed in order
- Prevent access to the input tab in a few cases
- Hide the input tab when the trigger node is selected as this node
takes no input
- Hide the input tab when the selected node has not been executed yet or
is currently executed
- Fallback to the Node tab when the Input tab can't be accessed

## Successful workflow execution


https://github.com/user-attachments/assets/4a2bb5f5-450c-46ed-b2d7-a14d3b1e5c1f

## Failed workflow execution


https://github.com/user-attachments/assets/3be2784e-e76c-48ab-aef5-17f63410898e

Closes https://github.com/twentyhq/core-team-issues/issues/433
2025-03-06 17:49:10 +01:00

213 lines
5.6 KiB
TypeScript

import { z } from 'zod';
// Base schemas
export const objectRecordSchema = z.record(z.any());
export const baseWorkflowActionSettingsSchema = z.object({
input: z.object({}).passthrough(),
outputSchema: z.object({}).passthrough(),
errorHandlingOptions: z.object({
retryOnFailure: z.object({
value: z.boolean(),
}),
continueOnFailure: z.object({
value: z.boolean(),
}),
}),
});
export const baseWorkflowActionSchema = z.object({
id: z.string(),
name: z.string(),
valid: z.boolean(),
});
export const baseTriggerSchema = z.object({
name: z.string().optional(),
type: z.string(),
});
// Action settings schemas
export const workflowCodeActionSettingsSchema =
baseWorkflowActionSettingsSchema.extend({
input: z.object({
serverlessFunctionId: z.string(),
serverlessFunctionVersion: z.string(),
serverlessFunctionInput: z.record(z.any()),
}),
});
export const workflowSendEmailActionSettingsSchema =
baseWorkflowActionSettingsSchema.extend({
input: z.object({
connectedAccountId: z.string(),
email: z.string(),
subject: z.string().optional(),
body: z.string().optional(),
}),
});
export const workflowCreateRecordActionSettingsSchema =
baseWorkflowActionSettingsSchema.extend({
input: z.object({
objectName: z.string(),
objectRecord: objectRecordSchema,
}),
});
export const workflowUpdateRecordActionSettingsSchema =
baseWorkflowActionSettingsSchema.extend({
input: z.object({
objectName: z.string(),
objectRecord: objectRecordSchema,
objectRecordId: z.string(),
fieldsToUpdate: z.array(z.string()),
}),
});
export const workflowDeleteRecordActionSettingsSchema =
baseWorkflowActionSettingsSchema.extend({
input: z.object({
objectName: z.string(),
objectRecordId: z.string(),
}),
});
export const workflowFindRecordsActionSettingsSchema =
baseWorkflowActionSettingsSchema.extend({
input: z.object({
objectName: z.string(),
limit: z.number().optional(),
}),
});
// Action schemas
export const workflowCodeActionSchema = baseWorkflowActionSchema.extend({
type: z.literal('CODE'),
settings: workflowCodeActionSettingsSchema,
});
export const workflowSendEmailActionSchema = baseWorkflowActionSchema.extend({
type: z.literal('SEND_EMAIL'),
settings: workflowSendEmailActionSettingsSchema,
});
export const workflowCreateRecordActionSchema = baseWorkflowActionSchema.extend(
{
type: z.literal('CREATE_RECORD'),
settings: workflowCreateRecordActionSettingsSchema,
},
);
export const workflowUpdateRecordActionSchema = baseWorkflowActionSchema.extend(
{
type: z.literal('UPDATE_RECORD'),
settings: workflowUpdateRecordActionSettingsSchema,
},
);
export const workflowDeleteRecordActionSchema = baseWorkflowActionSchema.extend(
{
type: z.literal('DELETE_RECORD'),
settings: workflowDeleteRecordActionSettingsSchema,
},
);
export const workflowFindRecordsActionSchema = baseWorkflowActionSchema.extend({
type: z.literal('FIND_RECORDS'),
settings: workflowFindRecordsActionSettingsSchema,
});
// Combined action schema
export const workflowActionSchema = z.discriminatedUnion('type', [
workflowCodeActionSchema,
workflowSendEmailActionSchema,
workflowCreateRecordActionSchema,
workflowUpdateRecordActionSchema,
workflowDeleteRecordActionSchema,
workflowFindRecordsActionSchema,
]);
// Trigger schemas
export const workflowDatabaseEventTriggerSchema = baseTriggerSchema.extend({
type: z.literal('DATABASE_EVENT'),
settings: z.object({
eventName: z.string(),
input: z.object({}).passthrough().optional(),
outputSchema: z.object({}).passthrough(),
objectType: z.string().optional(),
}),
});
export const workflowManualTriggerSchema = baseTriggerSchema.extend({
type: z.literal('MANUAL'),
settings: z.object({
objectType: z.string().optional(),
outputSchema: z.object({}).passthrough(),
}),
});
export const workflowCronTriggerSchema = baseTriggerSchema.extend({
type: z.literal('CRON'),
settings: z.discriminatedUnion('type', [
z.object({
type: z.literal('HOURS'),
schedule: z.object({ hour: z.number(), minute: z.number() }),
outputSchema: z.object({}).passthrough(),
}),
z.object({
type: z.literal('MINUTES'),
schedule: z.object({ minute: z.number() }),
outputSchema: z.object({}).passthrough(),
}),
z.object({
type: z.literal('CUSTOM'),
pattern: z.string(),
outputSchema: z.object({}).passthrough(),
}),
]),
});
// Combined trigger schema
export const workflowTriggerSchema = z.discriminatedUnion('type', [
workflowDatabaseEventTriggerSchema,
workflowManualTriggerSchema,
workflowCronTriggerSchema,
]);
// Step output schemas
const workflowExecutorOutputSchema = z.object({
result: z.any().optional(),
error: z.string().optional(),
});
export const workflowRunOutputStepsOutputSchema = z.record(
workflowExecutorOutputSchema,
);
// Final workflow run output schema
export const workflowRunOutputSchema = z.object({
flow: z.object({
trigger: workflowTriggerSchema,
steps: z.array(workflowActionSchema),
}),
stepsOutput: workflowRunOutputStepsOutputSchema.optional(),
error: z.string().optional(),
});
export const workflowRunContextSchema = z.record(z.any());
export const workflowRunSchema = z
.object({
__typename: z.literal('WorkflowRun'),
id: z.string(),
workflowVersionId: z.string(),
output: workflowRunOutputSchema.nullable(),
context: workflowRunContextSchema.nullable(),
createdAt: z.string(),
deletedAt: z.string().nullable(),
endedAt: z.string().nullable(),
name: z.string(),
})
.passthrough();