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

telefonicaid / iotagent-json / 6246870208

18 Sep 2023 10:19AM UTC coverage: 77.028% (-0.8%) from 77.778%
6246870208

push

github

web-flow
Merge pull request #751 from telefonicaid/remove/bidirectional_plugin

 remove bidirectional plugin

364 of 540 branches covered (0.0%)

Branch coverage included in aggregate %.

823 of 1001 relevant lines covered (82.22%)

69.49 hits per line

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

72.14
/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 intoTrans = iotAgentLib.intoTrans;
1✔
31
const _ = require('underscore');
1✔
32
const commandHandler = require('../commandHandler');
1✔
33
const async = require('async');
1✔
34
const apply = async.apply;
1✔
35
const request = iotAgentLib.request;
1✔
36
const errors = require('../errors');
1✔
37
const express = require('express');
1✔
38
const iotaUtils = require('../iotaUtils');
1✔
39
const http = require('http');
1✔
40
const https = require('https');
1✔
41
const commonBindings = require('../commonBindings');
1✔
42
const bodyParser = require('body-parser');
1✔
43
const constants = require('../constants');
1✔
44
const context = {
1✔
45
    op: 'IOTAJSON.HTTP.Binding'
46
};
47
const config = require('../configService');
1✔
48
let httpBindingServer;
49
const transport = 'HTTP';
1✔
50

51
const { promisify } = require('util');
1✔
52
const json = promisify(bodyParser.json({ strict: false })); // accept anything JSON.parse accepts.
1✔
53
const text = promisify(bodyParser.text());
1✔
54
const raw = promisify(bodyParser.raw());
1✔
55

56
function parserBody() {
57
    // generic bodyParser
58
    return function (req, res, next) {
118✔
59
        if (req.is('text/plain')) {
6✔
60
            text(req, res).then(() => next(), next);
2✔
61
        } else if (req.is('application/octet-stream')) {
4✔
62
            raw(req, res).then(() => next(), next);
2✔
63
        } else {
64
            // req.is('json')
65
            json(req, res).then(() => next(), next);
2✔
66
        }
67
    };
68
}
69

70
function checkMandatoryParams(queryPayload) {
71
    return function (req, res, next) {
590✔
72
        const notFoundParams = [];
48✔
73
        let error;
74

75
        req.apiKey = req.query.k;
48✔
76
        req.deviceId = req.query.i;
48✔
77
        req.attr = req.params ? req.params.attrValue : undefined;
48!
78

79
        if (!req.apiKey) {
48!
80
            notFoundParams.push('API Key');
×
81
        }
82

83
        if (!req.deviceId) {
48!
84
            notFoundParams.push('Device Id');
×
85
        }
86

87
        // Check if retrievingParam
88
        if (queryPayload && !req.query.d && req.query.getCmd !== '1') {
48!
89
            notFoundParams.push('Payload');
×
90
        }
91
        if (req.method === 'POST' && !req.is('json') && !req.is('text/plain') && !req.is('application/octet-stream')) {
48!
92
            error = new errors.UnsupportedType('application/json, text/plain, application/octet-stream');
×
93
        }
94

95
        if (notFoundParams.length !== 0) {
48!
96
            next(new errors.MandatoryParamsNotFound(notFoundParams));
×
97
        } else {
98
            next(error);
48✔
99
        }
100
    };
101
}
102

103
function parseData(req, res, next) {
104
    let data;
105
    let error;
106
    let payload;
107

108
    if (req.body) {
13✔
109
        config.getLogger().debug(context, 'Using body [%s]', req.body);
12✔
110
        data = req.body;
12✔
111
        regenerateTransid(data);
12✔
112
    } else {
113
        payload = req.query.d;
1✔
114
        regenerateTransid(payload);
1✔
115
        config.getLogger().debug(context, 'Parsing payload [%s]', payload);
1✔
116

117
        try {
1✔
118
            if (payload) {
1!
119
                data = JSON.parse(payload);
×
120
            }
121
        } catch (e) {
122
            error = e;
×
123
        }
124
    }
125

126
    if (error) {
13!
127
        next(error);
×
128
    } else {
129
        req.jsonPayload = data;
13✔
130

131
        config.getLogger().debug(context, 'Parsed data: [%j]', data);
13✔
132
        next();
13✔
133
    }
134
}
135

