node-red-contrib-influxdb3 1.0.2 → 1.0.3

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.
@@ -0,0 +1,240 @@
1
+ const path = require('path');
2
+
3
+ let mockLastClientOptions;
4
+ let mockLastClientInstance;
5
+ let mockLastPoint;
6
+
7
+ jest.mock('@influxdata/influxdb3-client', () => {
8
+ class MockInfluxDBClient {
9
+ constructor(options) {
10
+ mockLastClientOptions = options;
11
+ mockLastClientInstance = this;
12
+ this.write = jest.fn().mockResolvedValue(undefined);
13
+ this.close = jest.fn();
14
+ }
15
+ }
16
+
17
+ class MockPoint {
18
+ constructor(measurement) {
19
+ this.measurement = measurement;
20
+ this.tags = {};
21
+ this.integerFields = {};
22
+ this.floatFields = {};
23
+ this.stringFields = {};
24
+ this.booleanFields = {};
25
+ this.timestamp = null;
26
+ mockLastPoint = this;
27
+ }
28
+
29
+ setTag(key, value) {
30
+ this.tags[key] = value;
31
+ }
32
+
33
+ setIntegerField(key, value) {
34
+ this.integerFields[key] = value;
35
+ }
36
+
37
+ setFloatField(key, value) {
38
+ this.floatFields[key] = value;
39
+ }
40
+
41
+ setStringField(key, value) {
42
+ this.stringFields[key] = value;
43
+ }
44
+
45
+ setBooleanField(key, value) {
46
+ this.booleanFields[key] = value;
47
+ }
48
+
49
+ setTimestamp(ts) {
50
+ this.timestamp = ts;
51
+ }
52
+
53
+ toLineProtocol() {
54
+ return `lp:${this.measurement}`;
55
+ }
56
+ }
57
+
58
+ return {
59
+ InfluxDBClient: MockInfluxDBClient,
60
+ Point: MockPoint,
61
+ __getLastClientOptions: () => mockLastClientOptions,
62
+ __getLastClientInstance: () => mockLastClientInstance,
63
+ __getLastPoint: () => mockLastPoint
64
+ };
65
+ });
66
+
67
+ function buildRED() {
68
+ const types = {};
69
+ return {
70
+ log: {
71
+ info: jest.fn(),
72
+ warn: jest.fn(),
73
+ error: jest.fn()
74
+ },
75
+ nodes: {
76
+ createNode(node, config) {
77
+ node.credentials = config.credentials || {};
78
+ node.status = jest.fn();
79
+ node.error = jest.fn();
80
+ node.warn = jest.fn();
81
+ node.send = jest.fn();
82
+ node.on = jest.fn((event, handler) => {
83
+ node._handlers = node._handlers || {};
84
+ node._handlers[event] = handler;
85
+ });
86
+ },
87
+ registerType(name, ctor) {
88
+ types[name] = ctor;
89
+ },
90
+ getNode(id) {
91
+ return id;
92
+ }
93
+ },
94
+ _types: types
95
+ };
96
+ }
97
+
98
+ function setup() {
99
+ jest.resetModules();
100
+ const RED = buildRED();
101
+ require('../influxdb3.js')(RED);
102
+ const influxModule = require('@influxdata/influxdb3-client');
103
+ return { RED, influxModule };
104
+ }
105
+
106
+ beforeEach(() => {
107
+ mockLastClientOptions = undefined;
108
+ mockLastClientInstance = undefined;
109
+ mockLastPoint = undefined;
110
+ });
111
+
112
+ describe('InfluxDB v3 config node', () => {
113
+ test('normalizes host and uses provided config', () => {
114
+ const { RED, influxModule } = setup();
115
+ const ConfigCtor = RED._types['influxdb3-config'];
116
+
117
+ const configNode = new ConfigCtor({
118
+ host: 'https://example.com',
119
+ database: 'metrics',
120
+ name: 'Test',
121
+ credentials: { token: 'token' }
122
+ });
123
+
124
+ configNode.getClient();
125
+
126
+ const options = influxModule.__getLastClientOptions();
127
+ expect(options.host).toBe('https://example.com/');
128
+ expect(options.database).toBe('metrics');
129
+ expect(options.token).toBe('token');
130
+ });
131
+
132
+ test('sets extra CA certificate path when configured', () => {
133
+ const original = process.env.NODE_EXTRA_CA_CERTS;
134
+ const { RED } = setup();
135
+ const ConfigCtor = RED._types['influxdb3-config'];
136
+
137
+ const configNode = new ConfigCtor({
138
+ host: 'https://example.com',
139
+ database: 'metrics',
140
+ name: 'Test',
141
+ caCertPath: path.join('C:', 'certs', 'root.pem'),
142
+ credentials: { token: 'token' }
143
+ });
144
+
145
+ configNode.getClient();
146
+
147
+ expect(process.env.NODE_EXTRA_CA_CERTS).toBe(path.join('C:', 'certs', 'root.pem'));
148
+ process.env.NODE_EXTRA_CA_CERTS = original;
149
+ });
150
+ });
151
+
152
+ describe('InfluxDB v3 write node', () => {
153
+ test('writes line protocol from object payload', async () => {
154
+ const { RED, influxModule } = setup();
155
+ const ConfigCtor = RED._types['influxdb3-config'];
156
+ const WriteCtor = RED._types['influxdb3-write'];
157
+
158
+ const configNode = new ConfigCtor({
159
+ host: 'https://example.com',
160
+ database: 'metrics',
161
+ name: 'Test',
162
+ credentials: { token: 'token' }
163
+ });
164
+
165
+ const writeNode = new WriteCtor({
166
+ influxdb: configNode,
167
+ measurement: 'cpu',
168
+ database: ''
169
+ });
170
+
171
+ const msg = {
172
+ measurement: 'cpu',
173
+ payload: {
174
+ fields: {
175
+ temperature: 21.5,
176
+ count: 5
177
+ },
178
+ tags: { location: 'lab' },
179
+ integers: ['count'],
180
+ timestamp: 1700000000000
181
+ }
182
+ };
183
+
184
+ const send = jest.fn();
185
+ const done = jest.fn();
186
+ await writeNode._handlers.input(msg, send, done);
187
+
188
+ const point = influxModule.__getLastPoint();
189
+ const client = influxModule.__getLastClientInstance();
190
+
191
+ expect(point.floatFields.temperature).toBe(21.5);
192
+ expect(point.integerFields.count).toBe(5);
193
+ expect(point.tags.location).toBe('lab');
194
+
195
+ expect(client.write).toHaveBeenCalledWith('lp:cpu', 'metrics');
196
+ expect(send).toHaveBeenCalledWith(msg);
197
+ expect(done).toHaveBeenCalled();
198
+
199
+ if (writeNode._handlers.close) {
200
+ writeNode._handlers.close();
201
+ }
202
+ });
203
+
204
+ test('writes raw line protocol string with database override', async () => {
205
+ const { RED, influxModule } = setup();
206
+ const ConfigCtor = RED._types['influxdb3-config'];
207
+ const WriteCtor = RED._types['influxdb3-write'];
208
+
209
+ const configNode = new ConfigCtor({
210
+ host: 'https://example.com',
211
+ database: 'metrics',
212
+ name: 'Test',
213
+ credentials: { token: 'token' }
214
+ });
215
+
216
+ const writeNode = new WriteCtor({
217
+ influxdb: configNode,
218
+ measurement: '',
219
+ database: ''
220
+ });
221
+
222
+ const msg = {
223
+ payload: ' weather,location=lab temperature=18.5 ',
224
+ database: 'override-db'
225
+ };
226
+
227
+ const send = jest.fn();
228
+ const done = jest.fn();
229
+ await writeNode._handlers.input(msg, send, done);
230
+
231
+ const client = influxModule.__getLastClientInstance();
232
+ expect(client.write).toHaveBeenCalledWith('weather,location=lab temperature=18.5', 'override-db');
233
+ expect(send).toHaveBeenCalledWith(msg);
234
+ expect(done).toHaveBeenCalled();
235
+
236
+ if (writeNode._handlers.close) {
237
+ writeNode._handlers.close();
238
+ }
239
+ });
240
+ });
@@ -1,170 +1,170 @@
1
- [
2
- {
3
- "id": "example-flow",
4
- "type": "tab",
5
- "label": "InfluxDB v3 Example",
6
- "disabled": false,
7
- "info": "Example flow showing how to write data to InfluxDB v3"
8
- },
9
- {
10
- "id": "inject-temp",
11
- "type": "inject",
12
- "z": "example-flow",
13
- "name": "Generate Temperature Data",
14
- "props": [
15
- {
16
- "p": "payload"
17
- }
18
- ],
19
- "repeat": "5",
20
- "crontab": "",
21
- "once": false,
22
- "onceDelay": 0.1,
23
- "topic": "",
24
- "payload": "",
25
- "payloadType": "date",
26
- "x": 180,
27
- "y": 100,
28
- "wires": [
29
- [
30
- "format-temp"
31
- ]
32
- ]
33
- },
34
- {
35
- "id": "format-temp",
36
- "type": "function",
37
- "z": "example-flow",
38
- "name": "Format Temperature",
39
- "func": "// Generate random temperature between 18 and 28 degrees\nconst temp = 18 + Math.random() * 10;\nconst humidity = 40 + Math.random() * 30;\n\nmsg.measurement = 'environment';\nmsg.payload = {\n temperature: parseFloat(temp.toFixed(2)),\n humidity: parseFloat(humidity.toFixed(2)),\n tags: {\n location: 'office',\n sensor: 'dht22_01'\n }\n};\n\nreturn msg;",
40
- "outputs": 1,
41
- "noerr": 0,
42
- "initialize": "",
43
- "finalize": "",
44
- "libs": [],
45
- "x": 420,
46
- "y": 100,
47
- "wires": [
48
- [
49
- "write-influx",
50
- "debug-payload"
51
- ]
52
- ]
53
- },
54
- {
55
- "id": "write-influx",
56
- "type": "influxdb3-write",
57
- "z": "example-flow",
58
- "influxdb": "influxdb3-config",
59
- "name": "Write to InfluxDB v3",
60
- "measurement": "",
61
- "database": "",
62
- "x": 660,
63
- "y": 100,
64
- "wires": [
65
- [
66
- "debug-result"
67
- ]
68
- ]
69
- },
70
- {
71
- "id": "debug-payload",
72
- "type": "debug",
73
- "z": "example-flow",
74
- "name": "Debug Payload",
75
- "active": true,
76
- "tosidebar": true,
77
- "console": false,
78
- "tostatus": false,
79
- "complete": "payload",
80
- "targetType": "msg",
81
- "statusVal": "",
82
- "statusType": "auto",
83
- "x": 660,
84
- "y": 60,
85
- "wires": []
86
- },
87
- {
88
- "id": "debug-result",
89
- "type": "debug",
90
- "z": "example-flow",
91
- "name": "Debug Result",
92
- "active": true,
93
- "tosidebar": true,
94
- "console": false,
95
- "tostatus": false,
96
- "complete": "true",
97
- "targetType": "full",
98
- "statusVal": "",
99
- "statusType": "auto",
100
- "x": 870,
101
- "y": 100,
102
- "wires": []
103
- },
104
- {
105
- "id": "inject-line-protocol",
106
- "type": "inject",
107
- "z": "example-flow",
108
- "name": "Line Protocol Example",
109
- "props": [
110
- {
111
- "p": "payload"
112
- }
113
- ],
114
- "repeat": "",
115
- "crontab": "",
116
- "once": false,
117
- "onceDelay": 0.1,
118
- "topic": "",
119
- "payload": "weather,location=garden temperature=18.5,humidity=75,pressure=1013.25",
120
- "payloadType": "str",
121
- "x": 180,
122
- "y": 200,
123
- "wires": [
124
- [
125
- "write-influx-lp"
126
- ]
127
- ]
128
- },
129
- {
130
- "id": "write-influx-lp",
131
- "type": "influxdb3-write",
132
- "z": "example-flow",
133
- "influxdb": "influxdb3-config",
134
- "name": "Write Line Protocol",
135
- "measurement": "",
136
- "database": "",
137
- "x": 450,
138
- "y": 200,
139
- "wires": [
140
- [
141
- "debug-lp"
142
- ]
143
- ]
144
- },
145
- {
146
- "id": "debug-lp",
147
- "type": "debug",
148
- "z": "example-flow",
149
- "name": "Debug LP",
150
- "active": true,
151
- "tosidebar": true,
152
- "console": false,
153
- "tostatus": false,
154
- "complete": "true",
155
- "targetType": "full",
156
- "statusVal": "",
157
- "statusType": "auto",
158
- "x": 660,
159
- "y": 200,
160
- "wires": []
161
- },
162
- {
163
- "id": "influxdb3-config",
164
- "type": "influxdb3-config",
165
- "name": "My InfluxDB v3",
166
- "host": "https://us-east-1-1.aws.cloud2.influxdata.com",
167
- "database": "my-database"
168
- }
169
- ]
170
-
1
+ [
2
+ {
3
+ "id": "example-flow",
4
+ "type": "tab",
5
+ "label": "InfluxDB v3 Example",
6
+ "disabled": false,
7
+ "info": "Example flow showing how to write data to InfluxDB v3"
8
+ },
9
+ {
10
+ "id": "inject-temp",
11
+ "type": "inject",
12
+ "z": "example-flow",
13
+ "name": "Generate Temperature Data",
14
+ "props": [
15
+ {
16
+ "p": "payload"
17
+ }
18
+ ],
19
+ "repeat": "5",
20
+ "crontab": "",
21
+ "once": false,
22
+ "onceDelay": 0.1,
23
+ "topic": "",
24
+ "payload": "",
25
+ "payloadType": "date",
26
+ "x": 180,
27
+ "y": 100,
28
+ "wires": [
29
+ [
30
+ "format-temp"
31
+ ]
32
+ ]
33
+ },
34
+ {
35
+ "id": "format-temp",
36
+ "type": "function",
37
+ "z": "example-flow",
38
+ "name": "Format Temperature",
39
+ "func": "// Generate random temperature between 18 and 28 degrees\nconst temp = 18 + Math.random() * 10;\nconst humidity = 40 + Math.random() * 30;\n\nmsg.measurement = 'environment';\nmsg.payload = {\n temperature: parseFloat(temp.toFixed(2)),\n humidity: parseFloat(humidity.toFixed(2)),\n tags: {\n location: 'office',\n sensor: 'dht22_01'\n }\n};\n\nreturn msg;",
40
+ "outputs": 1,
41
+ "noerr": 0,
42
+ "initialize": "",
43
+ "finalize": "",
44
+ "libs": [],
45
+ "x": 420,
46
+ "y": 100,
47
+ "wires": [
48
+ [
49
+ "write-influx",
50
+ "debug-payload"
51
+ ]
52
+ ]
53
+ },
54
+ {
55
+ "id": "write-influx",
56
+ "type": "influxdb3-write",
57
+ "z": "example-flow",
58
+ "influxdb": "influxdb3-config",
59
+ "name": "Write to InfluxDB v3",
60
+ "measurement": "",
61
+ "database": "",
62
+ "x": 660,
63
+ "y": 100,
64
+ "wires": [
65
+ [
66
+ "debug-result"
67
+ ]
68
+ ]
69
+ },
70
+ {
71
+ "id": "debug-payload",
72
+ "type": "debug",
73
+ "z": "example-flow",
74
+ "name": "Debug Payload",
75
+ "active": true,
76
+ "tosidebar": true,
77
+ "console": false,
78
+ "tostatus": false,
79
+ "complete": "payload",
80
+ "targetType": "msg",
81
+ "statusVal": "",
82
+ "statusType": "auto",
83
+ "x": 660,
84
+ "y": 60,
85
+ "wires": []
86
+ },
87
+ {
88
+ "id": "debug-result",
89
+ "type": "debug",
90
+ "z": "example-flow",
91
+ "name": "Debug Result",
92
+ "active": true,
93
+ "tosidebar": true,
94
+ "console": false,
95
+ "tostatus": false,
96
+ "complete": "true",
97
+ "targetType": "full",
98
+ "statusVal": "",
99
+ "statusType": "auto",
100
+ "x": 870,
101
+ "y": 100,
102
+ "wires": []
103
+ },
104
+ {
105
+ "id": "inject-line-protocol",
106
+ "type": "inject",
107
+ "z": "example-flow",
108
+ "name": "Line Protocol Example",
109
+ "props": [
110
+ {
111
+ "p": "payload"
112
+ }
113
+ ],
114
+ "repeat": "",
115
+ "crontab": "",
116
+ "once": false,
117
+ "onceDelay": 0.1,
118
+ "topic": "",
119
+ "payload": "weather,location=garden temperature=18.5,humidity=75,pressure=1013.25",
120
+ "payloadType": "str",
121
+ "x": 180,
122
+ "y": 200,
123
+ "wires": [
124
+ [
125
+ "write-influx-lp"
126
+ ]
127
+ ]
128
+ },
129
+ {
130
+ "id": "write-influx-lp",
131
+ "type": "influxdb3-write",
132
+ "z": "example-flow",
133
+ "influxdb": "influxdb3-config",
134
+ "name": "Write Line Protocol",
135
+ "measurement": "",
136
+ "database": "",
137
+ "x": 450,
138
+ "y": 200,
139
+ "wires": [
140
+ [
141
+ "debug-lp"
142
+ ]
143
+ ]
144
+ },
145
+ {
146
+ "id": "debug-lp",
147
+ "type": "debug",
148
+ "z": "example-flow",
149
+ "name": "Debug LP",
150
+ "active": true,
151
+ "tosidebar": true,
152
+ "console": false,
153
+ "tostatus": false,
154
+ "complete": "true",
155
+ "targetType": "full",
156
+ "statusVal": "",
157
+ "statusType": "auto",
158
+ "x": 660,
159
+ "y": 200,
160
+ "wires": []
161
+ },
162
+ {
163
+ "id": "influxdb3-config",
164
+ "type": "influxdb3-config",
165
+ "name": "My InfluxDB v3",
166
+ "host": "https://us-east-1-1.aws.cloud2.influxdata.com",
167
+ "database": "my-database"
168
+ }
169
+ ]
170
+