Skip to content

Add Temporal Nexus Operation Handler#2842

Open
Quinn-With-Two-Ns wants to merge 3 commits intotemporalio:masterfrom
Quinn-With-Two-Ns:temporal-nexus-operation-handler
Open

Add Temporal Nexus Operation Handler#2842
Quinn-With-Two-Ns wants to merge 3 commits intotemporalio:masterfrom
Quinn-With-Two-Ns:temporal-nexus-operation-handler

Conversation

@Quinn-With-Two-Ns
Copy link
Copy Markdown
Contributor

@Quinn-With-Two-Ns Quinn-With-Two-Ns commented Apr 14, 2026

Add TemporalOperationHandler for generic Nexus operations

Summary

  • Adds TemporalOperationHandler, a generic OperationHandler implementation that maps Nexus operations to Temporal workflows via a composable StartFunction
  • Adds TemporalNexusClient for starting workflows (typed and untyped) from within Nexus operation handlers
  • Supports subclassing TemporalOperationHandler to customize cancel behavior by overriding cancelWorkflowRun
  • Extracts shared workflow start logic into NexusStartWorkflowHelper, reused by both WorkflowRunOperation and the new handler

Test plan

  • GenericHandlerTypedStartWorkflowTest — typed workflow with return value
  • GenericHandlerTypedProcTest — typed void workflow
  • GenericHandlerUntypedStartWorkflowTest — untyped workflow start
  • GenericHandlerSyncResultTest — synchronous result path
  • GenericHandlerCancelTest — cancel with default and custom behavior

Note

Medium Risk
Adds new (experimental) public Nexus handler/client APIs and changes cancel dispatch/token parsing, which could affect how operation tokens are interpreted and how cancellations propagate.

Overview
Introduces an experimental generic Nexus integration via TemporalOperationHandler, letting Nexus operation implementations return either sync results or async workflow-run tokens through TemporalOperationResult, with default cancel behavior that cancels the underlying workflow based on the parsed operation token.

Adds TemporalNexusClient helpers to start typed/untyped workflows from Nexus handlers, and extracts the common “start workflow + attach workflow links to the Nexus operation context” logic into NexusStartWorkflowHelper, which is now reused by WorkflowRunOperationImpl.

Generalizes operation-token handling by renaming WorkflowRunOperationToken to OperationToken and updating OperationTokenUtil to support loading tokens without asserting type (for cancel dispatch) while keeping a type-asserting workflow-run loader; updates and adds tests to cover the new handler paths (typed/untyped start, void workflows, sync result, and cancel behavior).

Reviewed by Cursor Bugbot for commit 05a2002. Bugbot is set up for automated code reviews on this repo. Configure here.

@Quinn-With-Two-Ns Quinn-With-Two-Ns marked this pull request as ready for review April 14, 2026 22:33
@Quinn-With-Two-Ns Quinn-With-Two-Ns requested a review from a team as a code owner April 14, 2026 22:33
TestMultiArgWorkflowFunctions.TestNoArgsWorkflowProc.class,
wf -> {
wf.proc();
return null;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maciejdudko @GregoryTravis This is one issue I found with the approach, due to ambiguity in the lambda we have to return null here for workflows that don't return anything.

IllegalArgumentException.class,
() ->
OperationTokenUtil.loadWorkflowRunOperationToken(
OperationTokenUtil.loadOperationToken(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test no longer validates token type rejection

Medium Severity

The badTokenUnknownType test case switched from loadWorkflowRunOperationToken to loadOperationToken, which does not validate the token type. The test still passes, but only because the token coincidentally has "v":1 (rejected by version validation), not because t:4 is an unknown type. This means loadWorkflowRunOperationToken's type-rejection logic is no longer covered by any negative test case in this file, creating a silent coverage gap for a production code path used by WorkflowRunOperationImpl.cancel.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6641b14. Configure here.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 05a2002. Configure here.

Assert.assertEquals("TestNexusService1", e.getService());
Assert.assertEquals("operation", e.getOperation());
token = OperationTokenUtil.loadWorkflowRunOperationToken(e.getOperationToken());
token = OperationTokenUtil.loadOperationToken(e.getOperationToken());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test No Longer Validates Operation Token Type

Medium Severity

AsyncWorkflowOperationTest was changed to call loadOperationToken instead of loadWorkflowRunOperationToken when asserting the contents of the operation token returned by WorkflowRunOperation. loadOperationToken skips type validation, so the test no longer verifies that the token type is WORKFLOW_RUN. If a regression causes WorkflowRunOperation to produce a token with the wrong type, this test won't catch it. Similarly, WorkflowRunTokenTest.loadWorkflowIdFromBadOperationToken's badTokenUnknownType case was switched to loadOperationToken, so it now throws only due to v:1, not due to the unknown type — the type-rejection behavior is no longer tested at all.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 05a2002. Configure here.

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