@hahnpro/flow-sdk 9.6.4 → 2025.2.0-beta.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +904 -0
  2. package/jest.config.ts +10 -0
  3. package/package.json +7 -20
  4. package/project.json +41 -0
  5. package/src/index.ts +15 -0
  6. package/src/lib/ContextManager.ts +111 -0
  7. package/src/lib/FlowApplication.ts +659 -0
  8. package/src/lib/FlowElement.ts +220 -0
  9. package/src/lib/FlowEvent.ts +73 -0
  10. package/src/lib/FlowLogger.ts +131 -0
  11. package/src/lib/FlowModule.ts +18 -0
  12. package/src/lib/RpcClient.ts +99 -0
  13. package/src/lib/TestModule.ts +14 -0
  14. package/src/lib/__pycache__/rpc_server.cpython-310.pyc +0 -0
  15. package/src/lib/amqp.ts +32 -0
  16. package/src/lib/extra-validators.ts +62 -0
  17. package/src/lib/flow.interface.ts +56 -0
  18. package/{dist/index.d.ts → src/lib/index.ts} +3 -0
  19. package/src/lib/nats.ts +140 -0
  20. package/src/lib/unit-decorators.ts +156 -0
  21. package/src/lib/unit-utils.ts +163 -0
  22. package/src/lib/units.ts +587 -0
  23. package/src/lib/utils.ts +176 -0
  24. package/test/context-manager-purpose.spec.ts +248 -0
  25. package/test/context-manager.spec.ts +55 -0
  26. package/test/context.spec.ts +180 -0
  27. package/test/event.spec.ts +155 -0
  28. package/test/extra-validators.spec.ts +84 -0
  29. package/test/flow-logger.spec.ts +104 -0
  30. package/test/flow.spec.ts +508 -0
  31. package/test/input-stream.decorator.spec.ts +379 -0
  32. package/test/long-rpc.test.py +14 -0
  33. package/test/long-running-rpc.spec.ts +60 -0
  34. package/test/message.spec.ts +57 -0
  35. package/test/mocks/logger.mock.ts +7 -0
  36. package/test/mocks/nats-connection.mock.ts +135 -0
  37. package/test/mocks/nats-prepare.reals-nats.ts +15 -0
  38. package/test/rpc.spec.ts +198 -0
  39. package/test/rpc.test.py +45 -0
  40. package/test/rx.spec.ts +92 -0
  41. package/test/unit-decorator.spec.ts +57 -0
  42. package/test/utils.spec.ts +210 -0
  43. package/test/validation.spec.ts +174 -0
  44. package/tsconfig.json +13 -0
  45. package/tsconfig.lib.json +22 -0
  46. package/tsconfig.spec.json +8 -0
  47. package/LICENSE +0 -21
  48. package/dist/ContextManager.d.ts +0 -40
  49. package/dist/ContextManager.js +0 -77
  50. package/dist/FlowApplication.d.ts +0 -85
  51. package/dist/FlowApplication.js +0 -500
  52. package/dist/FlowElement.d.ts +0 -67
  53. package/dist/FlowElement.js +0 -163
  54. package/dist/FlowEvent.d.ts +0 -25
  55. package/dist/FlowEvent.js +0 -71
  56. package/dist/FlowLogger.d.ts +0 -44
  57. package/dist/FlowLogger.js +0 -94
  58. package/dist/FlowModule.d.ts +0 -7
  59. package/dist/FlowModule.js +0 -13
  60. package/dist/RpcClient.d.ts +0 -13
  61. package/dist/RpcClient.js +0 -84
  62. package/dist/TestModule.d.ts +0 -2
  63. package/dist/TestModule.js +0 -27
  64. package/dist/amqp.d.ts +0 -14
  65. package/dist/amqp.js +0 -12
  66. package/dist/extra-validators.d.ts +0 -1
  67. package/dist/extra-validators.js +0 -51
  68. package/dist/flow.interface.d.ts +0 -48
  69. package/dist/flow.interface.js +0 -9
  70. package/dist/index.js +0 -18
  71. package/dist/nats.d.ts +0 -12
  72. package/dist/nats.js +0 -109
  73. package/dist/unit-decorators.d.ts +0 -39
  74. package/dist/unit-decorators.js +0 -156
  75. package/dist/unit-utils.d.ts +0 -8
  76. package/dist/unit-utils.js +0 -143
  77. package/dist/units.d.ts +0 -31
  78. package/dist/units.js +0 -570
  79. package/dist/utils.d.ts +0 -51
  80. package/dist/utils.js +0 -137
  81. /package/{dist → src/lib}/rpc_server.py +0 -0
@@ -0,0 +1,155 @@
1
+ import { FlowEvent } from '../src';
2
+
3
+ describe('Events', () => {
4
+ test('FLOW.EVE.1 test logging of events', () => {
5
+ let event;
6
+
7
+ event = createEvent('test');
8
+ expect(event.data).toEqual('test');
9
+ expect(event.datacontenttype).toEqual('text/plain');
10
+
11
+ event = createEvent('{ hello }');
12
+ expect(event.data).toEqual('{ hello }');
13
+ expect(event.datacontenttype).toEqual('text/plain');
14
+
15
+ event = createEvent('{ "hello": "world" }');
16
+ expect(event.data).toEqual({ hello: 'world' });
17
+ expect(event.datacontenttype).toEqual('application/json');
18
+
19
+ event = createEvent('[0, 1, 2, 3]');
20
+ expect(event.data).toEqual([0, 1, 2, 3]);
21
+ expect(event.datacontenttype).toEqual('application/json');
22
+
23
+ event = createEvent('["a", "b", "c"]');
24
+ expect(event.data).toEqual(['a', 'b', 'c']);
25
+ expect(event.datacontenttype).toEqual('application/json');
26
+
27
+ event = createEvent('[{ "hello": "world" }]');
28
+ expect(event.data).toEqual([{ hello: 'world' }]);
29
+ expect(event.datacontenttype).toEqual('application/json');
30
+
31
+ event = createEvent('true');
32
+ expect(event.data).toEqual(true);
33
+ expect(event.datacontenttype).toEqual('application/json');
34
+
35
+ event = createEvent('null');
36
+ expect(event.data).toEqual(null);
37
+ expect(event.datacontenttype).toEqual('application/json');
38
+
39
+ event = createEvent(42);
40
+ expect(event.data).toEqual('42');
41
+ expect(event.datacontenttype).toEqual('text/plain');
42
+
43
+ event = createEvent(true);
44
+ expect(event.data).toEqual('true');
45
+ expect(event.datacontenttype).toEqual('text/plain');
46
+
47
+ event = createEvent(false);
48
+ expect(event.data).toEqual('false');
49
+ expect(event.datacontenttype).toEqual('text/plain');
50
+
51
+ event = createEvent(undefined);
52
+ expect(event.data).toEqual({});
53
+ expect(event.datacontenttype).toEqual('application/json');
54
+
55
+ event = createEvent(null);
56
+ expect(event.data).toEqual({});
57
+ expect(event.datacontenttype).toEqual('application/json');
58
+
59
+ event = createEvent(NaN);
60
+ expect(event.data).toEqual('NaN');
61
+ expect(event.datacontenttype).toEqual('text/plain');
62
+
63
+ event = createEvent({ test: 123 });
64
+ expect(event.data).toEqual({ test: 123 });
65
+ expect(event.datacontenttype).toEqual('application/json');
66
+
67
+ event = createEvent({ test: 'abc', nested: { foo: 'bar' } });
68
+ expect(event.data.test).toEqual('abc');
69
+ expect(event.data.nested.foo).toEqual('bar');
70
+ expect(event.datacontenttype).toEqual('application/json');
71
+
72
+ const testInst = new TestClass();
73
+ testInst.stringProp = 'abc';
74
+ testInst.numProp = 42;
75
+ testInst.boolProp = false;
76
+ event = createEvent(testInst);
77
+ expect(event.data).toEqual({ stringProp: 'abc', numProp: 42, boolProp: false });
78
+ expect(event.datacontenttype).toEqual('application/json');
79
+
80
+ event = createEvent(new Error('err'));
81
+ expect(event.data.message).toEqual('err');
82
+ expect(event.data.stack).toBeDefined();
83
+ expect(typeof event.data.stack).toBe('string');
84
+ expect(event.datacontenttype).toEqual('application/json');
85
+ });
86
+
87
+ test('FLOW.EVE.2 test imutability of event data', () => {
88
+ let data;
89
+ let event;
90
+ let eventData;
91
+
92
+ data = { test: 'abc' };
93
+ event = new FlowEvent({ id: 'test' }, data);
94
+ expect(event.getData()).toEqual(data);
95
+ data.test = 'xyz';
96
+ expect(event.getData()).not.toEqual(data);
97
+ expect(event.getData()).toEqual({ ...data, test: 'abc' });
98
+
99
+ data = { test: 'abc', x: { y: 'z' } };
100
+ event = new FlowEvent({ id: 'test' }, data);
101
+ expect(event.getData()).toEqual(data);
102
+ data.test = 'xyz';
103
+ data.x.y = 'A';
104
+ expect(event.getData()).not.toEqual(data);
105
+ expect(event.getData()).toEqual({ test: 'abc', x: { y: 'z' } });
106
+
107
+ data = 'foo';
108
+ event = new FlowEvent({ id: 'test' }, data);
109
+ expect(event.getData()).toEqual(data);
110
+ data = 'bar';
111
+ expect(event.getData()).not.toEqual(data);
112
+ expect(event.getData()).toEqual('foo');
113
+
114
+ data = { test: 'abc' };
115
+ event = new FlowEvent({ id: 'test' }, data);
116
+ eventData = event.getData();
117
+ expect(eventData).toEqual(data);
118
+ data.test = 'xyz';
119
+ expect(eventData).not.toEqual(data);
120
+ expect(eventData).toEqual({ test: 'abc' });
121
+
122
+ data = { test: 'abc' };
123
+ event = new FlowEvent({ id: 'test' }, data);
124
+ eventData = event.getData();
125
+ expect(eventData).toEqual(data);
126
+ eventData.test = 'xyz';
127
+ expect(eventData).not.toEqual(data);
128
+ expect(eventData).toEqual({ test: 'xyz' });
129
+ expect(event.getData()).toEqual({ test: 'abc' });
130
+ expect(data).toEqual({ test: 'abc' });
131
+
132
+ data = {};
133
+ event = new FlowEvent({ id: 'test' }, data);
134
+ expect(event.getData()).toEqual({});
135
+
136
+ data = null;
137
+ event = new FlowEvent({ id: 'test' }, data);
138
+ expect(event.getData()).toEqual({});
139
+
140
+ data = undefined;
141
+ event = new FlowEvent({ id: 'test' }, data);
142
+ expect(event.getData()).toEqual({});
143
+ });
144
+ });
145
+
146
+ function createEvent(message: any) {
147
+ const event = new FlowEvent({ id: 'test' }, message);
148
+ return JSON.parse(JSON.stringify(event.format()));
149
+ }
150
+
151
+ class TestClass {
152
+ stringProp: string;
153
+ numProp: number;
154
+ boolProp: boolean;
155
+ }
@@ -0,0 +1,84 @@
1
+ import { validate } from 'class-validator';
2
+
3
+ import { IncompatableWith } from '../src';
4
+
5
+ class Test {
6
+ @IncompatableWith(['test2'])
7
+ test1: string;
8
+
9
+ @IncompatableWith(['test1'])
10
+ test2: string;
11
+ }
12
+
13
+ class Test2 {
14
+ @IncompatableWith(['test2', 'test3'])
15
+ test1: string;
16
+
17
+ @IncompatableWith(['test1'])
18
+ test2: string;
19
+
20
+ @IncompatableWith(['test1'])
21
+ test3: string;
22
+ }
23
+
24
+ describe('extra validators', () => {
25
+ test('FLOW.EXV.1 should work when no incompatible field is present', (done) => {
26
+ const test = new Test();
27
+ test.test1 = 'test';
28
+ validate(test).then((errors) => {
29
+ expect(errors.length).toEqual(0);
30
+ done();
31
+ });
32
+ });
33
+
34
+ test('FLOW.EXV.2 should fail if both are not present', (done) => {
35
+ const test = new Test();
36
+ validate(test).then((errors) => {
37
+ expect(errors.length).toEqual(2);
38
+ done();
39
+ });
40
+ });
41
+
42
+ test('FLOW.EXV.3 validation should fail when incompatible fields are present', (done) => {
43
+ const test = new Test();
44
+ test.test1 = 'test';
45
+ test.test2 = 'test';
46
+ validate(test).then((errors) => {
47
+ expect(errors.length).toEqual(2);
48
+ done();
49
+ });
50
+ });
51
+
52
+ test('FLOW.EXV.4 validation should work with multiple incompatible fields', (done) => {
53
+ const test = new Test2();
54
+ test.test1 = 'test';
55
+ validate(test).then((errors) => {
56
+ expect(errors.length).toEqual(0);
57
+ done();
58
+ });
59
+ });
60
+
61
+ test('FLOW.EXV.5 validation should fail when multiple incompatible fields are present', (done) => {
62
+ const test = new Test2();
63
+ test.test1 = 'test';
64
+ test.test2 = 'test';
65
+ validate(test).then((errors) => {
66
+ expect(errors.length).toEqual(2);
67
+ });
68
+
69
+ const test2 = new Test2();
70
+ test2.test1 = 'test';
71
+ test2.test3 = 'test';
72
+ validate(test2).then((errors) => {
73
+ expect(errors.length).toEqual(2);
74
+ });
75
+
76
+ const test3 = new Test2();
77
+ test3.test2 = 'test';
78
+ test3.test3 = 'test';
79
+ validate(test3).then((errors) => {
80
+ expect(errors.length).toEqual(0);
81
+ done();
82
+ });
83
+ });
84
+ });
@@ -0,0 +1,104 @@
1
+ import { FlowLogger } from '../src';
2
+ import { loggerMock } from './mocks/logger.mock';
3
+
4
+ describe('Flow Logger', () => {
5
+ let flowLogger: FlowLogger;
6
+ let parseLogMessageSpy: jest.SpyInstance;
7
+
8
+ beforeEach(() => {
9
+ flowLogger = new FlowLogger({ id: 'testLogger' }, loggerMock);
10
+ parseLogMessageSpy = jest.spyOn(flowLogger as any, 'parseMessageToFlowLog');
11
+ });
12
+
13
+ it('FLOW.FL.0: init', () => {
14
+ expect(flowLogger).toBeDefined();
15
+ });
16
+
17
+ describe('FLOW.FL.1: parse log message correctly', () => {
18
+ it('FLOW.FL.1.1: log with plain string', () => {
19
+ const message = 'test debug message';
20
+ const parsedMessage = (flowLogger as any).parseMessageToFlowLog(message);
21
+ expect(Object.keys(parsedMessage)).toEqual(['message']);
22
+ expect(typeof parsedMessage.message).toBe('string');
23
+ expect(parsedMessage.message).toBe(message);
24
+ });
25
+
26
+ it('FLOW.FL.1.2: log with int', () => {
27
+ const message = 1;
28
+ const parsedMessage = (flowLogger as any).parseMessageToFlowLog(message);
29
+ expect(Object.keys(parsedMessage)).toEqual(['message']);
30
+ expect(typeof parsedMessage.message).toBe('string');
31
+ expect(parsedMessage.message).toBe('1');
32
+ });
33
+
34
+ it('FLOW.FL.1.3: log with object', () => {
35
+ const message = { rolf: 1 };
36
+ const parsedMessage = (flowLogger as any).parseMessageToFlowLog(message);
37
+ expect(Object.keys(parsedMessage)).toEqual(['message']);
38
+ expect(typeof parsedMessage.message).toBe('string');
39
+ expect(parsedMessage.message).toBe(JSON.stringify(message));
40
+ });
41
+
42
+ it('FLOW.FL.1.4: log with correct object', () => {
43
+ const message = { message: 'test debug message 1' };
44
+ const parsedMessage = (flowLogger as any).parseMessageToFlowLog(message);
45
+ expect(Object.keys(parsedMessage)).toEqual(['message']);
46
+ expect(typeof parsedMessage.message).toBe('string');
47
+ expect(parsedMessage.message).toBe(message.message);
48
+ });
49
+ });
50
+
51
+ describe('FLOW.FL.2: FlowLogger should parse every log message to FlowLog', () => {
52
+ it('FLOW.FL.2.1: log with plain string', () => {
53
+ const logSpy = jest.spyOn(flowLogger, 'debug');
54
+ const message = 'test debug message';
55
+ flowLogger.debug(message);
56
+ expect(logSpy).toHaveBeenCalledWith(message);
57
+ expect(parseLogMessageSpy).toHaveBeenCalledWith(message, 'debug', undefined);
58
+ expect(parseLogMessageSpy).toHaveReturnedWith(
59
+ expect.objectContaining({
60
+ message: 'test debug message',
61
+ }),
62
+ );
63
+ });
64
+
65
+ it('FLOW.FL.2.2: log with int', () => {
66
+ const logSpy = jest.spyOn(flowLogger, 'debug');
67
+ const message = 1;
68
+ flowLogger.debug(message);
69
+ expect(logSpy).toHaveBeenCalledWith(message);
70
+ expect(parseLogMessageSpy).toHaveBeenCalledWith(message, 'debug', undefined);
71
+ expect(parseLogMessageSpy).toHaveReturnedWith(
72
+ expect.objectContaining({
73
+ message: '1',
74
+ }),
75
+ );
76
+ });
77
+
78
+ it('FLOW.FL.2.3: log with object', () => {
79
+ const logSpy = jest.spyOn(flowLogger, 'debug');
80
+ const message = { rolf: 1 };
81
+ flowLogger.debug(message);
82
+ expect(logSpy).toHaveBeenCalledWith(message);
83
+ expect(parseLogMessageSpy).toHaveBeenCalledWith(message, 'debug', undefined);
84
+ expect(parseLogMessageSpy).toHaveReturnedWith(
85
+ expect.objectContaining({
86
+ message: JSON.stringify({ rolf: 1 }),
87
+ }),
88
+ );
89
+ });
90
+
91
+ it('FLOW.FL.2.4: log with correct object', () => {
92
+ const logSpy = jest.spyOn(flowLogger, 'debug');
93
+ const message = { message: '1' };
94
+ flowLogger.debug(message);
95
+ expect(logSpy).toHaveBeenCalledWith(message);
96
+ expect(parseLogMessageSpy).toHaveBeenCalledWith(message, 'debug', undefined);
97
+ expect(parseLogMessageSpy).toHaveReturnedWith(
98
+ expect.objectContaining({
99
+ message: message.message,
100
+ }),
101
+ );
102
+ });
103
+ });
104
+ });