Skip to content

Commit 1ab03c1

Browse files
dandavisonmjameswh
andcommitted
Update-with-start (#1585)
Co-authored-by: James Watkins-Harvey <james.watkinsharvey@temporal.io> (cherry picked from commit dfb524e)
1 parent 0a69f7a commit 1ab03c1

File tree

6 files changed

+663
-83
lines changed

6 files changed

+663
-83
lines changed

packages/client/src/helpers.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ServiceError as GrpcServiceError } from '@grpc/grpc-js';
1+
import { ServiceError as GrpcServiceError, status as grpcStatus } from '@grpc/grpc-js';
22
import {
33
LoadedDataConverter,
44
mapFromPayloads,
@@ -103,6 +103,7 @@ export function decodeCountWorkflowExecutionsResponse(
103103
}
104104

105105
type ErrorDetailsName = `temporal.api.errordetails.v1.${keyof typeof temporal.api.errordetails.v1}`;
106+
type FailureName = `temporal.api.failure.v1.${keyof typeof temporal.api.failure.v1}`;
106107

107108
/**
108109
* If the error type can be determined based on embedded grpc error details,
@@ -123,6 +124,28 @@ export function rethrowKnownErrorTypes(err: GrpcServiceError): void {
123124
const { namespace } = temporal.api.errordetails.v1.NamespaceNotFoundFailure.decode(entry.value);
124125
throw new NamespaceNotFoundError(namespace);
125126
}
127+
case 'temporal.api.errordetails.v1.MultiOperationExecutionFailure': {
128+
// MultiOperationExecutionFailure contains error statuses for multiple
129+
// operations. A MultiOperationExecutionAborted error status means that
130+
// the corresponding operation was aborted due to an error in one of the
131+
// other operations. We rethrow the first operation error that is not
132+
// MultiOperationExecutionAborted.
133+
const { statuses } = temporal.api.errordetails.v1.MultiOperationExecutionFailure.decode(entry.value);
134+
for (const status of statuses) {
135+
const detail = status.details?.[0];
136+
const statusType = detail?.type_url?.replace(/^type.googleapis.com\//, '') as FailureName | undefined;
137+
if (
138+
statusType === 'temporal.api.failure.v1.MultiOperationExecutionAborted' ||
139+
status.code === grpcStatus.OK
140+
) {
141+
continue;
142+
}
143+
err.message = status.message ?? err.message;
144+
err.code = status.code || err.code;
145+
err.details = detail?.value?.toString() || err.details;
146+
throw err;
147+
}
148+
}
126149
}
127150
}
128151
}

packages/client/src/interceptors.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,32 @@ export interface WorkflowStartUpdateOutput {
4242
readonly outcome?: temporal.api.update.v1.IOutcome;
4343
}
4444

45+
/**
46+
* Input for WorkflowClientInterceptor.startUpdateWithStart
47+
*
48+
* @experimental Update-with-Start is an experimental feature and may be subject to change.
49+
*/
50+
export interface WorkflowStartUpdateWithStartInput {
51+
readonly workflowType: string;
52+
readonly workflowStartOptions: CompiledWorkflowOptions;
53+
readonly workflowStartHeaders: Headers;
54+
readonly updateName: string;
55+
readonly updateArgs: unknown[];
56+
readonly updateOptions: WorkflowUpdateOptions;
57+
readonly updateHeaders: Headers;
58+
}
59+
60+
/**
61+
* Output for WorkflowClientInterceptor.startUpdateWithStart
62+
*
63+
* @experimental Update-with-Start is an experimental feature and may be subject to change.
64+
*/
65+
export interface WorkflowStartUpdateWithStartOutput {
66+
readonly workflowExecution: WorkflowExecution;
67+
readonly updateId: string;
68+
readonly updateOutcome?: temporal.api.update.v1.IOutcome;
69+
}
70+
4571
/** Input for WorkflowClientInterceptor.signal */
4672
export interface WorkflowSignalInput {
4773
readonly signalName: string;
@@ -105,6 +131,15 @@ export interface WorkflowClientInterceptor {
105131
input: WorkflowStartUpdateInput,
106132
next: Next<this, 'startUpdate'>
107133
) => Promise<WorkflowStartUpdateOutput>;
134+
/**
135+
* Intercept a service call to startUpdateWithStart
136+
*
137+
* @experimental Update-with-Start is an experimental feature and may be subject to change.
138+
*/
139+
startUpdateWithStart?: (
140+
input: WorkflowStartUpdateWithStartInput,
141+
next: Next<this, 'startUpdateWithStart'>
142+
) => Promise<WorkflowStartUpdateWithStartOutput>;
108143
/**
109144
* Intercept a service call to signalWorkflowExecution
110145
*

0 commit comments

Comments
 (0)