@hahnpro/flow-sdk 4.14.5 → 4.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FlowApplication.d.ts +3 -0
- package/dist/FlowApplication.js +35 -15
- package/dist/api/endpoint.interface.d.ts +3 -0
- package/dist/api/endpoint.service.d.ts +1 -1
- package/dist/api/endpoint.service.js +2 -2
- package/dist/api/mock/api.mock.d.ts +3 -0
- package/dist/api/mock/api.mock.js +3 -0
- package/dist/api/mock/endpoint.mock.service.d.ts +1 -1
- package/dist/api/mock/endpoint.mock.service.js +1 -1
- package/package.json +7 -7
|
@@ -15,10 +15,13 @@ export declare class FlowApplication {
|
|
|
15
15
|
private elements;
|
|
16
16
|
private logger;
|
|
17
17
|
private outputStreamMap;
|
|
18
|
+
private outputQueueMetrics;
|
|
18
19
|
private performanceMap;
|
|
19
20
|
private properties;
|
|
20
21
|
private _rpcClient;
|
|
21
22
|
constructor(modules: ClassType<any>[], flow: Flow, logger?: Logger, amqpConnection?: AmqpConnection, skipApi?: boolean);
|
|
23
|
+
private setQueueMetrics;
|
|
24
|
+
private updateMetrics;
|
|
22
25
|
subscribe: (streamId: string, observer: PartialObserver<FlowEvent>) => import("rxjs").Subscription;
|
|
23
26
|
emit: (event: FlowEvent) => void;
|
|
24
27
|
emitPartial: (completeEvent: FlowEvent, partialEvent: FlowEvent) => void;
|
package/dist/FlowApplication.js
CHANGED
|
@@ -14,7 +14,9 @@ const api_1 = require("./api");
|
|
|
14
14
|
const utils_1 = require("./utils");
|
|
15
15
|
const FlowLogger_1 = require("./FlowLogger");
|
|
16
16
|
const RpcClient_1 = require("./RpcClient");
|
|
17
|
-
const MAX_EVENT_SIZE_BYTES = 512 * 1024;
|
|
17
|
+
const MAX_EVENT_SIZE_BYTES = +process.env.MAX_EVENT_SIZE_BYTES || 512 * 1024;
|
|
18
|
+
const WARN_EVENT_PROCESSING_SEC = +process.env.WARN_EVENT_PROCESSING_SEC || 60;
|
|
19
|
+
const WARN_EVENT_QUEUE_SIZE = +process.env.WARN_EVENT_QUEUE_SIZE || 100;
|
|
18
20
|
class FlowApplication {
|
|
19
21
|
constructor(modules, flow, logger, amqpConnection, skipApi) {
|
|
20
22
|
var _a, _b, _c;
|
|
@@ -22,7 +24,31 @@ class FlowApplication {
|
|
|
22
24
|
this.declarations = {};
|
|
23
25
|
this.elements = {};
|
|
24
26
|
this.outputStreamMap = new Map();
|
|
27
|
+
this.outputQueueMetrics = new Map();
|
|
25
28
|
this.performanceMap = new Map();
|
|
29
|
+
this.setQueueMetrics = (id) => {
|
|
30
|
+
const metrics = this.outputQueueMetrics.get(id) || { size: 0, lastAdd: 0, lastRemove: Date.now(), warnings: 0 };
|
|
31
|
+
const secsProcessing = Math.round((metrics.lastAdd - metrics.lastRemove) / 1000);
|
|
32
|
+
metrics.size++;
|
|
33
|
+
metrics.lastAdd = Date.now();
|
|
34
|
+
if (secsProcessing >= WARN_EVENT_PROCESSING_SEC * (metrics.warnings + 1)) {
|
|
35
|
+
this.logger.warn(`Input stream "${id}" has ${metrics.size} queued events and the last event has been processing for ${secsProcessing}s`);
|
|
36
|
+
metrics.warnings++;
|
|
37
|
+
}
|
|
38
|
+
else if (metrics.size % WARN_EVENT_QUEUE_SIZE === 0) {
|
|
39
|
+
this.logger.warn(`Input stream "${id}" has ${metrics.size} queued events`);
|
|
40
|
+
}
|
|
41
|
+
this.outputQueueMetrics.set(id, metrics);
|
|
42
|
+
};
|
|
43
|
+
this.updateMetrics = (id) => {
|
|
44
|
+
const metrics = this.outputQueueMetrics.get(id);
|
|
45
|
+
if (metrics) {
|
|
46
|
+
metrics.size = metrics.size > 0 ? metrics.size - 1 : 0;
|
|
47
|
+
metrics.lastRemove = Date.now();
|
|
48
|
+
metrics.warnings = 0;
|
|
49
|
+
this.outputQueueMetrics.set(id, metrics);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
26
52
|
this.subscribe = (streamId, observer) => this.getOutputStream(streamId).subscribe(observer);
|
|
27
53
|
this.emit = (event) => {
|
|
28
54
|
if (event) {
|
|
@@ -31,9 +57,6 @@ class FlowApplication {
|
|
|
31
57
|
if (this.outputStreamMap.has(event.getStreamId())) {
|
|
32
58
|
this.getOutputStream(event.getStreamId()).next(event);
|
|
33
59
|
}
|
|
34
|
-
else {
|
|
35
|
-
throw new Error('Output Stream ' + event.getStreamId() + ' is not defined');
|
|
36
|
-
}
|
|
37
60
|
}
|
|
38
61
|
catch (err) {
|
|
39
62
|
this.logger.error(err);
|
|
@@ -42,13 +65,8 @@ class FlowApplication {
|
|
|
42
65
|
};
|
|
43
66
|
this.emitPartial = (completeEvent, partialEvent) => {
|
|
44
67
|
try {
|
|
45
|
-
if (completeEvent) {
|
|
46
|
-
|
|
47
|
-
this.getOutputStream(completeEvent.getStreamId()).next(completeEvent);
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
throw new Error('Output Stream ' + completeEvent.getStreamId() + ' is not defined');
|
|
51
|
-
}
|
|
68
|
+
if (completeEvent && this.outputStreamMap.has(completeEvent.getStreamId())) {
|
|
69
|
+
this.getOutputStream(completeEvent.getStreamId()).next(completeEvent);
|
|
52
70
|
}
|
|
53
71
|
if (partialEvent) {
|
|
54
72
|
this.publishEvent(partialEvent);
|
|
@@ -196,7 +214,8 @@ class FlowApplication {
|
|
|
196
214
|
if (!source || !target) {
|
|
197
215
|
continue;
|
|
198
216
|
}
|
|
199
|
-
const
|
|
217
|
+
const sourceStreamId = `${source}.${sourceStream}`;
|
|
218
|
+
const targetStreamId = `${target}.${targetStream}`;
|
|
200
219
|
const element = this.elements[target];
|
|
201
220
|
if (!element || !element.constructor) {
|
|
202
221
|
throw new Error(target + ' has not been initialized');
|
|
@@ -207,9 +226,9 @@ class FlowApplication {
|
|
|
207
226
|
}
|
|
208
227
|
const streamOptions = Reflect.getMetadata(`stream:options:${targetStream}`, element.constructor) || {};
|
|
209
228
|
const concurrent = streamOptions.concurrent || 1;
|
|
210
|
-
const outputStream = this.getOutputStream(
|
|
229
|
+
const outputStream = this.getOutputStream(sourceStreamId);
|
|
211
230
|
outputStream
|
|
212
|
-
.pipe((0, operators_1.mergeMap)(async (event) => {
|
|
231
|
+
.pipe((0, operators_1.tap)(() => this.setQueueMetrics(targetStreamId)), (0, operators_1.mergeMap)(async (event) => {
|
|
213
232
|
this.performanceMap.set(event.getId(), perf_hooks_1.performance.eventLoopUtilization());
|
|
214
233
|
try {
|
|
215
234
|
await element[streamHandler](event);
|
|
@@ -224,12 +243,13 @@ class FlowApplication {
|
|
|
224
243
|
}
|
|
225
244
|
return event;
|
|
226
245
|
}, concurrent), (0, operators_1.tap)((event) => {
|
|
246
|
+
this.updateMetrics(targetStreamId);
|
|
227
247
|
let elu = this.performanceMap.get(event.getId());
|
|
228
248
|
if (elu) {
|
|
229
249
|
this.performanceMap.delete(event.getId());
|
|
230
250
|
elu = perf_hooks_1.performance.eventLoopUtilization(elu);
|
|
231
251
|
if (elu.utilization > 0.7 && elu.active > 1000) {
|
|
232
|
-
this.logger.warn(`High event loop utilization detected for ${
|
|
252
|
+
this.logger.warn(`High event loop utilization detected for ${targetStreamId} with event ${event.getId()}! Handler has been active for ${Number(elu.active).toFixed(2)}ms with a utilization of ${Number(elu.utilization * 100).toFixed(2)}%. Consider refactoring or move tasks to a worker thread.`);
|
|
233
253
|
}
|
|
234
254
|
}
|
|
235
255
|
}))
|
|
@@ -3,6 +3,6 @@ import { DataService } from './data.service';
|
|
|
3
3
|
import { Endpoint, EndpointLog } from './endpoint.interface';
|
|
4
4
|
export declare class EndpointService extends DataService<Endpoint> {
|
|
5
5
|
constructor(httpClient: HttpClient);
|
|
6
|
-
sendNotification(endpointId: string, subject: string, message: string, group: string, eventLink?: string): Promise<void>;
|
|
6
|
+
sendNotification(endpointId: string, subject: string, message: string, group: string, eventLink?: string, assetLink?: string): Promise<void>;
|
|
7
7
|
readLastLogByGroup(endpointId: string, group: string): Promise<EndpointLog>;
|
|
8
8
|
}
|
|
@@ -6,8 +6,8 @@ class EndpointService extends data_service_1.DataService {
|
|
|
6
6
|
constructor(httpClient) {
|
|
7
7
|
super(httpClient, process.env.DEBUG_ENDPOINT_URL || 'api/notification/endpoints');
|
|
8
8
|
}
|
|
9
|
-
sendNotification(endpointId, subject, message, group, eventLink) {
|
|
10
|
-
const body = Object.assign({ subject, message, group }, (eventLink && { eventLink }));
|
|
9
|
+
sendNotification(endpointId, subject, message, group, eventLink, assetLink) {
|
|
10
|
+
const body = Object.assign(Object.assign({ subject, message, group }, (eventLink && { eventLink })), (assetLink && { assetLink }));
|
|
11
11
|
return this.httpClient.post(`${this.basePath}/${endpointId}`, body);
|
|
12
12
|
}
|
|
13
13
|
readLastLogByGroup(endpointId, group) {
|
|
@@ -101,6 +101,9 @@ export interface EndpointInit {
|
|
|
101
101
|
authToken: string;
|
|
102
102
|
recipients?: string[];
|
|
103
103
|
};
|
|
104
|
+
notificationCheck?: number;
|
|
105
|
+
notificationCount?: number;
|
|
106
|
+
notificationPause?: number;
|
|
104
107
|
readPermissions?: string[];
|
|
105
108
|
readWritePermissions?: string[];
|
|
106
109
|
}
|
|
@@ -58,6 +58,9 @@ class MockAPI {
|
|
|
58
58
|
description: value.description,
|
|
59
59
|
status: value.status,
|
|
60
60
|
config: value.config,
|
|
61
|
+
notificationCheck: value.notificationCheck,
|
|
62
|
+
notificationCount: value.notificationCount,
|
|
63
|
+
notificationPause: value.notificationPause,
|
|
61
64
|
readPermissions: [],
|
|
62
65
|
readWritePermissions: [],
|
|
63
66
|
}));
|
|
@@ -3,7 +3,7 @@ import { EndpointService } from '../endpoint.service';
|
|
|
3
3
|
import { DataMockService } from './data.mock.service';
|
|
4
4
|
export declare class EndpointMockService extends DataMockService<Endpoint> implements EndpointService {
|
|
5
5
|
constructor(endpoints: Endpoint[]);
|
|
6
|
-
sendNotification(endpointId: string, subject: string, message: string, group: string, eventLink?: string): Promise<void>;
|
|
6
|
+
sendNotification(endpointId: string, subject: string, message: string, group: string, eventLink?: string, assetLink?: string): Promise<void>;
|
|
7
7
|
readLastLogByGroup(endpointId: string, group: string): Promise<{
|
|
8
8
|
id: string;
|
|
9
9
|
endpoint: string;
|
|
@@ -7,7 +7,7 @@ class EndpointMockService extends data_mock_service_1.DataMockService {
|
|
|
7
7
|
super();
|
|
8
8
|
this.data = endpoints;
|
|
9
9
|
}
|
|
10
|
-
sendNotification(endpointId, subject, message, group, eventLink) {
|
|
10
|
+
sendNotification(endpointId, subject, message, group, eventLink, assetLink) {
|
|
11
11
|
return Promise.resolve();
|
|
12
12
|
}
|
|
13
13
|
readLastLogByGroup(endpointId, group) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hahnpro/flow-sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.17.0",
|
|
4
4
|
"description": "SDK for building Flow Modules",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"amqp-connection-manager": "^3.7.0",
|
|
28
28
|
"amqplib": "^0.8.0",
|
|
29
|
-
"axios": "^0.
|
|
29
|
+
"axios": "^0.22.0",
|
|
30
30
|
"class-transformer": "0.3.1",
|
|
31
31
|
"class-validator": "~0.12.2",
|
|
32
32
|
"class-validator-jsonschema": "^2.2.0",
|
|
@@ -38,19 +38,19 @@
|
|
|
38
38
|
"p-queue": "^6.6.2",
|
|
39
39
|
"python-shell": "^3.0.0",
|
|
40
40
|
"reflect-metadata": "^0.1.13",
|
|
41
|
-
"rxjs": "^7.
|
|
41
|
+
"rxjs": "^7.4.0",
|
|
42
42
|
"string-interp": "^0.3.6",
|
|
43
43
|
"uuid": "^8.3.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@golevelup/nestjs-rabbitmq": "^1.
|
|
46
|
+
"@golevelup/nestjs-rabbitmq": "^1.18.0",
|
|
47
47
|
"@types/amqp-connection-manager": "^2.0.12",
|
|
48
48
|
"@types/amqplib": "^0.8.2",
|
|
49
49
|
"@types/jest": "^27.0.2",
|
|
50
|
-
"@types/lodash": "^4.14.
|
|
51
|
-
"@types/node": "^14.17.
|
|
50
|
+
"@types/lodash": "^4.14.175",
|
|
51
|
+
"@types/node": "^14.17.21",
|
|
52
52
|
"axios-mock-adapter": "^1.20.0",
|
|
53
|
-
"jest": "^27.2.
|
|
53
|
+
"jest": "^27.2.4",
|
|
54
54
|
"typescript": "^4.4.3"
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|