• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

telefonicaid / iotagent-json / 10922684788

18 Sep 2024 01:01PM UTC coverage: 79.817% (+0.1%) from 79.695%
10922684788

push

github

web-flow
Merge pull request #840 from telefonicaid/AlvaroVega-patch-1

disable  subscription_management test

495 of 699 branches covered (70.82%)

Branch coverage included in aggregate %.

1075 of 1268 relevant lines covered (84.78%)

130.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.04
/lib/bindings/HTTPBinding.js
1
/*
2
 * Copyright 2016 Telefonica Investigación y Desarrollo, S.A.U
3
 *
4
 * This file is part of iotagent-json
5
 *
6
 * iotagent-json is free software: you can redistribute it and/or
7
 * modify it under the terms of the GNU Affero General Public License as
8
 * published by the Free Software Foundation, either version 3 of the License,
9
 * or (at your option) any later version.
10
 *
11
 * iotagent-json is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 * See the GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public
17
 * License along with iotagent-json.
18
 * If not, seehttp://www.gnu.org/licenses/.
19
 *
20
 * For those usages not covered by the GNU Affero General Public License
21
 * please contact with::[contacto@tid.es]
22
 */
23

24
/* eslint-disable no-unused-vars */
25

26
const fs = require('fs');
1✔
27
const iotAgentLib = require('iotagent-node-lib');
1✔
28
const regenerateTransid = iotAgentLib.regenerateTransid;
1✔
29
const finishSouthBoundTransaction = iotAgentLib.finishSouthBoundTransaction;
1✔
30
const fillService = iotAgentLib.fillService;
1✔
31
const intoTrans = iotAgentLib.intoTrans;
1✔
32
const _ = require('underscore');
1✔
33
const commandHandler = require('../commandHandler');
1✔
34
const async = require('async');
1✔
35
const apply = async.apply;
1✔
36
const request = iotAgentLib.request;
1✔
37
const errors = require('../errors');
1✔
38
const express = require('express');
1✔
39
const iotaUtils = require('../iotaUtils');
1✔
40
const http = require('http');
1✔
41
const https = require('https');
1✔
42
const commonBindings = require('../commonBindings');
1✔
43
const bodyParser = require('body-parser');
1✔
44
require('body-parser-xml')(bodyParser);
1✔
45
const constants = require('../constants');
1✔
46
let context = {
1✔
47
    op: 'IOTAJSON.HTTP.Binding'
48
};
49

50
const config = require('../configService');
1✔
51
let httpBindingServer;
52
const transport = 'HTTP';
1✔
53

54
const { promisify } = require('util');
1✔
55
const json = promisify(bodyParser.json({ strict: false, limit: config.getConfig().iota.expressLimit })); // accept anything JSON.parse accepts.
1✔
56
const text = promisify(bodyParser.text({ limit: config.getConfig().iota.expressLimit }));
1✔
57
const raw = promisify(bodyParser.raw({ limit: config.getConfig().iota.expressLimit }));
1✔
58
const xml2js = require('xml2js');
1✔
59
const xmlStripPrefix = xml2js.processors.stripPrefix;
1✔
60
const xml = promisify(
1✔
61
    bodyParser.xml({
62
        xmlParseOptions: {
63
            // XML namespaces might change from one request to the next.
64
            // It is useful to remove them from the document,
65
            // to be able to refer to tags later in JEXL transformations.
66
            // See https://github.com/Leonidas-from-XIV/node-xml2js/issues/87
67
            tagNameProcessors: [xmlStripPrefix],
68
            attrNameProcessors: [xmlStripPrefix]
69
        }
70
    })
71
);
72

73
function parserBody() {
74
    // generic bodyParser
75
    return function (req, res, next) {
248✔
76
        if (req.is('text/plain')) {
8✔
77
            text(req, res).then(() => next(), next);
2✔
78
        } else if (req.is('application/octet-stream')) {
6✔
79
            raw(req, res).then(() => next(), next);
2✔
80
        } else if (req.is('application/soap+xml')) {
4✔
81
            xml(req, res).then(() => next(), next);
2✔
82
        } else {
83
            // req.is('json')
84
            json(req, res).then(() => next(), next);
2✔
85
        }
86
    };
87
}
88

89
function checkMandatoryParams(queryPayload) {
90
    return function (req, res, next) {
1,240✔
91
        const notFoundParams = [];
147✔
92
        let error;
93

94
        req.apiKey = req.query.k;
147✔
95
        req.deviceId = req.query.i;
147✔
96
        req.attr = req.params ? req.params.attrValue : undefined;
147!
97

98
        if (!req.apiKey) {
147!
99
            notFoundParams.push('API Key');
×
100
        }
101

102
        if (!req.deviceId) {
147!
103
            notFoundParams.push('Device Id');
×
104
        }
105

106
        // Check if retrievingParam
107
        if (queryPayload && !req.query.d && req.query.getCmd !== '1') {
147!
108
            notFoundParams.push('Payload');
×
109
        }
110
        if (
147!
111
            req.method === 'POST' &&
305✔
112
            !req.is('json') &&
113
            !req.is('text/plain') &&
114
            !req.is('application/octet-stream') &&
115
            !req.is('application/soap+xml')
116
        ) {
117
            error = new errors.UnsupportedType(
×
118
                'application/json, text/plain, application/octet-stream, application/soap+xml'
119
            );
120
        }
121

122
        if (notFoundParams.length !== 0) {
147!
123
            next(new errors.MandatoryParamsNotFound(notFoundParams));
×
124
        } else {
125
            next(error);
147✔
126
        }
127
    };
128
}
129

130
function parseData(req, res, next) {
131
    let data;
132
    let error;
133
    let payload;
134

135
    if (req.body) {
15✔
136
        config.getLogger().debug(context, 'Using body %s', req.body);
14✔
137
        data = req.body;
14✔
138
        regenerateTransid(data);
14✔
139
    } else {
140
        payload = req.query.d;
1✔
141
        regenerateTransid(payload);
1✔
142
        config.getLogger().debug(context, 'Parsing payload %s', payload);
1✔
143

144
        try {
1✔
145
            if (payload) {
1!
146
                data = JSON.parse(payload);
×
147
            }
148
        } catch (e) {
149
            error = e;
×
150
        }
151
    }
152

153
    if (error) {
15!
154
        next(error);
×
155
    } else {
156
        req.jsonPayload = data;
15✔
157
        if (req.body !== undefined) {
15✔
158
            try {
14✔
159
                // This is just for log data
160
                data = data.toString('hex');
14✔
161
            } catch (e) {
162
                // no error should be reported
163
            }
164
        }
165
        config.getLogger().debug(context, 'Parsed data: %j', data);
15✔
166
        next();
15✔
167
    }
168
}
169

170
function parseDataMultipleMeasure(req, res, next) {
171
    let data;
172
    let error;
173
    let payload;
174
    let dataArray;
175
    dataArray = [];
132✔
176
    if (req.body) {
132!
177
        config.getLogger().debug(context, 'Using body %s', req.body);
132✔
178
        if (!Array.isArray(req.body)) {
132✔
179
            dataArray.push(req.body);
116✔
180
        } else {
181
            dataArray = req.body;
16✔
182
        }
183
        regenerateTransid(dataArray);
132✔
184
    } else {
185
        payload = req.query.d;
×
186
        regenerateTransid(payload);
×
187
        config.getLogger().debug(context, 'Parsing payload %s', payload);
×
188
        try {
×
189
            if (payload) {
×
190
                data = JSON.parse(payload);
×
191
                dataArray.push(data);
×
192
            }
193
        } catch (e) {
194
            error = e;
×
195
        }
196
    }
197
    if (error) {
132!
198
        next(error);
×
199
    } else {
200
        req.jsonPayload = dataArray;
132✔
201
        config.getLogger().debug(context, 'Parsed data array: %j', dataArray);
132✔
202
        next();
132✔
203
    }
204
}
205

