@hotmeshio/hotmesh 0.0.54 → 0.0.56
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/README.md +0 -3
- package/build/modules/enums.js +1 -10
- package/build/modules/key.d.ts +0 -38
- package/build/modules/key.js +4 -46
- package/build/modules/utils.d.ts +0 -8
- package/build/modules/utils.js +0 -14
- package/build/package.json +11 -4
- package/build/services/activities/activity.d.ts +0 -28
- package/build/services/activities/activity.js +1 -46
- package/build/services/activities/await.js +0 -4
- package/build/services/activities/cycle.d.ts +0 -7
- package/build/services/activities/cycle.js +1 -16
- package/build/services/activities/hook.d.ts +0 -6
- package/build/services/activities/hook.js +2 -12
- package/build/services/activities/interrupt.js +0 -8
- package/build/services/activities/signal.d.ts +0 -6
- package/build/services/activities/signal.js +0 -15
- package/build/services/activities/trigger.d.ts +0 -4
- package/build/services/activities/trigger.js +1 -7
- package/build/services/activities/worker.js +0 -4
- package/build/services/collator/index.d.ts +0 -70
- package/build/services/collator/index.js +1 -91
- package/build/services/compiler/deployer.js +6 -38
- package/build/services/compiler/index.d.ts +0 -15
- package/build/services/compiler/index.js +0 -20
- package/build/services/compiler/validator.d.ts +0 -3
- package/build/services/compiler/validator.js +0 -25
- package/build/services/connector/clients/ioredis.d.ts +2 -2
- package/build/services/connector/clients/ioredis.js +0 -2
- package/build/services/connector/clients/redis.d.ts +4 -4
- package/build/services/connector/clients/redis.js +1 -3
- package/build/services/connector/index.d.ts +1 -1
- package/build/services/connector/index.js +0 -2
- package/build/services/durable/client.d.ts +1 -26
- package/build/services/durable/client.js +0 -56
- package/build/services/durable/exporter.d.ts +0 -22
- package/build/services/durable/exporter.js +1 -30
- package/build/services/durable/handle.d.ts +0 -36
- package/build/services/durable/handle.js +0 -46
- package/build/services/durable/index.d.ts +0 -4
- package/build/services/durable/index.js +0 -4
- package/build/services/durable/schemas/factory.d.ts +0 -29
- package/build/services/durable/schemas/factory.js +0 -29
- package/build/services/durable/search.d.ts +1 -36
- package/build/services/durable/search.js +57 -56
- package/build/services/durable/worker.js +2 -22
- package/build/services/durable/workflow.d.ts +0 -114
- package/build/services/durable/workflow.js +4 -144
- package/build/services/engine/index.d.ts +1 -6
- package/build/services/engine/index.js +1 -43
- package/build/services/exporter/index.d.ts +0 -27
- package/build/services/exporter/index.js +0 -33
- package/build/services/hotmesh/index.d.ts +2 -2
- package/build/services/hotmesh/index.js +1 -9
- package/build/services/logger/index.js +0 -2
- package/build/services/mapper/index.d.ts +0 -14
- package/build/services/mapper/index.js +0 -14
- package/build/services/pipe/functions/date.d.ts +0 -7
- package/build/services/pipe/functions/date.js +0 -7
- package/build/services/pipe/functions/math.js +0 -2
- package/build/services/pipe/index.d.ts +0 -15
- package/build/services/pipe/index.js +2 -23
- package/build/services/quorum/index.d.ts +0 -7
- package/build/services/quorum/index.js +0 -21
- package/build/services/reporter/index.d.ts +0 -5
- package/build/services/reporter/index.js +0 -9
- package/build/services/router/index.d.ts +0 -9
- package/build/services/router/index.js +2 -38
- package/build/services/serializer/index.js +7 -26
- package/build/services/store/cache.d.ts +0 -18
- package/build/services/store/cache.js +0 -18
- package/build/services/store/clients/ioredis.d.ts +1 -1
- package/build/services/store/clients/ioredis.js +0 -1
- package/build/services/store/clients/redis.d.ts +1 -1
- package/build/services/store/index.d.ts +0 -55
- package/build/services/store/index.js +5 -81
- package/build/services/stream/clients/ioredis.d.ts +1 -1
- package/build/services/stream/clients/ioredis.js +1 -4
- package/build/services/stream/clients/redis.d.ts +1 -1
- package/build/services/sub/clients/ioredis.d.ts +1 -1
- package/build/services/sub/clients/redis.d.ts +1 -1
- package/build/services/task/index.d.ts +0 -9
- package/build/services/task/index.js +0 -31
- package/build/services/telemetry/index.d.ts +0 -7
- package/build/services/telemetry/index.js +1 -13
- package/build/services/worker/index.d.ts +0 -4
- package/build/services/worker/index.js +2 -6
- package/build/types/activity.d.ts +0 -81
- package/build/types/durable.d.ts +25 -177
- package/build/types/exporter.d.ts +0 -13
- package/build/types/hotmesh.d.ts +4 -16
- package/build/types/hotmesh.js +0 -3
- package/build/types/index.d.ts +4 -6
- package/build/types/index.js +4 -3
- package/build/types/job.d.ts +1 -86
- package/build/types/pipe.d.ts +0 -65
- package/build/types/quorum.d.ts +15 -10
- package/build/types/redis.d.ts +225 -7
- package/build/types/redis.js +9 -0
- package/build/types/stream.d.ts +0 -58
- package/build/types/stream.js +0 -4
- package/package.json +11 -4
- package/types/durable.ts +121 -3
- package/types/hotmesh.ts +3 -6
- package/types/index.ts +23 -10
- package/types/job.ts +1 -1
- package/types/quorum.ts +22 -0
- package/types/redis.ts +267 -18
- package/build/types/ioredisclient.d.ts +0 -5
- package/build/types/ioredisclient.js +0 -5
- package/build/types/redisclient.d.ts +0 -26
- package/build/types/redisclient.js +0 -2
- package/modules/enums.ts +0 -62
- package/modules/errors.ts +0 -280
- package/modules/key.ts +0 -101
- package/modules/storage.ts +0 -3
- package/modules/utils.ts +0 -242
- package/services/activities/activity.ts +0 -589
- package/services/activities/await.ts +0 -113
- package/services/activities/cycle.ts +0 -115
- package/services/activities/hook.ts +0 -197
- package/services/activities/index.ts +0 -19
- package/services/activities/interrupt.ts +0 -172
- package/services/activities/signal.ts +0 -148
- package/services/activities/trigger.ts +0 -295
- package/services/activities/worker.ts +0 -107
- package/services/collator/README.md +0 -102
- package/services/collator/index.ts +0 -291
- package/services/compiler/deployer.ts +0 -504
- package/services/compiler/index.ts +0 -98
- package/services/compiler/validator.ts +0 -158
- package/services/connector/clients/ioredis.ts +0 -57
- package/services/connector/clients/redis.ts +0 -72
- package/services/connector/index.ts +0 -42
- package/services/durable/client.ts +0 -266
- package/services/durable/connection.ts +0 -10
- package/services/durable/exporter.ts +0 -232
- package/services/durable/handle.ts +0 -160
- package/services/durable/index.ts +0 -27
- package/services/durable/schemas/factory.ts +0 -2358
- package/services/durable/search.ts +0 -196
- package/services/durable/worker.ts +0 -401
- package/services/durable/workflow.ts +0 -557
- package/services/engine/index.ts +0 -761
- package/services/exporter/index.ts +0 -146
- package/services/hotmesh/index.ts +0 -237
- package/services/logger/index.ts +0 -79
- package/services/mapper/index.ts +0 -89
- package/services/pipe/functions/array.ts +0 -78
- package/services/pipe/functions/bitwise.ts +0 -27
- package/services/pipe/functions/conditional.ts +0 -35
- package/services/pipe/functions/date.ts +0 -220
- package/services/pipe/functions/index.ts +0 -27
- package/services/pipe/functions/json.ts +0 -11
- package/services/pipe/functions/logical.ts +0 -11
- package/services/pipe/functions/math.ts +0 -217
- package/services/pipe/functions/number.ts +0 -75
- package/services/pipe/functions/object.ts +0 -98
- package/services/pipe/functions/string.ts +0 -86
- package/services/pipe/functions/symbol.ts +0 -39
- package/services/pipe/functions/unary.ts +0 -19
- package/services/pipe/index.ts +0 -216
- package/services/quorum/index.ts +0 -319
- package/services/reporter/index.ts +0 -387
- package/services/router/index.ts +0 -426
- package/services/serializer/README.md +0 -10
- package/services/serializer/index.ts +0 -285
- package/services/store/cache.ts +0 -172
- package/services/store/clients/ioredis.ts +0 -145
- package/services/store/clients/redis.ts +0 -191
- package/services/store/index.ts +0 -1091
- package/services/stream/clients/ioredis.ts +0 -157
- package/services/stream/clients/redis.ts +0 -158
- package/services/stream/index.ts +0 -58
- package/services/sub/clients/ioredis.ts +0 -83
- package/services/sub/clients/redis.ts +0 -74
- package/services/sub/index.ts +0 -25
- package/services/task/index.ts +0 -250
- package/services/telemetry/index.ts +0 -273
- package/services/worker/index.ts +0 -248
- package/types/ioredisclient.ts +0 -10
- package/types/redisclient.ts +0 -30
|
@@ -1,589 +0,0 @@
|
|
|
1
|
-
import { HMSH_EXPIRE_DURATION } from '../../modules/enums';
|
|
2
|
-
import {
|
|
3
|
-
CollationError,
|
|
4
|
-
GenerationalError,
|
|
5
|
-
GetStateError,
|
|
6
|
-
InactiveJobError } from '../../modules/errors';
|
|
7
|
-
import {
|
|
8
|
-
formatISODate,
|
|
9
|
-
getValueByPath,
|
|
10
|
-
guid,
|
|
11
|
-
restoreHierarchy } from '../../modules/utils';
|
|
12
|
-
import { CollatorService } from '../collator';
|
|
13
|
-
import { EngineService } from '../engine';
|
|
14
|
-
import { ILogger } from '../logger';
|
|
15
|
-
import { MapperService } from '../mapper';
|
|
16
|
-
import { Pipe } from '../pipe';
|
|
17
|
-
import { MDATA_SYMBOLS } from '../serializer';
|
|
18
|
-
import { StoreService } from '../store';
|
|
19
|
-
import { TelemetryService } from '../telemetry';
|
|
20
|
-
import {
|
|
21
|
-
ActivityData,
|
|
22
|
-
ActivityLeg,
|
|
23
|
-
ActivityMetadata,
|
|
24
|
-
ActivityType,
|
|
25
|
-
Consumes } from '../../types/activity';
|
|
26
|
-
import { JobState, JobStatus } from '../../types/job';
|
|
27
|
-
import {
|
|
28
|
-
MultiResponseFlags,
|
|
29
|
-
RedisClient,
|
|
30
|
-
RedisMulti } from '../../types/redis';
|
|
31
|
-
import { StringAnyType, StringScalarType } from '../../types/serializer';
|
|
32
|
-
import {
|
|
33
|
-
StreamCode,
|
|
34
|
-
StreamData,
|
|
35
|
-
StreamDataType,
|
|
36
|
-
StreamStatus } from '../../types/stream';
|
|
37
|
-
import { TransitionRule } from '../../types/transition';
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* The base class for all activities
|
|
41
|
-
*/
|
|
42
|
-
class Activity {
|
|
43
|
-
config: ActivityType;
|
|
44
|
-
data: ActivityData;
|
|
45
|
-
hook: ActivityData;
|
|
46
|
-
metadata: ActivityMetadata;
|
|
47
|
-
store: StoreService<RedisClient, RedisMulti>
|
|
48
|
-
context: JobState;
|
|
49
|
-
engine: EngineService;
|
|
50
|
-
logger: ILogger;
|
|
51
|
-
status: StreamStatus = StreamStatus.SUCCESS;
|
|
52
|
-
code: StreamCode = 200;
|
|
53
|
-
leg: ActivityLeg;
|
|
54
|
-
adjacencyList: StreamData[];
|
|
55
|
-
adjacentIndex = 0;
|
|
56
|
-
|
|
57
|
-
constructor(
|
|
58
|
-
config: ActivityType,
|
|
59
|
-
data: ActivityData,
|
|
60
|
-
metadata: ActivityMetadata,
|
|
61
|
-
hook: ActivityData | null,
|
|
62
|
-
engine: EngineService,
|
|
63
|
-
context?: JobState) {
|
|
64
|
-
this.config = config;
|
|
65
|
-
this.data = data;
|
|
66
|
-
this.metadata = metadata;
|
|
67
|
-
this.hook = hook;
|
|
68
|
-
this.engine = engine;
|
|
69
|
-
this.context = context || { data: {}, metadata: {} } as JobState;
|
|
70
|
-
this.logger = engine.logger;
|
|
71
|
-
this.store = engine.store;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
setLeg(leg: ActivityLeg): void {
|
|
75
|
-
this.leg = leg;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Upon entering leg 1 of a duplexed activty, verify
|
|
80
|
-
* all aspects of the entry including job and activty state
|
|
81
|
-
*/
|
|
82
|
-
async verifyEntry() {
|
|
83
|
-
this.setLeg(1);
|
|
84
|
-
await this.getState();
|
|
85
|
-
CollatorService.assertJobActive(
|
|
86
|
-
this.context.metadata.js,
|
|
87
|
-
this.context.metadata.jid,
|
|
88
|
-
this.metadata.aid
|
|
89
|
-
);
|
|
90
|
-
await CollatorService.notarizeEntry(this);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Upon entering leg 2 of a duplexed activty, verify
|
|
95
|
-
* all aspects of the re-entry including job and activty state
|
|
96
|
-
*/
|
|
97
|
-
async verifyReentry(): Promise<number> {
|
|
98
|
-
const guid = this.context.metadata.guid;
|
|
99
|
-
this.setLeg(2);
|
|
100
|
-
await this.getState();
|
|
101
|
-
CollatorService.assertJobActive(
|
|
102
|
-
this.context.metadata.js,
|
|
103
|
-
this.context.metadata.jid,
|
|
104
|
-
this.metadata.aid
|
|
105
|
-
);
|
|
106
|
-
return await CollatorService.notarizeReentry(this, guid);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
//******** DUPLEX RE-ENTRY POINT ********//
|
|
110
|
-
async processEvent(status: StreamStatus = StreamStatus.SUCCESS, code: StreamCode = 200, type: 'hook' | 'output' = 'output'): Promise<void> {
|
|
111
|
-
this.setLeg(2);
|
|
112
|
-
const jid = this.context.metadata.jid;
|
|
113
|
-
if (!jid) {
|
|
114
|
-
this.logger.error('activity-process-event-error', { message: 'job id is undefined' });
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const aid = this.metadata.aid;
|
|
118
|
-
this.status = status;
|
|
119
|
-
this.code = code;
|
|
120
|
-
this.logger.debug('activity-process-event', { topic: this.config.subtype, jid, aid, status, code });
|
|
121
|
-
let telemetry: TelemetryService;
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
const collationKey = await this.verifyReentry();
|
|
125
|
-
|
|
126
|
-
this.adjacentIndex = CollatorService.getDimensionalIndex(collationKey);
|
|
127
|
-
telemetry = new TelemetryService(
|
|
128
|
-
this.engine.appId,
|
|
129
|
-
this.config,
|
|
130
|
-
this.metadata,
|
|
131
|
-
this.context,
|
|
132
|
-
);
|
|
133
|
-
telemetry.startActivitySpan(this.leg);
|
|
134
|
-
let multiResponse: MultiResponseFlags;
|
|
135
|
-
|
|
136
|
-
if (status === StreamStatus.PENDING) {
|
|
137
|
-
multiResponse = await this.processPending(telemetry, type);
|
|
138
|
-
} else if (status === StreamStatus.SUCCESS) {
|
|
139
|
-
multiResponse = await this.processSuccess(telemetry, type);
|
|
140
|
-
} else {
|
|
141
|
-
multiResponse = await this.processError(telemetry, type);
|
|
142
|
-
}
|
|
143
|
-
this.transitionAdjacent(multiResponse, telemetry);
|
|
144
|
-
} catch (error) {
|
|
145
|
-
if (error instanceof CollationError) {
|
|
146
|
-
this.logger.info('process-event-inactive-error', { ...error });
|
|
147
|
-
return;
|
|
148
|
-
} else if (error instanceof InactiveJobError) {
|
|
149
|
-
this.logger.info('process-event-inactive-job-error', { ...error });
|
|
150
|
-
return;
|
|
151
|
-
} else if (error instanceof GenerationalError) {
|
|
152
|
-
this.logger.info('process-event-generational-job-error', { ...error });
|
|
153
|
-
return;
|
|
154
|
-
} else if (error instanceof GetStateError) {
|
|
155
|
-
this.logger.info('process-event-get-job-error', { ...error });
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
this.logger.error('activity-process-event-error', { ...error, message: error.message, stack: error.stack, name: error.name });
|
|
159
|
-
telemetry && telemetry.setActivityError(error.message);
|
|
160
|
-
throw error;
|
|
161
|
-
} finally {
|
|
162
|
-
telemetry?.endActivitySpan();
|
|
163
|
-
this.logger.debug('activity-process-event-end', { jid, aid });
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async processPending(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags> {
|
|
168
|
-
this.bindActivityData(type);
|
|
169
|
-
this.adjacencyList = await this.filterAdjacent();
|
|
170
|
-
this.mapJobData();
|
|
171
|
-
const multi = this.store.getMulti();
|
|
172
|
-
await this.setState(multi);
|
|
173
|
-
await CollatorService.notarizeContinuation(this, multi);
|
|
174
|
-
|
|
175
|
-
await this.setStatus(this.adjacencyList.length, multi);
|
|
176
|
-
return await multi.exec() as MultiResponseFlags;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async processSuccess(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags> {
|
|
180
|
-
this.bindActivityData(type);
|
|
181
|
-
this.adjacencyList = await this.filterAdjacent();
|
|
182
|
-
this.mapJobData();
|
|
183
|
-
const multi = this.store.getMulti();
|
|
184
|
-
await this.setState(multi);
|
|
185
|
-
await CollatorService.notarizeCompletion(this, multi);
|
|
186
|
-
|
|
187
|
-
await this.setStatus(this.adjacencyList.length - 1, multi);
|
|
188
|
-
return await multi.exec() as MultiResponseFlags;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async processError(telemetry: TelemetryService, type: string): Promise<MultiResponseFlags> {
|
|
192
|
-
this.bindActivityError(this.data);
|
|
193
|
-
this.adjacencyList = await this.filterAdjacent();
|
|
194
|
-
if (!this.adjacencyList.length) {
|
|
195
|
-
this.bindJobError(this.data);
|
|
196
|
-
}
|
|
197
|
-
this.mapJobData();
|
|
198
|
-
const multi = this.store.getMulti();
|
|
199
|
-
await this.setState(multi);
|
|
200
|
-
await CollatorService.notarizeCompletion(this, multi);
|
|
201
|
-
|
|
202
|
-
await this.setStatus(this.adjacencyList.length - 1, multi);
|
|
203
|
-
return await multi.exec() as MultiResponseFlags;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async transitionAdjacent(multiResponse: MultiResponseFlags, telemetry: TelemetryService): Promise<void> {
|
|
207
|
-
telemetry.mapActivityAttributes();
|
|
208
|
-
const jobStatus = this.resolveStatus(multiResponse);
|
|
209
|
-
const attrs: StringScalarType = { 'app.job.jss': jobStatus };
|
|
210
|
-
//adjacencyList membership has already been set at this point (according to activity status)
|
|
211
|
-
const messageIds = await this.transition(this.adjacencyList, jobStatus);
|
|
212
|
-
if (messageIds?.length) {
|
|
213
|
-
attrs['app.activity.mids'] = messageIds.join(',')
|
|
214
|
-
}
|
|
215
|
-
telemetry.setActivityAttributes(attrs);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
resolveStatus(multiResponse: MultiResponseFlags): number {
|
|
219
|
-
const activityStatus = multiResponse[multiResponse.length - 1];
|
|
220
|
-
if (Array.isArray(activityStatus)) {
|
|
221
|
-
return Number(activityStatus[1]);
|
|
222
|
-
} else {
|
|
223
|
-
return Number(activityStatus);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
mapJobData(): void {
|
|
228
|
-
if(this.config.job?.maps) {
|
|
229
|
-
const mapper = new MapperService(this.config.job.maps, this.context);
|
|
230
|
-
const output = mapper.mapRules();
|
|
231
|
-
if (output) {
|
|
232
|
-
for (const key in output) {
|
|
233
|
-
const f1 = key.indexOf('[');
|
|
234
|
-
//keys with array notation suffix `somekey[]` represent
|
|
235
|
-
//dynamically-keyed mappings whose `value` must be moved to the output.
|
|
236
|
-
//The `value` must be an object with keys appropriate to the
|
|
237
|
-
//notation type: `somekey[0] (array)`, `somekey[-] (mark)`, OR `somekey[_] (search)`
|
|
238
|
-
if (f1 > -1) {
|
|
239
|
-
const amount = key.substring(f1 + 1).split(']')[0];
|
|
240
|
-
if (!isNaN(Number(amount))) {
|
|
241
|
-
const left = key.substring(0, f1);
|
|
242
|
-
output[left] = output[key];
|
|
243
|
-
delete output[key];
|
|
244
|
-
} else if (amount === '-' || amount === '_') {
|
|
245
|
-
const obj = output[key];
|
|
246
|
-
Object.keys(obj).forEach((newKey) => {
|
|
247
|
-
output[newKey] = obj[newKey];
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
this.context.data = output;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
mapInputData(): void {
|
|
258
|
-
if(this.config.input?.maps) {
|
|
259
|
-
const mapper = new MapperService(this.config.input.maps, this.context);
|
|
260
|
-
this.context.data = mapper.mapRules();
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
mapOutputData(): void {
|
|
265
|
-
//activity YAML may include output map data that produces/extends activity output data.
|
|
266
|
-
if(this.config.output?.maps) {
|
|
267
|
-
const mapper = new MapperService(this.config.output.maps, this.context);
|
|
268
|
-
const actOutData = mapper.mapRules();
|
|
269
|
-
const activityId = this.metadata.aid;
|
|
270
|
-
const data = { ...this.context[activityId].output, ...actOutData };
|
|
271
|
-
this.context[activityId].output.data = data;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
async registerTimeout(): Promise<void> {
|
|
276
|
-
//set timeout in support of hook and/or duplex
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Any StreamMessage with a status of ERROR is bound to the activity
|
|
281
|
-
*/
|
|
282
|
-
bindActivityError(data: Record<string, unknown>): void {
|
|
283
|
-
const md = this.context[this.metadata.aid].output.metadata;
|
|
284
|
-
md.err = JSON.stringify(this.data);
|
|
285
|
-
//(temporary...useful for mapping error parts in the app.yaml)
|
|
286
|
-
md.$error = { ...data, is_stream_error: true };
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* unhandled activity errors (activities that return an ERROR StreamMessage
|
|
291
|
-
* status and have no adjacent children to transition to) are bound to the job
|
|
292
|
-
*/
|
|
293
|
-
bindJobError(data: Record<string, unknown>): void {
|
|
294
|
-
this.context.metadata.err = JSON.stringify({ ...data, is_stream_error: true });
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async getTriggerConfig(): Promise<ActivityType> {
|
|
298
|
-
return await this.store.getSchema(
|
|
299
|
-
this.config.trigger,
|
|
300
|
-
await this.engine.getVID()
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
getJobStatus(): null | number {
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async setStatus(amount: number, multi?: RedisMulti): Promise<void> {
|
|
309
|
-
const { id: appId } = await this.engine.getVID();
|
|
310
|
-
await this.store.setStatus(
|
|
311
|
-
amount,
|
|
312
|
-
this.context.metadata.jid,
|
|
313
|
-
appId,
|
|
314
|
-
multi
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
authorizeEntry(state: StringAnyType): string[] {
|
|
319
|
-
//pre-authorize activity state to allow entry for adjacent activities
|
|
320
|
-
return this.adjacencyList?.map((streamData) => {
|
|
321
|
-
const { metadata: { aid } } = streamData;
|
|
322
|
-
state[`${aid}/output/metadata/as`] = CollatorService.getSeed();
|
|
323
|
-
return aid;
|
|
324
|
-
}) ?? [];
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
bindDimensionalAddress(state: StringAnyType) {
|
|
328
|
-
const dad = this.resolveDad();
|
|
329
|
-
state[`${this.metadata.aid}/output/metadata/dad`] = dad;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
async setState(multi?: RedisMulti): Promise<string> {
|
|
333
|
-
const jobId = this.context.metadata.jid;
|
|
334
|
-
this.bindJobMetadata();
|
|
335
|
-
this.bindActivityMetadata();
|
|
336
|
-
let state: StringAnyType = {};
|
|
337
|
-
await this.bindJobState(state);
|
|
338
|
-
const presets = this.authorizeEntry(state);
|
|
339
|
-
this.bindDimensionalAddress(state);
|
|
340
|
-
this.bindActivityState(state);
|
|
341
|
-
//symbolNames holds symkeys
|
|
342
|
-
const symbolNames = [
|
|
343
|
-
`$${this.config.subscribes}`,
|
|
344
|
-
this.metadata.aid,
|
|
345
|
-
...presets
|
|
346
|
-
];
|
|
347
|
-
const dIds = CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], this.resolveDad());
|
|
348
|
-
return await this.store.setState(state, this.getJobStatus(), jobId, symbolNames, dIds, multi);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
bindJobMetadata(): void {
|
|
352
|
-
//both legs of the most recently run activity (1 and 2) modify ju (job_updated)
|
|
353
|
-
this.context.metadata.ju = formatISODate(new Date());
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
bindActivityMetadata(): void {
|
|
357
|
-
const self: StringAnyType = this.context['$self'];
|
|
358
|
-
if (!self.output.metadata) {
|
|
359
|
-
self.output.metadata = {};
|
|
360
|
-
}
|
|
361
|
-
if (this.status === StreamStatus.ERROR) {
|
|
362
|
-
self.output.metadata.err = JSON.stringify(this.data);
|
|
363
|
-
}
|
|
364
|
-
const ts = formatISODate(new Date());
|
|
365
|
-
self.output.metadata.ac = ts;
|
|
366
|
-
self.output.metadata.au = ts;
|
|
367
|
-
self.output.metadata.atp = this.config.type;
|
|
368
|
-
if (this.config.subtype) {
|
|
369
|
-
self.output.metadata.stp = this.config.subtype;
|
|
370
|
-
}
|
|
371
|
-
self.output.metadata.aid = this.metadata.aid;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
async bindJobState(state: StringAnyType): Promise<void> {
|
|
375
|
-
const triggerConfig = await this.getTriggerConfig();
|
|
376
|
-
const PRODUCES = [
|
|
377
|
-
...(triggerConfig.PRODUCES || []),
|
|
378
|
-
...this.bindJobMetadataPaths()
|
|
379
|
-
];
|
|
380
|
-
for (const path of PRODUCES) {
|
|
381
|
-
const value = getValueByPath(this.context, path);
|
|
382
|
-
if (value !== undefined) {
|
|
383
|
-
state[path] = value;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
for (let key in this.context?.data ?? {}) {
|
|
387
|
-
if (key.startsWith('-') || key.startsWith('_')) {
|
|
388
|
-
state[key] = this.context.data[key];
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
TelemetryService.bindJobTelemetryToState(state, this.config, this.context);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
bindActivityState(state: StringAnyType,): void {
|
|
395
|
-
const produces = [
|
|
396
|
-
...this.config.produces,
|
|
397
|
-
...this.bindActivityMetadataPaths()
|
|
398
|
-
];
|
|
399
|
-
for (const path of produces) {
|
|
400
|
-
const prefixedPath = `${this.metadata.aid}/${path}`;
|
|
401
|
-
const value = getValueByPath(this.context, prefixedPath);
|
|
402
|
-
if (value !== undefined) {
|
|
403
|
-
state[prefixedPath] = value;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
TelemetryService.bindActivityTelemetryToState(state, this.config, this.metadata, this.context, this.leg);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
bindJobMetadataPaths(): string[] {
|
|
410
|
-
return MDATA_SYMBOLS.JOB_UPDATE.KEYS.map((key) => `metadata/${key}`);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
bindActivityMetadataPaths(): string[] {
|
|
414
|
-
const keys_to_save = this.leg === 1 ? 'ACTIVITY': 'ACTIVITY_UPDATE'
|
|
415
|
-
return MDATA_SYMBOLS[keys_to_save].KEYS.map((key) => `output/metadata/${key}`);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
async getState() {
|
|
419
|
-
const gid = this.context.metadata.gid;
|
|
420
|
-
const jobSymbolHashName = `$${this.config.subscribes}`;
|
|
421
|
-
const consumes: Consumes = {
|
|
422
|
-
[jobSymbolHashName]: MDATA_SYMBOLS.JOB.KEYS.map((key) => `metadata/${key}`)
|
|
423
|
-
};
|
|
424
|
-
for (let [activityId, paths] of Object.entries(this.config.consumes)) {
|
|
425
|
-
if(activityId === '$job') {
|
|
426
|
-
for (const path of paths) {
|
|
427
|
-
consumes[jobSymbolHashName].push(path);
|
|
428
|
-
}
|
|
429
|
-
} else {
|
|
430
|
-
if (activityId === '$self') {
|
|
431
|
-
activityId = this.metadata.aid;
|
|
432
|
-
}
|
|
433
|
-
if (!consumes[activityId]) {
|
|
434
|
-
consumes[activityId] = [];
|
|
435
|
-
}
|
|
436
|
-
for (const path of paths) {
|
|
437
|
-
consumes[activityId].push(`${activityId}/${path}`);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
TelemetryService.addTargetTelemetryPaths(consumes, this.config, this.metadata, this.leg);
|
|
442
|
-
let { dad, jid } = this.context.metadata;
|
|
443
|
-
const dIds = CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad || '');
|
|
444
|
-
//`state` is a unidimensional hash; context is a tree
|
|
445
|
-
const [state, _status] = await this.store.getState(jid, consumes, dIds);
|
|
446
|
-
this.context = restoreHierarchy(state) as JobState;
|
|
447
|
-
this.assertGenerationalId(this.context?.metadata?.gid, gid);
|
|
448
|
-
this.initDimensionalAddress(dad);
|
|
449
|
-
this.initSelf(this.context);
|
|
450
|
-
this.initPolicies(this.context);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* if the job is created/deleted/created with the same key,
|
|
455
|
-
* the 'gid' ensures no stale messages (such as sleep delays)
|
|
456
|
-
* enter the workstream. Any message with a mismatched gid
|
|
457
|
-
* belongs to a prior job and can safely be ignored/dropped.
|
|
458
|
-
*/
|
|
459
|
-
assertGenerationalId(jobGID: string, msgGID?: string) {
|
|
460
|
-
if (msgGID !== jobGID) {
|
|
461
|
-
throw new GenerationalError(
|
|
462
|
-
jobGID,
|
|
463
|
-
msgGID,
|
|
464
|
-
this.context?.metadata?.jid ?? '',
|
|
465
|
-
this.context?.metadata?.aid ?? '',
|
|
466
|
-
this.context?.metadata?.dad ?? ''
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
initDimensionalAddress(dad: string): void {
|
|
472
|
-
this.metadata.dad = dad;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
initSelf(context: StringAnyType): JobState {
|
|
476
|
-
const activityId = this.metadata.aid;
|
|
477
|
-
if (!context[activityId]) {
|
|
478
|
-
context[activityId] = { };
|
|
479
|
-
}
|
|
480
|
-
const self = context[activityId];
|
|
481
|
-
if (!self.output) {
|
|
482
|
-
self.output = { };
|
|
483
|
-
}
|
|
484
|
-
if (!self.input) {
|
|
485
|
-
self.input = { };
|
|
486
|
-
}
|
|
487
|
-
if (!self.hook) {
|
|
488
|
-
self.hook = { };
|
|
489
|
-
}
|
|
490
|
-
if (!self.output.metadata) {
|
|
491
|
-
self.output.metadata = { };
|
|
492
|
-
}
|
|
493
|
-
//prebind the updated timestamp (mappings need the time)
|
|
494
|
-
self.output.metadata.au = formatISODate(new Date());
|
|
495
|
-
context['$self'] = self;
|
|
496
|
-
context['$job'] = context; //NEVER call STRINGIFY! (now circular)
|
|
497
|
-
return context as JobState;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
initPolicies(context: JobState) {
|
|
501
|
-
const expire = Pipe.resolve(
|
|
502
|
-
this.config.expire ?? HMSH_EXPIRE_DURATION,
|
|
503
|
-
context
|
|
504
|
-
);
|
|
505
|
-
context.metadata.expire = expire;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
bindActivityData(type: 'output' | 'hook'): void {
|
|
509
|
-
this.context[this.metadata.aid][type].data = this.data;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
resolveDad(): string {
|
|
513
|
-
let dad = this.metadata.dad;
|
|
514
|
-
if (this.adjacentIndex > 0) {
|
|
515
|
-
//if adjacent index > 0 the activity is cycling; replace last index with cycle index
|
|
516
|
-
dad = `${dad.substring(0, dad.lastIndexOf(','))},${this.adjacentIndex}`
|
|
517
|
-
}
|
|
518
|
-
return dad;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
resolveAdjacentDad(): string {
|
|
522
|
-
//concat self and child dimension (all children (leg 1) begin life at 0)
|
|
523
|
-
return `${this.resolveDad()}${CollatorService.getDimensionalSeed(0)}`;
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
async filterAdjacent(): Promise<StreamData[]> {
|
|
527
|
-
const adjacencyList: StreamData[] = [];
|
|
528
|
-
const transitions = await this.store.getTransitions(await this.engine.getVID());
|
|
529
|
-
const transition = transitions[`.${this.metadata.aid}`];
|
|
530
|
-
//resolve the dimensional address for adjacent children
|
|
531
|
-
const adjacentDad = this.resolveAdjacentDad();
|
|
532
|
-
if (transition) {
|
|
533
|
-
for (const toActivityId in transition) {
|
|
534
|
-
const transitionRule: boolean | TransitionRule = transition[toActivityId];
|
|
535
|
-
if (MapperService.evaluate(transitionRule, this.context, this.code)) {
|
|
536
|
-
adjacencyList.push({
|
|
537
|
-
metadata: {
|
|
538
|
-
guid: guid(),
|
|
539
|
-
jid: this.context.metadata.jid,
|
|
540
|
-
gid: this.context.metadata.gid,
|
|
541
|
-
dad: adjacentDad,
|
|
542
|
-
aid: toActivityId,
|
|
543
|
-
spn: this.context['$self'].output.metadata?.l2s,
|
|
544
|
-
trc: this.context.metadata.trc,
|
|
545
|
-
},
|
|
546
|
-
type: StreamDataType.TRANSITION,
|
|
547
|
-
data: {}
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
return adjacencyList;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
async transition(adjacencyList: StreamData[], jobStatus: JobStatus): Promise<string[]> {
|
|
556
|
-
if (this.jobWasInterrupted(jobStatus)) {
|
|
557
|
-
return;
|
|
558
|
-
}
|
|
559
|
-
let mIds: string[] = [];
|
|
560
|
-
let emit: boolean = false;
|
|
561
|
-
if (this.config.emit) {
|
|
562
|
-
emit = Pipe.resolve(this.config.emit, this.context);
|
|
563
|
-
}
|
|
564
|
-
if (jobStatus <= 0 || emit) {
|
|
565
|
-
await this.engine.runJobCompletionTasks(
|
|
566
|
-
this.context,
|
|
567
|
-
{ emit: jobStatus > 0 },
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
if (adjacencyList.length && jobStatus > 0) {
|
|
571
|
-
const multi = this.store.getMulti();
|
|
572
|
-
for (const execSignal of adjacencyList) {
|
|
573
|
-
await this.engine.router?.publishMessage(null, execSignal, multi);
|
|
574
|
-
}
|
|
575
|
-
mIds = (await multi.exec()) as string[];
|
|
576
|
-
}
|
|
577
|
-
return mIds;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* A job with a vale < -100_000_000 is considered interrupted,
|
|
582
|
-
* as the interruption event decrements the job status by 1billion.
|
|
583
|
-
*/
|
|
584
|
-
jobWasInterrupted(jobStatus: JobStatus): boolean {
|
|
585
|
-
return jobStatus < -100_000_000;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
export { Activity, ActivityType };
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GenerationalError,
|
|
3
|
-
GetStateError,
|
|
4
|
-
InactiveJobError } from '../../modules/errors';
|
|
5
|
-
import { guid } from '../../modules/utils';
|
|
6
|
-
import { Activity } from './activity';
|
|
7
|
-
import { CollatorService } from '../collator';
|
|
8
|
-
import { EngineService } from '../engine';
|
|
9
|
-
import { Pipe } from '../pipe';
|
|
10
|
-
import { TelemetryService } from '../telemetry';
|
|
11
|
-
import {
|
|
12
|
-
ActivityData,
|
|
13
|
-
ActivityMetadata,
|
|
14
|
-
AwaitActivity,
|
|
15
|
-
ActivityType } from '../../types/activity';
|
|
16
|
-
import { JobState } from '../../types/job';
|
|
17
|
-
import { MultiResponseFlags, RedisMulti } from '../../types/redis';
|
|
18
|
-
import { StreamData, StreamDataType } from '../../types/stream';
|
|
19
|
-
|
|
20
|
-
class Await extends Activity {
|
|
21
|
-
config: AwaitActivity;
|
|
22
|
-
|
|
23
|
-
constructor(
|
|
24
|
-
config: ActivityType,
|
|
25
|
-
data: ActivityData,
|
|
26
|
-
metadata: ActivityMetadata,
|
|
27
|
-
hook: ActivityData | null,
|
|
28
|
-
engine: EngineService,
|
|
29
|
-
context?: JobState) {
|
|
30
|
-
super(config, data, metadata, hook, engine, context);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
//******** INITIAL ENTRY POINT (A) ********//
|
|
34
|
-
async process(): Promise<string> {
|
|
35
|
-
this.logger.debug('await-process', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid });
|
|
36
|
-
let telemetry: TelemetryService;
|
|
37
|
-
try {
|
|
38
|
-
await this.verifyEntry();
|
|
39
|
-
|
|
40
|
-
telemetry = new TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
|
|
41
|
-
telemetry.startActivitySpan(this.leg);
|
|
42
|
-
this.mapInputData();
|
|
43
|
-
|
|
44
|
-
//save state and authorize reentry
|
|
45
|
-
const multi = this.store.getMulti();
|
|
46
|
-
//todo: await this.registerTimeout();
|
|
47
|
-
const messageId = await this.execActivity(multi);
|
|
48
|
-
await CollatorService.authorizeReentry(this, multi);
|
|
49
|
-
await this.setState(multi);
|
|
50
|
-
await this.setStatus(0, multi);
|
|
51
|
-
const multiResponse = await multi.exec() as MultiResponseFlags;
|
|
52
|
-
|
|
53
|
-
//telemetry
|
|
54
|
-
telemetry.mapActivityAttributes();
|
|
55
|
-
const jobStatus = this.resolveStatus(multiResponse);
|
|
56
|
-
telemetry.setActivityAttributes({
|
|
57
|
-
'app.activity.mid': messageId,
|
|
58
|
-
'app.job.jss': jobStatus
|
|
59
|
-
});
|
|
60
|
-
return this.context.metadata.aid;
|
|
61
|
-
} catch (error) {
|
|
62
|
-
if (error instanceof InactiveJobError) {
|
|
63
|
-
this.logger.error('await-inactive-job-error', { ...error });
|
|
64
|
-
return;
|
|
65
|
-
} else if (error instanceof GenerationalError) {
|
|
66
|
-
this.logger.info('process-event-generational-job-error', { ...error });
|
|
67
|
-
return;
|
|
68
|
-
} else if (error instanceof GetStateError) {
|
|
69
|
-
this.logger.error('await-get-state-error', { ...error });
|
|
70
|
-
return;
|
|
71
|
-
} else {
|
|
72
|
-
this.logger.error('await-process-error', { ...error });
|
|
73
|
-
}
|
|
74
|
-
telemetry.setActivityError(error.message);
|
|
75
|
-
throw error;
|
|
76
|
-
} finally {
|
|
77
|
-
telemetry?.endActivitySpan();
|
|
78
|
-
this.logger.debug('await-process-end', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid });
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async execActivity(multi: RedisMulti): Promise<string> {
|
|
83
|
-
const topic = Pipe.resolve(this.config.subtype, this.context);
|
|
84
|
-
const streamData: StreamData = {
|
|
85
|
-
metadata: {
|
|
86
|
-
guid: guid(),
|
|
87
|
-
jid: this.context.metadata.jid,
|
|
88
|
-
gid: this.context.metadata.gid,
|
|
89
|
-
dad: this.metadata.dad,
|
|
90
|
-
aid: this.metadata.aid,
|
|
91
|
-
topic,
|
|
92
|
-
spn: this.context['$self'].output.metadata?.l1s,
|
|
93
|
-
trc: this.context.metadata.trc,
|
|
94
|
-
},
|
|
95
|
-
type: StreamDataType.AWAIT,
|
|
96
|
-
data: this.context.data
|
|
97
|
-
};
|
|
98
|
-
if (this.config.await !== true) {
|
|
99
|
-
const doAwait = Pipe.resolve(this.config.await, this.context);
|
|
100
|
-
if (doAwait === false) {
|
|
101
|
-
streamData.metadata.await = false;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (this.config.retry) {
|
|
105
|
-
streamData.policies = {
|
|
106
|
-
retry: this.config.retry
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return (await this.engine.router?.publishMessage(null, streamData, multi)) as string;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export { Await };
|