feat(workflow): surface manual-trigger payload + metadata in variable picker (#21692)
## Summary Step 2 of the manual-trigger output schema restructuring (expand → display → migrate → contract). Builds on the now-merged #21676 (which expanded the runtime payload to serve `payload` and `metadata` siblings at the trigger root). This PR **surfaces** those in the variable picker as nested, expandable nodes: - `trigger.payload.{record fields}` — the record(s) that triggered the run - `trigger.metadata.workspaceMemberId` — who triggered it The flat root fields (`trigger.id`, etc.) remain available, so existing saved variable references keep working until a later migration phase moves them. ### Changes - **twenty-shared**: metadata/payload label constants + `build-manual-trigger-metadata-node` util + barrel exports. - **twenty-front**: `computeStepOutputSchema` MANUAL branch now nests `payload` (RecordNode for SINGLE_RECORD, array Node for BULK_RECORDS, omitted for GLOBAL) and `metadata`; `ManualTriggerOutputSchema` type updated to `{ payload?; metadata }`. - **twenty-server**: `computeTriggerOutputSchemaFromAvailability` mirrors the same nested shape for server-side validation. The key is `metadata` (not `_metadata`) — custom fields can't start with `_`, so collision risk was deemed acceptable. ## Test plan - [x] `npx nx build twenty-shared` - [x] `computeStepOutputSchema` unit tests pass (55) - [x] Manual: create a manual-trigger workflow (GLOBAL / single-record / bulk), confirm the picker shows `payload` and `metadata` as expandable folders and that selecting a field yields `{{trigger.payload.<field>}}` / `{{trigger.metadata.workspaceMemberId}}` > Note: server typecheck has pre-existing unrelated failures on main (Stripe billing mocks, gmail mocks); none touch workflow files. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21692?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
+37
-9
@@ -4,12 +4,16 @@ import { isString } from '@sniptt/guards';
|
||||
import { isDefined, isValidVariable } from 'twenty-shared/utils';
|
||||
import {
|
||||
BaseOutputSchemaV2,
|
||||
buildManualTriggerMetadataNode,
|
||||
BulkRecordsAvailability,
|
||||
extractRawVariableNamePart,
|
||||
GlobalAvailability,
|
||||
navigateOutputSchemaProperty,
|
||||
SingleRecordAvailability,
|
||||
TRIGGER_STEP_ID,
|
||||
WORKFLOW_TRIGGER_METADATA_KEY,
|
||||
WORKFLOW_TRIGGER_PAYLOAD_KEY,
|
||||
WORKFLOW_TRIGGER_PAYLOAD_LABEL,
|
||||
WorkflowActionType,
|
||||
} from 'twenty-shared/workflow';
|
||||
|
||||
@@ -291,14 +295,28 @@ export class WorkflowSchemaWorkspaceService {
|
||||
workspaceId: string;
|
||||
}): Promise<OutputSchema> {
|
||||
if (availability.type === 'GLOBAL') {
|
||||
return {};
|
||||
return {
|
||||
[WORKFLOW_TRIGGER_METADATA_KEY]: buildManualTriggerMetadataNode(),
|
||||
};
|
||||
}
|
||||
|
||||
if (availability.type === 'SINGLE_RECORD') {
|
||||
return this.computeRecordOutputSchema({
|
||||
const recordOutputSchema = await this.computeRecordOutputSchema({
|
||||
objectType: availability.objectNameSingular,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const payload: Node = {
|
||||
isLeaf: false,
|
||||
type: 'object',
|
||||
label: WORKFLOW_TRIGGER_PAYLOAD_LABEL,
|
||||
value: recordOutputSchema,
|
||||
};
|
||||
|
||||
return {
|
||||
[WORKFLOW_TRIGGER_PAYLOAD_KEY]: payload,
|
||||
[WORKFLOW_TRIGGER_METADATA_KEY]: buildManualTriggerMetadataNode(),
|
||||
};
|
||||
}
|
||||
|
||||
if (availability.type === 'BULK_RECORDS') {
|
||||
@@ -308,15 +326,25 @@ export class WorkflowSchemaWorkspaceService {
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return {
|
||||
[objectMetadataInfo.flatObjectMetadata.namePlural]: {
|
||||
label: objectMetadataInfo.flatObjectMetadata.labelPlural,
|
||||
isLeaf: true,
|
||||
type: 'array',
|
||||
value:
|
||||
'Array of ' + objectMetadataInfo.flatObjectMetadata.labelPlural,
|
||||
const payload: Node = {
|
||||
isLeaf: false,
|
||||
type: 'object',
|
||||
label: WORKFLOW_TRIGGER_PAYLOAD_LABEL,
|
||||
value: {
|
||||
[objectMetadataInfo.flatObjectMetadata.namePlural]: {
|
||||
label: objectMetadataInfo.flatObjectMetadata.labelPlural,
|
||||
isLeaf: true,
|
||||
type: 'array',
|
||||
value:
|
||||
'Array of ' + objectMetadataInfo.flatObjectMetadata.labelPlural,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
[WORKFLOW_TRIGGER_PAYLOAD_KEY]: payload,
|
||||
[WORKFLOW_TRIGGER_METADATA_KEY]: buildManualTriggerMetadataNode(),
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
Reference in New Issue
Block a user