206
function executeCommand(apiKey, group, device, cmdName, serializedPayload, contentType, callback) {
207
    const options = {
7✔
208
        url: device.endpoint || (group && group.endpoint ? group.endpoint : undefined),
11!
209
        method: 'POST',
210
        body: serializedPayload,
211
        headers: {
212
            'fiware-service': device.service,
213
            'fiware-servicepath': device.subservice,
214
            'content-type': contentType
215
        }
216
    };
217
    if (options.endpoint) {
7!
218
        // endpoint could be an expression
219
        const parser = iotAgentLib.dataPlugins.expressionTransformation;
×
220
        let attrList = iotAgentLib.dataPlugins.utils.getIdTypeServSubServiceFromDevice(device);
×
221
        attrList = device.staticAttributes ? attrList.concat(device.staticAttributes) : attrList.concat([]);
×
222
        const ctxt = parser.extractContext(attrList, device);
×
223
        config.getLogger().debug(context, 'attrList %j for device %j', attrList, device);
×
224
        // expression result will be the full command payload
225
        let endpointRes = null;
×
226
        try {
×
227
            endpointRes = parser.applyExpression(options.endpoint, ctxt, device);
×
228
        } catch (e) {
229
            // no error should be reported
230
        }
231
        options.url = endpointRes ? endpointRes : options.endpoint;
×
232
    }
233
    if (config.getConfig().http.timeout) {
7!
234
        options.timeout = config.getConfig().http.timeout;
×
235
    }
236
    config
7✔
237
        .getLogger()
238
        .debug(
239
            context,
240
            'Sending command %s with payload %s and with http options %j',
241
            cmdName,
242
            serializedPayload,
243
            options
244
        );
245
    request(options, function (error, response, body) {
7✔
246
        if (error || !response || (response.statusCode !== 200 && response.statusCode !== 201)) {
7!
247
            callback(new errors.HTTPCommandResponseError(response ? response.statusCode : 400, error));
×
248
        } else if (apiKey) {
7!
249
            config
7✔
250
                .getLogger()
251
                .info(
252
                    context,
253
                    'Cmd: %j was sent to the device %s with http options %j',
254
                    serializedPayload,
255
                    cmdName,
256
                    options
257
                );
258
            process.nextTick(commandHandler.updateCommand.bind(null, apiKey, device.id, device, body));
7✔
259
            callback();
7✔
260
        } else {
261
            config
×
262
                .getLogger()
263
                .info(
264
                    context,
265
                    'Cmd: %j was sent to the device %s with http options %j',
266
                    serializedPayload,
267
                    cmdName,
268
                    options
269
                );
270
            callback();
×
271
        }
272
    });
273
}
274

275
function addTimestamp(req, res, next) {
276
    const arr = req.jsonPayload;
142✔
277
    let timeStampData;
278
    for (const i in arr) {
142✔
279
        if (req.query.t && arr[i]) {
371✔
280
            timeStampData = arr[i];
4✔
281
            timeStampData[constants.TIMESTAMP_ATTRIBUTE] = req.query.t;
4✔
282
        }
283
    }
284
    next();
142✔
285
}
286

