Files
twenty/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts

316 lines
8.6 KiB
TypeScript

import { FieldMetadataType } from 'twenty-shared/types';
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(),
nextStepIds: z.array(z.string()).optional().nullable(),
});
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(),
filter: z
.object({
recordFilterGroups: z.array(z.object({})).optional(),
recordFilters: z.array(z.object({})).optional(),
gqlOperationFilter: z.object({}).optional().nullable(),
})
.optional(),
}),
});
export const workflowFormActionSettingsSchema =
baseWorkflowActionSettingsSchema.extend({
input: z.array(
z.object({
id: z.string(),
name: z.string(),
label: z.string(),
type: z.union([
z.literal(FieldMetadataType.TEXT),
z.literal(FieldMetadataType.NUMBER),
z.literal(FieldMetadataType.DATE),
z.literal('RECORD'),
]),
placeholder: z.string().optional(),
settings: z.record(z.any()).optional(),
value: z.any().optional(),
}),
),
});
export const workflowHttpRequestActionSettingsSchema =
baseWorkflowActionSettingsSchema.extend({
input: z.object({
url: z.string(),
method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
headers: z.record(z.string()).optional(),
body: z
.record(
z.union([
z.string(),
z.number(),
z.boolean(),
z.null(),
z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])),
]),
)
.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,
});
export const workflowFormActionSchema = baseWorkflowActionSchema.extend({
type: z.literal('FORM'),
settings: workflowFormActionSettingsSchema,
});
export const workflowHttpRequestActionSchema = baseWorkflowActionSchema.extend({
type: z.literal('HTTP_REQUEST'),
settings: workflowHttpRequestActionSettingsSchema,
});
// Combined action schema
export const workflowActionSchema = z.discriminatedUnion('type', [
workflowCodeActionSchema,
workflowSendEmailActionSchema,
workflowCreateRecordActionSchema,
workflowUpdateRecordActionSchema,
workflowDeleteRecordActionSchema,
workflowFindRecordsActionSchema,
workflowFormActionSchema,
workflowHttpRequestActionSchema,
]);
// 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(),
fields: z.array(z.string()).optional().nullable(),
}),
});
export const workflowManualTriggerSchema = baseTriggerSchema.extend({
type: z.literal('MANUAL'),
settings: z.object({
objectType: z.string().optional(),
outputSchema: z.object({}).passthrough(),
icon: z.string().optional(),
}),
});
export const workflowCronTriggerSchema = baseTriggerSchema.extend({
type: z.literal('CRON'),
settings: z.discriminatedUnion('type', [
z.object({
type: z.literal('DAYS'),
schedule: z.object({
day: z.number().min(1),
hour: z.number().min(0).max(23),
minute: z.number().min(0).max(59),
}),
outputSchema: z.object({}).passthrough(),
}),
z.object({
type: z.literal('HOURS'),
schedule: z.object({
hour: z.number().min(1),
minute: z.number().min(0).max(59),
}),
outputSchema: z.object({}).passthrough(),
}),
z.object({
type: z.literal('MINUTES'),
schedule: z.object({ minute: z.number().min(1) }),
outputSchema: z.object({}).passthrough(),
}),
z.object({
type: z.literal('CUSTOM'),
pattern: z.string(),
outputSchema: z.object({}).passthrough(),
}),
]),
});
export const workflowWebhookTriggerSchema = baseTriggerSchema.extend({
type: z.literal('WEBHOOK'),
settings: z.discriminatedUnion('httpMethod', [
z.object({
outputSchema: z.object({}).passthrough(),
httpMethod: z.literal('GET'),
authentication: z.literal('API_KEY').nullable(),
}),
z.object({
outputSchema: z.object({}).passthrough(),
httpMethod: z.literal('POST'),
expectedBody: z.object({}).passthrough(),
authentication: z.literal('API_KEY').nullable(),
}),
]),
});
// Combined trigger schema
export const workflowTriggerSchema = z.discriminatedUnion('type', [
workflowDatabaseEventTriggerSchema,
workflowManualTriggerSchema,
workflowCronTriggerSchema,
workflowWebhookTriggerSchema,
]);
// Step output schemas
export const workflowExecutorOutputSchema = z.object({
result: z.any().optional(),
error: z.string().optional(),
pendingEvent: z.boolean().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 workflowRunStatusSchema = z.enum([
'NOT_STARTED',
'RUNNING',
'COMPLETED',
'FAILED',
]);
export const workflowRunSchema = z
.object({
__typename: z.literal('WorkflowRun'),
id: z.string(),
workflowVersionId: z.string(),
workflowId: z.string(),
output: workflowRunOutputSchema.nullable(),
context: workflowRunContextSchema.nullable(),
status: workflowRunStatusSchema,
createdAt: z.string(),
deletedAt: z.string().nullable(),
endedAt: z.string().nullable(),
name: z.string(),
})
.passthrough();