Skip to content

Commit

Permalink
Update-with-start (#1585)
Browse files Browse the repository at this point in the history
Co-authored-by: James Watkins-Harvey <james.watkinsharvey@temporal.io>
  • Loading branch information
dandavison and mjameswh authored Dec 20, 2024
1 parent e9184bb commit dfb524e
Show file tree
Hide file tree
Showing 7 changed files with 668 additions and 84 deletions.
6 changes: 5 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 24 additions & 1 deletion packages/client/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ServiceError as GrpcServiceError } from '@grpc/grpc-js';
import { ServiceError as GrpcServiceError, status as grpcStatus } from '@grpc/grpc-js';
import {
LoadedDataConverter,
mapFromPayloads,
Expand Down Expand Up @@ -103,6 +103,7 @@ export function decodeCountWorkflowExecutionsResponse(
}

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

/**
* If the error type can be determined based on embedded grpc error details,
Expand All @@ -123,6 +124,28 @@ export function rethrowKnownErrorTypes(err: GrpcServiceError): void {
const { namespace } = temporal.api.errordetails.v1.NamespaceNotFoundFailure.decode(entry.value);
throw new NamespaceNotFoundError(namespace);
}
case 'temporal.api.errordetails.v1.MultiOperationExecutionFailure': {
// MultiOperationExecutionFailure contains error statuses for multiple
// operations. A MultiOperationExecutionAborted error status means that
// the corresponding operation was aborted due to an error in one of the
// other operations. We rethrow the first operation error that is not
// MultiOperationExecutionAborted.
const { statuses } = temporal.api.errordetails.v1.MultiOperationExecutionFailure.decode(entry.value);
for (const status of statuses) {
const detail = status.details?.[0];
const statusType = detail?.type_url?.replace(/^type.googleapis.com\//, '') as FailureName | undefined;
if (
statusType === 'temporal.api.failure.v1.MultiOperationExecutionAborted' ||
status.code === grpcStatus.OK
) {
continue;
}
err.message = status.message ?? err.message;
err.code = status.code || err.code;
err.details = detail?.value?.toString() || err.details;
throw err;
}
}
}
}
}
Expand Down
35 changes: 35 additions & 0 deletions packages/client/src/interceptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,32 @@ export interface WorkflowStartUpdateOutput {
readonly outcome?: temporal.api.update.v1.IOutcome;
}

/**
* Input for WorkflowClientInterceptor.startUpdateWithStart
*
* @experimental Update-with-Start is an experimental feature and may be subject to change.
*/
export interface WorkflowStartUpdateWithStartInput {
readonly workflowType: string;
readonly workflowStartOptions: CompiledWorkflowOptions;
readonly workflowStartHeaders: Headers;
readonly updateName: string;
readonly updateArgs: unknown[];
readonly updateOptions: WorkflowUpdateOptions;
readonly updateHeaders: Headers;
}

/**
* Output for WorkflowClientInterceptor.startUpdateWithStart
*
* @experimental Update-with-Start is an experimental feature and may be subject to change.
*/
export interface WorkflowStartUpdateWithStartOutput {
readonly workflowExecution: WorkflowExecution;
readonly updateId: string;
readonly updateOutcome?: temporal.api.update.v1.IOutcome;
}

/** Input for WorkflowClientInterceptor.signal */
export interface WorkflowSignalInput {
readonly signalName: string;
Expand Down Expand Up @@ -105,6 +131,15 @@ export interface WorkflowClientInterceptor {
input: WorkflowStartUpdateInput,
next: Next<this, 'startUpdate'>
) => Promise<WorkflowStartUpdateOutput>;
/**
* Intercept a service call to startUpdateWithStart
*
* @experimental Update-with-Start is an experimental feature and may be subject to change.
*/
startUpdateWithStart?: (
input: WorkflowStartUpdateWithStartInput,
next: Next<this, 'startUpdateWithStart'>
) => Promise<WorkflowStartUpdateWithStartOutput>;
/**
* Intercept a service call to signalWorkflowExecution
*
Expand Down
Loading

0 comments on commit dfb524e

Please sign in to comment.