287
function handleIncomingMeasure(req, res, next) {
288
    let values;
289
    context = fillService(context, { service: 'n/a', subservice: 'n/a' });
142✔
290
    config
142✔
291
        .getLogger()
292
        .debug(context, 'Processing multiple HTTP measures for device %s with apiKey %s', req.deviceId, req.apiKey);
293

294
    function updateCommandHandler(error) {
295
        if (error) {
141!
296
            next(error);
×
297
            config.getLogger().error(
×
298
                context,
299
                /*jshint quotmark: double */
300
                "MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %j",
301
                /*jshint quotmark: single */
302
                error
303
            );
304
        } else {
305
            config
141✔
306
                .getLogger()
307
                .info(
308
                    context,
309
                    'Multiple measures for device %s with apiKey %s successfully updated',
310
                    req.deviceId,
311
                    req.apiKey
312
                );
313

314
            finishSouthBoundTransaction(next);
141✔
315
        }
316
    }
317

318
    function processHTTPWithDevice(device) {
319
        let payloadDataArr;
320
        let attributeArr;
321
        let attributeValues;
322
        attributeArr = [];
142✔
323
        payloadDataArr = [];
142✔
324

325
        if (req.attr && req.jsonPayload) {
142✔
326
            config.getLogger().debug(context, 'Parsing attr %s with value %s', req.attr, req.jsonPayload);
8✔
327
            try {
8✔
328
                req.jsonPayload = req.jsonPayload.toString('hex');
8✔
329
            } catch (e) {
330
                // no error should be reported
331
            }
332
            const theAttr = [{ name: req.attr, value: req.jsonPayload, type: 'None' }];
8✔
333
            attributeArr.push(theAttr);
8✔
334
        } else {
335
            if (!Array.isArray(req.jsonPayload)) {
134✔
336
                payloadDataArr.push(req.jsonPayload);
2✔
337
            } else {
338
                payloadDataArr = req.jsonPayload;
132✔
339
            }
340

341
            if (req.jsonPayload) {
134✔
342
                config.getLogger().debug(context, 'Parsing payloadDataArr %j for device %j', payloadDataArr, device);
133✔
343
                for (const i in payloadDataArr) {
133✔
344
                    values = commonBindings.extractAttributes(device, payloadDataArr[i], device.payloadType);
167✔
345
                    if (values && values[0] && values[0][0]) {
167✔
346
                        // Check multimeasure from a ngsiv2/ngsild entities array
347
                        attributeArr = attributeArr.concat(values);
2✔
348
                    } else {
349
                        attributeArr.push(values);
165✔
350
                    }
351
                }
352
            } else {
353
                attributeArr = [];
1✔
354
            }
355
        }
356
        if (attributeArr.length === 0) {
142✔
357
            finishSouthBoundTransaction(next);
1✔
358
        } else {
359
            config
141✔
360
                .getLogger()
361
                .debug(context, 'Processing measure device %s with attributeArr %j', device.name, attributeArr);
362
            if (req.isCommand) {
141✔
363
                const executions = [];
1✔
364
                for (const j in attributeArr) {
1✔
365
                    attributeValues = attributeArr[j];
1✔
366
                    for (const k in attributeValues) {
1✔
367
                        executions.push(
1✔
368
                            iotAgentLib.setCommandResult.bind(
369
                                null,
370
                                device.name,
371
                                config.getConfig().iota.defaultResource,
372
                                req.apiKey,
373
                                attributeValues[k].name,
374
                                attributeValues[k].value,
375
                                constants.COMMAND_STATUS_COMPLETED,
376
                                device
377
                            )
378
                        );
379
                    }
380
                }
381
                async.parallel(executions, updateCommandHandler);
1✔
382
            } else {
383
                iotAgentLib.update(device.name, device.type, '', attributeArr, device, updateCommandHandler);
140✔
384
            }
385
        }
386
    }
387

388
    function processDeviceMeasure(error, device) {
389
        if (error) {
142!
390
            next(error);
×
391
        } else {
392
            const localContext = _.clone(context);
142✔
393
            req.device = device;
142✔
394
            localContext.service = device.service;
142✔
395
            localContext.subservice = device.subservice;
142✔
396
            intoTrans(localContext, processHTTPWithDevice)(device);
142✔
397
        }
398
    }
399

400
    iotaUtils.retrieveDevice(req.deviceId, req.apiKey, processDeviceMeasure);
142✔
401
}
402

403
function isCommand(req, res, next) {
404
    if (
1!
405
        req.path ===
406
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_COMMANDS_PATH
1!
407
    ) {
408
        req.isCommand = true;
1✔
409
    }
410

411
    next();
1✔
412
}
413

414
function sendConfigurationToDevice(apiKey, group, deviceId, results, callback) {
415
    function handleDeviceResponse(innerCallback) {
416
        return function (error, response, body) {
4✔
417
            if (error) {
4!
418
                innerCallback(error);
×
419
            } else if (response && response.statusCode !== 200) {
4!
420
                innerCallback(new errors.DeviceEndpointError(response.statusCode, body));
×
421
            } else {
422
                innerCallback();
4✔
423
            }
424
        };
425
    }
426

427
    function sendRequest(device, group, results, innerCallback) {
428
        const resultRequest = {
4✔
429
            url:
430
                (device.endpoint || (group && group.endpoint ? group.endpoint : undefined)) +
4!
431
                constants.HTTP_CONFIGURATION_PATH,
432

433
            method: 'POST',
434
            json: iotaUtils.createConfigurationNotification(results),
435
            headers: {
436
                'fiware-service': device.service,
437
                'fiware-servicepath': device.subservice,
438
                'content-type': 'application/json'
439
            }
440
        };
441

442
        request(resultRequest, handleDeviceResponse(innerCallback));
4✔
443
    }
444
    iotaUtils.retrieveDevice(deviceId, apiKey, function (error, device) {
4✔
445
        if (error) {
4!
446
            callback(error);
×
447
        } else if (!device.endpoint) {
4!
448
            callback(new errors.EndpointNotFound(device.id));
×
449
        } else {
450
            sendRequest(device, group, results, callback);
4✔
451
        }
452
    });
453
}
454

