|
15 | 15 | overload, |
16 | 16 | ) |
17 | 17 |
|
18 | | -from nexusrpc import HandlerError, HandlerErrorType |
| 18 | +from nexusrpc import HandlerError, HandlerErrorType, OperationError, OperationErrorState |
19 | 19 | from nexusrpc.handler import StartOperationResultAsync, StartOperationResultSync |
20 | 20 | from typing_extensions import Self |
21 | 21 |
|
22 | 22 | import temporalio.common |
23 | 23 | from temporalio.nexus._operation_context import ( |
| 24 | + _start_nexus_backed_workflow_update, |
24 | 25 | _start_nexus_backing_workflow, |
25 | 26 | _TemporalStartOperationContext, |
26 | 27 | ) |
| 28 | +from temporalio.nexus._token import UpdateHandle |
27 | 29 | from temporalio.types import ( |
28 | 30 | MethodAsyncNoParam, |
29 | 31 | MethodAsyncSingleParam, |
|
35 | 37 |
|
36 | 38 | if TYPE_CHECKING: |
37 | 39 | import temporalio.client |
| 40 | + import temporalio.workflow |
38 | 41 |
|
39 | 42 |
|
40 | 43 | _ResultT = TypeVar("_ResultT") |
@@ -279,6 +282,85 @@ async def start_workflow( |
279 | 282 | """ |
280 | 283 | ... |
281 | 284 |
|
| 285 | + # Overload for no-param update |
| 286 | + @overload |
| 287 | + async def start_workflow_update( |
| 288 | + self, |
| 289 | + workflow_id: str, |
| 290 | + update: temporalio.workflow.UpdateMethodMultiParam[[Any], ReturnType], |
| 291 | + *, |
| 292 | + id: str | None = None, |
| 293 | + rpc_metadata: Mapping[str, str | bytes] = {}, |
| 294 | + rpc_timeout: timedelta | None = None, |
| 295 | + ) -> TemporalOperationResult[ReturnType]: ... |
| 296 | + |
| 297 | + # Overload for single-param update |
| 298 | + @overload |
| 299 | + async def start_workflow_update( |
| 300 | + self, |
| 301 | + workflow_id: str, |
| 302 | + update: temporalio.workflow.UpdateMethodMultiParam[ |
| 303 | + [Any, ParamType], ReturnType |
| 304 | + ], |
| 305 | + arg: ParamType, |
| 306 | + *, |
| 307 | + id: str | None = None, |
| 308 | + rpc_metadata: Mapping[str, str | bytes] = {}, |
| 309 | + rpc_timeout: timedelta | None = None, |
| 310 | + ) -> TemporalOperationResult[ReturnType]: ... |
| 311 | + |
| 312 | + # Overload for multi-param update |
| 313 | + @overload |
| 314 | + async def start_workflow_update( |
| 315 | + self, |
| 316 | + workflow_id: str, |
| 317 | + update: temporalio.workflow.UpdateMethodMultiParam[MultiParamSpec, ReturnType], |
| 318 | + *, |
| 319 | + args: MultiParamSpec.args, # type: ignore |
| 320 | + id: str | None = None, |
| 321 | + rpc_metadata: Mapping[str, str | bytes] = {}, |
| 322 | + rpc_timeout: timedelta | None = None, |
| 323 | + ) -> TemporalOperationResult[ReturnType]: ... |
| 324 | + |
| 325 | + # Overload for string-name update |
| 326 | + @overload |
| 327 | + async def start_workflow_update( |
| 328 | + self, |
| 329 | + workflow_id: str, |
| 330 | + update: str, |
| 331 | + arg: Any = temporalio.common._arg_unset, |
| 332 | + *, |
| 333 | + args: Sequence[Any] = [], |
| 334 | + id: str | None = None, |
| 335 | + result_type: type[ReturnType] | None = None, |
| 336 | + rpc_metadata: Mapping[str, str | bytes] = {}, |
| 337 | + rpc_timeout: timedelta | None = None, |
| 338 | + ) -> TemporalOperationResult[ReturnType]: ... |
| 339 | + |
| 340 | + # draft-review: check why run_id and first_execution_run_id are not used |
| 341 | + # for update workflow in python sdk |
| 342 | + @abstractmethod |
| 343 | + async def start_workflow_update( |
| 344 | + self, |
| 345 | + workflow_id: str, |
| 346 | + update: str | Callable, |
| 347 | + arg: Any = temporalio.common._arg_unset, |
| 348 | + *, |
| 349 | + args: Sequence[Any] = [], |
| 350 | + id: str | None = None, |
| 351 | + result_type: type | None = None, |
| 352 | + rpc_metadata: Mapping[str, str | bytes] = {}, |
| 353 | + rpc_timeout: timedelta | None = None, |
| 354 | + # run_id: str | None = None, |
| 355 | + # first_execution_run_id: str | None = None, |
| 356 | + ) -> TemporalOperationResult[Any]: |
| 357 | + """Start a workflow update as the backing asynchronous Nexus operation. |
| 358 | +
|
| 359 | + .. warning:: |
| 360 | + This API is experimental and unstable. |
| 361 | + """ |
| 362 | + ... |
| 363 | + |
282 | 364 |
|
283 | 365 | class _TemporalNexusClient(TemporalNexusClient): # pyright: ignore[reportUnusedClass] |
284 | 366 | """Nexus-aware wrapper around a Temporal Client. |
@@ -377,3 +459,52 @@ async def start_workflow( |
377 | 459 | ) |
378 | 460 |
|
379 | 461 | return TemporalOperationResult.async_token(wf_handle.to_token()) |
| 462 | + |
| 463 | + async def start_workflow_update( |
| 464 | + self, |
| 465 | + workflow_id: str, |
| 466 | + update: str | Callable, |
| 467 | + arg: Any = temporalio.common._arg_unset, |
| 468 | + *, |
| 469 | + args: Sequence[Any] = [], |
| 470 | + id: str | None = None, |
| 471 | + result_type: type | None = None, |
| 472 | + rpc_metadata: Mapping[str, str | bytes] = {}, |
| 473 | + rpc_timeout: timedelta | None = None, |
| 474 | + # run_id: str | None = None, |
| 475 | + # first_execution_run_id: str | None = None, |
| 476 | + ) -> TemporalOperationResult[Any]: |
| 477 | + """Start a workflow update as the backing asynchronous Nexus operation.""" |
| 478 | + if not self._temporal_context.nexus_context.callback_url: |
| 479 | + raise HandlerError( |
| 480 | + "callback URL is required for a workflow update Nexus operation", |
| 481 | + type=HandlerErrorType.BAD_REQUEST, |
| 482 | + ) |
| 483 | + with self._reserve_async_start(): |
| 484 | + update_handle = await _start_nexus_backed_workflow_update( |
| 485 | + temporal_context=self._temporal_context, |
| 486 | + workflow_id=workflow_id, |
| 487 | + update=update, |
| 488 | + arg=arg, |
| 489 | + args=args, |
| 490 | + id=id, |
| 491 | + result_type=result_type, |
| 492 | + rpc_metadata=rpc_metadata, |
| 493 | + rpc_timeout=rpc_timeout, |
| 494 | + # run_id=run_id, |
| 495 | + # first_execution_run_id=first_execution_run_id, |
| 496 | + ) |
| 497 | + # If the update has already completed, return the result synchronously |
| 498 | + # This is in-line with the Go implementation as well |
| 499 | + if update_handle._known_outcome is not None: |
| 500 | + try: |
| 501 | + result = await update_handle.result() |
| 502 | + except temporalio.client.WorkflowUpdateFailedError as err: |
| 503 | + raise OperationError( |
| 504 | + str(err), state=OperationErrorState.FAILED |
| 505 | + ) from err |
| 506 | + return TemporalOperationResult.sync(result) |
| 507 | + nexus_handle: UpdateHandle[Any] = ( |
| 508 | + UpdateHandle._unsafe_from_client_workflow_update_handle(update_handle) |
| 509 | + ) |
| 510 | + return TemporalOperationResult.async_token(nexus_handle.to_token()) |
0 commit comments