Workflow: cross-app client operations - #1201
Conversation
Adds an optional app_id argument to every client-level workflow operation on DaprWorkflowClient and its async counterpart: schedule_new_workflow, get_workflow_state, wait_for_workflow_start, wait_for_workflow_completion, raise_workflow_event, terminate_workflow, pause_workflow, resume_workflow and purge_workflow. When set, the operation targets a workflow instance owned by another app in the same namespace, and the target app's WorkflowAccessPolicy decides whether it is permitted. When unset or equal to the local app, the behaviour is unchanged. The vendored durabletask client carries the value as a TaskRouter with targetAppID on each request, built by the new new_task_router helper. An older runtime ignores the field and applies the operation locally. Signed-off-by: joshvanl <me@joshvanl.dev>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1201 +/- ##
==========================================
+ Coverage 83.15% 83.70% +0.54%
==========================================
Files 123 123
Lines 10260 10266 +6
==========================================
+ Hits 8532 8593 +61
+ Misses 1728 1673 -55 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟡 Changes recommended
Proto provenance is inconsistent with generated output, and cross-app purge semantics are documented incorrectly.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds cross-app routing to synchronous and asynchronous workflow client operations using TaskRouter.
Changes:
- Adds optional
app_idforwarding across workflow lifecycle operations. - Regenerates durabletask protocol bindings and improves regeneration provenance.
- Adds unit and integration coverage for cross-app routing.
File summaries
| File | Description |
|---|---|
tools/regen_durabletask_protos.sh |
Supports local proto sources and records provenance. |
tests/integration/test_workflow_cross_app.py |
Tests cross-app workflow operations end to end. |
tests/integration/conftest.py |
Refines app-channel readiness checks. |
tests/integration/apps/workflow_host.py |
Provides the remote workflow host. |
tests/ext/workflow/test_workflow_client.py |
Tests sync app_id forwarding. |
tests/ext/workflow/test_workflow_client_aio.py |
Tests async app_id forwarding. |
tests/ext/workflow/durabletask/test_orchestration_executor.py |
Updates action-router assertions. |
tests/ext/workflow/durabletask/test_client_routing.py |
Verifies routing on generated requests. |
dapr/ext/workflow/dapr_workflow_client.py |
Exposes cross-app sync operations. |
dapr/ext/workflow/aio/dapr_workflow_client.py |
Exposes cross-app async operations. |
dapr/ext/workflow/_durabletask/internal/PROTO_SOURCE_COMMIT_HASH |
Updates recorded proto source revision. |
dapr/ext/workflow/_durabletask/internal/orchestrator_service_pb2.pyi |
Adds typed operation-router fields. |
dapr/ext/workflow/_durabletask/internal/orchestrator_service_pb2.py |
Updates generated operation protocol descriptors. |
dapr/ext/workflow/_durabletask/internal/orchestrator_actions_pb2.pyi |
Updates generated action types. |
dapr/ext/workflow/_durabletask/internal/orchestrator_actions_pb2.py |
Updates generated action descriptors. |
dapr/ext/workflow/_durabletask/internal/orchestration_pb2.pyi |
Adds generated workflow metadata types. |
dapr/ext/workflow/_durabletask/internal/orchestration_pb2.py |
Updates orchestration descriptors. |
dapr/ext/workflow/_durabletask/internal/history_events_pb2.pyi |
Adds child-retry metadata typing. |
dapr/ext/workflow/_durabletask/internal/history_events_pb2.py |
Updates history event descriptors. |
dapr/ext/workflow/_durabletask/internal/helpers.py |
Keeps routing on enclosing workflow actions. |
dapr/ext/workflow/_durabletask/internal/backend_service_pb2.pyi |
Adds unique-instance request typing. |
dapr/ext/workflow/_durabletask/internal/backend_service_pb2.py |
Updates backend protocol descriptors. |
dapr/ext/workflow/_durabletask/client.py |
Builds and attaches sync task routers. |
dapr/ext/workflow/_durabletask/aio/client.py |
Builds and attaches async task routers. |
Review details
Files not reviewed (5)
- dapr/ext/workflow/_durabletask/internal/backend_service_pb2.py: Generated file
- dapr/ext/workflow/_durabletask/internal/history_events_pb2.py: Generated file
- dapr/ext/workflow/_durabletask/internal/orchestration_pb2.py: Generated file
- dapr/ext/workflow/_durabletask/internal/orchestrator_actions_pb2.py: Generated file
- dapr/ext/workflow/_durabletask/internal/orchestrator_service_pb2.py: Generated file
Suppressed comments (1)
tests/ext/workflow/durabletask/test_orchestration_executor.py:770
- This is the
without_app_idcase, so the docstring still describes the opposite scenario.
"""Tests that the workflow action carries correct router fields when app_id is specified"""
- Files reviewed: 19/24 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| fi | ||
| echo "Using local durabletask-protobuf checkout at ${DURABLETASK_PROTOBUF_DIR}" | ||
| proto_dir="${DURABLETASK_PROTOBUF_DIR}/protos" | ||
| proto_source_commit="$(git -C "${DURABLETASK_PROTOBUF_DIR}" rev-parse HEAD 2>/dev/null || true)" |
| recursive: The optional flag to also purge data from all child workflows. | ||
| app_id: The optional ID of the app hosting the workflow instance, when it is | ||
| hosted by a different app. The target app's WorkflowAccessPolicy governs whether | ||
| this operation is permitted. | ||
| Requires a Dapr runtime with cross-app workflow support; older runtimes | ||
| ignore app_id and apply the operation to the local app. |
| recursive: The optional flag to also purge data from all child workflows. | ||
| app_id: The optional ID of the app hosting the workflow instance, when it is | ||
| hosted by a different app. The target app's WorkflowAccessPolicy governs whether | ||
| this operation is permitted. | ||
| Requires a Dapr runtime with cross-app workflow support; older runtimes | ||
| ignore app_id and apply the operation to the local app. |
|
|
||
| def test_schedule_activity_actions_router_without_app_id(): | ||
| """Tests that scheduleTask action contains correct router fields when app_id is specified""" | ||
| """Tests that the workflow action carries correct router fields when app_id is specified""" |
Adds an optional app_id argument to every client-level workflow operation on DaprWorkflowClient and its async counterpart: schedule_new_workflow, get_workflow_state, wait_for_workflow_start, wait_for_workflow_completion, raise_workflow_event, terminate_workflow, pause_workflow, resume_workflow and purge_workflow. When set, the operation targets a workflow instance owned by another app in the same namespace, and the target app's WorkflowAccessPolicy decides whether it is permitted. When unset or equal to the local app, the behaviour is unchanged.
The vendored durabletask client carries the value as a TaskRouter with targetAppID on each request, built by the new new_task_router helper. An older runtime ignores the field and applies the operation locally.