@temporalio/client 1.7.4 → 1.8.1
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/async-completion-client.js +25 -22
- package/lib/async-completion-client.js.map +1 -1
- package/lib/build-id-types.d.ts +95 -0
- package/lib/build-id-types.js +28 -0
- package/lib/build-id-types.js.map +1 -0
- package/lib/client.d.ts +7 -0
- package/lib/client.js +6 -0
- package/lib/client.js.map +1 -1
- package/lib/connection.d.ts +2 -1
- package/lib/connection.js +1 -1
- package/lib/connection.js.map +1 -1
- package/lib/errors.d.ts +4 -4
- package/lib/errors.js +7 -7
- package/lib/errors.js.map +1 -1
- package/lib/helpers.d.ts +10 -0
- package/lib/helpers.js +31 -1
- package/lib/helpers.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/schedule-client.d.ts +1 -1
- package/lib/schedule-client.js +36 -27
- package/lib/schedule-client.js.map +1 -1
- package/lib/schedule-helpers.js +7 -7
- package/lib/schedule-helpers.js.map +1 -1
- package/lib/schedule-types.d.ts +5 -5
- package/lib/task-queue-client.d.ts +122 -0
- package/lib/task-queue-client.js +229 -0
- package/lib/task-queue-client.js.map +1 -0
- package/lib/workflow-client.d.ts +1 -1
- package/lib/workflow-client.js +93 -81
- package/lib/workflow-client.js.map +1 -1
- package/package.json +4 -4
- package/src/async-completion-client.ts +29 -23
- package/src/build-id-types.ts +146 -0
- package/src/client.ts +13 -0
- package/src/connection.ts +4 -4
- package/src/errors.ts +7 -6
- package/src/helpers.ts +36 -1
- package/src/index.ts +11 -0
- package/src/schedule-client.ts +35 -27
- package/src/schedule-helpers.ts +8 -7
- package/src/schedule-types.ts +5 -5
- package/src/task-queue-client.ts +297 -0
- package/src/workflow-client.ts +98 -84
package/src/schedule-types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { checkExtends, Replace } from '@temporalio/common/lib/type-helpers';
|
|
2
|
-
import { SearchAttributes, Workflow } from '@temporalio/common';
|
|
2
|
+
import { Duration, SearchAttributes, Workflow } from '@temporalio/common';
|
|
3
3
|
import type { temporal } from '@temporalio/proto';
|
|
4
4
|
import { WorkflowStartOptions } from './workflow-options';
|
|
5
5
|
|
|
@@ -46,7 +46,7 @@ export interface ScheduleOptions<A extends ScheduleOptionsAction = ScheduleOptio
|
|
|
46
46
|
* @default 1 minute
|
|
47
47
|
* @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
|
|
48
48
|
*/
|
|
49
|
-
catchupWindow?:
|
|
49
|
+
catchupWindow?: Duration;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* When an Action times out or reaches the end of its Retry Policy, {@link pause}.
|
|
@@ -465,7 +465,7 @@ export interface ScheduleSpec {
|
|
|
465
465
|
* @default 0
|
|
466
466
|
* @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
|
|
467
467
|
*/
|
|
468
|
-
jitter?:
|
|
468
|
+
jitter?: Duration;
|
|
469
469
|
|
|
470
470
|
/**
|
|
471
471
|
* IANA timezone name, for example `US/Pacific`.
|
|
@@ -659,7 +659,7 @@ export interface IntervalSpec {
|
|
|
659
659
|
*
|
|
660
660
|
* @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
|
|
661
661
|
*/
|
|
662
|
-
every:
|
|
662
|
+
every: Duration;
|
|
663
663
|
|
|
664
664
|
/**
|
|
665
665
|
* Value is rounded to the nearest second.
|
|
@@ -667,7 +667,7 @@ export interface IntervalSpec {
|
|
|
667
667
|
* @default 0
|
|
668
668
|
* @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
|
|
669
669
|
*/
|
|
670
|
-
offset?:
|
|
670
|
+
offset?: Duration;
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
/**
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { status } from '@grpc/grpc-js';
|
|
2
|
+
import { filterNullAndUndefined } from '@temporalio/common/lib/internal-non-workflow';
|
|
3
|
+
import { assertNever, RequireAtLeastOne } from '@temporalio/common/lib/type-helpers';
|
|
4
|
+
import { temporal } from '@temporalio/proto';
|
|
5
|
+
import { BaseClient, BaseClientOptions, defaultBaseClientOptions, LoadedWithDefaults } from './base-client';
|
|
6
|
+
import { WorkflowService } from './types';
|
|
7
|
+
import { BuildIdOperation, versionSetsFromProto, WorkerBuildIdVersionSets } from './build-id-types';
|
|
8
|
+
import { isGrpcServiceError, ServiceError } from './errors';
|
|
9
|
+
import { rethrowKnownErrorTypes } from './helpers';
|
|
10
|
+
|
|
11
|
+
type IUpdateWorkerBuildIdCompatibilityRequest =
|
|
12
|
+
temporal.api.workflowservice.v1.IUpdateWorkerBuildIdCompatibilityRequest;
|
|
13
|
+
type GetWorkerTaskReachabilityResponse = temporal.api.workflowservice.v1.GetWorkerTaskReachabilityResponse;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @experimental
|
|
17
|
+
*/
|
|
18
|
+
export type TaskQueueClientOptions = BaseClientOptions;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @experimental
|
|
22
|
+
*/
|
|
23
|
+
export type LoadedTaskQueueClientOptions = LoadedWithDefaults<TaskQueueClientOptions>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A stand-in for a Build Id for unversioned Workers
|
|
27
|
+
* @experimental
|
|
28
|
+
*/
|
|
29
|
+
export const UnversionedBuildId = Symbol.for('__temporal_unversionedBuildId');
|
|
30
|
+
export type UnversionedBuildIdType = typeof UnversionedBuildId;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Client for starting Workflow executions and creating Workflow handles
|
|
34
|
+
*
|
|
35
|
+
* @experimental
|
|
36
|
+
*/
|
|
37
|
+
export class TaskQueueClient extends BaseClient {
|
|
38
|
+
public readonly options: LoadedTaskQueueClientOptions;
|
|
39
|
+
|
|
40
|
+
constructor(options?: TaskQueueClientOptions) {
|
|
41
|
+
super(options);
|
|
42
|
+
this.options = {
|
|
43
|
+
...defaultBaseClientOptions(),
|
|
44
|
+
...filterNullAndUndefined(options ?? {}),
|
|
45
|
+
loadedDataConverter: this.dataConverter,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Raw gRPC access to the Temporal service.
|
|
51
|
+
*
|
|
52
|
+
* **NOTE**: The namespace provided in {@link options} is **not** automatically set on requests
|
|
53
|
+
* using this service attribute.
|
|
54
|
+
*/
|
|
55
|
+
get workflowService(): WorkflowService {
|
|
56
|
+
return this.connection.workflowService;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Used to add new Build Ids or otherwise update the relative compatibility of Build Ids as
|
|
61
|
+
* defined on a specific task queue for the Worker Versioning feature. For more on this feature,
|
|
62
|
+
* see https://docs.temporal.io/workers#worker-versioning
|
|
63
|
+
*
|
|
64
|
+
* @param taskQueue The task queue to make changes to.
|
|
65
|
+
* @param operation The operation to be performed.
|
|
66
|
+
*/
|
|
67
|
+
public async updateBuildIdCompatibility(taskQueue: string, operation: BuildIdOperation): Promise<void> {
|
|
68
|
+
const request: IUpdateWorkerBuildIdCompatibilityRequest = {
|
|
69
|
+
namespace: this.options.namespace,
|
|
70
|
+
taskQueue,
|
|
71
|
+
};
|
|
72
|
+
switch (operation.operation) {
|
|
73
|
+
case 'addNewIdInNewDefaultSet':
|
|
74
|
+
request.addNewBuildIdInNewDefaultSet = operation.buildId;
|
|
75
|
+
break;
|
|
76
|
+
case 'addNewCompatibleVersion':
|
|
77
|
+
request.addNewCompatibleBuildId = {
|
|
78
|
+
newBuildId: operation.buildId,
|
|
79
|
+
existingCompatibleBuildId: operation.existingCompatibleBuildId,
|
|
80
|
+
};
|
|
81
|
+
break;
|
|
82
|
+
case 'promoteSetByBuildId':
|
|
83
|
+
request.promoteSetByBuildId = operation.buildId;
|
|
84
|
+
break;
|
|
85
|
+
case 'promoteBuildIdWithinSet':
|
|
86
|
+
request.promoteBuildIdWithinSet = operation.buildId;
|
|
87
|
+
break;
|
|
88
|
+
case 'mergeSets':
|
|
89
|
+
request.mergeSets = {
|
|
90
|
+
primarySetBuildId: operation.primaryBuildId,
|
|
91
|
+
secondarySetBuildId: operation.secondaryBuildId,
|
|
92
|
+
};
|
|
93
|
+
break;
|
|
94
|
+
default:
|
|
95
|
+
assertNever('Unknown build id update operation', operation);
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
await this.workflowService.updateWorkerBuildIdCompatibility(request);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
this.rethrowGrpcError(e, 'Unexpected error updating Build Id compatibility');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Fetch the sets of compatible Build Ids for a given task queue.
|
|
106
|
+
*
|
|
107
|
+
* @param taskQueue The task queue to fetch the compatibility information for.
|
|
108
|
+
* @returns The sets of compatible Build Ids for the given task queue, or undefined if the queue
|
|
109
|
+
* has no Build Ids defined on it.
|
|
110
|
+
*/
|
|
111
|
+
public async getBuildIdCompatability(taskQueue: string): Promise<WorkerBuildIdVersionSets | undefined> {
|
|
112
|
+
let resp;
|
|
113
|
+
try {
|
|
114
|
+
resp = await this.workflowService.getWorkerBuildIdCompatibility({
|
|
115
|
+
taskQueue,
|
|
116
|
+
namespace: this.options.namespace,
|
|
117
|
+
});
|
|
118
|
+
} catch (e) {
|
|
119
|
+
this.rethrowGrpcError(e, 'Unexpected error fetching Build Id compatibility');
|
|
120
|
+
}
|
|
121
|
+
if (resp.majorVersionSets == null || resp.majorVersionSets.length === 0) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
return versionSetsFromProto(resp);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Fetches task reachability to determine whether a worker may be retired. The request may specify
|
|
129
|
+
* task queues to query for or let the server fetch all task queues mapped to the given build IDs.
|
|
130
|
+
*
|
|
131
|
+
* When requesting a large number of task queues or all task queues associated with the given
|
|
132
|
+
* build ids in a namespace, all task queues will be listed in the response but some of them may
|
|
133
|
+
* not contain reachability information due to a server enforced limit. When reaching the limit,
|
|
134
|
+
* task queues that reachability information could not be retrieved for will be marked with a
|
|
135
|
+
* `NotFetched` entry in {@link BuildIdReachability.taskQueueReachability}. The caller may issue
|
|
136
|
+
* another call to get the reachability for those task queues.
|
|
137
|
+
*/
|
|
138
|
+
public async getReachability(options: ReachabilityOptions): Promise<ReachabilityResponse> {
|
|
139
|
+
let resp;
|
|
140
|
+
const buildIds = options.buildIds?.map((bid) => {
|
|
141
|
+
if (bid === UnversionedBuildId) {
|
|
142
|
+
return '';
|
|
143
|
+
}
|
|
144
|
+
return bid;
|
|
145
|
+
});
|
|
146
|
+
try {
|
|
147
|
+
resp = await this.workflowService.getWorkerTaskReachability({
|
|
148
|
+
namespace: this.options.namespace,
|
|
149
|
+
taskQueues: options.taskQueues,
|
|
150
|
+
buildIds,
|
|
151
|
+
reachability: reachabilityTypeToProto(options.reachability),
|
|
152
|
+
});
|
|
153
|
+
} catch (e) {
|
|
154
|
+
this.rethrowGrpcError(e, 'Unexpected error fetching Build Id reachability');
|
|
155
|
+
}
|
|
156
|
+
return reachabilityResponseFromProto(resp);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected rethrowGrpcError(err: unknown, fallbackMessage: string): never {
|
|
160
|
+
if (isGrpcServiceError(err)) {
|
|
161
|
+
rethrowKnownErrorTypes(err);
|
|
162
|
+
if (err.code === status.NOT_FOUND) {
|
|
163
|
+
throw new BuildIdNotFoundError(err.details ?? 'Build Id not found');
|
|
164
|
+
}
|
|
165
|
+
throw new ServiceError(fallbackMessage, { cause: err });
|
|
166
|
+
}
|
|
167
|
+
throw new ServiceError('Unexpected error while making gRPC request');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Options for {@link TaskQueueClient.getReachability}
|
|
173
|
+
*/
|
|
174
|
+
export type ReachabilityOptions = RequireAtLeastOne<BaseReachabilityOptions, 'buildIds' | 'taskQueues'>;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* There are different types of reachability:
|
|
178
|
+
* - `NEW_WORKFLOWS`: The Build Id might be used by new workflows
|
|
179
|
+
* - `EXISTING_WORKFLOWS` The Build Id might be used by open workflows and/or closed workflows.
|
|
180
|
+
* - `OPEN_WORKFLOWS` The Build Id might be used by open workflows
|
|
181
|
+
* - `CLOSED_WORKFLOWS` The Build Id might be used by closed workflows
|
|
182
|
+
*/
|
|
183
|
+
export type ReachabilityType = 'NEW_WORKFLOWS' | 'EXISTING_WORKFLOWS' | 'OPEN_WORKFLOWS' | 'CLOSED_WORKFLOWS';
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* See {@link ReachabilityOptions}
|
|
187
|
+
*/
|
|
188
|
+
export interface BaseReachabilityOptions {
|
|
189
|
+
/**
|
|
190
|
+
* A list of build ids to query the reachability of. Currently, at least one Build Id must be
|
|
191
|
+
* specified, but this restriction may be lifted in the future.
|
|
192
|
+
*/
|
|
193
|
+
buildIds: (string | UnversionedBuildIdType)[];
|
|
194
|
+
/**
|
|
195
|
+
* A list of task queues with Build Ids defined on them that the request is
|
|
196
|
+
* concerned with.
|
|
197
|
+
*/
|
|
198
|
+
taskQueues?: string[];
|
|
199
|
+
/** The kind of reachability this request is concerned with. */
|
|
200
|
+
reachability?: ReachabilityType;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface ReachabilityResponse {
|
|
204
|
+
/** Maps Build Ids to their reachability information. */
|
|
205
|
+
buildIdReachability: Record<string | UnversionedBuildIdType, BuildIdReachability>;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type ReachabilityTypeResponse = ReachabilityType | 'NOT_FETCHED';
|
|
209
|
+
|
|
210
|
+
export interface BuildIdReachability {
|
|
211
|
+
/**
|
|
212
|
+
* Maps Task Queue names to how the Build Id may be reachable from them. If they are not
|
|
213
|
+
* reachable, the map value will be an empty array.
|
|
214
|
+
*/
|
|
215
|
+
taskQueueReachability: Record<string, ReachabilityTypeResponse[]>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function reachabilityTypeToProto(type: ReachabilityType | undefined | null): temporal.api.enums.v1.TaskReachability {
|
|
219
|
+
switch (type) {
|
|
220
|
+
case null:
|
|
221
|
+
case undefined:
|
|
222
|
+
return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_UNSPECIFIED;
|
|
223
|
+
case 'NEW_WORKFLOWS':
|
|
224
|
+
return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_NEW_WORKFLOWS;
|
|
225
|
+
case 'EXISTING_WORKFLOWS':
|
|
226
|
+
return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_EXISTING_WORKFLOWS;
|
|
227
|
+
case 'OPEN_WORKFLOWS':
|
|
228
|
+
return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_OPEN_WORKFLOWS;
|
|
229
|
+
case 'CLOSED_WORKFLOWS':
|
|
230
|
+
return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_CLOSED_WORKFLOWS;
|
|
231
|
+
default:
|
|
232
|
+
assertNever('Unknown Build Id reachability operation', type);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function reachabilityResponseFromProto(resp: GetWorkerTaskReachabilityResponse): ReachabilityResponse {
|
|
237
|
+
return {
|
|
238
|
+
buildIdReachability: Object.fromEntries(
|
|
239
|
+
resp.buildIdReachability.map((bir) => {
|
|
240
|
+
const taskQueueReachability: Record<string, ReachabilityTypeResponse[]> = {};
|
|
241
|
+
if (bir.taskQueueReachability != null) {
|
|
242
|
+
for (const tqr of bir.taskQueueReachability) {
|
|
243
|
+
if (tqr.taskQueue == null) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (tqr.reachability == null) {
|
|
247
|
+
taskQueueReachability[tqr.taskQueue] = [];
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
taskQueueReachability[tqr.taskQueue] = tqr.reachability.map(reachabilityTypeFromProto);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
let bid: string | UnversionedBuildIdType;
|
|
254
|
+
if (bir.buildId) {
|
|
255
|
+
bid = bir.buildId;
|
|
256
|
+
} else {
|
|
257
|
+
bid = UnversionedBuildId;
|
|
258
|
+
}
|
|
259
|
+
return [bid, { taskQueueReachability }];
|
|
260
|
+
})
|
|
261
|
+
) as Record<string | UnversionedBuildIdType, BuildIdReachability>,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function reachabilityTypeFromProto(rtype: temporal.api.enums.v1.TaskReachability): ReachabilityTypeResponse {
|
|
266
|
+
switch (rtype) {
|
|
267
|
+
case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_UNSPECIFIED:
|
|
268
|
+
return 'NOT_FETCHED';
|
|
269
|
+
case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_NEW_WORKFLOWS:
|
|
270
|
+
return 'NEW_WORKFLOWS';
|
|
271
|
+
case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_EXISTING_WORKFLOWS:
|
|
272
|
+
return 'EXISTING_WORKFLOWS';
|
|
273
|
+
case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_OPEN_WORKFLOWS:
|
|
274
|
+
return 'OPEN_WORKFLOWS';
|
|
275
|
+
case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_CLOSED_WORKFLOWS:
|
|
276
|
+
return 'CLOSED_WORKFLOWS';
|
|
277
|
+
default:
|
|
278
|
+
return assertNever('Unknown Build Id reachability operation', rtype);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Thrown when one or more Build Ids are not found while using the {@link TaskQueueClient}.
|
|
284
|
+
*
|
|
285
|
+
* It could be because:
|
|
286
|
+
* - Id passed is incorrect
|
|
287
|
+
* - Build Id has been scavenged by the server.
|
|
288
|
+
*
|
|
289
|
+
* @experimental
|
|
290
|
+
*/
|
|
291
|
+
export class BuildIdNotFoundError extends Error {
|
|
292
|
+
public readonly name: string = 'BuildIdNotFoundError';
|
|
293
|
+
|
|
294
|
+
constructor(message: string) {
|
|
295
|
+
super(message);
|
|
296
|
+
}
|
|
297
|
+
}
|
package/src/workflow-client.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
WorkflowExecutionAlreadyStartedError,
|
|
19
19
|
WorkflowNotFoundError,
|
|
20
20
|
WorkflowResultType,
|
|
21
|
+
extractWorkflowType,
|
|
21
22
|
} from '@temporalio/common';
|
|
22
23
|
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
23
24
|
import { History } from '@temporalio/common/lib/proto-utils';
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
filterNullAndUndefined,
|
|
31
32
|
} from '@temporalio/common/lib/internal-non-workflow';
|
|
32
33
|
import { temporal } from '@temporalio/proto';
|
|
33
|
-
import {
|
|
34
|
+
import { ServiceError, WorkflowContinuedAsNewError, WorkflowFailedError, isGrpcServiceError } from './errors';
|
|
34
35
|
import {
|
|
35
36
|
WorkflowCancelInput,
|
|
36
37
|
WorkflowClientInterceptor,
|
|
@@ -59,7 +60,7 @@ import {
|
|
|
59
60
|
WorkflowSignalWithStartOptions,
|
|
60
61
|
WorkflowStartOptions,
|
|
61
62
|
} from './workflow-options';
|
|
62
|
-
import { executionInfoFromRaw } from './helpers';
|
|
63
|
+
import { executionInfoFromRaw, rethrowKnownErrorTypes } from './helpers';
|
|
63
64
|
import {
|
|
64
65
|
BaseClient,
|
|
65
66
|
BaseClientOptions,
|
|
@@ -345,7 +346,7 @@ export class WorkflowClient extends BaseClient {
|
|
|
345
346
|
options: WithWorkflowArgs<T, WorkflowOptions>,
|
|
346
347
|
interceptors: WorkflowClientInterceptor[]
|
|
347
348
|
): Promise<string> {
|
|
348
|
-
const workflowType =
|
|
349
|
+
const workflowType = extractWorkflowType(workflowTypeOrFunc);
|
|
349
350
|
assertRequiredWorkflowOptions(options);
|
|
350
351
|
const compiledOptions = compileWorkflowOptions(ensureArgs(options));
|
|
351
352
|
|
|
@@ -369,7 +370,7 @@ export class WorkflowClient extends BaseClient {
|
|
|
369
370
|
options: WithWorkflowArgs<T, WorkflowSignalWithStartOptions<SA>>,
|
|
370
371
|
interceptors: WorkflowClientInterceptor[]
|
|
371
372
|
): Promise<string> {
|
|
372
|
-
const workflowType =
|
|
373
|
+
const workflowType = extractWorkflowType(workflowTypeOrFunc);
|
|
373
374
|
const { signal, signalArgs, ...rest } = options;
|
|
374
375
|
assertRequiredWorkflowOptions(rest);
|
|
375
376
|
const compiledOptions = compileWorkflowOptions(ensureArgs(rest));
|
|
@@ -486,7 +487,7 @@ export class WorkflowClient extends BaseClient {
|
|
|
486
487
|
try {
|
|
487
488
|
res = await this.workflowService.getWorkflowExecutionHistory(req);
|
|
488
489
|
} catch (err) {
|
|
489
|
-
this.rethrowGrpcError(err,
|
|
490
|
+
this.rethrowGrpcError(err, 'Failed to get Workflow execution history', { workflowId, runId });
|
|
490
491
|
}
|
|
491
492
|
const events = res.history?.events;
|
|
492
493
|
|
|
@@ -580,18 +581,21 @@ export class WorkflowClient extends BaseClient {
|
|
|
580
581
|
}
|
|
581
582
|
}
|
|
582
583
|
|
|
583
|
-
protected rethrowGrpcError(err: unknown,
|
|
584
|
-
if (
|
|
584
|
+
protected rethrowGrpcError(err: unknown, fallbackMessage: string, workflowExecution?: WorkflowExecution): never {
|
|
585
|
+
if (isGrpcServiceError(err)) {
|
|
586
|
+
rethrowKnownErrorTypes(err);
|
|
587
|
+
|
|
585
588
|
if (err.code === grpcStatus.NOT_FOUND) {
|
|
586
589
|
throw new WorkflowNotFoundError(
|
|
587
590
|
err.details ?? 'Workflow not found',
|
|
588
|
-
workflowExecution
|
|
589
|
-
workflowExecution
|
|
591
|
+
workflowExecution?.workflowId ?? '',
|
|
592
|
+
workflowExecution?.runId
|
|
590
593
|
);
|
|
591
594
|
}
|
|
595
|
+
|
|
592
596
|
throw new ServiceError(fallbackMessage, { cause: err });
|
|
593
597
|
}
|
|
594
|
-
throw new ServiceError('Unexpected error while making gRPC request');
|
|
598
|
+
throw new ServiceError('Unexpected error while making gRPC request', { cause: err as Error });
|
|
595
599
|
}
|
|
596
600
|
|
|
597
601
|
/**
|
|
@@ -600,23 +604,27 @@ export class WorkflowClient extends BaseClient {
|
|
|
600
604
|
* Used as the final function of the query interceptor chain
|
|
601
605
|
*/
|
|
602
606
|
protected async _queryWorkflowHandler(input: WorkflowQueryInput): Promise<unknown> {
|
|
607
|
+
const req: temporal.api.workflowservice.v1.IQueryWorkflowRequest = {
|
|
608
|
+
queryRejectCondition: input.queryRejectCondition,
|
|
609
|
+
namespace: this.options.namespace,
|
|
610
|
+
execution: input.workflowExecution,
|
|
611
|
+
query: {
|
|
612
|
+
queryType: input.queryType,
|
|
613
|
+
queryArgs: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
|
|
614
|
+
header: { fields: input.headers },
|
|
615
|
+
},
|
|
616
|
+
};
|
|
603
617
|
let response: temporal.api.workflowservice.v1.QueryWorkflowResponse;
|
|
604
618
|
try {
|
|
605
|
-
response = await this.workflowService.queryWorkflow(
|
|
606
|
-
queryRejectCondition: input.queryRejectCondition,
|
|
607
|
-
namespace: this.options.namespace,
|
|
608
|
-
execution: input.workflowExecution,
|
|
609
|
-
query: {
|
|
610
|
-
queryType: input.queryType,
|
|
611
|
-
queryArgs: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
|
|
612
|
-
header: { fields: input.headers },
|
|
613
|
-
},
|
|
614
|
-
});
|
|
619
|
+
response = await this.workflowService.queryWorkflow(req);
|
|
615
620
|
} catch (err) {
|
|
616
|
-
if (
|
|
617
|
-
|
|
621
|
+
if (isGrpcServiceError(err)) {
|
|
622
|
+
rethrowKnownErrorTypes(err);
|
|
623
|
+
if (err.code === grpcStatus.INVALID_ARGUMENT) {
|
|
624
|
+
throw new QueryNotRegisteredError(err.message.replace(/^3 INVALID_ARGUMENT: /, ''), err.code);
|
|
625
|
+
}
|
|
618
626
|
}
|
|
619
|
-
this.rethrowGrpcError(err,
|
|
627
|
+
this.rethrowGrpcError(err, 'Failed to query Workflow', input.workflowExecution);
|
|
620
628
|
}
|
|
621
629
|
if (response.queryRejected) {
|
|
622
630
|
if (response.queryRejected.status === undefined || response.queryRejected.status === null) {
|
|
@@ -637,19 +645,20 @@ export class WorkflowClient extends BaseClient {
|
|
|
637
645
|
* Used as the final function of the signal interceptor chain
|
|
638
646
|
*/
|
|
639
647
|
protected async _signalWorkflowHandler(input: WorkflowSignalInput): Promise<void> {
|
|
648
|
+
const req: temporal.api.workflowservice.v1.ISignalWorkflowExecutionRequest = {
|
|
649
|
+
identity: this.options.identity,
|
|
650
|
+
namespace: this.options.namespace,
|
|
651
|
+
workflowExecution: input.workflowExecution,
|
|
652
|
+
requestId: uuid4(),
|
|
653
|
+
// control is unused,
|
|
654
|
+
signalName: input.signalName,
|
|
655
|
+
header: { fields: input.headers },
|
|
656
|
+
input: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
|
|
657
|
+
};
|
|
640
658
|
try {
|
|
641
|
-
await this.workflowService.signalWorkflowExecution(
|
|
642
|
-
identity: this.options.identity,
|
|
643
|
-
namespace: this.options.namespace,
|
|
644
|
-
workflowExecution: input.workflowExecution,
|
|
645
|
-
requestId: uuid4(),
|
|
646
|
-
// control is unused,
|
|
647
|
-
signalName: input.signalName,
|
|
648
|
-
header: { fields: input.headers },
|
|
649
|
-
input: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
|
|
650
|
-
});
|
|
659
|
+
await this.workflowService.signalWorkflowExecution(req);
|
|
651
660
|
} catch (err) {
|
|
652
|
-
this.rethrowGrpcError(err,
|
|
661
|
+
this.rethrowGrpcError(err, 'Failed to signal Workflow', input.workflowExecution);
|
|
653
662
|
}
|
|
654
663
|
}
|
|
655
664
|
|
|
@@ -661,37 +670,37 @@ export class WorkflowClient extends BaseClient {
|
|
|
661
670
|
protected async _signalWithStartWorkflowHandler(input: WorkflowSignalWithStartInput): Promise<string> {
|
|
662
671
|
const { identity } = this.options;
|
|
663
672
|
const { options, workflowType, signalName, signalArgs, headers } = input;
|
|
673
|
+
const req: temporal.api.workflowservice.v1.ISignalWithStartWorkflowExecutionRequest = {
|
|
674
|
+
namespace: this.options.namespace,
|
|
675
|
+
identity,
|
|
676
|
+
requestId: uuid4(),
|
|
677
|
+
workflowId: options.workflowId,
|
|
678
|
+
workflowIdReusePolicy: options.workflowIdReusePolicy,
|
|
679
|
+
workflowType: { name: workflowType },
|
|
680
|
+
input: { payloads: await encodeToPayloads(this.dataConverter, ...options.args) },
|
|
681
|
+
signalName,
|
|
682
|
+
signalInput: { payloads: await encodeToPayloads(this.dataConverter, ...signalArgs) },
|
|
683
|
+
taskQueue: {
|
|
684
|
+
kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
|
|
685
|
+
name: options.taskQueue,
|
|
686
|
+
},
|
|
687
|
+
workflowExecutionTimeout: options.workflowExecutionTimeout,
|
|
688
|
+
workflowRunTimeout: options.workflowRunTimeout,
|
|
689
|
+
workflowTaskTimeout: options.workflowTaskTimeout,
|
|
690
|
+
retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
|
|
691
|
+
memo: options.memo ? { fields: await encodeMapToPayloads(this.dataConverter, options.memo) } : undefined,
|
|
692
|
+
searchAttributes: options.searchAttributes
|
|
693
|
+
? {
|
|
694
|
+
indexedFields: mapToPayloads(searchAttributePayloadConverter, options.searchAttributes),
|
|
695
|
+
}
|
|
696
|
+
: undefined,
|
|
697
|
+
cronSchedule: options.cronSchedule,
|
|
698
|
+
header: { fields: headers },
|
|
699
|
+
};
|
|
664
700
|
try {
|
|
665
|
-
|
|
666
|
-
namespace: this.options.namespace,
|
|
667
|
-
identity,
|
|
668
|
-
requestId: uuid4(),
|
|
669
|
-
workflowId: options.workflowId,
|
|
670
|
-
workflowIdReusePolicy: options.workflowIdReusePolicy,
|
|
671
|
-
workflowType: { name: workflowType },
|
|
672
|
-
input: { payloads: await encodeToPayloads(this.dataConverter, ...options.args) },
|
|
673
|
-
signalName,
|
|
674
|
-
signalInput: { payloads: await encodeToPayloads(this.dataConverter, ...signalArgs) },
|
|
675
|
-
taskQueue: {
|
|
676
|
-
kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
|
|
677
|
-
name: options.taskQueue,
|
|
678
|
-
},
|
|
679
|
-
workflowExecutionTimeout: options.workflowExecutionTimeout,
|
|
680
|
-
workflowRunTimeout: options.workflowRunTimeout,
|
|
681
|
-
workflowTaskTimeout: options.workflowTaskTimeout,
|
|
682
|
-
retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
|
|
683
|
-
memo: options.memo ? { fields: await encodeMapToPayloads(this.dataConverter, options.memo) } : undefined,
|
|
684
|
-
searchAttributes: options.searchAttributes
|
|
685
|
-
? {
|
|
686
|
-
indexedFields: mapToPayloads(searchAttributePayloadConverter, options.searchAttributes),
|
|
687
|
-
}
|
|
688
|
-
: undefined,
|
|
689
|
-
cronSchedule: options.cronSchedule,
|
|
690
|
-
header: { fields: headers },
|
|
691
|
-
});
|
|
692
|
-
return runId;
|
|
701
|
+
return (await this.workflowService.signalWithStartWorkflowExecution(req)).runId;
|
|
693
702
|
} catch (err) {
|
|
694
|
-
this.rethrowGrpcError(err, { workflowId: options.workflowId }
|
|
703
|
+
this.rethrowGrpcError(err, 'Failed to signalWithStart Workflow', { workflowId: options.workflowId });
|
|
695
704
|
}
|
|
696
705
|
}
|
|
697
706
|
|
|
@@ -729,8 +738,7 @@ export class WorkflowClient extends BaseClient {
|
|
|
729
738
|
header: { fields: headers },
|
|
730
739
|
};
|
|
731
740
|
try {
|
|
732
|
-
|
|
733
|
-
return res.runId;
|
|
741
|
+
return (await this.workflowService.startWorkflowExecution(req)).runId;
|
|
734
742
|
} catch (err: any) {
|
|
735
743
|
if (err.code === grpcStatus.ALREADY_EXISTS) {
|
|
736
744
|
throw new WorkflowExecutionAlreadyStartedError(
|
|
@@ -739,7 +747,7 @@ export class WorkflowClient extends BaseClient {
|
|
|
739
747
|
workflowType
|
|
740
748
|
);
|
|
741
749
|
}
|
|
742
|
-
this.rethrowGrpcError(err, { workflowId: opts.workflowId }
|
|
750
|
+
this.rethrowGrpcError(err, 'Failed to start Workflow', { workflowId: opts.workflowId });
|
|
743
751
|
}
|
|
744
752
|
}
|
|
745
753
|
|
|
@@ -751,18 +759,19 @@ export class WorkflowClient extends BaseClient {
|
|
|
751
759
|
protected async _terminateWorkflowHandler(
|
|
752
760
|
input: WorkflowTerminateInput
|
|
753
761
|
): Promise<TerminateWorkflowExecutionResponse> {
|
|
762
|
+
const req: temporal.api.workflowservice.v1.ITerminateWorkflowExecutionRequest = {
|
|
763
|
+
namespace: this.options.namespace,
|
|
764
|
+
identity: this.options.identity,
|
|
765
|
+
...input,
|
|
766
|
+
details: {
|
|
767
|
+
payloads: input.details ? await encodeToPayloads(this.dataConverter, ...input.details) : undefined,
|
|
768
|
+
},
|
|
769
|
+
firstExecutionRunId: input.firstExecutionRunId,
|
|
770
|
+
};
|
|
754
771
|
try {
|
|
755
|
-
return await this.workflowService.terminateWorkflowExecution(
|
|
756
|
-
namespace: this.options.namespace,
|
|
757
|
-
identity: this.options.identity,
|
|
758
|
-
...input,
|
|
759
|
-
details: {
|
|
760
|
-
payloads: input.details ? await encodeToPayloads(this.dataConverter, ...input.details) : undefined,
|
|
761
|
-
},
|
|
762
|
-
firstExecutionRunId: input.firstExecutionRunId,
|
|
763
|
-
});
|
|
772
|
+
return await this.workflowService.terminateWorkflowExecution(req);
|
|
764
773
|
} catch (err) {
|
|
765
|
-
this.rethrowGrpcError(err,
|
|
774
|
+
this.rethrowGrpcError(err, 'Failed to terminate Workflow', input.workflowExecution);
|
|
766
775
|
}
|
|
767
776
|
}
|
|
768
777
|
|
|
@@ -781,7 +790,7 @@ export class WorkflowClient extends BaseClient {
|
|
|
781
790
|
firstExecutionRunId: input.firstExecutionRunId,
|
|
782
791
|
});
|
|
783
792
|
} catch (err) {
|
|
784
|
-
this.rethrowGrpcError(err,
|
|
793
|
+
this.rethrowGrpcError(err, 'Failed to cancel workflow', input.workflowExecution);
|
|
785
794
|
}
|
|
786
795
|
}
|
|
787
796
|
|
|
@@ -797,7 +806,7 @@ export class WorkflowClient extends BaseClient {
|
|
|
797
806
|
execution: input.workflowExecution,
|
|
798
807
|
});
|
|
799
808
|
} catch (err) {
|
|
800
|
-
this.rethrowGrpcError(err,
|
|
809
|
+
this.rethrowGrpcError(err, 'Failed to describe workflow', input.workflowExecution);
|
|
801
810
|
}
|
|
802
811
|
}
|
|
803
812
|
|
|
@@ -925,12 +934,17 @@ export class WorkflowClient extends BaseClient {
|
|
|
925
934
|
protected async *_list(options?: ListOptions): AsyncIterable<WorkflowExecutionInfo> {
|
|
926
935
|
let nextPageToken: Uint8Array = Buffer.alloc(0);
|
|
927
936
|
for (;;) {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
937
|
+
let response: temporal.api.workflowservice.v1.ListWorkflowExecutionsResponse;
|
|
938
|
+
try {
|
|
939
|
+
response = await this.workflowService.listWorkflowExecutions({
|
|
940
|
+
namespace: this.options.namespace,
|
|
941
|
+
query: options?.query,
|
|
942
|
+
nextPageToken,
|
|
943
|
+
pageSize: options?.pageSize,
|
|
944
|
+
});
|
|
945
|
+
} catch (e) {
|
|
946
|
+
this.rethrowGrpcError(e, 'Failed to list workflows', undefined);
|
|
947
|
+
}
|
|
934
948
|
// Not decoding memo payloads concurrently even though we could have to keep the lazy nature of this iterator.
|
|
935
949
|
// Decoding is done for `memo` fields which tend to be small.
|
|
936
950
|
// We might decide to change that based on user feedback.
|