455
function handleConfigurationRequest(req, res, next) {
456
    function replyToDevice(error) {
457
        if (error) {
5!
458
            res.status(error.code).json(error);
×
459
        } else {
460
            res.status(200).json({});
5✔
461
        }
462
    }
463
    iotaUtils.retrieveDevice(req.deviceId, req.apiKey, function (error, device) {
5✔
464
        if (error) {
5!
465
            next(error);
×
466
        } else {
467
            iotaUtils.manageConfiguration(
5✔
468
                req.apiKey,
469
                req.deviceId,
470
                device,
471
                req.jsonPayload,
472
                sendConfigurationToDevice,
473
                replyToDevice
474
            );
475
        }
476
    });
477
}
478

479
function handleError(error, req, res, next) {
480
    let code = 500;
×
481

482
    config.getLogger().debug(context, 'Error %s handling request: %s', error.name, error.message);
×
483

484
    if (error.code && String(error.code).match(/^[2345]\d\d$/)) {
×
485
        code = error.code;
×
486
    }
487

488
    res.status(code).json({
×
489
        name: error.name,
490
        message: error.message
491
    });
492
}
493

494
/**
495
 * Just fills in the transport protocol in case there is none and polling if endpoint.
496
 *
497
 * @param {Object} device           Device object containing all the information about the device.
498
 * @param {Object} group            Group object containing all the information about the device.
499
 */
500
function setPollingAndDefaultTransport(device, group, callback) {
501
    config.getLogger().debug(context, 'httpbinding.setPollingAndDefaultTransport device %j group %j', device, group);
155✔
502
    if (!device.transport) {
155✔
503
        device.transport = group && group.transport ? group.transport : 'HTTP';
83!
504
    }
505

506
    if (device.transport === 'HTTP') {
155✔
507
        if (device.endpoint) {
105✔
508
            device.polling = false;
69✔
509
        } else {
510
            device.polling = !(group && group.endpoint);
36✔
511
        }
512
    }
513

514
    callback(null, device);
155✔
515
}
516

517
/**
518
 * Device provisioning handler.
519
 *
520
 * @param {Object} device           Device object containing all the information about the provisioned device.
521
 */
522
function deviceProvisioningHandler(device, callback) {
523
    config.getLogger().debug(context, 'httpbinding.deviceProvisioningHandler device %j', device);
151✔
524
    let group = {};
151✔
525
    iotAgentLib.getConfigurationSilently(config.getConfig().iota.defaultResource || '', device.apikey, function (
151!
526
        error,
527
        foundGroup
528
    ) {
529
        if (!error) {
151!
530
            group = foundGroup;
×
531
        }
532
        config.getLogger().debug(context, 'httpbinding.deviceProvisioningHandler group %j', group);
151✔
533
        setPollingAndDefaultTransport(device, group, callback);
151✔
534
    });
535
}
536

537
/**
538
 * Device updating handler. This handler just fills in the transport protocol in case there is none.
539
 *
540
 * @param {Object} device           Device object containing all the information about the updated device.
541
 */
542
function deviceUpdatingHandler(newDevice, oldDevice, callback) {
543
    config
4✔
544
        .getLogger()
545
        .debug(context, 'httpbinding.deviceUpdatingHandler newDevice %j oldDevice %j', newDevice, oldDevice);
546
    let group = {};
4✔
547
    iotAgentLib.getConfigurationSilently(config.getConfig().iota.defaultResource || '', oldDevice.apikey, function (
4!
548
        error,
549
        foundGroup
550
    ) {
551
        if (!error) {
4!
552
            group = foundGroup;
×
553
        }
554
        config.getLogger().debug(context, 'httpbinding.deviceUpdatingHandler group %j', group);
4✔
555
        setPollingAndDefaultTransport(newDevice, group, callback);
4✔
556
    });
557
}
558