136
function parseDataMultipleMeasure(req, res, next) {
137
    let data;
138
    let error;
139
    let payload;
140
    let dataArray;
141
    dataArray = [];
35✔
142
    if (req.body) {
35!
143
        config.getLogger().debug(context, 'Using body [%s]', req.body);
35✔
144
        if (!Array.isArray(req.body)) {
35✔
145
            dataArray.push(req.body);
29✔
146
        } else {
147
            dataArray = req.body;
6✔
148
        }
149
        regenerateTransid(dataArray);
35✔
150
    } else {
151
        payload = req.query.d;
×
152
        regenerateTransid(payload);
×
153
        config.getLogger().debug(context, 'Parsing payload [%s]', payload);
×
154
        try {
×
155
            if (payload) {
×
156
                data = JSON.parse(payload);
×
157
                dataArray.push(data);
×
158
            }
159
        } catch (e) {
160
            error = e;
×
161
        }
162
    }
163
    if (error) {
35!
164
        next(error);
×
165
    } else {
166
        req.jsonPayload = dataArray;
35✔
167
        config.getLogger().debug(context, 'Parsed data array: [%j]', dataArray);
35✔
168
        next();
35✔
169
    }
170
}
171

172
function executeCommand(apiKey, device, cmdName, serializedPayload, contentType, callback) {
173
    const options = {
6✔
174
        url: device.endpoint,
175
        method: 'POST',
176
        body: serializedPayload,
177
        headers: {
178
            'fiware-service': device.service,
179
            'fiware-servicepath': device.subservice,
180
            'content-type': contentType
181
        }
182
    };
183
    if (device.endpoint) {
6!
184
        // device.endpoint or another field like device.endpointExp ?
185
        const parser = iotAgentLib.dataPlugins.expressionTransformation;
6✔
186
        let attrList = iotAgentLib.dataPlugins.utils.getIdTypeServSubServiceFromDevice(device);
6✔
187
        attrList = device.staticAttributes ? attrList.concat(device.staticAttributes) : attrList.concat([]);
6!
188
        const ctxt = parser.extractContext(attrList, device);
6✔
189
        config.getLogger().debug(context, 'attrList [%j] for device %j', attrList, device);
6✔
190
        // expression result will be the full command payload
191
        let endpointRes = null;
6✔
192
        try {
6✔
193
            endpointRes = parser.applyExpression(device.endpoint, ctxt, device);
6✔
194
        } catch (e) {
195
            // no error should be reported
196
        }
197
        options.url = endpointRes ? endpointRes : device.endpoint;
6!
198
    }
199
    if (config.getConfig().http.timeout) {
6!
200
        options.timeout = config.getConfig().http.timeout;
×
201
    }
202
    config
6✔
203
        .getLogger()
204
        .debug(
205
            context,
206
            'Sending command [%s] with payload [%s] and with http options [%j]',
207
            cmdName,
208
            serializedPayload,
209
            options
210
        );
211
    request(options, function (error, response, body) {
6✔
212
        if (error || !response || (response.statusCode !== 200 && response.statusCode !== 201)) {
6!
213
            callback(new errors.HTTPCommandResponseError(response ? response.statusCode : 400, error));
×
214
        } else if (apiKey) {
6!
215
            config
6✔
216
                .getLogger()
217
                .info(
218
                    context,
219
                    'Cmd:\n %j was sent to the device %s with http options %j',
220
                    serializedPayload,
221
                    cmdName,
222
                    options
223
                );
224
            process.nextTick(commandHandler.updateCommand.bind(null, apiKey, device.id, device, body));
6✔
225
            callback();
6✔
226
        } else {
227
            config
×
228
                .getLogger()
229
                .info(
230
                    context,
231
                    'Cmd:\n %j was sent to the device %s with http options %j',
232
                    serializedPayload,
233
                    cmdName,
234
                    options
235
                );
236
            callback();
×
237
        }
238
    });
239
}
240

