Skip to content

feat(flows): try a flow against an object without effect (dry-run) - #4753

Merged
rubenvdlinde merged 1 commit into
developmentfrom
feature/flow-dry-run
Oct 21, 2026
Merged

rubenvdlinde merged 1 commit into
developmentfrom
feature/flow-dry-run

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Adds a way to try a flow against a chosen object without running it for real. It powers dossiq's "Testen met een zaak" button on the flow page (board DqFlow). Please don't merge yet; this is up for review.

The endpoint

POST /api/flows/{id}/dry-run takes {register, schema, objectId}. It walks the flow's graph through the real FlowEngine, but with a PreviewStepDispatcher in place of RegistryStepDispatcher. Branches are therefore decided exactly as in a real run. The answer lists the steps the engine reached, in order. Each step carries nodeId, type, label, status, itemsIn, itemsOut and a preview, and the answer ends with the run's end status.

What a dry-run performs and what it only describes

  • Only nodes that declare the new IFlowSideEffectFree interface are performed: set-fields, switch, route, filter, merge, explode, end and the three triggers. Performing them keeps the items that later conditions read the same as in a real run.
  • Every other step is described and not performed. A node that implements the new IFlowNodePreview interface says what it would do:
    • object-write reports its operation and the fields it would set;
    • send-email, send-notification and send-talk-message report their recipients and text, composed by FlowMessagePreview with the same recipient resolution and templating a real send uses.
  • Any other node is reported as a step that would run.

Guarantees and access

  • Nothing is stored or sent. The marking lives on an unsaved FlowRun, no run row is queued, and the object is read with the caller's own RBAC and without a read audit entry (_audit: false).
  • The endpoint is gated by the flow.update right. It answers 401 without a session, 403 without the right, 400 without an object, and 404 for a flow or object the caller cannot reach.
  • It lives in a separate FlowDryRunController because FlowController's constructor is already at its limit.

Refactor

resolveRecipients moves from FlowMessagingService into FlowRecipientAddresses, unchanged, so the send path and the preview share it. This also keeps FlowMessagingService under phpmd's class-length limit.

Tests

  • New: PreviewStepDispatcherTest, FlowDryRunServiceTest (real engine, a switch flow tried against two objects), FlowDryRunControllerTest, two previews in FlowMessagingServiceTest, and one in ObjectWriteNodeTest.
  • The Flow unit suite gives the same result as development: 10 failures in FlowMessagingServiceExternalRecipientsTest, all of which also fail on development in a standalone checkout.
  • phpcs, phpstan and phpmd are clean on the changed files.

Spec

openspec/changes/flow-dry-run.

Written with Claude Code (claude-opus-5-5).

🤖 Generated with Claude Code

POST /api/flows/{id}/dry-run {register, schema, objectId} walks the flow
through the real engine with a preview dispatcher. Steps that declare
IFlowSideEffectFree (set-fields, switch, route, filter, merge, explode,
end, the triggers) run, so branches are decided as in a run; every other
step is described and not performed. object-write previews its fields;
send-email, send-notification and send-talk-message preview their
composed recipients and text (FlowMessagePreview, recipients resolved
through FlowRecipientAddresses, which now owns resolveRecipients). The
marking lives on an unsaved run record, the object is read with the
caller's RBAC and without a read audit, and nothing is sent. Gated by the
flow.update right.

Assisted-by: ClaudeCode:claude-opus-5-5
@rubenvdlinde
rubenvdlinde merged commit 5697761 into development Oct 21, 2026
44 of 48 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 2d552b5

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-specs ✅
test-l10n ✅
test-l10n-parity ✅
format ✅
check-schema-l10n ✅
composer ✅ ✅ 173/173
npm ✅ ✅ 648/648
app:check-code ⏭️
info.xml ✅
REUSE ✅
lockfile sync ✅
PHPUnit ❌
Newman ❌
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-21 17:18 UTC

Download the full PDF report from the workflow artifacts.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant