feat(flow): visual flow builder engine — event catalog, NC Flow interop, object-CRUD actions - #2042
Merged
Merged
Conversation
added 6 commits
July 23, 2026 09:04
…r + NC-Flow interop Visual builder over the existing x-openregister-flows contract (CnGraphCanvas), an event-catalog trigger model (object-CRUD → all NC events), object-CRUD action nodes, and bidirectional Nextcloud Flow interop (NC operations as blocks; OR flows exposed as an IOperation). Phased tasks 1–5.
Add EventCatalogService as the single source of truth for flow triggers, exposed via GET /api/flow/event-catalog for the visual builder. Extends the trigger set past created/updated/deleted to the object lifecycle events OpenRegister already dispatches — locked, unlocked, reverted and state-transitioned — routed to the flow runner by a new EventCatalogListener (create/update/delete stay with FlowActionListener to avoid double-firing). FlowActionService now matches a flow's trigger via EventCatalogService:: aliasesFor(), so the canonical ids (object.created) and the pre-catalog aliases (created) are interchangeable and existing flows keep firing unchanged. Every catalog id is a real, dispatched, object-carrying event — no declared-but-never-fired triggers. Endpoint live-verified on 8090.
Register OpenRegister objects as a Nextcloud Flow (workflowengine) entity and expose 'Run an OpenRegister flow' as a Flow operation, so the visual flow builder and native Flow compose both ways: - RegisterObjectEntity (IEntity): object created/updated/deleted become triggers in the Flow rule builder, reusing OR's own dispatched event classes so the engine wires them automatically. - RunFlowOperation (ISpecificOperation, scoped to that entity): runs a named flow declared on the object's schema when a rule matches. - FlowActionService::runNamedFlow() runs one flow's actions by name, ignoring its trigger, since the Flow rule already gated it — this is non-redundant with the always-on FlowActionListener because the admin can add Flow's checks (confidentiality, register, …) to decide when. Registration guarded by class_exists so boot never fatals without the soft-dependency workflowengine app. Live-verified on 8090: 'Run an OpenRegister flow' appears in the Flow admin UI and the trigger dropdown lists OpenRegister object created/updated/deleted. (Full rule->fire->action e2e not yet run.) Note: Phase 3a (invoking arbitrary NC Flow operations from our builder) is intentionally not built — OCP\WorkflowEngine exposes no public invoke-operation API, so it could only be a dead block.
Extend the flow engine's action set beyond notifications to act on the object graph: - object.set-field / object.update: merge templated fields onto the triggering object and persist (PUT-semantic — existing fields carried forward so nothing is dropped). - object.create: create a new object in a target register/schema. - object.delete: delete the triggering object (or a target UUID). - condition: a guard that halts the flow when its expression is false (operators eq/ne/empty/notEmpty/contains); runAction now returns bool and the action loop breaks on a failed condition. Guarded against unbounded recursion: an object-CRUD write re-dispatches a lifecycle event back into run(); an activeObjects UUID set makes the re-entry a no-op (try/finally) so a flow that writes its own object can't loop. FlowActionService now depends on ObjectService; @register/@Schema added to the template context. Live-verified on 8090: an 'updated' flow with object.set-field stamped a Case title on update, persisted, and did NOT loop; an invalid value was correctly rejected by schema validation and logged without corrupting the write.
Mark Phases 1, 2, 4 complete and 3b done (all live-verified on 8090); record Phase 3a as intentionally deferred (no public NC WorkflowEngine invoke API). openspec validate: change is valid.
…or-UI follow-up A global NC Flow rule (OR object updated -> Run flow 'NC stamp', trigger object.locked) stamped the object title on update — the NC Flow path ran the OR flow. Follow-up 3.5: frontend operator-settings component so the flow-name is enterable in the Flow admin UI (today via the workflows API).
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ❌ | ✅ 172/172 | |||
| npm | ❌ | ❌ | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-23 09:16 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backend for the visual flow builder (companion to the nextcloud-vue
feat/vue-3UI).What
EventCatalogService+GET /api/flow/event-catalog): flow triggers beyond CRUD — object created/updated/deleted (legacy aliases) + locked/unlocked/reverted/state-transitioned, routed byEventCatalogListener.FlowActionServicematches viaaliasesFor()so legacy flows keep firing.RegisterObjectEntity(IEntity) +RunFlowOperation(ISpecificOperation) register OR objects + a 'Run an OpenRegister flow' operation in native Nextcloud Flow.runNamedFlow()runs a flow by name. Guarded by class_exists.object.set-field/object.create/object.delete+conditionguard inrunAction(); recursion-guarded via an activeObjects UUID set.Verified live (8090)
createdflows still fire.object.set-fieldon update stamped a Case title, persisted, no infinite loop.Not in scope
OCP\WorkflowEnginehas no public invoke API; would be a dead block.OpenSpec:
openspec/changes/visual-flow-builder(validates clean).🤖 Generated with Claude Code