241
function addTimestamp(req, res, next) {
242
    const arr = req.jsonPayload;
43✔
243
    let timeStampData;
244
    for (const i in arr) {
43✔
245
        if (req.query.t && arr[i]) {
244✔
246
            timeStampData = arr[i];
4✔
247
            timeStampData[constants.TIMESTAMP_ATTRIBUTE] = req.query.t;
4✔
248
        }
249
    }
250
    next();
43✔
251
}
252

253
function handleIncomingMeasure(req, res, next) {
254
    let values;
255

256
    config
43✔
257
        .getLogger()
258
        .debug(context, 'Processing multiple HTTP measures for device [%s] with apiKey [%s]', req.deviceId, req.apiKey);
259

260
    function updateCommandHandler(error) {
261
        if (error) {
48!
262
            next(error);
×
263
            config.getLogger().error(
×
264
                context,
265
                /*jshint quotmark: double */
266
                "MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %j",
267
                /*jshint quotmark: single */
268
                error
269
            );
270
        } else {
271
            config
48✔
272
                .getLogger()
273
                .info(
274
                    context,
275
                    'Multiple measures for device [%s] with apiKey [%s] successfully updated',
276
                    req.deviceId,
277
                    req.apiKey
278
                );
279

280
            finishSouthBoundTransaction(next);
48✔
281
        }
282
    }
283

284
    function processHTTPWithDevice(device) {
285
        let payloadDataArr;
286
        let attributeArr;
287
        let attributeValues;
288
        attributeArr = [];
43✔
289
        payloadDataArr = [];
43✔
290

291
        if (req.attr && req.jsonPayload) {
43✔
292
            config.getLogger().debug(context, 'Parsing attr [%s] with value [%s]', req.attr, req.jsonPayload);
6✔
293
            const theAttr = [{ name: req.attr, value: req.jsonPayload, type: 'None' }];
6✔
294
            attributeArr.push(theAttr);
6✔
295
        } else {
296
            if (!Array.isArray(req.jsonPayload)) {
37✔
297
                payloadDataArr.push(req.jsonPayload);
2✔
298
            } else {
299
                payloadDataArr = req.jsonPayload;
35✔
300
            }
301
            if (req.jsonPayload) {
37✔
302
                for (const i in payloadDataArr) {
36✔
303
                    values = commonBindings.extractAttributes(device, payloadDataArr[i]);
42✔
304
                    attributeArr.push(values);
42✔
305
                }
306
            } else {
307
                attributeArr = [];
1✔
308
            }
309
        }
310

311
        if (attributeArr.length === 0) {
43✔
312
            finishSouthBoundTransaction(next);
1✔
313
        } else {
314
            for (const j in attributeArr) {
42✔
315
                attributeValues = attributeArr[j];
48✔
316
                config
48✔
317
                    .getLogger()
318
                    .debug(
319
                        context,
320
                        'Processing measure device [%s] with attributeArr [%j] attributeValues [%j]',
321
                        device.name,
322
                        attributeArr,
323
                        attributeValues
324
                    );
325
                if (req.isCommand) {
48✔
326
                    const executions = [];
1✔
327
                    for (const k in attributeValues) {
1✔
328
                        executions.push(
1✔
329
                            iotAgentLib.setCommandResult.bind(
330
                                null,
331
                                device.name,
332
                                config.getConfig().iota.defaultResource,
333
                                req.apiKey,
334
                                attributeValues[k].name,
335
                                attributeValues[k].value,
336
                                constants.COMMAND_STATUS_COMPLETED,
337
                                device
338
                            )
339
                        );
340
                    }
341
                    async.parallel(executions, updateCommandHandler);
1✔
342
                } else if (attributeValues.length > 0) {
47!
343
                    iotAgentLib.update(device.name, device.type, '', attributeValues, device, updateCommandHandler);
47✔
344
                } else {
345
                    finishSouthBoundTransaction(next);
×
346
                }
347
            }
348
        }
349
    }
350

351
    function processDeviceMeasure(error, device) {
352
        if (error) {
43!
353
            next(error);
×
354
        } else {
355
            const localContext = _.clone(context);
43✔
356
            req.device = device;
43✔
357
            localContext.service = device.service;
43✔
358
            localContext.subservice = device.subservice;
43✔
359
            intoTrans(localContext, processHTTPWithDevice)(device);
43✔
360
        }
361
    }
362

363
    iotaUtils.retrieveDevice(req.deviceId, req.apiKey, transport, processDeviceMeasure);
43✔
364
}
365

366
function isCommand(req, res, next) {
367
    if (
1!
368
        req.path ===
369
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_COMMANDS_PATH
1!
370
    ) {
371
        req.isCommand = true;
1✔
372
    }
373

374
    next();
1✔
375
}
376

377
function sendConfigurationToDevice(apiKey, deviceId, results, callback) {
378
    function handleDeviceResponse(innerCallback) {
379
        return function (error, response, body) {
4✔
380
            if (error) {
4!
381
                innerCallback(error);
×
382
            } else if (response && response.statusCode !== 200) {
4!
383
                innerCallback(new errors.DeviceEndpointError(response.statusCode, body));
×
384
            } else {
385
                innerCallback();
4✔
386
            }
387
        };
388
    }
389

390
    function sendRequest(device, results, innerCallback) {
391
        const resultRequest = {
4✔
392
            url: device.endpoint + constants.HTTP_CONFIGURATION_PATH,
393
            method: 'POST',
394
            json: iotaUtils.createConfigurationNotification(results),
395
            headers: {
396
                'fiware-service': device.service,
397
                'fiware-servicepath': device.subservice,
398
                'content-type': 'application/json'
399
            }
400
        };
401

402
        request(resultRequest, handleDeviceResponse(innerCallback));
4✔
403
    }
404
    iotaUtils.retrieveDevice(deviceId, apiKey, transport, function (error, device) {
4✔
405
        if (error) {
4!
406
            callback(error);
×
407
        } else if (!device.endpoint) {
4!
408
            callback(new errors.EndpointNotFound(device.id));
×
409
        } else {
410
            sendRequest(device, results, callback);
4✔
411
        }
412
    });
413
}
414

415
function handleConfigurationRequest(req, res, next) {
416
    function replyToDevice(error) {
417
        if (error) {
5!
418
            res.status(error.code).json(error);
×
419
        } else {
420
            res.status(200).json({});
5✔
421
        }
422
    }
423
    iotaUtils.retrieveDevice(req.deviceId, req.apiKey, transport, function (error, device) {
5✔
424
        if (error) {
5!
425
            next(error);
×
426
        } else {
427
            iotaUtils.manageConfiguration(
5✔
428
                req.apiKey,
429
                req.deviceId,
430
                device,
431
                req.jsonPayload,
432
                sendConfigurationToDevice,
433
                replyToDevice
434
            );
435
        }
436
    });
437
}
438

439
function handleError(error, req, res, next) {
440
    let code = 500;
×
441

442
    config.getLogger().debug(context, 'Error [%s] handling request: %s', error.name, error.message);
×
443

444
    if (error.code && String(error.code).match(/^[2345]\d\d$/)) {
×
445
        code = error.code;
×
446
    }
447

448
    res.status(code).json({
×
449
        name: error.name,
450
        message: error.message
451
    });
452
}
453

454
/**
455
 * Just fills in the transport protocol in case there is none and polling if endpoint.
456
 *
457
 * @param {Object} device           Device object containing all the information about the device.
458
 */
459
function setPollingAndDefaultTransport(device, callback) {
460
    if (!device.transport) {
114✔
461
        device.transport = 'HTTP';
45✔
462
    }
463

464
    if (device.transport === 'HTTP') {
114✔
465
        if (device.endpoint) {
65✔
466
            device.polling = false;
37✔
467
        } else {
468
            device.polling = true;
28✔
469
        }
470
    }
471

472
    callback(null, device);
114✔
473
}
474

475
/**
476
 * Device provisioning handler.
477
 *
478
 * @param {Object} device           Device object containing all the information about the provisioned device.
479
 */
480
function deviceProvisioningHandler(device, callback) {
481
    config.getLogger().debug(context, 'httpbinding.deviceProvisioningHandler [%j]', device);
112✔
482
    setPollingAndDefaultTransport(device, callback);
112✔
483
}
484

485
/**
486
 * Device updating handler. This handler just fills in the transport protocol in case there is none.
487
 *
488
 * @param {Object} device           Device object containing all the information about the updated device.
489
 */
490
function deviceUpdatingHandler(device, callback) {
491
    config.getLogger().debug(context, 'httpbinding.deviceUpdatingHandler [%j]', device);
2✔
492
    setPollingAndDefaultTransport(device, callback);
2✔
493
}
494

495
/**
496
 * This middleware checks whether there is any polling command pending to be sent to the device. If there is some,
497
 * add the command information to the return payload. Otherwise it returns an empty payload.
498
 */
499
function returnCommands(req, res, next) {
500
    function updateCommandStatus(device, commandList) {
501
        function createCommandUpdate(command) {
502
            return apply(
6✔
503
                iotAgentLib.setCommandResult,
504
                device.name,
505
                device.resource,
506
                req.query.k,
507
                command.name,
508
                ' ',
509
                'DELIVERED',
510
                device
511
            );
512
        }
513

514
        function cleanCommand(command) {
515
            return apply(iotAgentLib.removeCommand, device.service, device.subservice, device.id, command.name);
6✔
516
        }
517

518
        const updates = commandList.map(createCommandUpdate);
6✔
519
        const cleanCommands = commandList.map(cleanCommand);
6✔
520
        if (updates) {
6!
521
            async.parallel(updates.concat(cleanCommands), function (error, results) {
6✔
522
                if (error) {
6!
523
                    config
×
524
                        .getLogger()
525
                        .error(
526
                            context,
527
                            'Error updating command status after delivering commands for device [%s]',
528
                            device.id
529
                        );
530
                } else {
531
                    config
6✔
532
                        .getLogger()
533
                        .debug(
534
                            context,
535
                            'Command status updated successfully after delivering command list to device [%s]',
536
                            device.id
537
                        );
538
                }
539
            });
540
        }
541
    }
542

543
    function parseCommand(item) {
544
        const result = {};
6✔
545
        const cleanedValue = String(item.value).trim();
6✔
546

547
        if (cleanedValue !== '') {
6!
548
            result[item.name] = item.value;
6✔
549
        }
550
        return result;
6✔
551
    }
552

553
    function concatCommand(previous, current) {
554
        if (previous === {}) {
6!
555
            return current;
×
556
        }
557
        return _.extend(previous, current);
6✔
558
    }
559
    if (req.query && req.query.getCmd === '1') {
43✔
560
        iotAgentLib.commandQueue(req.device.service, req.device.subservice, req.deviceId, function (error, list) {
6✔
561
            if (error || !list || list.count === 0) {
6!
562
                if (req.accepts('json')) {
×
563
                    res.status(200).send({});
×
564
                } else {
565
                    res.status(200).send('');
×
566
                }
567
            } else {
568
                if (req.accepts('json')) {
6!
569
                    res.status(200).send(list.commands.map(parseCommand).reduce(concatCommand, {}));
6✔
570
                } else {
571
                    res.status(200).send(JSON.stringify(list.commands.map(parseCommand).reduce(concatCommand, {})));
×
572
                }
573
                process.nextTick(updateCommandStatus.bind(null, req.device, list.commands));
6✔
574
            }
575
        });
576
    } else if (req.accepts('json')) {
37!
577
        res.status(200).send({});
37✔
578
    } else {
579
        res.status(200).send('');
×
580
    }
581
}
582

583
function start(callback) {
584
    const baseRoot = '/';
118✔
585

586
    httpBindingServer = {
118✔
587
        server: null,
588
        app: express(),
589
        router: express.Router()
590
    };
591

592
    if (!config.getConfig().http) {
118!
593
        config
×
594
            .getLogger()
595
            .fatal(context, 'GLOBAL-002: Configuration error. Configuration object [config.http] is missing');
596
        callback(new errors.ConfigurationError('config.http'));
×
597
        return;
×
598
    }
599

600
    httpBindingServer.app.set('port', config.getConfig().http.port);
118✔
601
    httpBindingServer.app.set('host', config.getConfig().http.host || '0.0.0.0');
118!
602

603
    httpBindingServer.router.get(
118✔
604
        config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH,
118!
605
        checkMandatoryParams(true),
606
        parseData,
607
        addTimestamp,
608
        handleIncomingMeasure,
609
        returnCommands
610
    );
611

612
    httpBindingServer.router.post(
118✔
613
        config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH,
118!
614
        bodyParser.json({ strict: false }), // accept anything JSON.parse accepts
615
        checkMandatoryParams(false),
616
        parseDataMultipleMeasure,
617
        addTimestamp,
618
        handleIncomingMeasure,
619
        returnCommands
620
    );
621

622
    httpBindingServer.router.post(
118✔
623
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) +
118!
624
            '/' +
625
            constants.MEASURES_SUFIX +
626
            '/:attrValue',
627
        parserBody(),
628
        checkMandatoryParams(false),
629
        parseData, // non multiple measures are expected in this route
630
        addTimestamp,
631
        handleIncomingMeasure,
632
        returnCommands
633
    );
634

635
    httpBindingServer.router.post(
118✔
636
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_COMMANDS_PATH,
118!
637
        bodyParser.json({ strict: false }), // accept anything JSON.parse accepts.
638
        checkMandatoryParams(false),
639
        parseData,
640
        addTimestamp,
641
        isCommand,
642
        handleIncomingMeasure,
643
        returnCommands
644
    );
645

646
    httpBindingServer.router.post(
118✔
647
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_CONFIGURATION_PATH,
118!
648
        bodyParser.json({ strict: false }), // accept anything JSON.parse accepts.
649
        checkMandatoryParams(false),
650
        parseData,
651
        handleConfigurationRequest
652
    );
653

654
    httpBindingServer.app.use(baseRoot, httpBindingServer.router);
118✔
655
    httpBindingServer.app.use(handleError);
118✔
656

657
    if (config.getConfig().http && config.getConfig().http.key && config.getConfig().http.cert) {
118!
658
        const privateKey = fs.readFileSync(config.getConfig().http.key, 'utf8');
×
659
        const certificate = fs.readFileSync(config.getConfig().http.cert, 'utf8');
×
660
        const credentials = { key: privateKey, cert: certificate };
×
661

662
        config.getLogger().info(context, 'HTTPS Binding listening on port [%s]', config.getConfig().http.port);
×
663
        httpBindingServer.server = https.createServer(credentials, httpBindingServer.app);
×
664
    } else {
665
        config.getLogger().info(context, 'HTTP Binding listening on port [%s]', config.getConfig().http.port);
118✔
666
        httpBindingServer.server = http.createServer(httpBindingServer.app);
118✔
667
    }
668

669
    httpBindingServer.server.listen(httpBindingServer.app.get('port'), httpBindingServer.app.get('host'), callback);
118✔
670
}
671

672
function stop(callback) {
673
    config.getLogger().info(context, 'Stopping JSON HTTP Binding: ');
118✔
674

675
    if (httpBindingServer) {
118!
676
        httpBindingServer.server.close(function () {
118✔
677
            config.getLogger().info(context, 'HTTP Binding Stopped');
118✔
678
            callback();
118✔
679
        });
680
    } else {
681
        callback();
×
682
    }
683
}
684

685
function sendPushNotifications(device, values, callback) {
686
    const executions = _.flatten(values.map(commandHandler.generateCommandExecution.bind(null, null, device)));
×
687

688
    async.series(executions, function (error) {
×
689
        callback(error);
×
690
    });
691
}
692

693
function storePollNotifications(device, values, callback) {
694
    function addPollNotification(item, innerCallback) {
695
        iotAgentLib.addCommand(device.service, device.subservice, device.id, item, innerCallback);
×
696
    }
697

698
    async.map(values, addPollNotification, callback);
×
699
}
700

701
function notificationHandler(device, values, callback) {
702
    if (device.endpoint) {
×
703
        sendPushNotifications(device, values, callback);
×
704
    } else {
705
        storePollNotifications(device, values, callback);
×
706
    }
707
}
708

709
exports.start = start;
1✔
710
exports.stop = stop;
1✔
711
exports.sendConfigurationToDevice = sendConfigurationToDevice;
1✔
712
exports.deviceProvisioningHandler = deviceProvisioningHandler;
1✔
713
exports.deviceUpdatingHandler = deviceUpdatingHandler;
1✔
714
exports.notificationHandler = notificationHandler;
1✔
715
exports.executeCommand = executeCommand;
1✔
716
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