559
/**
560
 * This middleware checks whether there is any polling command pending to be sent to the device. If there is some,
561
 * add the command information to the return payload. Otherwise it returns an empty payload.
562
 */
563
function returnCommands(req, res, next) {
564
    function updateCommandStatus(device, commandList) {
565
        context = fillService(context, device);
6✔
566
        function createCommandUpdate(command) {
567
            return apply(
6✔
568
                iotAgentLib.setCommandResult,
569
                device.name,
570
                device.resource,
571
                req.query.k,
572
                command.name,
573
                ' ',
574
                'DELIVERED',
575
                device
576
            );
577
        }
578

579
        function cleanCommand(command) {
580
            return apply(iotAgentLib.removeCommand, device.service, device.subservice, device.id, command.name);
6✔
581
        }
582

583
        const updates = commandList.map(createCommandUpdate);
6✔
584
        const cleanCommands = commandList.map(cleanCommand);
6✔
585
        if (updates) {
6!
586
            async.parallel(updates.concat(cleanCommands), function (error, results) {
6✔
587
                if (error) {
6!
588
                    config
×
589
                        .getLogger()
590
                        .error(
591
                            context,
592
                            'Error updating command status after delivering commands for device %s',
593
                            device.id
594
                        );
595
                } else {
596
                    config
6✔
597
                        .getLogger()
598
                        .debug(
599
                            context,
600
                            'Command status updated successfully after delivering command list to device %s',
601
                            device.id
602
                        );
603
                }
604
            });
605
        }
606
    }
607

608
    function parseCommand(item) {
609
        const result = {};
6✔
610
        const cleanedValue = String(item.value).trim();
6✔
611

612
        if (cleanedValue !== '') {
6!
613
            result[item.name] = item.value;
6✔
614
        }
615
        return result;
6✔
616
    }
617

618
    function concatCommand(previous, current) {
619
        if (previous === {}) {
6!
620
            return current;
×
621
        }
622
        return _.extend(previous, current);
6✔
623
    }
624
    if (req.query && req.query.getCmd === '1') {
142✔
625
        iotAgentLib.commandQueue(req.device.service, req.device.subservice, req.deviceId, function (error, list) {
6✔
626
            if (error || !list || list.count === 0) {
6!
627
                if (req.accepts('json')) {
×
628
                    res.status(200).send({});
×
629
                } else {
630
                    res.status(200).send('');
×
631
                }
632
            } else {
633
                if (req.accepts('json')) {
6!
634
                    res.status(200).send(list.commands.map(parseCommand).reduce(concatCommand, {}));
6✔
635
                } else {
636
                    res.status(200).send(JSON.stringify(list.commands.map(parseCommand).reduce(concatCommand, {})));
×
637
                }
638
                process.nextTick(updateCommandStatus.bind(null, req.device, list.commands));
6✔
639
            }
640
        });
641
    } else if (req.accepts('json')) {
136!
642
        res.status(200).send({});
136✔
643
    } else {
644
        res.status(200).send('');
×
645
    }
646
}
647

648
function start(callback) {
649
    const baseRoot = '/';
248✔
650

651
    httpBindingServer = {
248✔
652
        server: null,
653
        app: express(),
654
        router: express.Router()
655
    };
656

657
    if (!config.getConfig().http) {
248!
658
        config
×
659
            .getLogger()
660
            .fatal(context, 'GLOBAL-002: Configuration error. Configuration object [config.http] is missing');
661
        callback(new errors.ConfigurationError('config.http'));
×
662
        return;
×
663
    }
664

665
    httpBindingServer.app.set('port', config.getConfig().http.port);
248✔
666
    httpBindingServer.app.set('host', config.getConfig().http.host || '0.0.0.0');
248!
667

668
    httpBindingServer.router.get(
248✔
669
        config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH,
248!
670
        checkMandatoryParams(true),
671
        parseData,
672
        addTimestamp,
673
        handleIncomingMeasure,
674
        returnCommands
675
    );
676

677
    httpBindingServer.router.post(
248✔
678
        config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH,
248!
679
        bodyParser.json({ strict: false, limit: config.getConfig().iota.expressLimit }), // accept anything JSON.parse accepts
680
        checkMandatoryParams(false),
681
        parseDataMultipleMeasure,
682
        addTimestamp,
683
        handleIncomingMeasure,
684
        returnCommands
685
    );
686

687
    httpBindingServer.router.post(
248✔
688
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) +
248!
689
            '/' +
690
            constants.MEASURES_SUFIX +
691
            '/:attrValue',
692
        parserBody(),
693
        checkMandatoryParams(false),
694
        parseData, // non multiple measures are expected in this route
695
        addTimestamp,
696
        handleIncomingMeasure,
697
        returnCommands
698
    );
699

700
    httpBindingServer.router.post(
248✔
701
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_COMMANDS_PATH,
248!
702
        bodyParser.json({ strict: false, limit: config.getConfig().iota.expressLimit }), // accept anything JSON.parse accepts.
703
        checkMandatoryParams(false),
704
        parseData,
705
        addTimestamp,
706
        isCommand,
707
        handleIncomingMeasure,
708
        returnCommands
709
    );
710

711
    httpBindingServer.router.post(
248✔
712
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_CONFIGURATION_PATH,
248!
713
        bodyParser.json({ strict: false, limit: config.getConfig().iota.expressLimit }), // accept anything JSON.parse accepts.
714
        checkMandatoryParams(false),
715
        parseData,
716
        handleConfigurationRequest
717
    );
718

719
    httpBindingServer.app.use(baseRoot, httpBindingServer.router);
248✔
720
    httpBindingServer.app.use(handleError);
248✔
721

722
    if (config.getConfig().http && config.getConfig().http.key && config.getConfig().http.cert) {
248!
723
        const privateKey = fs.readFileSync(config.getConfig().http.key, 'utf8');
×
724
        const certificate = fs.readFileSync(config.getConfig().http.cert, 'utf8');
×
725
        const credentials = { key: privateKey, cert: certificate };
×
726

727
        config.getLogger().info(context, 'HTTPS Binding listening on port %s', config.getConfig().http.port);
×
728
        httpBindingServer.server = https.createServer(credentials, httpBindingServer.app);
×
729
    } else {
730
        config.getLogger().info(context, 'HTTP Binding listening on port %s', config.getConfig().http.port);
248✔
731
        httpBindingServer.server = http.createServer(httpBindingServer.app);
248✔
732
    }
733

734
    httpBindingServer.server.listen(httpBindingServer.app.get('port'), httpBindingServer.app.get('host'), callback);
248✔
735
}
736

737
function stop(callback) {
738
    config.getLogger().info(context, 'Stopping JSON HTTP Binding: ');
248✔
739

740
    if (httpBindingServer) {
248!
741
        httpBindingServer.server.close(function () {
248✔
742
            config.getLogger().info(context, 'HTTP Binding Stopped');
248✔
743
            callback();
248✔
744
        });
745
    } else {
746
        callback();
×
747
    }
748
}
749

750
function sendPushNotifications(device, values, callback) {
751
    const executions = _.flatten(values.map(commandHandler.generateCommandExecution.bind(null, null, device)));
×
752

753
    async.series(executions, function (error) {
×
754
        callback(error);
×
755
    });
756
}
757

758
function storePollNotifications(device, values, callback) {
759
    function addPollNotification(item, innerCallback) {
760
        iotAgentLib.addCommand(device.service, device.subservice, device.id, item, innerCallback);
×
761
    }
762

763
    async.map(values, addPollNotification, callback);
×
764
}
765

766
function notificationHandler(device, values, callback) {
767
    if (device.endpoint) {
×
768
        sendPushNotifications(device, values, callback);
×
769
    } else {
770
        storePollNotifications(device, values, callback);
×
771
    }
772
}
773

774
exports.start = start;
1✔
775
exports.stop = stop;
1✔
776
exports.sendConfigurationToDevice = sendConfigurationToDevice;
1✔
777
exports.deviceProvisioningHandler = deviceProvisioningHandler;
1✔
778
exports.deviceUpdatingHandler = deviceUpdatingHandler;
1✔
779
exports.notificationHandler = notificationHandler;
1✔
780
exports.executeCommand = executeCommand;
1✔
781
exports.protocol = 'HTTP';
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc