[Workflows] Allow iterator to take whole item as variable#22031
[Workflows] Allow iterator to take whole item as variable#22031ijreilly wants to merge 2 commits into
Conversation
When using the iterator node, the variable picker only let you drill into a field of the item being iterated on (currentItem). You can now select the whole currentItem object directly from the variable dropdown.
|
👋 Thanks for contributing to Twenty! Your PR has been set to draft while you work on it. Once you're done, mark it as Ready for review and our automated checks will run. Looking forward to your contribution! |
There was a problem hiding this comment.
Pull request overview
This PR improves workflow iterator ergonomics by enabling users to (1) select the entire currentItem inside iterator loops and (2) iterate over a Code/Logic Function step that returns a top-level array by exposing a “Whole list” option and inferring the per-item schema.
Changes:
- Add flattened top-level array detection utilities (
isFlattenedArrayOutputSchema,getCurrentItemSchemaFromFlattenedArrayOutputSchema) and export them viatwenty-shared/workflow. - Update output-schema variable search and iterator item-schema inference so
{{stepId}}can represent a whole-list array for flattened outputs. - Update the variable picker UI to offer “Use the whole item” (iterator
currentItem) and “Whole list” (flattened top-level array outputs), plus add/adjust tests and Code Action input typing for arrays.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/twenty-shared/src/workflow/workflow-schema/utils/search-variable-in-output-schema.ts | Recognizes {{stepId}} as an array when the Code step output schema is a flattened top-level array. |
| packages/twenty-shared/src/workflow/workflow-schema/utils/flattened-array-output-schema.ts | Introduces detection + “current item schema” extraction for flattened array-shaped output schemas. |
| packages/twenty-shared/src/workflow/workflow-schema/utils/tests/search-variable-in-output-schema.iterator.test.ts | Adds coverage for selecting {{step.currentItem}} as a whole item. |
| packages/twenty-shared/src/workflow/workflow-schema/utils/tests/search-variable-in-output-schema.code.test.ts | Adds coverage for flattened top-level array behavior ({{stepId}} as ARRAY, indexed access still works). |
| packages/twenty-shared/src/workflow/workflow-schema/utils/tests/flattened-array-output-schema.test.ts | Adds direct tests for flattened-array detection and current-item schema extraction. |
| packages/twenty-shared/src/workflow/workflow-schema/index.ts | Re-exports the new flattened-array utilities from the workflow-schema barrel. |
| packages/twenty-shared/src/workflow/index.ts | Re-exports the new utilities from the top-level workflow barrel for cross-package consumption. |
| packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts | Allows iterators to infer item schema from a variable targeting the whole step output ({{stepId}}) when it’s a flattened array schema. |
| packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx | Adds UI entries for “Use the whole item” (iterator currentItem) and “Whole list” (flattened top-level arrays). |
| packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsLeafKind.ts | Adds an 'array' leaf kind for code-action input schema properties typed as arrays. |
| packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/tests/getWorkflowCodeFieldsLeafKind.test.ts | Adds tests for primitive arrays mapping to the new 'array' kind. |
| packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFieldLeaf.tsx | Renders the new array input component when the schema property is an array. |
| packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFieldArrayInput.tsx | New component for editing array-typed code-action inputs (JSON array or standalone variable). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Renders the current value as the JSON string the user edits. Arrays are | ||
| // stringified; a standalone variable (or any legacy raw string) is shown as-is | ||
| // so it can be re-parsed on the next edit. | ||
| const getStringifiedDefaultValue = (value: unknown): string | null => { |
There was a problem hiding this comment.
this already exists in the code base. I'm surprised we are re-inventing the wheel here
(also use typeguards isNonEmptyString...)
| return; | ||
| } | ||
|
|
||
| let parsedValue: unknown; |
There was a problem hiding this comment.
should be extracted in a private function to avoid using "let" which is side effect prone
🔍 Visual Regression Review —
|
| Story | Verdict | Confidence | Explained by | |
|---|---|---|---|---|
| 🟡 | ui-input-imageinput-imageinput--with-picture |
uncertain | 82% | — |
| 🟡 | modules-auth-logo--with-primary-and-secondary-logo |
uncertain | 82% | — |
| 🟡 | modules-auth-logo--with-primary-logo-and-placeholder |
uncertain | 82% | — |
| 🟡 | ui-data-field-input-datetimefieldinput--click-outside |
uncertain | 78% | — |
| 🟡 | ui-data-field-input-datetimefieldinput--enter |
uncertain | 78% | — |
| 🟡 | ui-data-field-input-datetimefieldinput--escape |
uncertain | 78% | — |
| 🟡 | ui-input-internal-internaldatepicker--default |
uncertain | 78% | — |
| 🟡 | ui-input-internal-internaldatepicker--with-time-input |
uncertain | 78% | — |
| 🟡 | ui-data-field-input-datetimefieldinput--default |
uncertain | 78% | — |
| 🟡 | ui-input-internal-internaldatepicker--with-open-year-select |
uncertain | 78% | — |
| 🟡 | modules-auth-logo--with-secondary-logo |
uncertain | 82% | — |
| 🟡 | ui-input-internal-internaldatepicker--with-open-month-select |
uncertain | 78% | — |
| 🟡 | ui-layout-dropdown-dropdownmenuheader--context-dropdown-and-avatar |
uncertain | 82% | — |
| 🟡 | ui-layout-dropdown-dropdownmenuheader--start-avatar |
uncertain | 82% | — |
| 🟡 | ui-layout-tablist-tablist--default |
uncertain | 72% | — |
| 🟡 | modules-settings-settingsdatepickerinput--changing-month-keeps-picker-open |
uncertain | 65% | — |
| 🟡 | modules-settings-settingsdatepickerinput--default |
uncertain | 65% | — |
Changed stories
| Story | Diff % |
|---|---|
| ui-input-imageinput-imageinput--with-picture | 6% |
| modules-auth-logo--with-primary-and-secondary-logo | 5% |
| modules-auth-logo--with-primary-logo-and-placeholder | 4% |
| ui-data-field-input-datetimefieldinput--click-outside | 2% |
| ui-data-field-input-datetimefieldinput--enter | 2% |
| ui-data-field-input-datetimefieldinput--escape | 2% |
| modules-auth-logo--with-secondary-logo | 1% |
| ui-layout-dropdown-dropdownmenuheader--context-dropdown-and-avatar | 1% |
| ui-layout-dropdown-dropdownmenuheader--start-avatar | 1% |
| ui-input-internal-internaldatepicker--default | 1% |
| ui-input-internal-internaldatepicker--with-time-input | 1% |
| ui-data-field-input-datetimefieldinput--default | 1% |
| ui-input-internal-internaldatepicker--with-open-year-select | 1% |
| ui-input-internal-internaldatepicker--with-open-month-select | 1% |
| ui-layout-tablist-tablist--default | 0% |
| modules-objectrecord-recordcalendar-month--default | 0% |
2 new stories
- modules-settings-settingsdatepickerinput--changing-month-keeps-picker-open
- modules-settings-settingsdatepickerinput--default
View run details · advisory mode
| @@ -80,6 +83,61 @@ export const WorkflowVariablesDropdownStepItems = ({ | |||
| ); | |||
| }; | |||
|
|
|||
| // The iterator exposes the element currently being iterated on as `currentItem`. | |||
| // When it is an object we let the user select the whole item, not only one of its fields. | |||
| const iteratorCurrentItemNode = isIteratorOutputSchema( | |||
There was a problem hiding this comment.
not a fan that we are adding this edge case in the main flow + that seems hard to maintain (complex boolean exptression, ternary) I would find a more scalable pattern + extract this to an util that can be easily tested.
|
|
||
| const variableTargetsWholeStepOutput = propertyPath.length === 0; | ||
|
|
||
| if (variableTargetsWholeStepOutput) { |
There was a problem hiding this comment.
I'm struggling to understand this code, why can't we infer from step.settings anymore? why do we fallback to DEFAULT_ITERATOR_CURRENT_ITEM ?
|
|
||
| describe('isFlattenedArrayOutputSchema', () => { | ||
| it('should return true for sequential numeric keys', () => { | ||
| const schema: BaseOutputSchemaV2 = { |
There was a problem hiding this comment.
this is surprising to me: this doesn't seem to be a "flattened array" but an object
There was a problem hiding this comment.
i guess this was already in place in the code for arrays returned by iterator?
| }; | ||
|
|
||
| export const getCurrentItemSchemaFromFlattenedArrayOutputSchema = ( | ||
| schema: BaseOutputSchemaV2 | undefined, |
There was a problem hiding this comment.
I recommend not calling utils with undefined and then support it through the utils. The best with typescript is to kill branches so the caller should not call this util at all.
getCurrentItemSchemaFromFlattenedArrayOutputSchema(schema: BaseOutputSchemaV2)
also when a function take multiple parameters, always pass an object, this enable to name parameters and eases devXP
Select the whole item in iterator loops, and iterate over a step's array output
Summary
Two related improvements to working with lists in workflows:
Together these complete the loop ergonomics: select a list → iterate → reference the current item (whole or by field) downstream — matching the model used by tools like Windmill.
What changed
Risks for existing workflows
None expected. The change is purely additive: