dotCMS / dotCMS/core

Workflow Center: per-item execution results, and enrich _bulkfire to support them

Open
#36,894 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dotCMS : Workflow Team : Scout Type : Task
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Description

Show the outcome of a bulk workflow action per item — succeeded, failed with a reason, skipped — and let the user retry the failures. Doing that needs the SSE endpoint to carry information it currently drops, so this issue covers both the backend change and the view built on it.

Part 1 — Enrich POST /api/v1/workflow/contentlet/actions/_bulkfire

Progress is already emitted per contentlet. WorkflowAPIImpl.fireBulkActionTask calls the callbacks once per item:

successConsumer.accept(1L);                        // no inode — just "+1"
failConsumer.accept(contentlet.getInode(), e);     // inode AND the exception

Three things are lost between there and the wire:

  1. The failure reason. WorkflowResource receives the exception and discards it: eventBuilder.data(Map.class, Map.of("failure", inode)). The message is already in hand — ActionFail.newInstance(user, inode, e) shows how the synchronous path localises it.
  2. Which item succeeded. The success event carries only {"success": 1}, so a running total is possible but attributing success to a specific row is not. Fixing this means the Consumer<Long> success callback carrying the inode as well.
  3. Skipped items. distributeWorkAndProcessNoReturn — the SSE path — never calls computeSkippedContentletsCount, so skipped is structurally absent from the stream. The synchronous path computes it up front and returns skippedCount plus a skipReason.
Part 2 — Results view

With those three in place, the view can show per-row state during the run and a final summary. Until then a client has to choose between live counters with no reasons (_bulkfire) or reasons only at the end (PUT .../bulk/fire, which returns fails[] with errorMessage, capped by workflow.action.bulk.maxexceptions, default 1000). It cannot have both from one run, because firing twice would run the action twice.

Evidence this is a real gap, not a preference. The legacy dialog appears to offer failure details but does not. In view_bulk_actions_inc.jsp the SSE path initialises var entity = {skippedCount: 0, successCount: 0, fails: 0}fails is a number — and the SSE listeners only increment a local counter and write it to the DOM. entity.fails stays 0, so toggleFailDetails() always renders "No failed actions" even when failures occurred, and the skipped row is permanently 0. The counters are live; the drill-down behind them is dead.

Acceptance Criteria

Backend

  • The failure SSE event carries the failure reason alongside the inode, localised the same way the synchronous fails[] entries are
  • The success SSE event identifies which contentlet succeeded, not only that one more did
  • The SSE path computes and emits skipped items with the reason, matching what the synchronous endpoint reports
  • The synchronous endpoint's response shape is unchanged — this is additive
  • Integration tests cover a run with successes, failures and skips, asserting the stream contents

Results view

  • Each row shows in-flight, succeeded, failed or skipped state as the run progresses
  • A failed row shows its reason inline
  • A failed row can be retried on its own, and all failed rows can be retried together
  • A final summary reports successful, failed and skipped totals
  • Totals in the summary reconcile with the per-row states — no row is unaccounted for
  • Retry-all is disabled while a run is in flight
  • The view degrades honestly if the stream ends early or drops: no row is left showing in-flight forever
  • Unit tests cover the stream-to-row-state mapping, including a failure arriving for an unknown inode
Priority

Medium

Additional Context

Consuming SSE from the frontend needs a shim: native EventSource cannot POST a body, which is why the legacy JSP bundles sse.js. Wiring that is part of this work.

Relevant code:

What Where
Per-item callbacks WorkflowAPIImpl.fireBulkActionTask
SSE event construction (drops the exception) WorkflowResourcePOST /contentlet/actions/_bulkfire
SSE path, no skip computation WorkflowAPIImpl.distributeWorkAndProcessNoReturn
Synchronous path, computes skips + fail messages WorkflowAPIImpl.distributeWorkAndProcess, ActionFail
Legacy consumer (counters live, details dead) view_bulk_actions_inc.jsp

Depends on #36819 for the preview the results view is entered from. Sibling issues: #36817, #36818, #36844, #36845.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with WorkflowAPIImpl.fireBulkActionTask and distributeWorkAndProcessNoReturn, then trace SSE construction in WorkflowResource and compare it with ActionFail and the synchronous path. Review view_bulk_actions_inc.jsp and the referenced integration and unit-test areas before mapping events to row states. Done means the stream includes success, failure reasons, and skips, while the view supports per-row results, retries, reconciled totals, and tests for unknown inodes and dropped streams.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, javascript
Domain
api, backend, frontend, testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.