Skip to content

[Workflows] Allow iterator to take whole item as variable#22031

Open
ijreilly wants to merge 2 commits into
mainfrom
feat/iterator-whole-item
Open

[Workflows] Allow iterator to take whole item as variable#22031
ijreilly wants to merge 2 commits into
mainfrom
feat/iterator-whole-item

Conversation

@ijreilly

@ijreilly ijreilly commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

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:

  • Pick the current item as a whole inside an iterator loop. Previously, in a node inside the loop, you could only reference individual fields of the Iterator's current item. Now you can select the whole item (e.g. a full record) — useful for passing it straight into a downstream step.
Screenshot 2026-06-23 at 17 02 47 - Iterate over a step's array output. A Code / Logic Function step that returns a top-level array couldn't be fed to the Iterator: its output was flattened into indexed entries (0, 1, …) with no way to select the array as a whole. A new "Whole list" option selects the step's entire output, and the Iterator infers the per-iteration item shape from it. Screenshot 2026-06-23 at 17 17 53

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

  • The variable picker offers a "Use the whole item" option when viewing an iterator's current item, and a "Whole list" option when a step returns a top-level array.
  • The Iterator's current-item schema can now be inferred from a variable pointing at a step's whole output.

Risks for existing workflows

None expected. The change is purely additive:

  • No DB migration and no change to how output schemas are stored or read — existing schemas, variables, and iterators behave identically.
  • No change to runtime variable resolution; existing {{step.field}} and current-item references are untouched.
  • The new options only apply to new selections (whole item / whole list); all existing paths take the unchanged code path.
  • The only edge case: array detection is heuristic (an output whose keys are exactly 0…n-1), so an object that happens to have those keys would also show "Whole list". This is rare for real outputs, affects nothing unless a user selects it, and fails safe — the Iterator validates its input and throws a clear "items must be an array" error if a non-array is passed.

Review in cubic

ijreilly added 2 commits June 22, 2026 13:50
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.
Copilot AI review requested due to automatic review settings June 23, 2026 15:19
@twenty-ci-bot-public

Copy link
Copy Markdown

👋 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!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 via twenty-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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 13 files

Re-trigger cubic

// 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 => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be extracted in a private function to avoid using "let" which is side effect prone

@twenty-ci-bot-public

Copy link
Copy Markdown

🔍 Visual Regression Review — twenty-front

✅ 17 visual change(s) reviewed — all explained by this PR.

Changed: 16 · Added: 2 · Removed: 0 · Unchanged: 662

17 item(s) to double-check (uncertain / low confidence)
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(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is surprising to me: this doesn't seem to be a "flattened array" but an object

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess this was already in place in the code for arrays returned by iterator?

};

export const getCurrentItemSchemaFromFlattenedArrayOutputSchema = (
schema: BaseOutputSchemaV2 | undefined,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@charlesBochet charlesBochet left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left comments!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants