@yarkivaev/source-to-sink 1.2.7 → 1.3.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/index.js +2 -0
- package/package.json +1 -1
- package/src/stompSend.js +72 -0
- package/test/test_stompSend.js +35 -0
package/index.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* - postgresSink: PostgreSQL sink (accepts URL)
|
|
12
12
|
* - mqttSource: MQTT subscription source (accepts URL)
|
|
13
13
|
* - stompSource: STOMP subscription source (accepts URL)
|
|
14
|
+
* - stompSend: one-shot STOMP publish (accepts URL)
|
|
14
15
|
* - lokiSource: Loki polling source (accepts URL)
|
|
15
16
|
* - modbusSource: Modbus TCP polling source (accepts host/port)
|
|
16
17
|
* - mqttSink: MQTT publishing sink (accepts URL)
|
|
@@ -34,6 +35,7 @@ export { default as clickhouseSink } from './src/clickhouseSink.js';
|
|
|
34
35
|
export { default as postgresSink } from './src/postgresSink.js';
|
|
35
36
|
export { default as mqttSource } from './src/mqttSource.js';
|
|
36
37
|
export { default as stompSource } from './src/stompSource.js';
|
|
38
|
+
export { default as stompSend } from './src/stompSend.js';
|
|
37
39
|
export { default as lokiSource } from './src/lokiSource.js';
|
|
38
40
|
export { default as modbusSource } from './src/modbusSource.js';
|
|
39
41
|
export { default as mqttSink } from './src/mqttSink.js';
|
package/package.json
CHANGED
package/src/stompSend.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import stompit from 'stompit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds STOMP connect options from a URL.
|
|
5
|
+
*
|
|
6
|
+
* @param {URL} parsed - Parsed URL object
|
|
7
|
+
* @param {object} options - Connection options
|
|
8
|
+
* @returns {object} stompit connect parameters
|
|
9
|
+
*/
|
|
10
|
+
function connectParams(parsed, options) {
|
|
11
|
+
return {
|
|
12
|
+
host: parsed.hostname,
|
|
13
|
+
port: parseInt(parsed.port, 10) || 61613,
|
|
14
|
+
connectHeaders: {
|
|
15
|
+
host: options.host || parsed.hostname,
|
|
16
|
+
login: options.login || '',
|
|
17
|
+
passcode: options.passcode || ''
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* One-shot STOMP publish of a JSON payload to a destination.
|
|
24
|
+
*
|
|
25
|
+
* Connects, sends one MESSAGE frame, disconnects. For operator decisions
|
|
26
|
+
* and other low-volume outbound events.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* await stompSend('stomp://rabbitmq:61613', '/exchange/scada.user_decisions',
|
|
30
|
+
* { machine: 'icht2', start: 1700000000, user: 'hmi', tags: ['heating'] },
|
|
31
|
+
* { login: 'guest', passcode: 'guest', host: '/' });
|
|
32
|
+
*
|
|
33
|
+
* @param {string} url - STOMP broker URL
|
|
34
|
+
* @param {string} destination - STOMP destination
|
|
35
|
+
* @param {object|string} payload - JSON-serializable body or raw string
|
|
36
|
+
* @param {object} [options] - STOMP connection options
|
|
37
|
+
* @param {string} [options.login] - Login
|
|
38
|
+
* @param {string} [options.passcode] - Passcode
|
|
39
|
+
* @param {string} [options.host] - Virtual host header
|
|
40
|
+
* @returns {Promise<void>} Resolves when the frame is sent and client disconnects
|
|
41
|
+
*/
|
|
42
|
+
export default function stompSend(url, destination, payload, options = {}) {
|
|
43
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
44
|
+
throw new Error('URL must be a non-empty string');
|
|
45
|
+
}
|
|
46
|
+
if (typeof destination !== 'string' || destination.length === 0) {
|
|
47
|
+
throw new Error('Destination must be a non-empty string');
|
|
48
|
+
}
|
|
49
|
+
const parsed = new URL(url);
|
|
50
|
+
const params = connectParams(parsed, options);
|
|
51
|
+
const body = typeof payload === 'string' ? payload : JSON.stringify(payload);
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
stompit.connect(params, (error, client) => {
|
|
54
|
+
if (error) {
|
|
55
|
+
reject(error);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const frame = client.send({
|
|
59
|
+
destination,
|
|
60
|
+
'content-type': 'application/json'
|
|
61
|
+
});
|
|
62
|
+
frame.end(body);
|
|
63
|
+
client.disconnect((disconnectError) => {
|
|
64
|
+
if (disconnectError) {
|
|
65
|
+
reject(disconnectError);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
resolve();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { describe, it } from 'mocha';
|
|
3
|
+
import stompSend from '../src/stompSend.js';
|
|
4
|
+
|
|
5
|
+
describe('stompSend', () => {
|
|
6
|
+
it('throws on missing url', () => {
|
|
7
|
+
assert.throws(
|
|
8
|
+
() => {
|
|
9
|
+
stompSend(null, '/exchange/test', {});
|
|
10
|
+
},
|
|
11
|
+
/URL must be a non-empty string/u,
|
|
12
|
+
'Should reject missing url'
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('throws on empty destination', () => {
|
|
17
|
+
assert.throws(
|
|
18
|
+
() => {
|
|
19
|
+
stompSend('stomp://localhost:61613', '', {});
|
|
20
|
+
},
|
|
21
|
+
/Destination must be a non-empty string/u,
|
|
22
|
+
'Should reject empty destination'
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns a promise', () => {
|
|
27
|
+
const pending = stompSend(
|
|
28
|
+
`stomp://\u00e9${Math.random()}:61613`,
|
|
29
|
+
'/exchange/scada.user_decisions',
|
|
30
|
+
{ machine: 'x', start: 1, user: 'u', tags: [] }
|
|
31
|
+
);
|
|
32
|
+
assert.strictEqual(typeof pending.then, 'function', 'Should return a promise');
|
|
33
|
+
pending.catch(() => {});
|
|
34
|
+
});
|
|
35
|
+
});
|