@temporalio/client 1.10.2 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/connection.js +1 -0
- package/lib/connection.js.map +1 -1
- package/lib/errors.d.ts +10 -0
- package/lib/errors.js +15 -1
- package/lib/errors.js.map +1 -1
- package/lib/grpc-retry.js +31 -0
- package/lib/grpc-retry.js.map +1 -1
- package/lib/helpers.js +1 -1
- package/lib/helpers.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/schedule-client.js +1 -2
- package/lib/schedule-client.js.map +1 -1
- package/lib/schedule-helpers.js +2 -4
- package/lib/schedule-helpers.js.map +1 -1
- package/lib/schedule-types.d.ts +1 -1
- package/lib/workflow-client.d.ts +27 -7
- package/lib/workflow-client.js +63 -25
- package/lib/workflow-client.js.map +1 -1
- package/lib/workflow-update-stage.d.ts +17 -0
- package/lib/workflow-update-stage.js +28 -0
- package/lib/workflow-update-stage.js.map +1 -0
- package/package.json +5 -5
- package/src/connection.ts +1 -0
- package/src/errors.ts +14 -0
- package/src/grpc-retry.ts +33 -0
- package/src/helpers.ts +2 -2
- package/src/index.ts +1 -0
- package/src/schedule-client.ts +8 -3
- package/src/schedule-helpers.ts +3 -5
- package/src/schedule-types.ts +1 -1
- package/src/workflow-client.ts +84 -47
- package/src/workflow-update-stage.ts +33 -0
package/src/workflow-client.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
WorkflowContinuedAsNewError,
|
|
39
39
|
WorkflowFailedError,
|
|
40
40
|
WorkflowUpdateFailedError,
|
|
41
|
+
WorkflowUpdateRPCTimeoutOrCancelledError,
|
|
41
42
|
isGrpcServiceError,
|
|
42
43
|
} from './errors';
|
|
43
44
|
import {
|
|
@@ -80,6 +81,8 @@ import {
|
|
|
80
81
|
WithDefaults,
|
|
81
82
|
} from './base-client';
|
|
82
83
|
import { mapAsyncIterable } from './iterators-utils';
|
|
84
|
+
import { WorkflowUpdateStage } from './workflow-update-stage';
|
|
85
|
+
import * as workflowUpdateStage from './workflow-update-stage';
|
|
83
86
|
|
|
84
87
|
/**
|
|
85
88
|
* A client side handle to a single Workflow instance.
|
|
@@ -118,7 +121,8 @@ export interface WorkflowHandle<T extends Workflow = Workflow> extends BaseWorkf
|
|
|
118
121
|
* @experimental Update is an experimental feature.
|
|
119
122
|
*
|
|
120
123
|
* @throws {@link WorkflowUpdateFailedError} if Update validation fails or if ApplicationFailure is thrown in the Update handler.
|
|
121
|
-
*
|
|
124
|
+
* @throws {@link WorkflowUpdateRPCTimeoutOrCancelledError} if this Update call timed out or was cancelled. This doesn't
|
|
125
|
+
* mean the update itself was timed out or cancelled.
|
|
122
126
|
* @param def an Update definition as returned from {@link defineUpdate}
|
|
123
127
|
* @param options Update arguments
|
|
124
128
|
*
|
|
@@ -138,30 +142,51 @@ export interface WorkflowHandle<T extends Workflow = Workflow> extends BaseWorkf
|
|
|
138
142
|
): Promise<Ret>;
|
|
139
143
|
|
|
140
144
|
/**
|
|
141
|
-
* Start an Update and receive a handle to the Update.
|
|
142
|
-
*
|
|
145
|
+
* Start an Update and receive a handle to the Update. The Update validator (if present) is run
|
|
146
|
+
* before the handle is returned.
|
|
143
147
|
*
|
|
144
148
|
* @experimental Update is an experimental feature.
|
|
145
149
|
*
|
|
146
150
|
* @throws {@link WorkflowUpdateFailedError} if Update validation fails.
|
|
151
|
+
* @throws {@link WorkflowUpdateRPCTimeoutOrCancelledError} if this Update call timed out or was cancelled. This doesn't
|
|
152
|
+
* mean the update itself was timed out or cancelled.
|
|
147
153
|
*
|
|
148
154
|
* @param def an Update definition as returned from {@link defineUpdate}
|
|
149
|
-
* @param options
|
|
155
|
+
* @param options update arguments, and update lifecycle stage to wait for
|
|
156
|
+
*
|
|
157
|
+
* Currently, startUpdate always waits until a worker is accepting tasks for the workflow and the
|
|
158
|
+
* update is accepted or rejected, and the options object must be at least
|
|
159
|
+
* ```ts
|
|
160
|
+
* {
|
|
161
|
+
* waitForStage: WorkflowUpdateStage.ACCEPTED
|
|
162
|
+
* }
|
|
163
|
+
* ```
|
|
164
|
+
* If the update takes arguments, then the options object must additionally contain an `args`
|
|
165
|
+
* property with an array of argument values.
|
|
150
166
|
*
|
|
151
167
|
* @example
|
|
152
168
|
* ```ts
|
|
153
|
-
* const updateHandle = await handle.startUpdate(incrementAndGetValueUpdate, {
|
|
169
|
+
* const updateHandle = await handle.startUpdate(incrementAndGetValueUpdate, {
|
|
170
|
+
* args: [2],
|
|
171
|
+
* waitForStage: WorkflowUpdateStage.ACCEPTED,
|
|
172
|
+
* });
|
|
154
173
|
* const updateResult = await updateHandle.result();
|
|
155
174
|
* ```
|
|
156
175
|
*/
|
|
157
176
|
startUpdate<Ret, Args extends [any, ...any[]], Name extends string = string>(
|
|
158
177
|
def: UpdateDefinition<Ret, Args, Name> | string,
|
|
159
|
-
options: WorkflowUpdateOptions & {
|
|
178
|
+
options: WorkflowUpdateOptions & {
|
|
179
|
+
args: Args;
|
|
180
|
+
waitForStage: WorkflowUpdateStage.ACCEPTED;
|
|
181
|
+
}
|
|
160
182
|
): Promise<WorkflowUpdateHandle<Ret>>;
|
|
161
183
|
|
|
162
184
|
startUpdate<Ret, Args extends [], Name extends string = string>(
|
|
163
185
|
def: UpdateDefinition<Ret, Args, Name> | string,
|
|
164
|
-
options
|
|
186
|
+
options: WorkflowUpdateOptions & {
|
|
187
|
+
args?: Args;
|
|
188
|
+
waitForStage: WorkflowUpdateStage.ACCEPTED;
|
|
189
|
+
}
|
|
165
190
|
): Promise<WorkflowUpdateHandle<Ret>>;
|
|
166
191
|
|
|
167
192
|
/**
|
|
@@ -692,6 +717,28 @@ export class WorkflowClient extends BaseClient {
|
|
|
692
717
|
}
|
|
693
718
|
}
|
|
694
719
|
|
|
720
|
+
protected rethrowUpdateGrpcError(
|
|
721
|
+
err: unknown,
|
|
722
|
+
fallbackMessage: string,
|
|
723
|
+
workflowExecution?: WorkflowExecution
|
|
724
|
+
): never {
|
|
725
|
+
if (isGrpcServiceError(err)) {
|
|
726
|
+
if (err.code === grpcStatus.DEADLINE_EXCEEDED || err.code === grpcStatus.CANCELLED) {
|
|
727
|
+
throw new WorkflowUpdateRPCTimeoutOrCancelledError(err.details ?? 'Workflow update call timeout or cancelled', {
|
|
728
|
+
cause: err,
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (err instanceof CancelledFailure) {
|
|
734
|
+
throw new WorkflowUpdateRPCTimeoutOrCancelledError(err.message ?? 'Workflow update call timeout or cancelled', {
|
|
735
|
+
cause: err,
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
this.rethrowGrpcError(err, fallbackMessage, workflowExecution);
|
|
740
|
+
}
|
|
741
|
+
|
|
695
742
|
protected rethrowGrpcError(err: unknown, fallbackMessage: string, workflowExecution?: WorkflowExecution): never {
|
|
696
743
|
if (isGrpcServiceError(err)) {
|
|
697
744
|
rethrowKnownErrorTypes(err);
|
|
@@ -756,15 +803,19 @@ export class WorkflowClient extends BaseClient {
|
|
|
756
803
|
* Used as the final function of the interceptor chain during startUpdate and executeUpdate.
|
|
757
804
|
*/
|
|
758
805
|
protected async _startUpdateHandler(
|
|
759
|
-
waitForStage:
|
|
806
|
+
waitForStage: WorkflowUpdateStage,
|
|
760
807
|
input: WorkflowStartUpdateInput
|
|
761
808
|
): Promise<WorkflowStartUpdateOutput> {
|
|
809
|
+
waitForStage = waitForStage >= WorkflowUpdateStage.ACCEPTED ? waitForStage : WorkflowUpdateStage.ACCEPTED;
|
|
810
|
+
const waitForStageProto = workflowUpdateStage.toProtoEnum(waitForStage);
|
|
762
811
|
const updateId = input.options?.updateId ?? uuid4();
|
|
763
812
|
const req: temporal.api.workflowservice.v1.IUpdateWorkflowExecutionRequest = {
|
|
764
813
|
namespace: this.options.namespace,
|
|
765
814
|
workflowExecution: input.workflowExecution,
|
|
766
815
|
firstExecutionRunId: input.firstExecutionRunId,
|
|
767
|
-
waitPolicy: {
|
|
816
|
+
waitPolicy: {
|
|
817
|
+
lifecycleStage: waitForStageProto,
|
|
818
|
+
},
|
|
768
819
|
request: {
|
|
769
820
|
meta: {
|
|
770
821
|
updateId,
|
|
@@ -777,12 +828,17 @@ export class WorkflowClient extends BaseClient {
|
|
|
777
828
|
},
|
|
778
829
|
},
|
|
779
830
|
};
|
|
780
|
-
let response: temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse;
|
|
781
831
|
|
|
832
|
+
// Repeatedly send UpdateWorkflowExecution until update is >= Accepted or >= `waitForStage` (if
|
|
833
|
+
// the server receives a request with an update ID that already exists, it responds with
|
|
834
|
+
// information for the existing update).
|
|
835
|
+
let response: temporal.api.workflowservice.v1.UpdateWorkflowExecutionResponse;
|
|
782
836
|
try {
|
|
783
|
-
|
|
837
|
+
do {
|
|
838
|
+
response = await this.workflowService.updateWorkflowExecution(req);
|
|
839
|
+
} while (response.stage < waitForStageProto);
|
|
784
840
|
} catch (err) {
|
|
785
|
-
this.
|
|
841
|
+
this.rethrowUpdateGrpcError(err, 'Workflow Update failed', input.workflowExecution);
|
|
786
842
|
}
|
|
787
843
|
return {
|
|
788
844
|
updateId,
|
|
@@ -830,21 +886,9 @@ export class WorkflowClient extends BaseClient {
|
|
|
830
886
|
updateRef: { workflowExecution, updateId },
|
|
831
887
|
identity: this.options.identity,
|
|
832
888
|
waitPolicy: {
|
|
833
|
-
lifecycleStage:
|
|
834
|
-
temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage
|
|
835
|
-
.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_COMPLETED,
|
|
889
|
+
lifecycleStage: workflowUpdateStage.toProtoEnum(WorkflowUpdateStage.COMPLETED),
|
|
836
890
|
},
|
|
837
891
|
};
|
|
838
|
-
|
|
839
|
-
// TODO: Users should be able to use client.withDeadline(timestamp) with a
|
|
840
|
-
// Date (as opposed to a duration) to control the total amount of time
|
|
841
|
-
// allowed for polling. However, this requires a server change such that the
|
|
842
|
-
// server swallows the gRPC timeout and instead responds with a well-formed
|
|
843
|
-
// PollWorkflowExecutionUpdateResponse, indicating that the requested
|
|
844
|
-
// lifecycle stage has not yet been reached at the time of the deadline
|
|
845
|
-
// expiry. See https://github.com/temporalio/temporal/issues/4742
|
|
846
|
-
|
|
847
|
-
// TODO: When temporal#4742 is released, stop catching DEADLINE_EXCEEDED.
|
|
848
892
|
for (;;) {
|
|
849
893
|
try {
|
|
850
894
|
const response = await this.workflowService.pollWorkflowExecutionUpdate(req);
|
|
@@ -852,9 +896,8 @@ export class WorkflowClient extends BaseClient {
|
|
|
852
896
|
return response.outcome;
|
|
853
897
|
}
|
|
854
898
|
} catch (err) {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
}
|
|
899
|
+
const wE = typeof workflowExecution.workflowId === 'string' ? workflowExecution : undefined;
|
|
900
|
+
this.rethrowUpdateGrpcError(err, 'Workflow Update Poll failed', wE as WorkflowExecution);
|
|
858
901
|
}
|
|
859
902
|
}
|
|
860
903
|
}
|
|
@@ -1050,10 +1093,9 @@ export class WorkflowClient extends BaseClient {
|
|
|
1050
1093
|
runIdForResult,
|
|
1051
1094
|
...resultOptions
|
|
1052
1095
|
}: WorkflowHandleOptions): WorkflowHandle<T> {
|
|
1053
|
-
// TODO (dan): Convert to class with this as a protected method
|
|
1054
1096
|
const _startUpdate = async <Ret, Args extends unknown[]>(
|
|
1055
1097
|
def: UpdateDefinition<Ret, Args> | string,
|
|
1056
|
-
waitForStage:
|
|
1098
|
+
waitForStage: WorkflowUpdateStage,
|
|
1057
1099
|
options?: WorkflowUpdateOptions & { args?: Args }
|
|
1058
1100
|
): Promise<WorkflowUpdateHandle<Ret>> => {
|
|
1059
1101
|
const next = this._startUpdateHandler.bind(this, waitForStage);
|
|
@@ -1069,12 +1111,16 @@ export class WorkflowClient extends BaseClient {
|
|
|
1069
1111
|
options: opts,
|
|
1070
1112
|
};
|
|
1071
1113
|
const output = await fn(input);
|
|
1072
|
-
|
|
1114
|
+
const handle = this.createWorkflowUpdateHandle<Ret>(
|
|
1073
1115
|
output.updateId,
|
|
1074
1116
|
input.workflowExecution.workflowId,
|
|
1075
1117
|
output.workflowRunId,
|
|
1076
1118
|
output.outcome
|
|
1077
1119
|
);
|
|
1120
|
+
if (!output.outcome && waitForStage === WorkflowUpdateStage.COMPLETED) {
|
|
1121
|
+
await this._pollForUpdateOutcome(handle.updateId, input.workflowExecution);
|
|
1122
|
+
}
|
|
1123
|
+
return handle;
|
|
1078
1124
|
};
|
|
1079
1125
|
|
|
1080
1126
|
return {
|
|
@@ -1130,25 +1176,18 @@ export class WorkflowClient extends BaseClient {
|
|
|
1130
1176
|
},
|
|
1131
1177
|
async startUpdate<Ret, Args extends any[]>(
|
|
1132
1178
|
def: UpdateDefinition<Ret, Args> | string,
|
|
1133
|
-
options
|
|
1179
|
+
options: WorkflowUpdateOptions & {
|
|
1180
|
+
args?: Args;
|
|
1181
|
+
waitForStage: WorkflowUpdateStage.ACCEPTED;
|
|
1182
|
+
}
|
|
1134
1183
|
): Promise<WorkflowUpdateHandle<Ret>> {
|
|
1135
|
-
return await _startUpdate(
|
|
1136
|
-
def,
|
|
1137
|
-
temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage
|
|
1138
|
-
.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_ACCEPTED,
|
|
1139
|
-
options
|
|
1140
|
-
);
|
|
1184
|
+
return await _startUpdate(def, options.waitForStage, options);
|
|
1141
1185
|
},
|
|
1142
1186
|
async executeUpdate<Ret, Args extends any[]>(
|
|
1143
1187
|
def: UpdateDefinition<Ret, Args> | string,
|
|
1144
1188
|
options?: WorkflowUpdateOptions & { args?: Args }
|
|
1145
1189
|
): Promise<Ret> {
|
|
1146
|
-
const handle = await _startUpdate(
|
|
1147
|
-
def,
|
|
1148
|
-
temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage
|
|
1149
|
-
.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_COMPLETED,
|
|
1150
|
-
options
|
|
1151
|
-
);
|
|
1190
|
+
const handle = await _startUpdate(def, WorkflowUpdateStage.COMPLETED, options);
|
|
1152
1191
|
return await handle.result();
|
|
1153
1192
|
},
|
|
1154
1193
|
getUpdateHandle<Ret>(updateId: string): WorkflowUpdateHandle<Ret> {
|
|
@@ -1254,9 +1293,7 @@ export class WorkflowClient extends BaseClient {
|
|
|
1254
1293
|
this._list(options),
|
|
1255
1294
|
async ({ workflowId, runId }) => ({
|
|
1256
1295
|
workflowId,
|
|
1257
|
-
history: await this.getHandle(workflowId, runId)
|
|
1258
|
-
.fetchHistory()
|
|
1259
|
-
.catch((_) => undefined),
|
|
1296
|
+
history: await this.getHandle(workflowId, runId).fetchHistory(),
|
|
1260
1297
|
}),
|
|
1261
1298
|
{ concurrency: intoHistoriesOptions?.concurrency ?? 5 }
|
|
1262
1299
|
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { temporal } from '@temporalio/proto';
|
|
2
|
+
import { checkExtends } from '@temporalio/common/lib/type-helpers';
|
|
3
|
+
|
|
4
|
+
export enum WorkflowUpdateStage {
|
|
5
|
+
/** This is not an allowed value. */
|
|
6
|
+
UNSPECIFIED = 0,
|
|
7
|
+
/** Admitted stage. This stage is reached when the server accepts the update request. It is not
|
|
8
|
+
* allowed to wait for this stage when using startUpdate, since the update request has not yet
|
|
9
|
+
* been durably persisted at this stage. */
|
|
10
|
+
ADMITTED = 1,
|
|
11
|
+
/** Accepted stage. This stage is reached when a workflow has received the update and either
|
|
12
|
+
* accepted it (i.e. it has passed validation, or there was no validator configured on the update
|
|
13
|
+
* handler) or rejected it. This is currently the only allowed value when using startUpdate. */
|
|
14
|
+
ACCEPTED = 2,
|
|
15
|
+
/** Completed stage. This stage is reached when a workflow has completed processing the
|
|
16
|
+
* update with either a success or failure. */
|
|
17
|
+
COMPLETED = 3,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
checkExtends<
|
|
21
|
+
`UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_${keyof typeof WorkflowUpdateStage}`,
|
|
22
|
+
keyof typeof temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage
|
|
23
|
+
>();
|
|
24
|
+
checkExtends<
|
|
25
|
+
keyof typeof temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage,
|
|
26
|
+
`UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_${keyof typeof WorkflowUpdateStage}`
|
|
27
|
+
>();
|
|
28
|
+
|
|
29
|
+
export function toProtoEnum(stage: WorkflowUpdateStage): temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage {
|
|
30
|
+
return temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage[
|
|
31
|
+
`UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_${WorkflowUpdateStage[stage] as keyof typeof WorkflowUpdateStage}`
|
|
32
|
+
];
|
|
33
|
+
}
|