@hotmeshio/hotmesh 0.0.16 → 0.0.18
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/build/modules/utils.d.ts +3 -0
- package/build/modules/utils.js +10 -1
- package/build/package.json +1 -1
- package/build/services/activities/activity.d.ts +4 -12
- package/build/services/activities/activity.js +19 -156
- package/build/services/activities/hook.d.ts +20 -0
- package/build/services/activities/hook.js +124 -0
- package/build/services/activities/index.d.ts +2 -0
- package/build/services/activities/index.js +2 -0
- package/build/services/activities/trigger.js +1 -1
- package/build/services/collator/index.js +0 -1
- package/build/services/compiler/deployer.d.ts +2 -0
- package/build/services/compiler/deployer.js +29 -2
- package/build/services/durable/client.d.ts +8 -1
- package/build/services/durable/client.js +47 -0
- package/build/services/durable/factory.js +88 -11
- package/build/services/durable/search.d.ts +15 -0
- package/build/services/durable/search.js +45 -0
- package/build/services/durable/workflow.d.ts +1 -0
- package/build/services/durable/workflow.js +36 -0
- package/build/services/engine/index.d.ts +7 -2
- package/build/services/engine/index.js +2 -1
- package/build/services/store/clients/ioredis.d.ts +1 -0
- package/build/services/store/clients/ioredis.js +12 -0
- package/build/services/store/clients/redis.d.ts +1 -0
- package/build/services/store/clients/redis.js +3 -0
- package/build/services/store/index.d.ts +1 -0
- package/build/services/telemetry/index.js +2 -1
- package/build/types/activity.d.ts +6 -3
- package/build/types/durable.d.ts +11 -1
- package/build/types/hook.d.ts +1 -0
- package/build/types/index.d.ts +2 -2
- package/modules/utils.ts +11 -0
- package/package.json +1 -1
- package/services/activities/activity.ts +20 -167
- package/services/activities/hook.ts +149 -0
- package/services/activities/index.ts +2 -0
- package/services/activities/trigger.ts +1 -1
- package/services/collator/index.ts +0 -1
- package/services/compiler/deployer.ts +32 -2
- package/services/durable/client.ts +51 -2
- package/services/durable/factory.ts +88 -11
- package/services/durable/search.ts +54 -0
- package/services/durable/workflow.ts +34 -1
- package/services/engine/index.ts +8 -4
- package/services/store/clients/ioredis.ts +13 -0
- package/services/store/clients/redis.ts +4 -0
- package/services/store/index.ts +1 -0
- package/services/telemetry/index.ts +2 -1
- package/types/activity.ts +7 -2
- package/types/durable.ts +9 -0
- package/types/hook.ts +1 -0
- package/types/index.ts +2 -0
|
@@ -24,6 +24,8 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
24
24
|
schema:
|
|
25
25
|
type: object
|
|
26
26
|
properties:
|
|
27
|
+
parentWorkflowId:
|
|
28
|
+
type: string
|
|
27
29
|
workflowId:
|
|
28
30
|
type: string
|
|
29
31
|
arguments:
|
|
@@ -46,12 +48,17 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
46
48
|
type: trigger
|
|
47
49
|
stats:
|
|
48
50
|
id: '{$self.input.data.workflowId}'
|
|
51
|
+
key: '{$self.input.data.parentWorkflowId}'
|
|
52
|
+
granularity: infinity
|
|
53
|
+
measures:
|
|
54
|
+
- measure: index
|
|
55
|
+
target: '{$self.input.data.parentWorkflowId}'
|
|
49
56
|
job:
|
|
50
57
|
maps:
|
|
51
58
|
done: false
|
|
52
59
|
|
|
53
60
|
a1:
|
|
54
|
-
type:
|
|
61
|
+
type: hook
|
|
55
62
|
cycle: true
|
|
56
63
|
output:
|
|
57
64
|
schema:
|
|
@@ -65,6 +72,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
65
72
|
w1:
|
|
66
73
|
type: worker
|
|
67
74
|
topic: '{t1.output.data.workflowTopic}'
|
|
75
|
+
emit: '{$job.data.done}'
|
|
68
76
|
retry:
|
|
69
77
|
'599': [2]
|
|
70
78
|
input:
|
|
@@ -122,6 +130,19 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
122
130
|
response: '{$self.output.data.response}'
|
|
123
131
|
done: '{$self.output.data.done}'
|
|
124
132
|
|
|
133
|
+
a2:
|
|
134
|
+
type: hook
|
|
135
|
+
title: Wait for cleanup signal
|
|
136
|
+
hook:
|
|
137
|
+
type: object
|
|
138
|
+
properties:
|
|
139
|
+
done:
|
|
140
|
+
type: boolean
|
|
141
|
+
job:
|
|
142
|
+
maps:
|
|
143
|
+
workflowId: '{t1.output.data.workflowId}'
|
|
144
|
+
|
|
145
|
+
|
|
125
146
|
a594:
|
|
126
147
|
title: Wait for signals
|
|
127
148
|
type: await
|
|
@@ -226,7 +247,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
226
247
|
|
|
227
248
|
a599:
|
|
228
249
|
title: Sleep exponentially longer before retrying
|
|
229
|
-
type:
|
|
250
|
+
type: hook
|
|
230
251
|
sleep: '{a1.output.data.duration}'
|
|
231
252
|
|
|
232
253
|
c599:
|
|
@@ -279,7 +300,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
279
300
|
done: true
|
|
280
301
|
|
|
281
302
|
s2:
|
|
282
|
-
title: Awaken
|
|
303
|
+
title: Awaken sleeping flows so they end and self-clean
|
|
283
304
|
type: signal
|
|
284
305
|
subtype: all
|
|
285
306
|
key_name: parentWorkflowId
|
|
@@ -351,10 +372,55 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
351
372
|
type: boolean
|
|
352
373
|
maps:
|
|
353
374
|
done: true
|
|
375
|
+
|
|
376
|
+
s4:
|
|
377
|
+
title: Awaken child FLOWS so they end and self-clean
|
|
378
|
+
type: signal
|
|
379
|
+
subtype: all
|
|
380
|
+
key_name: parentWorkflowId
|
|
381
|
+
key_value:
|
|
382
|
+
'@pipe':
|
|
383
|
+
- ['{$job.metadata.jid}', '-f']
|
|
384
|
+
- ['{@string.concat}']
|
|
385
|
+
topic: ${app}.childflow.awaken
|
|
386
|
+
resolver:
|
|
387
|
+
schema:
|
|
388
|
+
type: object
|
|
389
|
+
properties:
|
|
390
|
+
data:
|
|
391
|
+
type: object
|
|
392
|
+
properties:
|
|
393
|
+
parentWorkflowId:
|
|
394
|
+
type: string
|
|
395
|
+
scrub:
|
|
396
|
+
type: boolean
|
|
397
|
+
maps:
|
|
398
|
+
data:
|
|
399
|
+
parentWorkflowId:
|
|
400
|
+
'@pipe':
|
|
401
|
+
- ['{$job.metadata.jid}', '-f']
|
|
402
|
+
- ['{@string.concat}']
|
|
403
|
+
scrub: true
|
|
404
|
+
signal:
|
|
405
|
+
schema:
|
|
406
|
+
type: object
|
|
407
|
+
properties:
|
|
408
|
+
done:
|
|
409
|
+
type: boolean
|
|
410
|
+
maps:
|
|
411
|
+
done: true
|
|
354
412
|
|
|
355
413
|
transitions:
|
|
356
414
|
t1:
|
|
357
415
|
- to: a1
|
|
416
|
+
- to: a2
|
|
417
|
+
conditions:
|
|
418
|
+
match:
|
|
419
|
+
- expected: true
|
|
420
|
+
actual:
|
|
421
|
+
'@pipe':
|
|
422
|
+
- ['{$job.metadata.key}', true, false]
|
|
423
|
+
- ['{@conditional.ternary}']
|
|
358
424
|
a1:
|
|
359
425
|
- to: w1
|
|
360
426
|
w1:
|
|
@@ -376,11 +442,13 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
376
442
|
- to: s3
|
|
377
443
|
conditions:
|
|
378
444
|
code: [200, 598, 597, 596]
|
|
445
|
+
- to: s4
|
|
446
|
+
conditions:
|
|
447
|
+
code: [200, 598, 597, 596]
|
|
379
448
|
a594:
|
|
380
449
|
- to: c594
|
|
381
450
|
conditions:
|
|
382
451
|
code: 202
|
|
383
|
-
|
|
384
452
|
a595:
|
|
385
453
|
- to: c595
|
|
386
454
|
conditions:
|
|
@@ -388,6 +456,14 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
388
456
|
a599:
|
|
389
457
|
- to: c599
|
|
390
458
|
|
|
459
|
+
hooks:
|
|
460
|
+
${app}.childflow.awaken:
|
|
461
|
+
- to: a2
|
|
462
|
+
conditions:
|
|
463
|
+
match:
|
|
464
|
+
- expected: '{t1.output.data.workflowId}'
|
|
465
|
+
actual: '{$self.hook.data.id}'
|
|
466
|
+
|
|
391
467
|
- subscribes: ${app}.activity.execute
|
|
392
468
|
publishes: ${app}.activity.executed
|
|
393
469
|
|
|
@@ -463,7 +539,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
463
539
|
done: true
|
|
464
540
|
|
|
465
541
|
s1a:
|
|
466
|
-
type:
|
|
542
|
+
type: hook
|
|
467
543
|
title: Wait for cleanup signal
|
|
468
544
|
hook:
|
|
469
545
|
type: object
|
|
@@ -483,6 +559,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
483
559
|
hooks:
|
|
484
560
|
${app}.activity.awaken:
|
|
485
561
|
- to: s1a
|
|
562
|
+
keep_alive: true
|
|
486
563
|
conditions:
|
|
487
564
|
match:
|
|
488
565
|
- expected: '{t1a.output.data.workflowId}'
|
|
@@ -529,13 +606,13 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
529
606
|
target: '{$self.input.data.parentWorkflowId}'
|
|
530
607
|
|
|
531
608
|
a1s:
|
|
532
|
-
type:
|
|
609
|
+
type: hook
|
|
533
610
|
title: Sleep for a duration
|
|
534
611
|
sleep: '{t1s.output.data.duration}'
|
|
535
612
|
emit: true
|
|
536
613
|
|
|
537
614
|
a2s:
|
|
538
|
-
type:
|
|
615
|
+
type: hook
|
|
539
616
|
title: Wait for cleanup signal
|
|
540
617
|
hook:
|
|
541
618
|
type: object
|
|
@@ -607,7 +684,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
607
684
|
|
|
608
685
|
a1wc:
|
|
609
686
|
title: Split signal data
|
|
610
|
-
type:
|
|
687
|
+
type: hook
|
|
611
688
|
cycle: true
|
|
612
689
|
output:
|
|
613
690
|
schema:
|
|
@@ -645,7 +722,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
645
722
|
- ['{t1wc.output.data.signals}', 1]
|
|
646
723
|
- ['{@array.slice}']
|
|
647
724
|
a2wc:
|
|
648
|
-
type:
|
|
725
|
+
type: hook
|
|
649
726
|
output:
|
|
650
727
|
schema:
|
|
651
728
|
type: object
|
|
@@ -772,7 +849,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
772
849
|
target: '{$self.input.data.parentWorkflowId}'
|
|
773
850
|
|
|
774
851
|
a1ww:
|
|
775
|
-
type:
|
|
852
|
+
type: hook
|
|
776
853
|
title: Wait for custom signal
|
|
777
854
|
emit: true
|
|
778
855
|
hook:
|
|
@@ -787,7 +864,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
787
864
|
signalId: '{t1ww.output.data.signalId}'
|
|
788
865
|
|
|
789
866
|
a2ww:
|
|
790
|
-
type:
|
|
867
|
+
type: hook
|
|
791
868
|
title: Wait for cleanup signal
|
|
792
869
|
hook:
|
|
793
870
|
type: object
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { HotMeshService as HotMesh } from '../hotmesh'
|
|
2
|
+
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
3
|
+
import { StoreService } from '../store';
|
|
4
|
+
import { KeyService, KeyType } from '../../modules/key';
|
|
5
|
+
|
|
6
|
+
export class Search {
|
|
7
|
+
jobId: string;
|
|
8
|
+
hotMeshClient: HotMesh;
|
|
9
|
+
store: StoreService<RedisClient, RedisMulti> | null;
|
|
10
|
+
|
|
11
|
+
safeKey(key:string): string {
|
|
12
|
+
//note: protect the execution namespace with a prefix,
|
|
13
|
+
//so its design never conflicts with the hotmesh keyspace
|
|
14
|
+
return `_${key}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
constructor(workflowId: string, hotMeshClient: HotMesh) {
|
|
18
|
+
const keyParams = {
|
|
19
|
+
appId: hotMeshClient.appId,
|
|
20
|
+
jobId: ''
|
|
21
|
+
}
|
|
22
|
+
const hotMeshPrefix = KeyService.mintKey(hotMeshClient.namespace, KeyType.JOB_STATE, keyParams);
|
|
23
|
+
this.jobId = `${hotMeshPrefix}${workflowId}`;
|
|
24
|
+
this.hotMeshClient = hotMeshClient;
|
|
25
|
+
this.store = hotMeshClient.engine.store as StoreService<RedisClient, RedisMulti>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async set(key: string, value: string): Promise<void> {
|
|
29
|
+
await this.store.exec('HSET', this.jobId, this.safeKey(key), value.toString());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async get(key: string): Promise<string> {
|
|
33
|
+
try {
|
|
34
|
+
return await this.store.exec('HGET',this.jobId, this.safeKey(key)) as string;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
this.hotMeshClient.logger.error('durable-search-get-error', { err });
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async del(key: string): Promise<void> {
|
|
42
|
+
await this.store.exec('HDEL', this.jobId, this.safeKey(key));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async incr(key: string, val: number): Promise<number> {
|
|
46
|
+
return Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), val.toString()) as string);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async mult(key: string, val: number): Promise<number> {
|
|
50
|
+
const log = Math.log(val);
|
|
51
|
+
const logTotal = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), log.toString()) as string);
|
|
52
|
+
return Math.exp(logTotal);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -4,10 +4,11 @@ import { asyncLocalStorage } from './asyncLocalStorage';
|
|
|
4
4
|
import { WorkerService } from './worker';
|
|
5
5
|
import { ClientService as Client } from './client';
|
|
6
6
|
import { ConnectionService as Connection } from './connection';
|
|
7
|
-
import { ActivityConfig, ProxyType, WorkflowOptions } from "../../types/durable";
|
|
7
|
+
import { ActivityConfig, ProxyType, WorkflowConfig, WorkflowOptions, WorkflowSearchOptions } from "../../types/durable";
|
|
8
8
|
import { JobOutput, JobState } from '../../types';
|
|
9
9
|
import { ACTIVITY_PUBLISHES_TOPIC, ACTIVITY_SUBSCRIBES_TOPIC, SLEEP_SUBSCRIBES_TOPIC, WFS_SUBSCRIBES_TOPIC } from './factory';
|
|
10
10
|
import { DurableIncompleteSignalError, DurableSleepError, DurableWaitForSignalError } from '../../modules/errors';
|
|
11
|
+
import { Search } from './search';
|
|
11
12
|
|
|
12
13
|
/*
|
|
13
14
|
`proxyActivities` returns a wrapped instance of the
|
|
@@ -54,6 +55,7 @@ export class WorkflowService {
|
|
|
54
55
|
const COUNTER = store.get('counter');
|
|
55
56
|
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
56
57
|
const childJobId = `${workflowId}-$${options.workflowName}-${execIndex}`;
|
|
58
|
+
const parentWorkflowId = `${workflowId}-f`;
|
|
57
59
|
|
|
58
60
|
const client = new Client({
|
|
59
61
|
connection: await Connection.connect(WorkerService.connection),
|
|
@@ -71,6 +73,7 @@ export class WorkflowService {
|
|
|
71
73
|
handle = await client.workflow.start({
|
|
72
74
|
...options,
|
|
73
75
|
workflowId: childJobId,
|
|
76
|
+
parentWorkflowId,
|
|
74
77
|
workflowTrace,
|
|
75
78
|
workflowSpan,
|
|
76
79
|
});
|
|
@@ -95,6 +98,36 @@ export class WorkflowService {
|
|
|
95
98
|
return proxy;
|
|
96
99
|
}
|
|
97
100
|
|
|
101
|
+
static async data(command: 'del' | 'get' | 'set' | 'incr' | 'mult', ...args: string[]): Promise<number | boolean | string> {
|
|
102
|
+
const store = asyncLocalStorage.getStore();
|
|
103
|
+
if (!store) {
|
|
104
|
+
throw new Error('durable-store-not-found');
|
|
105
|
+
}
|
|
106
|
+
const workflowId = store.get('workflowId');
|
|
107
|
+
const workflowTopic = store.get('workflowTopic');
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const hotMeshClient = await WorkerService.getHotMesh(workflowTopic);
|
|
111
|
+
const search = new Search(workflowId, hotMeshClient);
|
|
112
|
+
if (command === 'get') {
|
|
113
|
+
return await search.get(args[0]) as string;
|
|
114
|
+
} else if (command === 'set') {
|
|
115
|
+
await search.set(args[0], args[1]);
|
|
116
|
+
return true;
|
|
117
|
+
} else if (command === 'del') {
|
|
118
|
+
await search.del(args[0]);
|
|
119
|
+
return true;
|
|
120
|
+
} else if (command === 'incr') {
|
|
121
|
+
return await search.incr(args[0], Number(args[1])) as number;
|
|
122
|
+
} else if (command === 'mult') {
|
|
123
|
+
return await search.mult(args[0], Number(args[1])) as number;
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.error(e);
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
98
131
|
static async sleep(duration: string): Promise<number> {
|
|
99
132
|
const seconds = ms(duration) / 1000;
|
|
100
133
|
|
package/services/engine/index.ts
CHANGED
|
@@ -3,10 +3,13 @@ import {
|
|
|
3
3
|
formatISODate,
|
|
4
4
|
getSubscriptionTopic,
|
|
5
5
|
identifyRedisType,
|
|
6
|
+
polyfill,
|
|
6
7
|
restoreHierarchy } from '../../modules/utils';
|
|
7
8
|
import Activities from '../activities';
|
|
8
|
-
import { Activity } from '../activities/activity';
|
|
9
9
|
import { Await } from '../activities/await';
|
|
10
|
+
import { Cycle } from '../activities/cycle';
|
|
11
|
+
import { Hook } from '../activities/hook';
|
|
12
|
+
import { Signal } from '../activities/signal';
|
|
10
13
|
import { Worker } from '../activities/worker';
|
|
11
14
|
import { Trigger } from '../activities/trigger';
|
|
12
15
|
import { CompilerService } from '../compiler';
|
|
@@ -235,9 +238,10 @@ class EngineService {
|
|
|
235
238
|
}
|
|
236
239
|
|
|
237
240
|
// ************* METADATA/MODEL METHODS *************
|
|
238
|
-
async initActivity(topic: string, data: JobData = {}, context?: JobState): Promise<
|
|
241
|
+
async initActivity(topic: string, data: JobData = {}, context?: JobState): Promise<Await|Cycle|Hook|Signal|Trigger|Worker> {
|
|
239
242
|
const [activityId, schema] = await this.getSchema(topic);
|
|
240
|
-
|
|
243
|
+
polyfill
|
|
244
|
+
const ActivityHandler = Activities[polyfill.resolveActivityType(schema.type)];
|
|
241
245
|
if (ActivityHandler) {
|
|
242
246
|
const utc = formatISODate(new Date());
|
|
243
247
|
const metadata: ActivityMetadata = {
|
|
@@ -334,7 +338,7 @@ class EngineService {
|
|
|
334
338
|
data: streamData.data,
|
|
335
339
|
};
|
|
336
340
|
if (streamData.type === StreamDataType.TIMEHOOK || streamData.type === StreamDataType.WEBHOOK || streamData.type === StreamDataType.TRANSITION) {
|
|
337
|
-
const activityHandler = await this.initActivity(`.${streamData.metadata.aid}`, context.data, context as JobState) as
|
|
341
|
+
const activityHandler = await this.initActivity(`.${streamData.metadata.aid}`, context.data, context as JobState) as Hook;
|
|
338
342
|
if (streamData.type === StreamDataType.TIMEHOOK) {
|
|
339
343
|
await activityHandler.processTimeHookEvent(streamData.metadata.jid);
|
|
340
344
|
} else if (streamData.type === StreamDataType.TRANSITION) {
|
|
@@ -5,6 +5,7 @@ import { Cache } from '../cache';
|
|
|
5
5
|
import { StoreService } from '../index';
|
|
6
6
|
import { RedisClientType, RedisMultiType } from '../../../types/ioredisclient';
|
|
7
7
|
import { ReclaimedMessageType } from '../../../types/stream';
|
|
8
|
+
import { type } from 'os';
|
|
8
9
|
|
|
9
10
|
class IORedisStoreService extends StoreService<RedisClientType, RedisMultiType> {
|
|
10
11
|
redisClient: RedisClientType;
|
|
@@ -22,6 +23,18 @@ class IORedisStoreService extends StoreService<RedisClientType, RedisMultiType>
|
|
|
22
23
|
return this.redisClient.multi();
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
async exec(...args: any[]): Promise<string|string[]|string[][]> {
|
|
27
|
+
const response = await this.redisClient.call.apply(this.redisClient, args as any);
|
|
28
|
+
if (typeof response === 'string') {
|
|
29
|
+
return response as string;
|
|
30
|
+
} else if (Array.isArray(response)) {
|
|
31
|
+
if (Array.isArray(response[0])) {
|
|
32
|
+
return response as string[][];
|
|
33
|
+
}
|
|
34
|
+
return response as string[];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
hGetAllResult(result: any) {
|
|
26
39
|
//ioredis response signature is [null, {}] or [null, null]
|
|
27
40
|
return result[1];
|
|
@@ -50,6 +50,10 @@ class RedisStoreService extends StoreService<RedisClientType, RedisMultiType> {
|
|
|
50
50
|
return multi as unknown as RedisMultiType;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
async exec(...args: any[]): Promise<string|string[]|string[][]> {
|
|
54
|
+
return await this.redisClient.sendCommand(args);
|
|
55
|
+
}
|
|
56
|
+
|
|
53
57
|
async publish(keyType: KeyType.QUORUM, message: Record<string, any>, appId: string, engineId?: string): Promise<boolean> {
|
|
54
58
|
const topic = this.mintKey(keyType, { appId, engineId });
|
|
55
59
|
const status: number = await this.redisClient.publish(topic, JSON.stringify(message));
|
package/services/store/index.ts
CHANGED
|
@@ -70,6 +70,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
70
70
|
|
|
71
71
|
//todo: standardize signatures and move concrete methods to this class
|
|
72
72
|
abstract getMulti(): U;
|
|
73
|
+
abstract exec(...args: any[]): Promise<string|string[]|string[][]>;
|
|
73
74
|
abstract publish(
|
|
74
75
|
keyType: KeyType.QUORUM,
|
|
75
76
|
message: Record<string, any>,
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
Context,
|
|
19
19
|
context,
|
|
20
20
|
SpanStatusCode } from '../../types/telemetry';
|
|
21
|
+
import { polyfill } from '../../modules/utils';
|
|
21
22
|
|
|
22
23
|
class TelemetryService {
|
|
23
24
|
span: Span;
|
|
@@ -253,7 +254,7 @@ class TelemetryService {
|
|
|
253
254
|
if (config.type === 'trigger') {
|
|
254
255
|
state[`${metadata.aid}/output/metadata/l1s`] = context['$self'].output.metadata.l1s;
|
|
255
256
|
state[`${metadata.aid}/output/metadata/l2s`] = context['$self'].output.metadata.l2s;
|
|
256
|
-
} else if (config.type === '
|
|
257
|
+
} else if (polyfill.resolveActivityType(config.type) === 'hook' && leg === 1) {
|
|
257
258
|
//activities run non-duplexed and only have a single leg
|
|
258
259
|
state[`${metadata.aid}/output/metadata/l1s`] = context['$self'].output.metadata.l1s;
|
|
259
260
|
state[`${metadata.aid}/output/metadata/l2s`] = context['$self'].output.metadata.l1s;
|
package/types/activity.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MetricTypes } from "./stats";
|
|
2
2
|
import { StreamRetryPolicy } from "./stream";
|
|
3
3
|
|
|
4
|
-
type ActivityExecutionType = 'trigger' | 'await' | 'worker' | 'activity' | 'emit' | 'iterate' | 'cycle' | 'signal';
|
|
4
|
+
type ActivityExecutionType = 'trigger' | 'await' | 'worker' | 'activity' | 'emit' | 'iterate' | 'cycle' | 'signal' | 'hook';
|
|
5
5
|
|
|
6
6
|
type Consumes = Record<string, string[]>;
|
|
7
7
|
|
|
@@ -65,6 +65,10 @@ interface CycleActivity extends BaseActivity {
|
|
|
65
65
|
ancestor: string; //ancestor activity id
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
interface HookActivity extends BaseActivity {
|
|
69
|
+
type: 'hook';
|
|
70
|
+
}
|
|
71
|
+
|
|
68
72
|
interface SignalActivity extends BaseActivity {
|
|
69
73
|
type: 'signal'; //signal activities call hook/hookAll
|
|
70
74
|
subtype: 'one' | 'all'; //trigger: hook(One) or hookAll
|
|
@@ -80,7 +84,7 @@ interface IterateActivity extends BaseActivity {
|
|
|
80
84
|
type: 'iterate';
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
type ActivityType = BaseActivity | TriggerActivity | AwaitActivity | WorkerActivity | IterateActivity;
|
|
87
|
+
type ActivityType = BaseActivity | TriggerActivity | AwaitActivity | WorkerActivity | IterateActivity | HookActivity;
|
|
84
88
|
|
|
85
89
|
type ActivityData = Record<string, any>;
|
|
86
90
|
type ActivityMetadata = {
|
|
@@ -124,6 +128,7 @@ export {
|
|
|
124
128
|
TriggerActivityStats,
|
|
125
129
|
AwaitActivity,
|
|
126
130
|
CycleActivity,
|
|
131
|
+
HookActivity,
|
|
127
132
|
SignalActivity,
|
|
128
133
|
BaseActivity,
|
|
129
134
|
IterateActivity,
|
package/types/durable.ts
CHANGED
|
@@ -7,13 +7,21 @@ type WorkflowConfig = {
|
|
|
7
7
|
initialInterval?: string; //default 1s
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
type WorkflowSearchOptions = {
|
|
11
|
+
index: string; //FT index name (myapp:myindex)
|
|
12
|
+
prefix: string[]; //FT prefixes (['myapp:myindex:prefix1', 'myapp:myindex:prefix2'])
|
|
13
|
+
schema: Record<string, {type: 'TEXT' | 'NUMERIC' | 'TAG', sortable: boolean}>;
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
type WorkflowOptions = {
|
|
11
17
|
taskQueue: string;
|
|
12
18
|
args: any[]; //input arguments to pass in
|
|
13
19
|
workflowId: string; //execution id (the job id)
|
|
14
20
|
workflowName?: string; //the name of the user's workflow function
|
|
21
|
+
parentWorkflowId?: string; //system reserved; the id of the parent; if present the flow will not self-clean until the parent that spawned it self-cleans
|
|
15
22
|
workflowTrace?: string;
|
|
16
23
|
workflowSpan?: string;
|
|
24
|
+
search?: WorkflowSearchOptions
|
|
17
25
|
config?: WorkflowConfig;
|
|
18
26
|
}
|
|
19
27
|
|
|
@@ -100,6 +108,7 @@ export {
|
|
|
100
108
|
WorkerConfig,
|
|
101
109
|
WorkflowConfig,
|
|
102
110
|
WorkerOptions,
|
|
111
|
+
WorkflowSearchOptions,
|
|
103
112
|
WorkflowDataType,
|
|
104
113
|
WorkflowOptions,
|
|
105
114
|
};
|
package/types/hook.ts
CHANGED
package/types/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export {
|
|
|
10
10
|
AwaitActivity,
|
|
11
11
|
BaseActivity,
|
|
12
12
|
CycleActivity,
|
|
13
|
+
HookActivity,
|
|
13
14
|
WorkerActivity,
|
|
14
15
|
IterateActivity,
|
|
15
16
|
SignalActivity,
|
|
@@ -39,6 +40,7 @@ export {
|
|
|
39
40
|
WorkflowConfig,
|
|
40
41
|
WorkerConfig,
|
|
41
42
|
WorkerOptions,
|
|
43
|
+
WorkflowSearchOptions,
|
|
42
44
|
WorkflowDataType,
|
|
43
45
|
WorkflowOptions,
|
|
44
46
|
}from './durable'
|