Morph many to one picker (#14155)

This PR follows the multiSelect PR merged previously. It will enable
morph relation Many to One to be handled from the table, using a
singleSelect picker

Main point : I decided to change the singleSelect API to take an array
of **objectMetadataName** instead of only one to deal with both our
usecases.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Guillim
2025-10-07 17:25:11 +02:00
committed by GitHub
parent 9f9c2d85ca
commit f60817a1e1
138 changed files with 2116 additions and 1763 deletions
@@ -1 +1 @@
export const TRIGGER_STEP_ID = 'trigger';
export const TRIGGER_STEP_ID = 'trigger';
@@ -4,7 +4,9 @@ export const baseTriggerSchema = z.object({
name: z
.string()
.optional()
.describe('Human-readable name for the trigger. Optional but recommended for clarity.'),
.describe(
'Human-readable name for the trigger. Optional but recommended for clarity.',
),
type: z
.enum(['DATABASE_EVENT', 'MANUAL', 'CRON', 'WEBHOOK'])
.describe(
@@ -14,10 +16,14 @@ export const baseTriggerSchema = z.object({
.object({ x: z.number(), y: z.number() })
.optional()
.nullable()
.describe('Position coordinates for the trigger in the workflow diagram. Use (0, 0) for the trigger step.'),
.describe(
'Position coordinates for the trigger in the workflow diagram. Use (0, 0) for the trigger step.',
),
nextStepIds: z
.array(z.string())
.optional()
.nullable()
.describe('Array of step IDs that the trigger connects to. These are the first steps in the workflow.'),
.describe(
'Array of step IDs that the trigger connects to. These are the first steps in the workflow.',
),
});
@@ -3,18 +3,26 @@ import { z } from 'zod';
export const baseWorkflowActionSchema = z.object({
id: z
.string()
.describe('Unique identifier for the workflow step. Must be unique within the workflow.'),
.describe(
'Unique identifier for the workflow step. Must be unique within the workflow.',
),
name: z
.string()
.describe('Human-readable name for the workflow step. Should clearly describe what the step does.'),
.describe(
'Human-readable name for the workflow step. Should clearly describe what the step does.',
),
valid: z
.boolean()
.describe('Whether the step configuration is valid. Set to true when all required fields are properly configured.'),
.describe(
'Whether the step configuration is valid. Set to true when all required fields are properly configured.',
),
nextStepIds: z
.array(z.string())
.optional()
.nullable()
.describe('Array of step IDs that this step connects to. Leave empty or null for the final step.'),
.describe(
'Array of step IDs that this step connects to. Leave empty or null for the final step.',
),
position: z
.object({ x: z.number(), y: z.number() })
.optional()
@@ -16,7 +16,9 @@ export const baseWorkflowActionSettingsSchema = z.object({
value: z.boolean().describe('Whether to retry the action if it fails.'),
}),
continueOnFailure: z.object({
value: z.boolean().describe('Whether to continue to the next step if this action fails.'),
value: z
.boolean()
.describe('Whether to continue to the next step if this action fails.'),
}),
}),
});
@@ -2,7 +2,9 @@ import { z } from 'zod';
import { baseWorkflowActionSchema } from './base-workflow-action-schema';
import { workflowCreateRecordActionSettingsSchema } from './create-record-action-settings-schema';
export const workflowCreateRecordActionSchema = baseWorkflowActionSchema.extend({
type: z.literal('CREATE_RECORD'),
settings: workflowCreateRecordActionSettingsSchema,
});
export const workflowCreateRecordActionSchema = baseWorkflowActionSchema.extend(
{
type: z.literal('CREATE_RECORD'),
settings: workflowCreateRecordActionSettingsSchema,
},
);
@@ -10,9 +10,6 @@ export const workflowCreateRecordActionSettingsSchema =
.describe(
'The name of the object to create a record in. Must be lowercase (e.g., "person", "company", "task").',
),
objectRecord: objectRecordSchema
.describe(
'The record data to create.',
)
objectRecord: objectRecordSchema.describe('The record data to create.'),
}),
});
@@ -2,7 +2,9 @@ import { z } from 'zod';
import { baseWorkflowActionSchema } from './base-workflow-action-schema';
import { workflowDeleteRecordActionSettingsSchema } from './delete-record-action-settings-schema';
export const workflowDeleteRecordActionSchema = baseWorkflowActionSchema.extend({
type: z.literal('DELETE_RECORD'),
settings: workflowDeleteRecordActionSettingsSchema,
});
export const workflowDeleteRecordActionSchema = baseWorkflowActionSchema.extend(
{
type: z.literal('DELETE_RECORD'),
settings: workflowDeleteRecordActionSettingsSchema,
},
);
@@ -20,7 +20,9 @@ export const workflowFilterActionSettingsSchema =
id: z.string(),
type: z.string(),
stepOutputKey: z.string(),
operand: z.enum(ViewFilterOperand).or(z.enum(ViewFilterOperandDeprecated)),
operand: z
.enum(ViewFilterOperand)
.or(z.enum(ViewFilterOperandDeprecated)),
value: z.string(),
stepFilterGroupId: z.string(),
positionInStepFilterGroup: z.number().optional(),
@@ -1,7 +1,7 @@
import { z } from 'zod';
import { baseTriggerSchema } from './base-trigger-schema';
export const workflowManualTriggerSchema = baseTriggerSchema
export const workflowManualTriggerSchema = baseTriggerSchema
.extend({
type: z.literal('MANUAL'),
settings: z.object({
@@ -34,4 +34,4 @@ import { baseTriggerSchema } from './base-trigger-schema';
})
.describe(
'Manual trigger that can be launched by the user. If a record is selected when launched, it is accessible via {{trigger.record.fieldName}}. If no record is selected, no data context is available.',
);
);
@@ -4,7 +4,7 @@ export const objectRecordSchema = z
.record(z.string(), z.any())
.describe(
'Record data object. Use nested objects for relationships (e.g., "company": {"id": "{{reference}}"}). Common patterns:\n' +
'- Person: {"name": {"firstName": "John", "lastName": "Doe"}, "emails": {"primaryEmail": "john@example.com"}, "company": {"id": "{{trigger.object.id}}"}}\n' +
'- Company: {"name": "Acme Corp", "domainName": {"primaryLinkUrl": "https://acme.com"}}\n' +
'- Task: {"title": "Follow up", "status": "TODO", "assignee": {"id": "{{user.id}}"}}',
'- Person: {"name": {"firstName": "John", "lastName": "Doe"}, "emails": {"primaryEmail": "john@example.com"}, "company": {"id": "{{trigger.object.id}}"}}\n' +
'- Company: {"name": "Acme Corp", "domainName": {"primaryLinkUrl": "https://acme.com"}}\n' +
'- Task: {"title": "Follow up", "status": "TODO", "assignee": {"id": "{{user.id}}"}}',
);
@@ -2,7 +2,9 @@ import { z } from 'zod';
import { baseWorkflowActionSchema } from './base-workflow-action-schema';
import { workflowUpdateRecordActionSettingsSchema } from './update-record-action-settings-schema';
export const workflowUpdateRecordActionSchema = baseWorkflowActionSchema.extend({
type: z.literal('UPDATE_RECORD'),
settings: workflowUpdateRecordActionSettingsSchema,
});
export const workflowUpdateRecordActionSchema = baseWorkflowActionSchema.extend(
{
type: z.literal('UPDATE_RECORD'),
settings: workflowUpdateRecordActionSettingsSchema,
},
);
@@ -6,12 +6,14 @@ export const workflowRunStateStepInfoSchema = z.object({
error: z.string().optional(),
status: workflowRunStepStatusSchema,
get history() {
return z.array(
workflowRunStateStepInfoSchema.pick({
result: true,
status: true,
error: true,
})
).optional();
}
return z
.array(
workflowRunStateStepInfoSchema.pick({
result: true,
status: true,
error: true,
}),
)
.optional();
},
});
@@ -1,6 +1,6 @@
import { type workflowRunStateStepInfoSchema } from "@/workflow/schemas/workflow-run-state-step-info-schema";
import { type workflowRunStateStepInfosSchema } from "@/workflow/schemas/workflow-run-state-step-infos-schema";
import type z from "zod";
import { type workflowRunStateStepInfoSchema } from '@/workflow/schemas/workflow-run-state-step-info-schema';
import { type workflowRunStateStepInfosSchema } from '@/workflow/schemas/workflow-run-state-step-infos-schema';
import type z from 'zod';
export enum StepStatus {
NOT_STARTED = 'NOT_STARTED',
@@ -11,6 +11,10 @@ export enum StepStatus {
PENDING = 'PENDING',
}
export type WorkflowRunStepInfo = z.infer<typeof workflowRunStateStepInfoSchema>
export type WorkflowRunStepInfo = z.infer<
typeof workflowRunStateStepInfoSchema
>;
export type WorkflowRunStepInfos = z.infer<typeof workflowRunStateStepInfosSchema>;
export type WorkflowRunStepInfos = z.infer<
typeof workflowRunStateStepInfosSchema
>;
@@ -12,7 +12,8 @@ export const canObjectBeManagedByWorkflow = ({
'dashboard',
];
return !excludedNonSystemObjectMetadataItemNames.includes(
nameSingular,
) && !isSystem;
return (
!excludedNonSystemObjectMetadataItemNames.includes(nameSingular) &&
!isSystem
);
};