4
4
CancelledFailure ,
5
5
compileRetryPolicy ,
6
6
mapToPayloads ,
7
+ HistoryAndWorkflowId ,
7
8
QueryDefinition ,
8
9
RetryState ,
9
10
searchAttributePayloadConverter ,
@@ -18,6 +19,7 @@ import {
18
19
WorkflowResultType ,
19
20
} from '@temporalio/common' ;
20
21
import { composeInterceptors } from '@temporalio/common/lib/interceptors' ;
22
+ import { History } from '@temporalio/common/lib/proto-utils' ;
21
23
import {
22
24
decodeArrayFromPayloads ,
23
25
decodeFromPayloadsAtIndex ,
@@ -65,6 +67,7 @@ import {
65
67
LoadedWithDefaults ,
66
68
WithDefaults ,
67
69
} from './base-client' ;
70
+ import { mapAsyncIterable } from './iterators-utils' ;
68
71
69
72
/**
70
73
* A client side handle to a single Workflow instance.
@@ -131,6 +134,11 @@ export interface WorkflowHandle<T extends Workflow = Workflow> extends BaseWorkf
131
134
*/
132
135
describe ( ) : Promise < WorkflowExecutionDescription > ;
133
136
137
+ /**
138
+ * Return a workflow execution's history
139
+ */
140
+ fetchHistory ( ) : Promise < History > ;
141
+
134
142
/**
135
143
* Readonly accessor to the underlying WorkflowClient
136
144
*/
@@ -245,6 +253,19 @@ interface WorkflowHandleOptions extends GetWorkflowHandleOptions {
245
253
runIdForResult ?: string ;
246
254
}
247
255
256
+ /**
257
+ * An iterable list of WorkflowExecution, as returned by {@link WorkflowClient.list}.
258
+ */
259
+ interface AsyncWorkflowListIterable extends AsyncIterable < WorkflowExecutionInfo > {
260
+ /**
261
+ * Return an iterable of histories corresponding to this iterable's WorkflowExecutions.
262
+ * Workflow histories will be fetched concurrently.
263
+ *
264
+ * Useful in batch replaying
265
+ */
266
+ intoHistories : ( intoHistoriesOptions ?: IntoHistoriesOptions ) => AsyncIterable < HistoryAndWorkflowId > ;
267
+ }
268
+
248
269
/**
249
270
* Options for {@link WorkflowClient.list}
250
271
*/
@@ -261,6 +282,31 @@ export interface ListOptions {
261
282
query ?: string ;
262
283
}
263
284
285
+ /**
286
+ * Options for {@link WorkflowClient.list().intoHistories()}
287
+ */
288
+ export interface IntoHistoriesOptions {
289
+ /**
290
+ * Maximum number of workflow histories to download concurrently.
291
+ *
292
+ * @default 5
293
+ */
294
+ concurrency ?: number ;
295
+
296
+ /**
297
+ * Maximum number of workflow histories to buffer ahead, ready for consumption.
298
+ *
299
+ * It is recommended to set `bufferLimit` to a rasonnably low number if it is expected that the
300
+ * iterable may be stopped before reaching completion (for example, when implementing a fail fast
301
+ * bach replay test).
302
+ *
303
+ * Ignored unless `concurrency > 1`. No limit applies if set to `undefined`.
304
+ *
305
+ * @default unlimited
306
+ */
307
+ bufferLimit ?: number ;
308
+ }
309
+
264
310
/**
265
311
* Client for starting Workflow executions and creating Workflow handles.
266
312
*
@@ -799,6 +845,22 @@ export class WorkflowClient extends BaseClient {
799
845
( info as unknown as WorkflowExecutionDescription ) . raw = raw ;
800
846
return info ;
801
847
} ,
848
+ async fetchHistory ( ) {
849
+ let nextPageToken : Uint8Array | undefined = undefined ;
850
+ const events = Array < temporal . api . history . v1 . IHistoryEvent > ( ) ;
851
+ for ( ; ; ) {
852
+ const response : temporal . api . workflowservice . v1 . GetWorkflowExecutionHistoryResponse =
853
+ await this . client . workflowService . getWorkflowExecutionHistory ( {
854
+ nextPageToken,
855
+ namespace : this . client . options . namespace ,
856
+ execution : { workflowId, runId } ,
857
+ } ) ;
858
+ events . push ( ...( response . history ?. events ?? [ ] ) ) ;
859
+ nextPageToken = response . nextPageToken ;
860
+ if ( nextPageToken == null || nextPageToken . length === 0 ) break ;
861
+ }
862
+ return temporal . api . history . v1 . History . create ( { events } ) ;
863
+ } ,
802
864
async signal < Args extends any [ ] > ( def : SignalDefinition < Args > | string , ...args : Args ) : Promise < void > {
803
865
const next = this . client . _signalWorkflowHandler . bind ( this . client ) ;
804
866
const fn = interceptors . length ? composeInterceptors ( interceptors , 'signal' , next ) : next ;
@@ -858,15 +920,7 @@ export class WorkflowClient extends BaseClient {
858
920
} ) ;
859
921
}
860
922
861
- /**
862
- * List workflows by given `query`.
863
- *
864
- * ⚠️ To use advanced query functionality, as of the 1.18 server release, you must use Elasticsearch based visibility.
865
- *
866
- * More info on the concept of "visibility" and the query syntax on the Temporal documentation site:
867
- * https://docs.temporal.io/visibility
868
- */
869
- public async * list ( options ?: ListOptions ) : AsyncIterable < WorkflowExecutionInfo > {
923
+ protected async * _list ( options ?: ListOptions ) : AsyncIterable < WorkflowExecutionInfo > {
870
924
let nextPageToken : Uint8Array = Buffer . alloc ( 0 ) ;
871
925
for ( ; ; ) {
872
926
const response = await this . workflowService . listWorkflowExecutions ( {
@@ -882,10 +936,36 @@ export class WorkflowClient extends BaseClient {
882
936
yield await executionInfoFromRaw ( raw , this . dataConverter ) ;
883
937
}
884
938
nextPageToken = response . nextPageToken ;
885
- if ( nextPageToken == null || nextPageToken . length == 0 ) break ;
939
+ if ( nextPageToken == null || nextPageToken . length === 0 ) break ;
886
940
}
887
941
}
888
942
943
+ /**
944
+ * List workflows by given `query`.
945
+ *
946
+ * ⚠️ To use advanced query functionality, as of the 1.18 server release, you must use Elasticsearch based visibility.
947
+ *
948
+ * More info on the concept of "visibility" and the query syntax on the Temporal documentation site:
949
+ * https://docs.temporal.io/visibility
950
+ */
951
+ public list ( options ?: ListOptions ) : AsyncWorkflowListIterable {
952
+ return {
953
+ [ Symbol . asyncIterator ] : ( ) => this . _list ( options ) [ Symbol . asyncIterator ] ( ) ,
954
+ intoHistories : ( intoHistoriesOptions ?: IntoHistoriesOptions ) => {
955
+ return mapAsyncIterable (
956
+ this . _list ( options ) ,
957
+ async ( { workflowId, runId } ) => ( {
958
+ workflowId,
959
+ history : await this . getHandle ( workflowId , runId )
960
+ . fetchHistory ( )
961
+ . catch ( ( _ ) => undefined ) ,
962
+ } ) ,
963
+ { concurrency : intoHistoriesOptions ?. concurrency ?? 5 }
964
+ ) ;
965
+ } ,
966
+ } ;
967
+ }
968
+
889
969
protected getOrMakeInterceptors ( workflowId : string , runId ?: string ) : WorkflowClientInterceptor [ ] {
890
970
if ( typeof this . options . interceptors === 'object' && 'calls' in this . options . interceptors ) {
891
971
// eslint-disable-next-line deprecation/deprecation
0 commit comments