@temporalio/client 1.2.0 → 1.4.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/async-completion-client.d.ts +17 -5
- package/lib/async-completion-client.js +28 -11
- package/lib/async-completion-client.js.map +1 -1
- package/lib/client.d.ts +92 -0
- package/lib/client.js +77 -0
- package/lib/client.js.map +1 -0
- package/lib/connection.d.ts +12 -4
- package/lib/connection.js +13 -4
- package/lib/connection.js.map +1 -1
- package/lib/grpc-retry.d.ts +51 -8
- package/lib/grpc-retry.js +73 -46
- package/lib/grpc-retry.js.map +1 -1
- package/lib/index.d.ts +6 -6
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/interceptors.d.ts +10 -2
- package/lib/types.d.ts +16 -15
- package/lib/types.js +28 -4
- package/lib/types.js.map +1 -1
- package/lib/workflow-client.d.ts +26 -10
- package/lib/workflow-client.js +51 -45
- package/lib/workflow-client.js.map +1 -1
- package/lib/workflow-options.d.ts +16 -5
- package/lib/workflow-options.js +1 -27
- package/lib/workflow-options.js.map +1 -1
- package/package.json +7 -9
- package/src/async-completion-client.ts +30 -8
- package/src/client.ts +151 -0
- package/src/connection.ts +21 -3
- package/src/grpc-retry.ts +129 -56
- package/src/index.ts +6 -5
- package/src/interceptors.ts +11 -2
- package/src/types.ts +19 -15
- package/src/workflow-client.ts +65 -49
- package/src/workflow-options.ts +21 -35
package/src/client.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { DataConverter, LoadedDataConverter } from '@temporalio/common';
|
|
2
|
+
import { filterNullAndUndefined, loadDataConverter } from '@temporalio/common/lib/internal-non-workflow';
|
|
3
|
+
import { Replace } from '@temporalio/common/lib/type-helpers';
|
|
4
|
+
import { temporal } from '@temporalio/proto';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import { AsyncCompletionClient } from './async-completion-client';
|
|
7
|
+
import { Connection } from './connection';
|
|
8
|
+
import { ClientInterceptors } from './interceptors';
|
|
9
|
+
import { ConnectionLike, Metadata, WorkflowService } from './types';
|
|
10
|
+
import { WorkflowClient } from './workflow-client';
|
|
11
|
+
|
|
12
|
+
export interface ClientOptions {
|
|
13
|
+
/**
|
|
14
|
+
* {@link DataConverter} to use for serializing and deserializing payloads
|
|
15
|
+
*/
|
|
16
|
+
dataConverter?: DataConverter;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Used to override and extend default Connection functionality
|
|
20
|
+
*
|
|
21
|
+
* Useful for injecting auth headers and tracing Workflow executions
|
|
22
|
+
*/
|
|
23
|
+
interceptors?: ClientInterceptors;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Identity to report to the server
|
|
27
|
+
*
|
|
28
|
+
* @default `${process.pid}@${os.hostname()}`
|
|
29
|
+
*/
|
|
30
|
+
identity?: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Connection to use to communicate with the server.
|
|
34
|
+
*
|
|
35
|
+
* By default `WorkflowClient` connects to localhost.
|
|
36
|
+
*
|
|
37
|
+
* Connections are expensive to construct and should be reused.
|
|
38
|
+
*/
|
|
39
|
+
connection?: ConnectionLike;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Server namespace
|
|
43
|
+
*
|
|
44
|
+
* @default default
|
|
45
|
+
*/
|
|
46
|
+
namespace?: string;
|
|
47
|
+
|
|
48
|
+
workflow?: {
|
|
49
|
+
/**
|
|
50
|
+
* Should a query be rejected by closed and failed workflows
|
|
51
|
+
*
|
|
52
|
+
* @default QUERY_REJECT_CONDITION_UNSPECIFIED which means that closed and failed workflows are still queryable
|
|
53
|
+
*/
|
|
54
|
+
queryRejectCondition?: temporal.api.enums.v1.QueryRejectCondition;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type ClientOptionsWithDefaults = Replace<
|
|
59
|
+
Required<ClientOptions>,
|
|
60
|
+
{
|
|
61
|
+
connection?: ConnectionLike;
|
|
62
|
+
}
|
|
63
|
+
>;
|
|
64
|
+
|
|
65
|
+
export type LoadedClientOptions = ClientOptionsWithDefaults & {
|
|
66
|
+
loadedDataConverter: LoadedDataConverter;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export function defaultClientOptions(): ClientOptionsWithDefaults {
|
|
70
|
+
return {
|
|
71
|
+
dataConverter: {},
|
|
72
|
+
identity: `${process.pid}@${os.hostname()}`,
|
|
73
|
+
interceptors: {},
|
|
74
|
+
namespace: 'default',
|
|
75
|
+
workflow: {
|
|
76
|
+
queryRejectCondition: temporal.api.enums.v1.QueryRejectCondition.QUERY_REJECT_CONDITION_UNSPECIFIED,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* High level SDK client.
|
|
83
|
+
*/
|
|
84
|
+
export class Client {
|
|
85
|
+
/**
|
|
86
|
+
* Underlying gRPC connection to the Temporal service
|
|
87
|
+
*/
|
|
88
|
+
public readonly connection: ConnectionLike;
|
|
89
|
+
public readonly options: LoadedClientOptions;
|
|
90
|
+
/**
|
|
91
|
+
* Workflow sub-client - use to start and interact with Workflows
|
|
92
|
+
*/
|
|
93
|
+
public readonly workflow: WorkflowClient;
|
|
94
|
+
/**
|
|
95
|
+
* (Async) Activity completion sub-client - use to manually manage Activities
|
|
96
|
+
*/
|
|
97
|
+
public readonly activity: AsyncCompletionClient;
|
|
98
|
+
|
|
99
|
+
constructor(options?: ClientOptions) {
|
|
100
|
+
this.connection = options?.connection ?? Connection.lazy();
|
|
101
|
+
this.options = {
|
|
102
|
+
...defaultClientOptions(),
|
|
103
|
+
...filterNullAndUndefined(options ?? {}),
|
|
104
|
+
loadedDataConverter: loadDataConverter(options?.dataConverter),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const { workflow, loadedDataConverter, interceptors, ...base } = this.options;
|
|
108
|
+
|
|
109
|
+
this.workflow = new WorkflowClient({
|
|
110
|
+
...base,
|
|
111
|
+
...workflow,
|
|
112
|
+
connection: this.connection,
|
|
113
|
+
dataConverter: loadedDataConverter,
|
|
114
|
+
interceptors: interceptors.workflow,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.activity = new AsyncCompletionClient({
|
|
118
|
+
...base,
|
|
119
|
+
connection: this.connection,
|
|
120
|
+
dataConverter: loadedDataConverter,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Raw gRPC access to the Temporal service.
|
|
126
|
+
*
|
|
127
|
+
* **NOTE**: The namespace provided in {@link options} is **not** automatically set on requests made via this service
|
|
128
|
+
* object.
|
|
129
|
+
*/
|
|
130
|
+
get workflowService(): WorkflowService {
|
|
131
|
+
return this.connection.workflowService;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Set the deadline for any service requests executed in `fn`'s scope.
|
|
136
|
+
*/
|
|
137
|
+
async withDeadline<R>(deadline: number | Date, fn: () => Promise<R>): Promise<R> {
|
|
138
|
+
return await this.connection.withDeadline(deadline, fn);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Set metadata for any service requests executed in `fn`'s scope.
|
|
143
|
+
*
|
|
144
|
+
* @returns returned value of `fn`
|
|
145
|
+
*
|
|
146
|
+
* @see {@link Connection.withMetadata}
|
|
147
|
+
*/
|
|
148
|
+
async withMetadata<R>(metadata: Metadata, fn: () => Promise<R>): Promise<R> {
|
|
149
|
+
return await this.connection.withMetadata(metadata, fn);
|
|
150
|
+
}
|
|
151
|
+
}
|
package/src/connection.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as grpc from '@grpc/grpc-js';
|
|
2
|
-
import { filterNullAndUndefined, normalizeTlsConfig, TLSConfig } from '@temporalio/internal-non-workflow
|
|
2
|
+
import { filterNullAndUndefined, normalizeTlsConfig, TLSConfig } from '@temporalio/common/lib/internal-non-workflow';
|
|
3
3
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
4
|
import type { RPCImpl } from 'protobufjs';
|
|
5
5
|
import { isServerErrorResponse, ServiceError } from './errors';
|
|
6
6
|
import { defaultGrpcRetryOptions, makeGrpcRetryInterceptor } from './grpc-retry';
|
|
7
7
|
import pkg from './pkg';
|
|
8
|
-
import { CallContext, Metadata, OperatorService, WorkflowService } from './types';
|
|
8
|
+
import { CallContext, HealthService, Metadata, OperatorService, WorkflowService } from './types';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* gRPC and Temporal Server connection options
|
|
@@ -63,7 +63,7 @@ export interface ConnectionOptions {
|
|
|
63
63
|
* Used either when connecting eagerly with {@link Connection.connect} or
|
|
64
64
|
* calling {@link Connection.ensureConnected}.
|
|
65
65
|
*
|
|
66
|
-
* @format {@link https://www.npmjs.com/package/ms | ms
|
|
66
|
+
* @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
|
|
67
67
|
* @default 10 seconds
|
|
68
68
|
*/
|
|
69
69
|
connectTimeout?: number | string;
|
|
@@ -146,7 +146,14 @@ export interface ConnectionCtorOptions {
|
|
|
146
146
|
* **NOTE**: The namespace provided in {@link options} is **not** automatically set on requests made to the service.
|
|
147
147
|
*/
|
|
148
148
|
readonly workflowService: WorkflowService;
|
|
149
|
+
/**
|
|
150
|
+
* Raw gRPC access to the Temporal {@link https://github.com/temporalio/api/blob/ddf07ab9933e8230309850e3c579e1ff34b03f53/temporal/api/operatorservice/v1/service.proto | operator service}.
|
|
151
|
+
*/
|
|
149
152
|
readonly operatorService: OperatorService;
|
|
153
|
+
/**
|
|
154
|
+
* Raw gRPC access to the standard gRPC {@link https://github.com/grpc/grpc/blob/92f58c18a8da2728f571138c37760a721c8915a2/doc/health-checking.md | health service}.
|
|
155
|
+
*/
|
|
156
|
+
readonly healthService: HealthService;
|
|
150
157
|
readonly callContextStorage: AsyncLocalStorage<CallContext>;
|
|
151
158
|
}
|
|
152
159
|
|
|
@@ -181,6 +188,7 @@ export class Connection {
|
|
|
181
188
|
* {@link https://github.com/temporalio/api/blob/master/temporal/api/operatorservice/v1/service.proto | Operator service}
|
|
182
189
|
*/
|
|
183
190
|
public readonly operatorService: OperatorService;
|
|
191
|
+
public readonly healthService: HealthService;
|
|
184
192
|
readonly callContextStorage: AsyncLocalStorage<CallContext>;
|
|
185
193
|
|
|
186
194
|
protected static createCtorOptions(options?: ConnectionOptions): ConnectionCtorOptions {
|
|
@@ -214,12 +222,20 @@ export class Connection {
|
|
|
214
222
|
interceptors: optionsWithDefaults?.interceptors,
|
|
215
223
|
});
|
|
216
224
|
const operatorService = OperatorService.create(operatorRpcImpl, false, false);
|
|
225
|
+
const healthRpcImpl = this.generateRPCImplementation({
|
|
226
|
+
serviceName: 'grpc.health.v1.Health',
|
|
227
|
+
client,
|
|
228
|
+
callContextStorage,
|
|
229
|
+
interceptors: optionsWithDefaults?.interceptors,
|
|
230
|
+
});
|
|
231
|
+
const healthService = HealthService.create(healthRpcImpl, false, false);
|
|
217
232
|
|
|
218
233
|
return {
|
|
219
234
|
client,
|
|
220
235
|
callContextStorage,
|
|
221
236
|
workflowService,
|
|
222
237
|
operatorService,
|
|
238
|
+
healthService,
|
|
223
239
|
options: optionsWithDefaults,
|
|
224
240
|
};
|
|
225
241
|
}
|
|
@@ -282,12 +298,14 @@ export class Connection {
|
|
|
282
298
|
client,
|
|
283
299
|
workflowService,
|
|
284
300
|
operatorService,
|
|
301
|
+
healthService,
|
|
285
302
|
callContextStorage,
|
|
286
303
|
}: ConnectionCtorOptions) {
|
|
287
304
|
this.options = options;
|
|
288
305
|
this.client = client;
|
|
289
306
|
this.workflowService = workflowService;
|
|
290
307
|
this.operatorService = operatorService;
|
|
308
|
+
this.healthService = healthService;
|
|
291
309
|
this.callContextStorage = callContextStorage;
|
|
292
310
|
}
|
|
293
311
|
|
package/src/grpc-retry.ts
CHANGED
|
@@ -1,37 +1,102 @@
|
|
|
1
|
-
import {
|
|
2
|
-
InterceptingCall,
|
|
3
|
-
Interceptor,
|
|
4
|
-
ListenerBuilder,
|
|
5
|
-
Metadata,
|
|
6
|
-
RequesterBuilder,
|
|
7
|
-
StatusObject,
|
|
8
|
-
} from '@grpc/grpc-js';
|
|
1
|
+
import { InterceptingCall, Interceptor, ListenerBuilder, RequesterBuilder, StatusObject } from '@grpc/grpc-js';
|
|
9
2
|
import * as grpc from '@grpc/grpc-js';
|
|
10
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @experimental
|
|
6
|
+
*/
|
|
11
7
|
export interface GrpcRetryOptions {
|
|
12
|
-
/** Maximum number of allowed retries. Defaults to 10. */
|
|
13
|
-
maxRetries: number;
|
|
14
|
-
|
|
15
8
|
/**
|
|
16
|
-
* A function which accepts the current retry attempt (starts at
|
|
9
|
+
* A function which accepts the current retry attempt (starts at 1) and returns the millisecond
|
|
17
10
|
* delay that should be applied before the next retry.
|
|
18
11
|
*/
|
|
19
|
-
delayFunction: (attempt: number) => number;
|
|
12
|
+
delayFunction: (attempt: number, status: StatusObject) => number;
|
|
20
13
|
|
|
21
14
|
/**
|
|
22
15
|
* A function which accepts a failed status object and returns true if the call should be retried
|
|
23
16
|
*/
|
|
24
|
-
retryableDecider: (status: StatusObject) => boolean;
|
|
17
|
+
retryableDecider: (attempt: number, status: StatusObject) => boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Options for the backoff formula: `factor ^ attempt * initialIntervalMs(status) * jitter(maxJitter)`
|
|
22
|
+
*
|
|
23
|
+
* @experimental
|
|
24
|
+
*/
|
|
25
|
+
export interface BackoffOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Exponential backoff factor
|
|
28
|
+
*
|
|
29
|
+
* @default 2
|
|
30
|
+
*/
|
|
31
|
+
factor: number;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Maximum number of attempts
|
|
35
|
+
*
|
|
36
|
+
* @default 10
|
|
37
|
+
*/
|
|
38
|
+
maxAttempts: number;
|
|
39
|
+
/**
|
|
40
|
+
* Maximum amount of jitter to apply
|
|
41
|
+
*
|
|
42
|
+
* @default 0.1
|
|
43
|
+
*/
|
|
44
|
+
maxJitter: number;
|
|
45
|
+
/**
|
|
46
|
+
* Function that returns the "initial" backoff interval based on the returned status.
|
|
47
|
+
*
|
|
48
|
+
* The default is 1 second for RESOURCE_EXHAUSTED errors and 20 millis for other retryable errors.
|
|
49
|
+
*/
|
|
50
|
+
initialIntervalMs(status: StatusObject): number;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Function that returns the "maximum" backoff interval based on the returned status.
|
|
54
|
+
*
|
|
55
|
+
* The default is 10 seconds regardless of the status.
|
|
56
|
+
*/
|
|
57
|
+
maxIntervalMs(status: StatusObject): number;
|
|
25
58
|
}
|
|
26
59
|
|
|
27
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Add defaults as documented in {@link BackoffOptions}
|
|
62
|
+
*/
|
|
63
|
+
function withDefaultBackoffOptions({
|
|
64
|
+
maxAttempts,
|
|
65
|
+
factor,
|
|
66
|
+
maxJitter,
|
|
67
|
+
initialIntervalMs,
|
|
68
|
+
}: Partial<BackoffOptions>): BackoffOptions {
|
|
28
69
|
return {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
70
|
+
maxAttempts: maxAttempts ?? 10,
|
|
71
|
+
factor: factor ?? 2,
|
|
72
|
+
maxJitter: maxJitter ?? 0.1,
|
|
73
|
+
initialIntervalMs: initialIntervalMs ?? defaultInitialIntervalMs,
|
|
74
|
+
maxIntervalMs() {
|
|
75
|
+
return 10_000;
|
|
76
|
+
},
|
|
32
77
|
};
|
|
33
78
|
}
|
|
34
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Generates the default retry behavior based on given backoff options
|
|
82
|
+
*
|
|
83
|
+
* @experimental
|
|
84
|
+
*/
|
|
85
|
+
export function defaultGrpcRetryOptions(options: Partial<BackoffOptions> = {}): GrpcRetryOptions {
|
|
86
|
+
const { maxAttempts, factor, maxJitter, initialIntervalMs, maxIntervalMs } = withDefaultBackoffOptions(options);
|
|
87
|
+
return {
|
|
88
|
+
delayFunction(attempt, status) {
|
|
89
|
+
return Math.min(maxIntervalMs(status), factor ** attempt * initialIntervalMs(status)) * jitter(maxJitter);
|
|
90
|
+
},
|
|
91
|
+
retryableDecider(attempt, status) {
|
|
92
|
+
return attempt < maxAttempts && isRetryableError(status);
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set of retryable gRPC status codes
|
|
99
|
+
*/
|
|
35
100
|
const retryableCodes = new Set([
|
|
36
101
|
grpc.status.UNKNOWN,
|
|
37
102
|
grpc.status.RESOURCE_EXHAUSTED,
|
|
@@ -45,69 +110,77 @@ export function isRetryableError(status: StatusObject): boolean {
|
|
|
45
110
|
return retryableCodes.has(status.code);
|
|
46
111
|
}
|
|
47
112
|
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Calculates random amount of jitter between 0 and `max`
|
|
115
|
+
*/
|
|
116
|
+
function jitter(max: number) {
|
|
117
|
+
return 1 - max + Math.random() * max * 2;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Default implementation - backs off more on RESOURCE_EXHAUSTED errors
|
|
122
|
+
*/
|
|
123
|
+
function defaultInitialIntervalMs({ code }: StatusObject) {
|
|
124
|
+
// Backoff more on RESOURCE_EXHAUSTED
|
|
125
|
+
if (code === grpc.status.RESOURCE_EXHAUSTED) {
|
|
126
|
+
return 1000;
|
|
127
|
+
}
|
|
128
|
+
return 20;
|
|
51
129
|
}
|
|
52
130
|
|
|
53
131
|
/**
|
|
54
132
|
* Returns a GRPC interceptor that will perform automatic retries for some types of failed calls
|
|
55
133
|
*
|
|
56
134
|
* @param retryOptions Options for the retry interceptor
|
|
135
|
+
*
|
|
136
|
+
* @experimental
|
|
57
137
|
*/
|
|
58
138
|
export function makeGrpcRetryInterceptor(retryOptions: GrpcRetryOptions): Interceptor {
|
|
59
139
|
return (options, nextCall) => {
|
|
60
|
-
let savedMetadata: Metadata;
|
|
61
140
|
let savedSendMessage: any;
|
|
62
141
|
let savedReceiveMessage: any;
|
|
63
|
-
let savedMessageNext: any;
|
|
142
|
+
let savedMessageNext: (message: any) => void;
|
|
143
|
+
|
|
64
144
|
const requester = new RequesterBuilder()
|
|
65
145
|
.withStart(function (metadata, _listener, next) {
|
|
66
|
-
|
|
67
|
-
|
|
146
|
+
// First attempt
|
|
147
|
+
let attempt = 1;
|
|
148
|
+
|
|
149
|
+
const listener = new ListenerBuilder()
|
|
68
150
|
.withOnReceiveMessage((message, next) => {
|
|
69
151
|
savedReceiveMessage = message;
|
|
70
152
|
savedMessageNext = next;
|
|
71
153
|
})
|
|
72
154
|
.withOnReceiveStatus((status, next) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
onReceiveMessage: (message) => {
|
|
155
|
+
const retry = () => {
|
|
156
|
+
attempt++;
|
|
157
|
+
const call = nextCall(options);
|
|
158
|
+
call.start(metadata, {
|
|
159
|
+
onReceiveMessage(message) {
|
|
79
160
|
savedReceiveMessage = message;
|
|
80
161
|
},
|
|
81
|
-
onReceiveStatus
|
|
82
|
-
if (retryOptions.retryableDecider(status)) {
|
|
83
|
-
if (retries <= retryOptions.maxRetries) {
|
|
84
|
-
setTimeout(() => retry(message, metadata), retryOptions.delayFunction(retries));
|
|
85
|
-
} else {
|
|
86
|
-
savedMessageNext(savedReceiveMessage);
|
|
87
|
-
next(status);
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
savedMessageNext(savedReceiveMessage);
|
|
91
|
-
// TODO: For reasons that are completely unclear to me, if you pass a handcrafted
|
|
92
|
-
// status object here, node will magically just exit at the end of this line.
|
|
93
|
-
// No warning, no nothing. Here be dragons.
|
|
94
|
-
next(status);
|
|
95
|
-
}
|
|
96
|
-
},
|
|
162
|
+
onReceiveStatus,
|
|
97
163
|
});
|
|
98
|
-
|
|
99
|
-
|
|
164
|
+
call.sendMessage(savedSendMessage);
|
|
165
|
+
call.halfClose();
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const onReceiveStatus = (status: StatusObject) => {
|
|
169
|
+
if (retryOptions.retryableDecider(attempt, status)) {
|
|
170
|
+
setTimeout(retry, retryOptions.delayFunction(attempt, status));
|
|
171
|
+
} else {
|
|
172
|
+
savedMessageNext(savedReceiveMessage);
|
|
173
|
+
// TODO: For reasons that are completely unclear to me, if you pass a handcrafted
|
|
174
|
+
// status object here, node will magically just exit at the end of this line.
|
|
175
|
+
// No warning, no nothing. Here be dragons.
|
|
176
|
+
next(status);
|
|
177
|
+
}
|
|
100
178
|
};
|
|
101
179
|
|
|
102
|
-
|
|
103
|
-
setTimeout(() => retry(savedSendMessage, savedMetadata), backOffAmount(retries));
|
|
104
|
-
} else {
|
|
105
|
-
savedMessageNext(savedReceiveMessage);
|
|
106
|
-
next(status);
|
|
107
|
-
}
|
|
180
|
+
onReceiveStatus(status);
|
|
108
181
|
})
|
|
109
182
|
.build();
|
|
110
|
-
next(metadata,
|
|
183
|
+
next(metadata, listener);
|
|
111
184
|
})
|
|
112
185
|
.withSendMessage((message, next) => {
|
|
113
186
|
savedSendMessage = message;
|
package/src/index.ts
CHANGED
|
@@ -17,17 +17,18 @@ export {
|
|
|
17
17
|
DataConverter,
|
|
18
18
|
defaultPayloadConverter,
|
|
19
19
|
ProtoFailure,
|
|
20
|
+
RetryPolicy,
|
|
20
21
|
ServerFailure,
|
|
21
22
|
TemporalFailure,
|
|
22
23
|
TerminatedFailure,
|
|
23
24
|
TimeoutFailure,
|
|
24
25
|
} from '@temporalio/common';
|
|
25
|
-
export { TLSConfig } from '@temporalio/internal-non-workflow
|
|
26
|
-
export
|
|
27
|
-
export * from '@temporalio/
|
|
28
|
-
export * from '@temporalio/
|
|
29
|
-
export * from '@temporalio/internal-workflow-common/lib/workflow-handle';
|
|
26
|
+
export { TLSConfig } from '@temporalio/common/lib/internal-non-workflow';
|
|
27
|
+
export * from '@temporalio/common/lib/errors';
|
|
28
|
+
export * from '@temporalio/common/lib/interfaces';
|
|
29
|
+
export * from '@temporalio/common/lib/workflow-handle';
|
|
30
30
|
export * from './async-completion-client';
|
|
31
|
+
export * from './client';
|
|
31
32
|
export { Connection, ConnectionOptions, ConnectionOptionsWithDefaults, LOCAL_TARGET } from './connection';
|
|
32
33
|
export * from './errors';
|
|
33
34
|
export * from './grpc-retry';
|
package/src/interceptors.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Headers, Next } from '@temporalio/
|
|
7
|
+
import { Headers, Next } from '@temporalio/common';
|
|
8
8
|
import { temporal } from '@temporalio/proto';
|
|
9
9
|
import {
|
|
10
10
|
DescribeWorkflowExecutionResponse,
|
|
@@ -112,7 +112,7 @@ export interface WorkflowClientCallsInterceptor {
|
|
|
112
112
|
describe?: (input: WorkflowDescribeInput, next: Next<this, 'describe'>) => Promise<DescribeWorkflowExecutionResponse>;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
interface WorkflowClientCallsInterceptorFactoryInput {
|
|
115
|
+
export interface WorkflowClientCallsInterceptorFactoryInput {
|
|
116
116
|
workflowId: string;
|
|
117
117
|
runId?: string;
|
|
118
118
|
}
|
|
@@ -130,3 +130,12 @@ export interface WorkflowClientCallsInterceptorFactory {
|
|
|
130
130
|
export interface WorkflowClientInterceptors {
|
|
131
131
|
calls?: WorkflowClientCallsInterceptorFactory[];
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Interceptors for any high-level SDK client.
|
|
136
|
+
*
|
|
137
|
+
* NOTE: Currently only for {@link WorkflowClient}. More will be added later as needed.
|
|
138
|
+
*/
|
|
139
|
+
export interface ClientInterceptors {
|
|
140
|
+
workflow?: WorkflowClientInterceptors;
|
|
141
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import type { SearchAttributes } from '@temporalio/
|
|
2
|
-
import
|
|
1
|
+
import type { SearchAttributes } from '@temporalio/common';
|
|
2
|
+
import * as proto from '@temporalio/proto';
|
|
3
3
|
import type * as grpc from '@grpc/grpc-js';
|
|
4
|
-
import Long from 'long';
|
|
5
4
|
|
|
6
5
|
export interface WorkflowExecution {
|
|
7
6
|
workflowId: string;
|
|
8
7
|
runId?: string;
|
|
9
8
|
}
|
|
10
|
-
export type StartWorkflowExecutionRequest = temporal.api.workflowservice.v1.IStartWorkflowExecutionRequest;
|
|
11
|
-
export type GetWorkflowExecutionHistoryRequest =
|
|
12
|
-
|
|
13
|
-
export type
|
|
9
|
+
export type StartWorkflowExecutionRequest = proto.temporal.api.workflowservice.v1.IStartWorkflowExecutionRequest;
|
|
10
|
+
export type GetWorkflowExecutionHistoryRequest =
|
|
11
|
+
proto.temporal.api.workflowservice.v1.IGetWorkflowExecutionHistoryRequest;
|
|
12
|
+
export type DescribeWorkflowExecutionResponse =
|
|
13
|
+
proto.temporal.api.workflowservice.v1.IDescribeWorkflowExecutionResponse;
|
|
14
|
+
export type TerminateWorkflowExecutionResponse =
|
|
15
|
+
proto.temporal.api.workflowservice.v1.ITerminateWorkflowExecutionResponse;
|
|
14
16
|
export type RequestCancelWorkflowExecutionResponse =
|
|
15
|
-
temporal.api.workflowservice.v1.IRequestCancelWorkflowExecutionResponse;
|
|
17
|
+
proto.temporal.api.workflowservice.v1.IRequestCancelWorkflowExecutionResponse;
|
|
16
18
|
|
|
17
19
|
export type WorkflowExecutionStatusName =
|
|
18
20
|
| 'UNSPECIFIED'
|
|
@@ -30,21 +32,23 @@ export interface WorkflowExecutionDescription {
|
|
|
30
32
|
workflowId: string;
|
|
31
33
|
runId: string;
|
|
32
34
|
taskQueue: string;
|
|
33
|
-
status: { code: temporal.api.enums.v1.WorkflowExecutionStatus; name: WorkflowExecutionStatusName };
|
|
34
|
-
historyLength:
|
|
35
|
+
status: { code: proto.temporal.api.enums.v1.WorkflowExecutionStatus; name: WorkflowExecutionStatusName };
|
|
36
|
+
historyLength: number;
|
|
35
37
|
startTime: Date;
|
|
36
38
|
executionTime?: Date;
|
|
37
39
|
closeTime?: Date;
|
|
38
40
|
memo?: Record<string, unknown>;
|
|
39
41
|
searchAttributes: SearchAttributes;
|
|
40
|
-
parentExecution?: Required<temporal.api.common.v1.IWorkflowExecution>;
|
|
42
|
+
parentExecution?: Required<proto.temporal.api.common.v1.IWorkflowExecution>;
|
|
41
43
|
raw: DescribeWorkflowExecutionResponse;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
export type WorkflowService = temporal.api.workflowservice.v1.WorkflowService;
|
|
45
|
-
export const { WorkflowService } = temporal.api.workflowservice.v1;
|
|
46
|
-
export type OperatorService = temporal.api.operatorservice.v1.OperatorService;
|
|
47
|
-
export const { OperatorService } = temporal.api.operatorservice.v1;
|
|
46
|
+
export type WorkflowService = proto.temporal.api.workflowservice.v1.WorkflowService;
|
|
47
|
+
export const { WorkflowService } = proto.temporal.api.workflowservice.v1;
|
|
48
|
+
export type OperatorService = proto.temporal.api.operatorservice.v1.OperatorService;
|
|
49
|
+
export const { OperatorService } = proto.temporal.api.operatorservice.v1;
|
|
50
|
+
export type HealthService = proto.grpc.health.v1.Health;
|
|
51
|
+
export const { Health: HealthService } = proto.grpc.health.v1;
|
|
48
52
|
|
|
49
53
|
/**
|
|
50
54
|
* Mapping of string to valid gRPC metadata value
|