@hotmeshio/hotmesh 0.0.53 → 0.0.54
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/errors.d.ts +6 -48
- package/build/modules/errors.js +5 -5
- package/build/package.json +1 -1
- package/build/services/activities/hook.js +5 -1
- package/build/services/activities/trigger.d.ts +5 -2
- package/build/services/activities/trigger.js +22 -1
- package/build/services/durable/client.js +1 -8
- package/build/services/durable/exporter.d.ts +24 -13
- package/build/services/durable/exporter.js +145 -127
- package/build/services/durable/handle.d.ts +2 -2
- package/build/services/durable/handle.js +2 -2
- package/build/services/durable/worker.js +1 -1
- package/build/services/durable/workflow.d.ts +29 -17
- package/build/services/durable/workflow.js +116 -96
- package/build/services/engine/index.d.ts +2 -2
- package/build/services/engine/index.js +2 -2
- package/build/services/hotmesh/index.d.ts +2 -2
- package/build/services/hotmesh/index.js +2 -2
- package/build/types/durable.d.ts +15 -3
- package/build/types/error.d.ts +48 -0
- package/build/types/error.js +2 -0
- package/build/types/exporter.d.ts +26 -20
- package/build/types/index.d.ts +2 -1
- package/build/types/job.d.ts +24 -1
- package/modules/errors.ts +18 -55
- package/package.json +1 -1
- package/services/activities/hook.ts +8 -1
- package/services/activities/trigger.ts +27 -2
- package/services/durable/client.ts +2 -8
- package/services/durable/exporter.ts +149 -128
- package/services/durable/handle.ts +3 -3
- package/services/durable/worker.ts +1 -1
- package/services/durable/workflow.ts +136 -103
- package/services/engine/index.ts +4 -3
- package/services/hotmesh/index.ts +4 -3
- package/types/durable.ts +18 -3
- package/types/error.ts +52 -0
- package/types/exporter.ts +31 -23
- package/types/index.ts +8 -1
- package/types/job.ts +27 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ActivityDuplex } from "../types/activity";
|
|
2
2
|
import { CollationFaultType, CollationStage } from "../types/collator";
|
|
3
|
+
import { DurableChildErrorType, DurableProxyErrorType, DurableSleepErrorType, DurableWaitForAllErrorType, DurableWaitForErrorType } from "../types/error";
|
|
3
4
|
declare class GetStateError extends Error {
|
|
4
5
|
jobId: string;
|
|
5
6
|
code: number;
|
|
@@ -14,12 +15,7 @@ declare class DurableWaitForError extends Error {
|
|
|
14
15
|
workflowId: string;
|
|
15
16
|
index: number;
|
|
16
17
|
workflowDimension: string;
|
|
17
|
-
constructor(params:
|
|
18
|
-
signalId: string;
|
|
19
|
-
index: number;
|
|
20
|
-
workflowDimension: string;
|
|
21
|
-
workflowId: string;
|
|
22
|
-
});
|
|
18
|
+
constructor(params: DurableWaitForErrorType);
|
|
23
19
|
}
|
|
24
20
|
declare class DurableProxyError extends Error {
|
|
25
21
|
activityName: string;
|
|
@@ -34,19 +30,7 @@ declare class DurableProxyError extends Error {
|
|
|
34
30
|
workflowDimension: string;
|
|
35
31
|
workflowId: string;
|
|
36
32
|
workflowTopic: string;
|
|
37
|
-
constructor(params:
|
|
38
|
-
arguments: string[];
|
|
39
|
-
activityName: string;
|
|
40
|
-
backoffCoefficient?: number;
|
|
41
|
-
index: number;
|
|
42
|
-
maximumAttempts?: number;
|
|
43
|
-
maximumInterval?: number;
|
|
44
|
-
originJobId: string | null;
|
|
45
|
-
parentWorkflowId: string;
|
|
46
|
-
workflowDimension: string;
|
|
47
|
-
workflowId: string;
|
|
48
|
-
workflowTopic: string;
|
|
49
|
-
});
|
|
33
|
+
constructor(params: DurableProxyErrorType);
|
|
50
34
|
}
|
|
51
35
|
declare class DurableChildError extends Error {
|
|
52
36
|
await: boolean;
|
|
@@ -61,19 +45,7 @@ declare class DurableChildError extends Error {
|
|
|
61
45
|
parentWorkflowId: string;
|
|
62
46
|
workflowId: string;
|
|
63
47
|
workflowTopic: string;
|
|
64
|
-
constructor(params:
|
|
65
|
-
arguments: string[];
|
|
66
|
-
await?: boolean;
|
|
67
|
-
backoffCoefficient?: number;
|
|
68
|
-
index: number;
|
|
69
|
-
maximumAttempts?: number;
|
|
70
|
-
maximumInterval?: number;
|
|
71
|
-
originJobId: string | null;
|
|
72
|
-
parentWorkflowId: string;
|
|
73
|
-
workflowDimension: string;
|
|
74
|
-
workflowId: string;
|
|
75
|
-
workflowTopic: string;
|
|
76
|
-
});
|
|
48
|
+
constructor(params: DurableChildErrorType);
|
|
77
49
|
}
|
|
78
50
|
declare class DurableWaitForAllError extends Error {
|
|
79
51
|
items: any[];
|
|
@@ -85,16 +57,7 @@ declare class DurableWaitForAllError extends Error {
|
|
|
85
57
|
parentWorkflowId: string;
|
|
86
58
|
workflowId: string;
|
|
87
59
|
workflowTopic: string;
|
|
88
|
-
constructor(params:
|
|
89
|
-
items: string[];
|
|
90
|
-
workflowId: string;
|
|
91
|
-
workflowTopic: string;
|
|
92
|
-
parentWorkflowId: string;
|
|
93
|
-
originJobId: string | null;
|
|
94
|
-
size: number;
|
|
95
|
-
index: number;
|
|
96
|
-
workflowDimension: string;
|
|
97
|
-
});
|
|
60
|
+
constructor(params: DurableWaitForAllErrorType);
|
|
98
61
|
}
|
|
99
62
|
declare class DurableSleepError extends Error {
|
|
100
63
|
workflowId: string;
|
|
@@ -102,12 +65,7 @@ declare class DurableSleepError extends Error {
|
|
|
102
65
|
duration: number;
|
|
103
66
|
index: number;
|
|
104
67
|
workflowDimension: string;
|
|
105
|
-
constructor(params:
|
|
106
|
-
duration: number;
|
|
107
|
-
index: number;
|
|
108
|
-
workflowDimension: string;
|
|
109
|
-
workflowId: string;
|
|
110
|
-
});
|
|
68
|
+
constructor(params: DurableSleepErrorType);
|
|
111
69
|
}
|
|
112
70
|
declare class DurableTimeoutError extends Error {
|
|
113
71
|
code: number;
|
package/build/modules/errors.js
CHANGED
|
@@ -18,7 +18,7 @@ class SetStateError extends Error {
|
|
|
18
18
|
exports.SetStateError = SetStateError;
|
|
19
19
|
class DurableWaitForError extends Error {
|
|
20
20
|
constructor(params) {
|
|
21
|
-
super(`
|
|
21
|
+
super(`WaitFor Interruption`);
|
|
22
22
|
this.signalId = params.signalId;
|
|
23
23
|
this.index = params.index;
|
|
24
24
|
this.workflowDimension = params.workflowDimension;
|
|
@@ -28,7 +28,7 @@ class DurableWaitForError extends Error {
|
|
|
28
28
|
exports.DurableWaitForError = DurableWaitForError;
|
|
29
29
|
class DurableProxyError extends Error {
|
|
30
30
|
constructor(params) {
|
|
31
|
-
super(`
|
|
31
|
+
super(`ProxyActivity Interruption`);
|
|
32
32
|
this.arguments = params.arguments;
|
|
33
33
|
this.workflowId = params.workflowId;
|
|
34
34
|
this.workflowTopic = params.workflowTopic;
|
|
@@ -46,7 +46,7 @@ class DurableProxyError extends Error {
|
|
|
46
46
|
exports.DurableProxyError = DurableProxyError;
|
|
47
47
|
class DurableChildError extends Error {
|
|
48
48
|
constructor(params) {
|
|
49
|
-
super(`
|
|
49
|
+
super(`ExecChild Interruption`);
|
|
50
50
|
this.arguments = params.arguments;
|
|
51
51
|
this.workflowId = params.workflowId;
|
|
52
52
|
this.workflowTopic = params.workflowTopic;
|
|
@@ -64,7 +64,7 @@ class DurableChildError extends Error {
|
|
|
64
64
|
exports.DurableChildError = DurableChildError;
|
|
65
65
|
class DurableWaitForAllError extends Error {
|
|
66
66
|
constructor(params) {
|
|
67
|
-
super(`
|
|
67
|
+
super(`Collation Interruption`);
|
|
68
68
|
this.items = params.items;
|
|
69
69
|
this.size = params.size;
|
|
70
70
|
this.workflowId = params.workflowId;
|
|
@@ -79,7 +79,7 @@ class DurableWaitForAllError extends Error {
|
|
|
79
79
|
exports.DurableWaitForAllError = DurableWaitForAllError;
|
|
80
80
|
class DurableSleepError extends Error {
|
|
81
81
|
constructor(params) {
|
|
82
|
-
super(`
|
|
82
|
+
super(`SleepFor Interruption`);
|
|
83
83
|
this.duration = params.duration;
|
|
84
84
|
this.workflowId = params.workflowId;
|
|
85
85
|
this.index = params.index;
|
package/build/package.json
CHANGED
|
@@ -65,7 +65,11 @@ class Hook extends activity_1.Activity {
|
|
|
65
65
|
* does this activity use a time-hook or web-hook
|
|
66
66
|
*/
|
|
67
67
|
doesHook() {
|
|
68
|
-
|
|
68
|
+
if (this.config.sleep) {
|
|
69
|
+
const duration = pipe_1.Pipe.resolve(this.config.sleep, this.context);
|
|
70
|
+
return !isNaN(duration) && Number(duration) > 0;
|
|
71
|
+
}
|
|
72
|
+
return !!this.config.hook?.topic;
|
|
69
73
|
}
|
|
70
74
|
async doHook(telemetry) {
|
|
71
75
|
const multi = this.store.getMulti();
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { Activity } from './activity';
|
|
2
2
|
import { EngineService } from '../engine';
|
|
3
3
|
import { ActivityData, ActivityMetadata, ActivityType, TriggerActivity } from '../../types/activity';
|
|
4
|
-
import { JobState } from '../../types/job';
|
|
4
|
+
import { JobState, ExtensionType } from '../../types/job';
|
|
5
5
|
import { RedisMulti } from '../../types/redis';
|
|
6
6
|
declare class Trigger extends Activity {
|
|
7
7
|
config: TriggerActivity;
|
|
8
8
|
constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
|
|
9
|
-
process(): Promise<string>;
|
|
9
|
+
process(options?: ExtensionType): Promise<string>;
|
|
10
|
+
safeKey(key: string): string;
|
|
11
|
+
bindSearchData(options?: ExtensionType): void;
|
|
12
|
+
bindMarkerData(options?: ExtensionType): void;
|
|
10
13
|
setStatus(amount: number): Promise<void>;
|
|
11
14
|
execAdjacentParent(): Promise<void>;
|
|
12
15
|
createInputContext(): Partial<JobState>;
|
|
@@ -13,7 +13,7 @@ class Trigger extends activity_1.Activity {
|
|
|
13
13
|
constructor(config, data, metadata, hook, engine, context) {
|
|
14
14
|
super(config, data, metadata, hook, engine, context);
|
|
15
15
|
}
|
|
16
|
-
async process() {
|
|
16
|
+
async process(options) {
|
|
17
17
|
this.logger.debug('trigger-process', { subscribes: this.config.subscribes });
|
|
18
18
|
let telemetry;
|
|
19
19
|
try {
|
|
@@ -26,6 +26,8 @@ class Trigger extends activity_1.Activity {
|
|
|
26
26
|
await this.setStateNX();
|
|
27
27
|
this.adjacencyList = await this.filterAdjacent();
|
|
28
28
|
await this.setStatus(this.adjacencyList.length);
|
|
29
|
+
this.bindSearchData(options);
|
|
30
|
+
this.bindMarkerData(options);
|
|
29
31
|
const multi = this.store.getMulti();
|
|
30
32
|
await this.setState(multi);
|
|
31
33
|
await this.setStats(multi);
|
|
@@ -61,6 +63,25 @@ class Trigger extends activity_1.Activity {
|
|
|
61
63
|
this.logger.debug('trigger-process-end', { subscribes: this.config.subscribes, jid: this.context.metadata.jid, gid: this.context.metadata.gid });
|
|
62
64
|
}
|
|
63
65
|
}
|
|
66
|
+
safeKey(key) {
|
|
67
|
+
return `_${key}`;
|
|
68
|
+
}
|
|
69
|
+
bindSearchData(options) {
|
|
70
|
+
if (options?.search) {
|
|
71
|
+
Object.keys(options.search).forEach((key) => {
|
|
72
|
+
this.context.data[this.safeKey(key)] = options.search[key].toString();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
bindMarkerData(options) {
|
|
77
|
+
if (options?.marker) {
|
|
78
|
+
Object.keys(options.marker).forEach((key) => {
|
|
79
|
+
if (key.startsWith('-')) {
|
|
80
|
+
this.context.data[key] = options.marker[key].toString();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
64
85
|
async setStatus(amount) {
|
|
65
86
|
this.context.metadata.js = amount;
|
|
66
87
|
}
|
|
@@ -103,14 +103,7 @@ class ClientService {
|
|
|
103
103
|
maximumInterval: (0, ms_1.default)(options.config?.maximumInterval || enums_1.HMSH_DURABLE_MAX_INTERVAL) / 1000,
|
|
104
104
|
};
|
|
105
105
|
const context = { metadata: { trc, spn }, data: {} };
|
|
106
|
-
const jobId = await hotMeshClient.pub(`${options.namespace ?? factory_1.APP_ID}.execute`, payload, context);
|
|
107
|
-
// Seed search data
|
|
108
|
-
if (jobId && options.search?.data) {
|
|
109
|
-
const searchSessionId = `-search-0`;
|
|
110
|
-
const search = new search_1.Search(jobId, hotMeshClient, searchSessionId);
|
|
111
|
-
const entries = Object.entries(options.search.data).flat();
|
|
112
|
-
await search.set(...entries);
|
|
113
|
-
}
|
|
106
|
+
const jobId = await hotMeshClient.pub(`${options.namespace ?? factory_1.APP_ID}.execute`, payload, context, { search: options?.search?.data, marker: options?.marker });
|
|
114
107
|
return new handle_1.WorkflowHandleService(hotMeshClient, workflowTopic, jobId);
|
|
115
108
|
},
|
|
116
109
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ILogger } from '../logger';
|
|
2
2
|
import { StoreService } from '../store';
|
|
3
|
-
import { ExportOptions, DurableJobExport } from '../../types/exporter';
|
|
3
|
+
import { ExportOptions, DurableJobExport, TimelineType, TransitionType, ExportFields } from '../../types/exporter';
|
|
4
4
|
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
5
5
|
import { StringStringType, Symbols } from "../../types/serializer";
|
|
6
6
|
declare class ExporterService {
|
|
@@ -8,33 +8,44 @@ declare class ExporterService {
|
|
|
8
8
|
logger: ILogger;
|
|
9
9
|
store: StoreService<RedisClient, RedisMulti>;
|
|
10
10
|
symbols: Promise<Symbols> | Symbols;
|
|
11
|
+
private static symbols;
|
|
11
12
|
constructor(appId: string, store: StoreService<RedisClient, RedisMulti>, logger: ILogger);
|
|
12
13
|
/**
|
|
13
14
|
* Convert the job hash from its compiles format into a DurableJobExport object with
|
|
14
15
|
* facets that describe the workflow in terms relevant to narrative storytelling.
|
|
15
16
|
*/
|
|
16
17
|
export(jobId: string, options?: ExportOptions): Promise<DurableJobExport>;
|
|
18
|
+
/**
|
|
19
|
+
* Inflates the job data from Redis into a DurableJobExport object
|
|
20
|
+
* @param jobHash - the job data from Redis
|
|
21
|
+
* @param dependencyList - the list of dependencies for the job
|
|
22
|
+
* @returns - the inflated job data
|
|
23
|
+
*/
|
|
24
|
+
inflate(jobHash: StringStringType, options: ExportOptions): DurableJobExport;
|
|
25
|
+
resolveValue(raw: string, withValues: boolean): Record<string, any> | string | number | null;
|
|
17
26
|
/**
|
|
18
27
|
* Inflates the key from Redis, 3-character symbol
|
|
19
28
|
* into a human-readable JSON path, reflecting the
|
|
20
29
|
* tree-like structure of the unidimensional Hash
|
|
30
|
+
* @private
|
|
21
31
|
*/
|
|
22
32
|
inflateKey(key: string): string;
|
|
33
|
+
filterFields(fullObject: DurableJobExport, block?: ExportFields[], allow?: ExportFields[]): Partial<DurableJobExport>;
|
|
34
|
+
inflateTransition(match: RegExpMatchArray, value: string, transitionsObject: Record<string, TransitionType>): void;
|
|
35
|
+
sortEntriesByCreated(obj: {
|
|
36
|
+
[key: string]: TransitionType;
|
|
37
|
+
}): TransitionType[];
|
|
23
38
|
/**
|
|
24
|
-
*
|
|
25
|
-
* organizing the dimensional isolate in sch a way asto interleave
|
|
26
|
-
* into a story
|
|
27
|
-
* @param data - the dependency data from Redis
|
|
28
|
-
* @returns - the organized dependency data
|
|
39
|
+
* marker names are overloaded with details like sequence, type, etc
|
|
29
40
|
*/
|
|
30
|
-
|
|
41
|
+
keyToObject(key: string): {
|
|
42
|
+
index: number;
|
|
43
|
+
dimension?: string;
|
|
44
|
+
secondary?: number;
|
|
45
|
+
};
|
|
31
46
|
/**
|
|
32
|
-
*
|
|
33
|
-
* @param jobHash - the job data from Redis
|
|
34
|
-
* @param dependencyList - the list of dependencies for the job
|
|
35
|
-
* @returns - the inflated job data
|
|
47
|
+
* idem list has a complicated sort order based on indexes and dimensions
|
|
36
48
|
*/
|
|
37
|
-
|
|
38
|
-
inflateProcess(match: RegExpMatchArray, value: string, replay: Record<string, Record<string, any>>): void;
|
|
49
|
+
sortParts(parts: TimelineType[]): TimelineType[];
|
|
39
50
|
}
|
|
40
51
|
export { ExporterService };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ExporterService = void 0;
|
|
4
|
-
const key_1 = require("../../modules/key");
|
|
5
4
|
const utils_1 = require("../../modules/utils");
|
|
6
5
|
const serializer_1 = require("../serializer");
|
|
7
6
|
class ExporterService {
|
|
@@ -15,174 +14,122 @@ class ExporterService {
|
|
|
15
14
|
* facets that describe the workflow in terms relevant to narrative storytelling.
|
|
16
15
|
*/
|
|
17
16
|
async export(jobId, options = {}) {
|
|
18
|
-
if (!this.
|
|
19
|
-
|
|
20
|
-
this.
|
|
17
|
+
if (!ExporterService.symbols.has(this.appId)) {
|
|
18
|
+
const symbols = this.store.getAllSymbols();
|
|
19
|
+
ExporterService.symbols.set(this.appId, await symbols);
|
|
21
20
|
}
|
|
22
21
|
const jobData = await this.store.getRaw(jobId);
|
|
23
|
-
const jobExport = this.inflate(jobData
|
|
22
|
+
const jobExport = this.inflate(jobData, options);
|
|
24
23
|
return jobExport;
|
|
25
24
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Inflates the key from Redis, 3-character symbol
|
|
28
|
-
* into a human-readable JSON path, reflecting the
|
|
29
|
-
* tree-like structure of the unidimensional Hash
|
|
30
|
-
*/
|
|
31
|
-
inflateKey(key) {
|
|
32
|
-
if (key in this.symbols) {
|
|
33
|
-
const path = this.symbols[key];
|
|
34
|
-
const parts = path.split('/');
|
|
35
|
-
return parts.join('/');
|
|
36
|
-
}
|
|
37
|
-
return key;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Inflates the dependency data from Redis into a DurableJobExport object by
|
|
41
|
-
* organizing the dimensional isolate in sch a way asto interleave
|
|
42
|
-
* into a story
|
|
43
|
-
* @param data - the dependency data from Redis
|
|
44
|
-
* @returns - the organized dependency data
|
|
45
|
-
*/
|
|
46
|
-
inflateDependencyData(data) {
|
|
47
|
-
return data.map((dependency, index) => {
|
|
48
|
-
const [action, topic, gid, dimension, ...jid] = dependency.split(key_1.VALSEP);
|
|
49
|
-
const job_id = jid.join(key_1.VALSEP);
|
|
50
|
-
return {
|
|
51
|
-
index,
|
|
52
|
-
action,
|
|
53
|
-
topic,
|
|
54
|
-
gid,
|
|
55
|
-
dimension,
|
|
56
|
-
job_id,
|
|
57
|
-
};
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
25
|
/**
|
|
61
26
|
* Inflates the job data from Redis into a DurableJobExport object
|
|
62
27
|
* @param jobHash - the job data from Redis
|
|
63
28
|
* @param dependencyList - the list of dependencies for the job
|
|
64
29
|
* @returns - the inflated job data
|
|
65
30
|
*/
|
|
66
|
-
inflate(jobHash) {
|
|
67
|
-
const
|
|
31
|
+
inflate(jobHash, options) {
|
|
32
|
+
const timeline = [];
|
|
68
33
|
const state = {};
|
|
69
34
|
const data = {};
|
|
70
|
-
const
|
|
71
|
-
const replay = {};
|
|
35
|
+
const transitionsObject = {};
|
|
72
36
|
const regex = /^([a-zA-Z]{3}),(\d+(?:,\d+)*)/;
|
|
73
37
|
Object.entries(jobHash).forEach(([key, value]) => {
|
|
74
38
|
const match = key.match(regex);
|
|
75
39
|
if (match) {
|
|
76
|
-
//
|
|
77
|
-
this.
|
|
40
|
+
//transitions
|
|
41
|
+
this.inflateTransition(match, value, transitionsObject);
|
|
78
42
|
}
|
|
79
43
|
else if (key.length === 3) {
|
|
80
|
-
//
|
|
44
|
+
//state
|
|
81
45
|
state[this.inflateKey(key)] = serializer_1.SerializerService.fromString(value);
|
|
82
46
|
}
|
|
83
47
|
else if (key.startsWith('_')) {
|
|
84
|
-
//
|
|
48
|
+
//data
|
|
85
49
|
data[key.substring(1)] = value;
|
|
86
50
|
}
|
|
87
51
|
else if (key.startsWith('-')) {
|
|
88
|
-
//
|
|
89
|
-
|
|
52
|
+
//timeline
|
|
53
|
+
const keyParts = this.keyToObject(key); //key parts have meaning
|
|
54
|
+
timeline.push({
|
|
55
|
+
...keyParts,
|
|
90
56
|
key,
|
|
91
|
-
value:
|
|
92
|
-
parts: extractParts(key),
|
|
57
|
+
value: this.resolveValue(value, options.values),
|
|
93
58
|
});
|
|
94
59
|
}
|
|
95
|
-
else {
|
|
96
|
-
//collator guids, etc
|
|
97
|
-
other.push([null, key, value]);
|
|
98
|
-
}
|
|
99
60
|
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
61
|
+
return this.filterFields({
|
|
62
|
+
data: (0, utils_1.restoreHierarchy)(data),
|
|
63
|
+
state: Object.entries((0, utils_1.restoreHierarchy)(state))[0][1],
|
|
64
|
+
status: parseInt(jobHash[':'], 10),
|
|
65
|
+
timeline: this.sortParts(timeline),
|
|
66
|
+
transitions: this.sortEntriesByCreated(transitionsObject),
|
|
67
|
+
}, options.block, options.allow);
|
|
68
|
+
}
|
|
69
|
+
resolveValue(raw, withValues) {
|
|
70
|
+
const resolved = serializer_1.SerializerService.fromString(raw);
|
|
71
|
+
if (withValues !== false) {
|
|
72
|
+
return resolved;
|
|
73
|
+
}
|
|
74
|
+
if (resolved && typeof resolved === 'object') {
|
|
75
|
+
if ('data' in resolved) {
|
|
76
|
+
resolved.data = {};
|
|
77
|
+
}
|
|
78
|
+
if ('$error' in resolved) {
|
|
79
|
+
resolved.$error = {};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return resolved;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Inflates the key from Redis, 3-character symbol
|
|
86
|
+
* into a human-readable JSON path, reflecting the
|
|
87
|
+
* tree-like structure of the unidimensional Hash
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
inflateKey(key) {
|
|
91
|
+
const symbols = ExporterService.symbols.get(this.appId);
|
|
92
|
+
if (key in symbols) {
|
|
93
|
+
const path = symbols[key];
|
|
94
|
+
const parts = path.split('/');
|
|
95
|
+
return parts.join('/');
|
|
96
|
+
}
|
|
97
|
+
return key;
|
|
98
|
+
}
|
|
99
|
+
filterFields(fullObject, block = [], allow = []) {
|
|
100
|
+
let result = {};
|
|
101
|
+
if (allow && allow.length > 0) {
|
|
102
|
+
allow.forEach(field => {
|
|
103
|
+
if (field in fullObject) {
|
|
104
|
+
result[field] = fullObject[field];
|
|
137
105
|
}
|
|
138
|
-
return 0;
|
|
139
106
|
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
result = { ...fullObject };
|
|
110
|
+
}
|
|
111
|
+
if (block && block.length > 0) {
|
|
112
|
+
block.forEach(field => {
|
|
113
|
+
if (field in result) {
|
|
114
|
+
delete result[field];
|
|
147
115
|
}
|
|
148
|
-
}
|
|
149
|
-
const parts = key.split('-');
|
|
150
|
-
if (parts.length === 4) {
|
|
151
|
-
//-proxy-5- -search-1-1-
|
|
152
|
-
return {
|
|
153
|
-
index: parseInt(parts[2], 10),
|
|
154
|
-
dimension: extractDimension(parts[1]),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
//-search,0,0-1-1- -proxy,0,0-1-
|
|
159
|
-
return {
|
|
160
|
-
index: parseInt(parts[2], 10),
|
|
161
|
-
secondary: parseInt(parts[3], 10),
|
|
162
|
-
dimension: extractDimension(parts[1]),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
116
|
+
});
|
|
165
117
|
}
|
|
166
|
-
return
|
|
167
|
-
data: (0, utils_1.restoreHierarchy)(data),
|
|
168
|
-
idempotents: sortParts(idempotents),
|
|
169
|
-
state: Object.entries((0, utils_1.restoreHierarchy)(state))[0][1],
|
|
170
|
-
status: jobHash[':'],
|
|
171
|
-
replay: sortEntriesByCreated(replay),
|
|
172
|
-
};
|
|
118
|
+
return result;
|
|
173
119
|
}
|
|
174
|
-
|
|
120
|
+
inflateTransition(match, value, transitionsObject) {
|
|
175
121
|
const [_, letters, dimensions] = match;
|
|
176
122
|
const path = this.inflateKey(letters);
|
|
177
123
|
const parts = path.split('/');
|
|
178
124
|
const activity = parts[0];
|
|
179
125
|
const isCreate = path.endsWith('/output/metadata/ac');
|
|
180
126
|
const isUpdate = path.endsWith('/output/metadata/au');
|
|
127
|
+
//for now only export activity start/stop; activity data would also be interesting
|
|
181
128
|
if (isCreate || isUpdate) {
|
|
182
129
|
const targetName = `${activity},${dimensions}`;
|
|
183
|
-
let target =
|
|
130
|
+
let target = transitionsObject[targetName];
|
|
184
131
|
if (!target) {
|
|
185
|
-
|
|
132
|
+
transitionsObject[targetName] = {
|
|
186
133
|
activity,
|
|
187
134
|
dimensions,
|
|
188
135
|
created: isCreate ? value : null,
|
|
@@ -194,5 +141,76 @@ class ExporterService {
|
|
|
194
141
|
}
|
|
195
142
|
}
|
|
196
143
|
}
|
|
144
|
+
sortEntriesByCreated(obj) {
|
|
145
|
+
const entriesArray = Object.values(obj);
|
|
146
|
+
entriesArray.sort((a, b) => {
|
|
147
|
+
return (a.created || a.updated).localeCompare(b.created || b.updated);
|
|
148
|
+
});
|
|
149
|
+
return entriesArray;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* marker names are overloaded with details like sequence, type, etc
|
|
153
|
+
*/
|
|
154
|
+
keyToObject(key) {
|
|
155
|
+
function extractDimension(label) {
|
|
156
|
+
const parts = label.split(',');
|
|
157
|
+
if (parts.length > 1) {
|
|
158
|
+
parts.shift();
|
|
159
|
+
return parts.join(',');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const parts = key.split('-');
|
|
163
|
+
if (parts.length === 4) {
|
|
164
|
+
//-proxy-5- -search-1-1-
|
|
165
|
+
return {
|
|
166
|
+
index: parseInt(parts[2], 10),
|
|
167
|
+
dimension: extractDimension(parts[1]),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
//-search,0,0-1-1- -proxy,0,0-1-
|
|
172
|
+
return {
|
|
173
|
+
index: parseInt(parts[2], 10),
|
|
174
|
+
secondary: parseInt(parts[3], 10),
|
|
175
|
+
dimension: extractDimension(parts[1]),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* idem list has a complicated sort order based on indexes and dimensions
|
|
181
|
+
*/
|
|
182
|
+
sortParts(parts) {
|
|
183
|
+
return parts.sort((a, b) => {
|
|
184
|
+
const { dimension: aDim, index: aIdx, secondary: aSec } = a;
|
|
185
|
+
const { dimension: bDim, index: bIdx, secondary: bSec } = b;
|
|
186
|
+
if (aDim === undefined && bDim !== undefined)
|
|
187
|
+
return -1;
|
|
188
|
+
if (aDim !== undefined && bDim === undefined)
|
|
189
|
+
return 1;
|
|
190
|
+
if (aDim !== undefined && bDim !== undefined) {
|
|
191
|
+
if (aDim < bDim)
|
|
192
|
+
return -1;
|
|
193
|
+
if (aDim > bDim)
|
|
194
|
+
return 1;
|
|
195
|
+
}
|
|
196
|
+
if (aIdx < bIdx)
|
|
197
|
+
return -1;
|
|
198
|
+
if (aIdx > bIdx)
|
|
199
|
+
return 1;
|
|
200
|
+
if (aSec === undefined && bSec !== undefined)
|
|
201
|
+
return -1;
|
|
202
|
+
if (aSec !== undefined && bSec === undefined)
|
|
203
|
+
return 1;
|
|
204
|
+
if (aSec !== undefined && bSec !== undefined) {
|
|
205
|
+
if (aSec < bSec)
|
|
206
|
+
return -1;
|
|
207
|
+
if (aSec > bSec)
|
|
208
|
+
return 1;
|
|
209
|
+
}
|
|
210
|
+
return 0;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
;
|
|
197
214
|
}
|
|
198
215
|
exports.ExporterService = ExporterService;
|
|
216
|
+
ExporterService.symbols = new Map();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExporterService } from './exporter';
|
|
2
2
|
import { HotMeshService as HotMesh } from '../hotmesh';
|
|
3
|
-
import { DurableJobExport } from '../../types/exporter';
|
|
3
|
+
import { DurableJobExport, ExportOptions } from '../../types/exporter';
|
|
4
4
|
import { JobInterruptOptions } from '../../types/job';
|
|
5
5
|
import { StreamError } from '../../types/stream';
|
|
6
6
|
export declare class WorkflowHandleService {
|
|
@@ -9,7 +9,7 @@ export declare class WorkflowHandleService {
|
|
|
9
9
|
workflowTopic: string;
|
|
10
10
|
workflowId: string;
|
|
11
11
|
constructor(hotMesh: HotMesh, workflowTopic: string, workflowId: string);
|
|
12
|
-
export(): Promise<DurableJobExport>;
|
|
12
|
+
export(options?: ExportOptions): Promise<DurableJobExport>;
|
|
13
13
|
/**
|
|
14
14
|
* Sends a signal to the workflow. This is a way to send
|
|
15
15
|
* a message to a workflow that is paused due to having
|
|
@@ -9,8 +9,8 @@ class WorkflowHandleService {
|
|
|
9
9
|
this.hotMesh = hotMesh;
|
|
10
10
|
this.exporter = new exporter_1.ExporterService(this.hotMesh.appId, this.hotMesh.engine.store, this.hotMesh.engine.logger);
|
|
11
11
|
}
|
|
12
|
-
async export() {
|
|
13
|
-
return this.exporter.export(this.workflowId);
|
|
12
|
+
async export(options) {
|
|
13
|
+
return this.exporter.export(this.workflowId, options);
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Sends a signal to the workflow. This is a way to send
|