Prerequisite for the workflow-core soft-ref migration: every
`workflowVersion` content write must go through a dedicated,
draft-guarded server mutation so it can later be wrapped in a
transactional core mirror. This closes the generic-CRUD holes that let
writes bypass that path.
## Part A - dedicated `updateWorkflowVersionTrigger` mutation
The builder saved a version's trigger through generic
`updateOneWorkflowVersion` - the only content write not going through a
dedicated mutation. Added:
- Server: `updateWorkflowVersionTrigger(input: { workflowVersionId,
trigger })` resolver +
`WorkflowVersionStepWorkspaceService.updateWorkflowVersionTrigger`,
draft-guarded via `getValidatedDraftWorkflowVersion` then
`updateWorkflowVersionStepsAndTrigger` (reuses existing write logic).
- Front: `useUpdateWorkflowVersionTrigger` now calls the dedicated
mutation instead of `useUpdateOneRecord`.
## Part B - restrict generic `updateOneWorkflowVersion`
`validateWorkflowVersionForUpdateOne` previously allowed writing
`trigger`, `position`, `workflowId` (re-parenting),
`coreWorkflowVersionId`, and let `steps: null` slip through on a draft.
It now rejects any update that sets `steps`, `trigger`, `status`,
`workflowId`, or `coreWorkflowVersionId`, or that clears the `name`,
while still allowing a plain rename. (A name-only allowlist was tried
first but blocked legitimate renames - at the pre-hook the generic
update payload is not single-key - so it was replaced by this denylist,
verified live.)
## Part C - close the destroy/restore hole
`workflowVersion` had no `destroyOne/destroyMany/restoreOne/restoreMany`
query hooks, so a caller with object permission could hard-destroy any
version (including active) or resurrect one with no validation. Added
pre-hooks that forbid all four via the API ("Method not allowed"),
matching the existing forbidden generic mutations (`createOne`,
`deleteMany`, ...). Rationale: there is no legitimate API use for
standalone version destroy/restore - retention purging happens through
the trash-cleanup cron (internal, not hook-gated) and restore happens
through the workflow-restore cascade or create-draft-from-version.
## Tests
Integration specs that set a trigger through the generic mutation were
migrated to the new `updateWorkflowVersionTrigger` mutation (new
`update-workflow-version-trigger.util.ts`). Unit test for the front hook
updated.
## Verification
- `twenty-server` + `twenty-front` typecheck: clean.
- oxlint + oxfmt on all changed files: clean.
- `graphql.ts` regenerated for the new mutation; its types match the
server DTOs exactly. Local `graphql:generate` introspects a running
server, so it only succeeds against a server built from this branch - CI
regenerates against the PR server and verifies.
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23207?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. -->
---------
Co-authored-by: prastoin <paul@twenty.com>
https://github.com/user-attachments/assets/5a25396f-8959-4bd8-93cb-1187559ffe5f
## Summary
Two workflow-run improvements, with all non-trivial logic isolated in
pure, unit-tested utils.
### 1. Idempotent stop
`stopWorkflowRun` no longer throws when a run is already in a terminal
status (`COMPLETED` / `FAILED` / `STOPPED`) or already `STOPPING`; it
returns the run unchanged. This fixes:
- bulk stop aborting on the first non-stoppable run in a
mixed/select-all selection,
- the click-vs-processing race on a single run (run finishes between
click and mutation).
It also releases the cached not-started throttle slot when stopping a
`NOT_STARTED` run (prevents counter drift), and ends runs with no
`state` directly.
### 2. Retry a failed run from the failing step
New `retryWorkflowRun` mutation (same guards/passthrough as
`stopWorkflowRun`). It resets the failed step(s) to `NOT_STARTED`, flips
the run to `RUNNING`, and enqueues a `RunWorkflowJob` with the steps to
re-execute; downstream execution and status computation are unchanged.
Logic lives in pure utils:
- `build-retry-step-infos.util.ts` - decides per failed step what to
reset; delegates iterator-specific logic to
`build-retry-iterator-step-infos.util.ts` (an iterator that failed
mid-loop is restored to `RUNNING` with cursor preserved, an iterator
that failed itself restarts its whole loop).
- `get-runnable-step-ids.util.ts` - reuses the executor's
`shouldExecuteStep` to also resume branches that never started (avoids
hangs), excluding loop-interior steps.
The service method only orchestrates; the job's status check is a race
guard (retriability is enforced in the service before enqueue).
A "Retry" command menu item surfaces only for `FAILED` runs
(`someEquals(selectedRecords, "status", "FAILED")`).
### 3. Keep the run diagram visible across regenerations
The run diagram is regenerated on every run state change, producing
fresh nodes without the dimensions Reactflow had measured. Reactflow
hides unmeasured nodes until it re-measures them, so the diagram could
flicker and disappear when the last regeneration before going idle left
nodes unmeasured (reproducible after retrying a failed run). The
regenerated nodes now carry over the previously measured dimensions (by
id) so they stay rendered.
## Test plan
- [x] Unit tests for both retry utils (9 cases: plain failed step,
non-failed untouched, iterator mid-loop restore, iterator self-failure,
frontier parent gating, entry steps, loop-interior exclusion, parallel
branches)
- [x] `twenty-server` + `twenty-front` typecheck
- [x] `lint:diff-with-main` clean for both packages
- [x] Manual: retry a failed run repeatedly and confirm the diagram
stays visible
- [ ] Manual: stop a COMPLETED/mixed selection (no error), retry a
failed run and confirm it resumes from the failing step
# Introduction
This PR is a followup of https://github.com/twentyhq/twenty/pull/20673
It aims to unify the authentication/permissions layer with all the
connectedAccount interactions across the application
## Deprecate
- findAll
- findById
## Email sync
An user can only sync the message of his own connected account
## Workflow email
- Related https://github.com/twentyhq/private-issues/issues/478
- Only reauthorize owned account
Avoid fetching full steps and trigger for versions that are not the
current version. Because those won't be used anyway. Better for
performances.
Only difficulty was for the `createDraft` mutation. I needed to return
the full created version so I can store it in cache and use it as new
`currentVersion`. Otherwise the current version is considered as
incomplete for a short time, since workflow is fetched separately from
the current version.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This PR should fix optimistic rendering issues on step updates:
- compute a diff for trigger and steps on mutations
- build an util that applies that diff (built a more robust version from
https://github.com/AsyncBanana/micropatch)
- apply diff in cache
Currently, when filling the form, values are not saved in the action
settings. This is an issue because we do not see the response in the
node settings, only in the output of the step.
This PR:
- adds a new endpoint to update a step in the run flow output
- updates this flow when a step is updated
https://github.com/user-attachments/assets/2e74a010-a0d2-4b87-bd1f-1c91f7ca6b60
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
- remove delete serverless function when archiving workflow version
- update copy serverless function to reset serverless function to old
version
- remove createNewWorkflowVersion and use createDraftFromWorkflowVersion
- fix step update issue and optimistic rendering when generate draft
from active version
- In the `formatFieldMetadataValue` function, allow people to call
TypeORM's `save()` method with unserialized JSON data.
- Create an `overrideWorkflowDraftVersion` mutation that takes a
workflow id and the id of the workflow version to use as the new draft
- If no draft exists yet, create one
- If a draft already exists, deactivate its serverless functions
- Duplicate every step. For serverless function steps, it includes
duplicating the functions
- Save the data of the step in DB
- Call the `overrideWorkflowDraftVersion` mutation in the old workflow
header and in the new Cmd+K actions
- I chose to not update the Apollo cache manually as the information of
the new draft are going to be automatically fetched once the user lands
on the workflow's show page. Note that we redirect the user to this page
after overriding the draft version.
- create a serverless function when creating a new workflow code step
- add code editor in workflow code step
- move workflowVersion steps management from frontend to backend
- add a custom resolver for workflow-version management
- fix optimistic rendering on frontend
- fix css
- delete serverless function when deleting workflow code step
TODO
- Don't update serverlessFunction if no code change
- Factorize what can be between crud trigger and crud step
- Publish serverless version when activating workflow
- delete serverless functions when deleting workflow or workflowVersion
- fix optimistic rendering for code updates
- Unify CRUD types
<img width="1279" alt="image"
src="https://github.com/user-attachments/assets/3d97ee9f-4b96-4abc-9d36-5c0280058be4">
## Setup
This PR can be tested only if some feature flags have specific values:
- `IsWorkflowEnabled` equals `true`
- `IsQueryRunnerTwentyORMEnabled` equals `false`
These feature flags weren't committed to don't break other branches.
## What this PR brings
- Display buttons to activate and deactivate a workflow version and a
button to discard the current draft version. I also scaffolded a "Test"
button, which doesn't do anything for now.
- Wired the activate, deactivate and discard draft buttons to the
backend.
- Made it possible to "edit" active and deactivated versions by
automatically creating a new draft version when the user tries to edit
the version.
- Hide the "Discard Draft", button if the current version is not a draft
or is the first version ever created.
- On the backend, don't consider discarded drafts when checking if a new
draft version can be created.
- On the backend, disallow deleting the first created workflow version.
Otherwise, we will end up with a blank canvas in the front end, and it
will be impossible to recover from it.
- On the backend, disallow running deactivation steps if the workflow
version is not currently active. Previously, we were throwing, which is
unnecessary as it's a valid case.
## Spotted bugs that we must dive into
### Duplicate workflow versions in Apollo cache
https://github.com/user-attachments/assets/7cfffd06-11e0-417a-8da0-f9a5f43b84e2
---------
Co-authored-by: Charles Bochet <charles@twenty.com>