@hahnpro/flow-sdk 2025.10.4 → 2025.11.1
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/package.json +3 -7
- package/src/lib/FlowApplication.d.ts +3 -12
- package/src/lib/FlowApplication.js +3 -37
- package/src/lib/FlowElement.d.ts +0 -4
- package/src/lib/FlowElement.js +0 -22
- package/src/lib/RpcClient.d.ts +0 -13
- package/src/lib/RpcClient.js +0 -88
- package/src/lib/amqp.d.ts +0 -14
- package/src/lib/amqp.js +0 -12
- package/src/lib/rpc_server.py +0 -116
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hahnpro/flow-sdk",
|
|
3
|
-
"version": "2025.
|
|
3
|
+
"version": "2025.11.1",
|
|
4
4
|
"description": "SDK for building Flow Modules",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -17,12 +17,10 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@hahnpro/hpc-api": "2025.
|
|
20
|
+
"@hahnpro/hpc-api": "2025.11.1",
|
|
21
21
|
"@nats-io/jetstream": "3.2.0",
|
|
22
22
|
"@nats-io/nats-core": "3.2.0",
|
|
23
23
|
"@nats-io/transport-node": "3.2.0",
|
|
24
|
-
"amqp-connection-manager": "4.1.15",
|
|
25
|
-
"amqplib": "0.10.9",
|
|
26
24
|
"class-transformer": "0.5.1",
|
|
27
25
|
"class-validator": "0.14.2",
|
|
28
26
|
"cloudevents": "10.0.0",
|
|
@@ -34,7 +32,6 @@
|
|
|
34
32
|
"string-interp": "0.3.6"
|
|
35
33
|
},
|
|
36
34
|
"devDependencies": {
|
|
37
|
-
"@types/amqplib": "0.10.8",
|
|
38
35
|
"@types/jest": "30.0.0",
|
|
39
36
|
"@types/lodash": "4.17.20",
|
|
40
37
|
"@types/node": "22.18.13",
|
|
@@ -46,8 +43,7 @@
|
|
|
46
43
|
"axios": "1.13.1",
|
|
47
44
|
"class-transformer": "0.5.1",
|
|
48
45
|
"class-validator": "0.14.2",
|
|
49
|
-
"lodash": "4.17.21"
|
|
50
|
-
"python-shell": "5.x"
|
|
46
|
+
"lodash": "4.17.21"
|
|
51
47
|
},
|
|
52
48
|
"engines": {
|
|
53
49
|
"node": ">=v22"
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { API, HttpClientService, MockAPI } from '@hahnpro/hpc-api';
|
|
3
3
|
import { NatsConnection, ConnectionOptions as NatsConnectionOptions } from '@nats-io/nats-core';
|
|
4
|
-
import { AmqpConnectionManager } from 'amqp-connection-manager';
|
|
5
4
|
import { CloudEvent } from 'cloudevents';
|
|
6
5
|
import { PartialObserver } from 'rxjs';
|
|
7
|
-
import { AmqpConnection, AmqpConnectionConfig } from './amqp';
|
|
8
6
|
import { ContextManager } from './ContextManager';
|
|
9
7
|
import { ClassType, Flow, FlowElementContext } from './flow.interface';
|
|
10
8
|
import { FlowEvent } from './FlowEvent';
|
|
11
9
|
import { Logger } from './FlowLogger';
|
|
12
|
-
import { RpcClient } from './RpcClient';
|
|
13
10
|
interface FlowAppConfig {
|
|
14
11
|
logger?: Logger;
|
|
15
|
-
amqpConfig?: AmqpConnectionConfig;
|
|
16
|
-
amqpConnection?: AmqpConnectionManager;
|
|
17
12
|
natsConfig?: NatsConnectionOptions;
|
|
18
13
|
natsConnection?: NatsConnection;
|
|
19
14
|
apiClient?: HttpClientService;
|
|
@@ -25,9 +20,6 @@ export declare class FlowApplication {
|
|
|
25
20
|
private modules;
|
|
26
21
|
private flow;
|
|
27
22
|
private _api;
|
|
28
|
-
private _rpcClient;
|
|
29
|
-
private amqpChannel;
|
|
30
|
-
private readonly amqpConnection;
|
|
31
23
|
private readonly natsConnectionConfig?;
|
|
32
24
|
private _natsConnection?;
|
|
33
25
|
private readonly baseLogger;
|
|
@@ -44,8 +36,7 @@ export declare class FlowApplication {
|
|
|
44
36
|
private readonly contextManager;
|
|
45
37
|
private natsMessageIterator;
|
|
46
38
|
constructor(modules: ClassType<any>[], flow: Flow, config?: FlowAppConfig);
|
|
47
|
-
constructor(modules: ClassType<any>[], flow: Flow, baseLogger?: Logger,
|
|
48
|
-
get rpcClient(): RpcClient;
|
|
39
|
+
constructor(modules: ClassType<any>[], flow: Flow, baseLogger?: Logger, natsConnection?: NatsConnection, skipApi?: boolean, explicitInit?: boolean);
|
|
49
40
|
get api(): API;
|
|
50
41
|
get natsConnection(): NatsConnection;
|
|
51
42
|
getContextManager(): ContextManager;
|
|
@@ -60,7 +51,7 @@ export declare class FlowApplication {
|
|
|
60
51
|
emitPartial: (completeEvent: FlowEvent, partialEvent: FlowEvent) => void;
|
|
61
52
|
onMessage: (cloudEvent: CloudEvent) => Promise<void>;
|
|
62
53
|
/**
|
|
63
|
-
* Publish a flow event to
|
|
54
|
+
* Publish a flow event to NATS
|
|
64
55
|
* If the event size exceeds the limit it will be truncated
|
|
65
56
|
*
|
|
66
57
|
* TODO warum darf hier nicht false zurückgegeben werden? -> erzeugt loop
|
|
@@ -68,7 +59,7 @@ export declare class FlowApplication {
|
|
|
68
59
|
publishNatsEventFlowlogs: (event: FlowEvent) => Promise<boolean>;
|
|
69
60
|
/**
|
|
70
61
|
* Calls onDestroy lifecycle method on all flow elements,
|
|
71
|
-
* closes
|
|
62
|
+
* closes NATS connection after allowing logs to be processed and published
|
|
72
63
|
* then exits process
|
|
73
64
|
*/
|
|
74
65
|
destroy(exitCode?: number): Promise<void>;
|
|
@@ -11,18 +11,16 @@ const lodash_1 = require("lodash");
|
|
|
11
11
|
const object_sizeof_1 = tslib_1.__importDefault(require("object-sizeof"));
|
|
12
12
|
const rxjs_1 = require("rxjs");
|
|
13
13
|
const operators_1 = require("rxjs/operators");
|
|
14
|
-
const amqp_1 = require("./amqp");
|
|
15
14
|
const ContextManager_1 = require("./ContextManager");
|
|
16
15
|
const flow_interface_1 = require("./flow.interface");
|
|
17
16
|
const FlowLogger_1 = require("./FlowLogger");
|
|
18
17
|
const nats_1 = require("./nats");
|
|
19
|
-
const RpcClient_1 = require("./RpcClient");
|
|
20
18
|
const utils_1 = require("./utils");
|
|
21
19
|
const MAX_EVENT_SIZE_BYTES = +process.env.MAX_EVENT_SIZE_BYTES || 512 * 1024; // 512kb
|
|
22
20
|
const WARN_EVENT_PROCESSING_SEC = +process.env.WARN_EVENT_PROCESSING_SEC || 60;
|
|
23
21
|
const WARN_EVENT_QUEUE_SIZE = +process.env.WARN_EVENT_QUEUE_SIZE || 100;
|
|
24
22
|
class FlowApplication {
|
|
25
|
-
constructor(modules, flow, baseLoggerOrConfig,
|
|
23
|
+
constructor(modules, flow, baseLoggerOrConfig, natsConnection, skipApi, explicitInit, mockApi) {
|
|
26
24
|
this.modules = modules;
|
|
27
25
|
this.flow = flow;
|
|
28
26
|
this.declarations = {};
|
|
@@ -167,13 +165,9 @@ class FlowApplication {
|
|
|
167
165
|
}
|
|
168
166
|
}
|
|
169
167
|
}
|
|
170
|
-
else if (cloudEvent.subject.endsWith('.destroy')) {
|
|
171
|
-
// TODO war com.flowstudio.deployment.destroy in RabbitMq: wo wird das jetzt wieder gesendet?
|
|
172
|
-
this.destroy();
|
|
173
|
-
}
|
|
174
168
|
};
|
|
175
169
|
/**
|
|
176
|
-
* Publish a flow event to
|
|
170
|
+
* Publish a flow event to NATS
|
|
177
171
|
* If the event size exceeds the limit it will be truncated
|
|
178
172
|
*
|
|
179
173
|
* TODO warum darf hier nicht false zurückgegeben werden? -> erzeugt loop
|
|
@@ -204,7 +198,6 @@ class FlowApplication {
|
|
|
204
198
|
if (baseLoggerOrConfig && !baseLoggerOrConfig.log) {
|
|
205
199
|
const config = baseLoggerOrConfig;
|
|
206
200
|
this.baseLogger = config.logger;
|
|
207
|
-
this.amqpConnection = config.amqpConnection || (0, amqp_1.createAmqpConnection)(config.amqpConfig);
|
|
208
201
|
this.natsConnectionConfig = config.natsConfig;
|
|
209
202
|
this._natsConnection = config.natsConnection;
|
|
210
203
|
this.skipApi = config.skipApi || false;
|
|
@@ -214,7 +207,6 @@ class FlowApplication {
|
|
|
214
207
|
}
|
|
215
208
|
else {
|
|
216
209
|
this.baseLogger = baseLoggerOrConfig;
|
|
217
|
-
this.amqpConnection = amqpConnection?.managedConnection;
|
|
218
210
|
this._natsConnection = natsConnection;
|
|
219
211
|
this.skipApi = skipApi || false;
|
|
220
212
|
explicitInit = explicitInit || false;
|
|
@@ -239,12 +231,6 @@ class FlowApplication {
|
|
|
239
231
|
this.init();
|
|
240
232
|
}
|
|
241
233
|
}
|
|
242
|
-
get rpcClient() {
|
|
243
|
-
if (!this._rpcClient && this.amqpConnection) {
|
|
244
|
-
this._rpcClient = new RpcClient_1.RpcClient(this.amqpConnection, this.logger);
|
|
245
|
-
}
|
|
246
|
-
return this._rpcClient;
|
|
247
|
-
}
|
|
248
234
|
get api() {
|
|
249
235
|
return this._api;
|
|
250
236
|
}
|
|
@@ -343,20 +329,6 @@ class FlowApplication {
|
|
|
343
329
|
await logErrorAndExit(`Could not set up consumer for deployment messages exchanges: ${e}`);
|
|
344
330
|
}
|
|
345
331
|
}
|
|
346
|
-
this.amqpChannel = this.amqpConnection?.createChannel({
|
|
347
|
-
json: true,
|
|
348
|
-
setup: async (channel) => {
|
|
349
|
-
try {
|
|
350
|
-
await channel.assertExchange('flow', 'direct', { durable: true }); // TODO wieso weshalb warum: wo wird das gebraucht?
|
|
351
|
-
}
|
|
352
|
-
catch (e) {
|
|
353
|
-
await logErrorAndExit(`Could not assert exchanges: ${e}`);
|
|
354
|
-
}
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
|
-
if (this.amqpChannel) {
|
|
358
|
-
await this.amqpChannel.waitForConnect();
|
|
359
|
-
}
|
|
360
332
|
for (const module of this.modules) {
|
|
361
333
|
const moduleName = Reflect.getMetadata('module:name', module);
|
|
362
334
|
const moduleDeclarations = Reflect.getMetadata('module:declarations', module);
|
|
@@ -447,7 +419,7 @@ class FlowApplication {
|
|
|
447
419
|
}
|
|
448
420
|
/**
|
|
449
421
|
* Calls onDestroy lifecycle method on all flow elements,
|
|
450
|
-
* closes
|
|
422
|
+
* closes NATS connection after allowing logs to be processed and published
|
|
451
423
|
* then exits process
|
|
452
424
|
*/
|
|
453
425
|
async destroy(exitCode = 0) {
|
|
@@ -456,18 +428,12 @@ class FlowApplication {
|
|
|
456
428
|
for (const element of Object.values(this.elements)) {
|
|
457
429
|
element?.onDestroy?.();
|
|
458
430
|
}
|
|
459
|
-
if (this._rpcClient) {
|
|
460
|
-
await this._rpcClient.close();
|
|
461
|
-
}
|
|
462
431
|
}
|
|
463
432
|
catch (err) {
|
|
464
433
|
this.logger.error(err);
|
|
465
434
|
}
|
|
466
435
|
// allow time for logs to be processed
|
|
467
436
|
await (0, utils_1.delay)(250);
|
|
468
|
-
if (this.amqpConnection) {
|
|
469
|
-
await this.amqpConnection.close();
|
|
470
|
-
}
|
|
471
437
|
// Close all output streams
|
|
472
438
|
for (const [id, stream] of this.outputStreamMap.entries()) {
|
|
473
439
|
try {
|
package/src/lib/FlowElement.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { API } from '@hahnpro/hpc-api';
|
|
2
|
-
import { PythonShell } from 'python-shell';
|
|
3
2
|
import { ClassType, DeploymentMessage, FlowContext, FlowElementContext } from './flow.interface';
|
|
4
3
|
import { Context } from './FlowApplication';
|
|
5
4
|
import { FlowEvent } from './FlowEvent';
|
|
@@ -14,7 +13,6 @@ export declare abstract class FlowElement<T = any> {
|
|
|
14
13
|
protected properties: T;
|
|
15
14
|
private propertiesWithPlaceholders;
|
|
16
15
|
private readonly app?;
|
|
17
|
-
private readonly rpcRoutingKey;
|
|
18
16
|
private stopPropagateStream;
|
|
19
17
|
constructor({ app, logger, ...metadata }: Context, properties?: unknown, propertiesClassType?: ClassType<T>, whitelist?: boolean);
|
|
20
18
|
/**
|
|
@@ -49,8 +47,6 @@ export declare abstract class FlowElement<T = any> {
|
|
|
49
47
|
protected logValidationErrors(error: any, parent?: string): void;
|
|
50
48
|
protected validateEventData<E>(classType: ClassType<E>, event: FlowEvent, whitelist?: boolean): E;
|
|
51
49
|
protected interpolate: (value: any, ...templateVariables: any) => any;
|
|
52
|
-
protected callRpcFunction(functionName: string, ...args: any[]): Promise<unknown>;
|
|
53
|
-
protected runPyRpcScript(scriptPath: string, ...args: (string | boolean | number)[]): PythonShell;
|
|
54
50
|
}
|
|
55
51
|
export declare function InputStream(id?: string, options?: {
|
|
56
52
|
concurrent?: number;
|
package/src/lib/FlowElement.js
CHANGED
|
@@ -5,7 +5,6 @@ exports.InputStream = InputStream;
|
|
|
5
5
|
exports.FlowFunction = FlowFunction;
|
|
6
6
|
const class_transformer_1 = require("class-transformer");
|
|
7
7
|
const class_validator_1 = require("class-validator");
|
|
8
|
-
const python_shell_1 = require("python-shell");
|
|
9
8
|
const FlowEvent_1 = require("./FlowEvent");
|
|
10
9
|
const FlowLogger_1 = require("./FlowLogger");
|
|
11
10
|
const utils_1 = require("./utils");
|
|
@@ -35,7 +34,6 @@ class FlowElement {
|
|
|
35
34
|
this.api = this.app?.api;
|
|
36
35
|
this.metadata = { ...metadata, functionFqn: this.functionFqn };
|
|
37
36
|
this.logger = new FlowLogger_1.FlowLogger(this.metadata, logger || undefined, this.app?.publishNatsEventFlowlogs);
|
|
38
|
-
this.rpcRoutingKey = (this.metadata.flowId || '') + (this.metadata.deploymentId || '') + this.metadata.id;
|
|
39
37
|
if (properties) {
|
|
40
38
|
this.setProperties(properties);
|
|
41
39
|
}
|
|
@@ -115,26 +113,6 @@ class FlowElement {
|
|
|
115
113
|
validateEventData(classType, event, whitelist = false) {
|
|
116
114
|
return this.validateProperties(classType, event.getData(), whitelist);
|
|
117
115
|
}
|
|
118
|
-
async callRpcFunction(functionName, ...args) {
|
|
119
|
-
return this.app?.rpcClient.callFunction(this.rpcRoutingKey, functionName, ...args);
|
|
120
|
-
}
|
|
121
|
-
runPyRpcScript(scriptPath, ...args) {
|
|
122
|
-
const options = {
|
|
123
|
-
mode: 'text',
|
|
124
|
-
pythonOptions: ['-u'],
|
|
125
|
-
args: [__dirname, this.rpcRoutingKey, ...args.map((v) => v.toString())],
|
|
126
|
-
};
|
|
127
|
-
const pyshell = new python_shell_1.PythonShell(scriptPath, options);
|
|
128
|
-
pyshell.on('message', (message) => {
|
|
129
|
-
this.logger.debug(message);
|
|
130
|
-
});
|
|
131
|
-
pyshell.end((err) => {
|
|
132
|
-
if (err) {
|
|
133
|
-
this.logger.error(err);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
return pyshell;
|
|
137
|
-
}
|
|
138
116
|
}
|
|
139
117
|
exports.FlowElement = FlowElement;
|
|
140
118
|
function InputStream(id = 'default', options) {
|
package/src/lib/RpcClient.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { AmqpConnectionManager } from 'amqp-connection-manager';
|
|
2
|
-
import { FlowLogger } from './FlowLogger';
|
|
3
|
-
export declare class RpcClient {
|
|
4
|
-
private readonly logger?;
|
|
5
|
-
private readonly channel;
|
|
6
|
-
private openRequests;
|
|
7
|
-
constructor(amqpConnection: AmqpConnectionManager, logger?: FlowLogger);
|
|
8
|
-
private onMessage;
|
|
9
|
-
callFunction: (routingKey: string, functionName: string, ...args: any[]) => Promise<unknown>;
|
|
10
|
-
declareFunction: (routingKey: string, name: string) => (...args: any[]) => Promise<unknown>;
|
|
11
|
-
close(): Promise<void>;
|
|
12
|
-
static formatTrace(stack?: string): string;
|
|
13
|
-
}
|
package/src/lib/RpcClient.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RpcClient = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const crypto_1 = require("crypto");
|
|
6
|
-
const object_sizeof_1 = tslib_1.__importDefault(require("object-sizeof"));
|
|
7
|
-
const MAX_MSG_SIZE = +process.env.MAX_RPC_MSG_SIZE_BYTES;
|
|
8
|
-
const WARN_MSG_SIZE = +process.env.WARN_RPC_MSG_SIZE_BYTES;
|
|
9
|
-
class RpcClient {
|
|
10
|
-
constructor(amqpConnection, logger) {
|
|
11
|
-
this.logger = logger;
|
|
12
|
-
this.openRequests = new Map();
|
|
13
|
-
this.onMessage = (msg) => {
|
|
14
|
-
if (this.openRequests.has(msg.properties.correlationId)) {
|
|
15
|
-
const { resolve, reject, trace } = this.openRequests.get(msg.properties.correlationId);
|
|
16
|
-
const response = JSON.parse(msg.content.toString());
|
|
17
|
-
switch (response.type) {
|
|
18
|
-
case 'reply':
|
|
19
|
-
resolve(response.value);
|
|
20
|
-
break;
|
|
21
|
-
case 'error': {
|
|
22
|
-
const err = new Error(response.message);
|
|
23
|
-
if (response.stack) {
|
|
24
|
-
const stack = RpcClient.formatTrace(response.stack);
|
|
25
|
-
err.stack = 'Remote Stack\n'.concat(stack, '\nLocal Stack\n', trace);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
err.stack = trace;
|
|
29
|
-
}
|
|
30
|
-
reject(err);
|
|
31
|
-
break;
|
|
32
|
-
}
|
|
33
|
-
default:
|
|
34
|
-
reject(response);
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
const message = `received unexpected response correlationID: ${msg.properties.correlationId}`;
|
|
40
|
-
/* eslint-disable-next-line no-console */
|
|
41
|
-
console.warn(message);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
this.callFunction = (routingKey, functionName, ...args) => {
|
|
45
|
-
// in case remote returns error add this to the trace
|
|
46
|
-
const stack = new Error('test').stack;
|
|
47
|
-
return new Promise((resolve, reject) => {
|
|
48
|
-
// save to correlationId-> resolve/reject map
|
|
49
|
-
// on return resolve or reject promise
|
|
50
|
-
if (MAX_MSG_SIZE || WARN_MSG_SIZE) {
|
|
51
|
-
const messageSize = (0, object_sizeof_1.default)(args);
|
|
52
|
-
if (messageSize > MAX_MSG_SIZE) {
|
|
53
|
-
throw new Error(`Max RPC message size exceeded: ${messageSize} bytes / ${MAX_MSG_SIZE} bytes`);
|
|
54
|
-
}
|
|
55
|
-
if (messageSize > WARN_MSG_SIZE) {
|
|
56
|
-
this.logger?.warn(`Large RPC message size detected: ${messageSize} bytes`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const call = { functionName, arguments: args };
|
|
60
|
-
const correlationId = (0, crypto_1.randomUUID)();
|
|
61
|
-
this.openRequests.set(correlationId, { resolve, reject, trace: RpcClient.formatTrace(stack) });
|
|
62
|
-
this.channel
|
|
63
|
-
.publish('rpc_direct_exchange', routingKey, call, { correlationId, replyTo: 'amq.rabbitmq.reply-to' })
|
|
64
|
-
.catch((err) => reject(err));
|
|
65
|
-
});
|
|
66
|
-
};
|
|
67
|
-
this.declareFunction = (routingKey, name) => {
|
|
68
|
-
return (...args) => this.callFunction(routingKey, name, ...args);
|
|
69
|
-
};
|
|
70
|
-
if (!amqpConnection) {
|
|
71
|
-
throw new Error('currently no amqp connection available');
|
|
72
|
-
}
|
|
73
|
-
this.channel = amqpConnection.createChannel({
|
|
74
|
-
json: true,
|
|
75
|
-
setup: async (channel) => {
|
|
76
|
-
await channel.assertExchange('rpc_direct_exchange', 'direct', { durable: false });
|
|
77
|
-
await channel.consume('amq.rabbitmq.reply-to', this.onMessage, { noAck: true });
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
close() {
|
|
82
|
-
return this.channel.close();
|
|
83
|
-
}
|
|
84
|
-
static formatTrace(stack = '') {
|
|
85
|
-
return stack.split('\n').splice(1).join('\n');
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
exports.RpcClient = RpcClient;
|
package/src/lib/amqp.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
|
|
2
|
-
export interface AmqpConnection {
|
|
3
|
-
managedChannel: ChannelWrapper;
|
|
4
|
-
managedConnection: AmqpConnectionManager;
|
|
5
|
-
}
|
|
6
|
-
export interface AmqpConnectionConfig {
|
|
7
|
-
protocol?: string;
|
|
8
|
-
hostname?: string;
|
|
9
|
-
vhost?: string;
|
|
10
|
-
user?: string;
|
|
11
|
-
password?: string;
|
|
12
|
-
port?: number;
|
|
13
|
-
}
|
|
14
|
-
export declare function createAmqpConnection(config: AmqpConnectionConfig): AmqpConnectionManager;
|
package/src/lib/amqp.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createAmqpConnection = createAmqpConnection;
|
|
4
|
-
const amqp_connection_manager_1 = require("amqp-connection-manager");
|
|
5
|
-
function createAmqpConnection(config) {
|
|
6
|
-
if (!config) {
|
|
7
|
-
return null;
|
|
8
|
-
}
|
|
9
|
-
const { protocol = process.env.RABBIT_PROTOCOL || 'amqp', hostname = process.env.RABBIT_HOST || 'localhost', port = +process.env.RABBIT_PORT || 5672, user = process.env.RABBIT_USER || 'guest', password = process.env.RABBIT_PASSWORD || 'guest', vhost = process.env.RABBIT_VHOST || '', } = config;
|
|
10
|
-
const uri = `${protocol}://${user}:${password}@${hostname}:${port}${vhost ? '/' + vhost : ''}`;
|
|
11
|
-
return (0, amqp_connection_manager_1.connect)(uri);
|
|
12
|
-
}
|
package/src/lib/rpc_server.py
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import json
|
|
3
|
-
from asyncio import Future
|
|
4
|
-
from functools import partial, wraps
|
|
5
|
-
from aio_pika import IncomingMessage, Exchange, Message, connect_robust, ExchangeType
|
|
6
|
-
from aio_pika.abc import AbstractRobustExchange
|
|
7
|
-
import os
|
|
8
|
-
|
|
9
|
-
user = os.getenv("RABBIT_USER", "guest")
|
|
10
|
-
password = os.getenv("RABBIT_PASSWORD", "guest")
|
|
11
|
-
host = os.getenv("RABBIT_HOST", "localhost")
|
|
12
|
-
port = os.getenv("RABBIT_PORT", "5672")
|
|
13
|
-
vhost = os.getenv("RABBIT_VHOST", "")
|
|
14
|
-
routingKey = os.getenv("RPC_ROUTING_KEY", "rpc")
|
|
15
|
-
max_msg_size = int(os.getenv("MAX_RPC_MSG_SIZE_BYTES", "0"))
|
|
16
|
-
|
|
17
|
-
remote_procedures = {}
|
|
18
|
-
flow_logs_exchange: AbstractRobustExchange
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def RemoteProcedure(func):
|
|
22
|
-
global remote_procedures
|
|
23
|
-
|
|
24
|
-
@wraps(func)
|
|
25
|
-
def function_wrapper(*args, **kwargs):
|
|
26
|
-
return func(*args, **kwargs)
|
|
27
|
-
|
|
28
|
-
remote_procedures[func.__name__] = func
|
|
29
|
-
|
|
30
|
-
return function_wrapper
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
loop = asyncio.new_event_loop()
|
|
34
|
-
asyncio.set_event_loop(loop)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
async def on_message(exchange: Exchange, message: IncomingMessage):
|
|
38
|
-
def callback(future: Future):
|
|
39
|
-
try:
|
|
40
|
-
res = future.result()
|
|
41
|
-
if type(res) is tuple and type(res[1]) is type and issubclass(res[1], json.JSONEncoder):
|
|
42
|
-
res = json.dumps(res[0], cls=res[1])
|
|
43
|
-
reply1 = {"type": "reply", "value": res}
|
|
44
|
-
except Exception as err:
|
|
45
|
-
# print(traceback.format_list(traceback.extract_stack(err)))
|
|
46
|
-
reply1 = {"type": "error", "message": str(err), "stack": "failed"}
|
|
47
|
-
|
|
48
|
-
asyncio.ensure_future(send_reply(exchange, reply1, message), loop=loop)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
request = json.loads(message.body.decode())
|
|
52
|
-
|
|
53
|
-
# call function
|
|
54
|
-
if remote_procedures.keys().__contains__(request["functionName"]):
|
|
55
|
-
func = remote_procedures.get(request["functionName"])
|
|
56
|
-
future = loop.run_in_executor(None, func, *request["arguments"])
|
|
57
|
-
future.add_done_callback(callback)
|
|
58
|
-
|
|
59
|
-
else:
|
|
60
|
-
reply = {
|
|
61
|
-
"type": "error",
|
|
62
|
-
"message": request["functionName"] + " is not a function",
|
|
63
|
-
}
|
|
64
|
-
await send_reply(exchange, reply, original_message=message)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
async def send_reply(exchange: Exchange, reply, original_message: Message):
|
|
68
|
-
body = json.dumps(reply).encode("utf-8")
|
|
69
|
-
|
|
70
|
-
if max_msg_size > 0 and len(body) > max_msg_size:
|
|
71
|
-
body = json.dumps(
|
|
72
|
-
{
|
|
73
|
-
"type": "error",
|
|
74
|
-
"message": "Max RPC message size exceeded: " + str(len(body)) + " bytes / " + max_msg_size + " bytes",
|
|
75
|
-
}
|
|
76
|
-
).encode("utf-8")
|
|
77
|
-
|
|
78
|
-
await exchange.publish(
|
|
79
|
-
Message(body=body, correlation_id=original_message.correlation_id),
|
|
80
|
-
routing_key=original_message.reply_to,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
async def main(loop, routing_key):
|
|
85
|
-
global flow_logs_exchange
|
|
86
|
-
|
|
87
|
-
url = "amqp://%s:%s@%s:%s/%s" % (user, password, host, port, vhost)
|
|
88
|
-
connection = await connect_robust(
|
|
89
|
-
url, loop=loop
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
channel = await connection.channel()
|
|
93
|
-
|
|
94
|
-
dest_exchange = await channel.declare_exchange(name="rpc_direct_exchange", type=ExchangeType.DIRECT)
|
|
95
|
-
flow_logs_exchange = await channel.declare_exchange(name='flowlogs', type=ExchangeType.FANOUT, durable=True)
|
|
96
|
-
|
|
97
|
-
queue = await channel.declare_queue("", exclusive=True)
|
|
98
|
-
|
|
99
|
-
await queue.bind(dest_exchange, routing_key)
|
|
100
|
-
|
|
101
|
-
await queue.consume(partial(
|
|
102
|
-
on_message, channel.default_exchange)
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def start_consumer(routing_key=routingKey):
|
|
107
|
-
loop.create_task(main(loop, routing_key))
|
|
108
|
-
loop.run_forever()
|
|
109
|
-
|
|
110
|
-
def log(message):
|
|
111
|
-
global flow_logs_exchange
|
|
112
|
-
|
|
113
|
-
if flow_logs_exchange is not None:
|
|
114
|
-
flow_logs_exchange.publish(Message(body=json.dumps(message).encode("utf-8")), "")
|
|
115
|
-
else:
|
|
116
|
-
print("Connection not established. Call start_consumer first.")
|