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

telefonicaid / iotagent-json / 8567463567

01 Apr 2024 09:30AM UTC coverage: 79.442% (-0.1%) from 79.543%
8567463567

push

github

web-flow
Merge pull request #821 from telefonicaid/dependabot/npm_and_yarn/express-4.19.2

Bump express from 4.18.1 to 4.19.2

494 of 701 branches covered (70.47%)

Branch coverage included in aggregate %.

1071 of 1269 relevant lines covered (84.4%)

113.9 hits per line

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

81.82
/lib/commandHandler.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
const async = require('async');
1✔
25
const iotAgentLib = require('iotagent-node-lib');
1✔
26
const iotaUtils = require('./iotaUtils');
1✔
27
const constants = require('./constants');
1✔
28
const transportSelector = require('./transportSelector');
1✔
29
const config = require('./configService');
1✔
30
const context = {
1✔
31
    op: 'IoTAgentJSON.Commands'
32
};
33

34
/**
35
 * Serializes a payload for a command depending on its payloadType if provided
36
 *
37
 * @param {String} payload          Payload to serialized
38
 * @param {Object} command          Command attribute
39
 * @return {Function}               Returns a serialized payload
40
 */
41
function serializedPayloadCommand(payload, command) {
42
    let serialized;
43
    if (command && command.payloadType) {
23✔
44
        switch (command.payloadType.toLowerCase()) {
8!
45
            case 'binaryfromstring':
46
                serialized = Buffer.from(payload.toString());
×
47
                break;
×
48
            case 'binaryfromhex':
49
                serialized = Buffer.from(payload, 'HEX');
3✔
50
                break;
3✔
51
            case 'binaryfromjson': // used by AMQP transport
52
                serialized = Buffer.from(JSON.stringify(payload));
4✔
53
                break;
4✔
54
            case 'text':
55
                serialized = payload;
1✔
56
                break;
1✔
57
            case 'json': // passthrough
58
            default:
59
                serialized = JSON.stringify(payload);
×
60
        }
61
    } else {
62
        serialized = JSON.stringify(payload);
15✔
63
    }
64
    return serialized;
23✔
65
}
66

67
/**
68
 * Generate a function that executes the given command in the device.
69
 *
70
 * @param {String} apiKey           APIKey of the device's service or default APIKey.
71
 * @param {Object} device           Object containing all the information about a device.
72
 * @param {Object} attribute        Attribute in NGSI format.
73
 * @return {Function}               Command execution function ready to be called with async.series.
74
 */
75
function generateCommandExecution(apiKey, device, group, attribute) {
76
    let payload = {};
23✔
77
    const command = device && device.commands.find((att) => att.name === attribute.name);
35✔
78

79
    if (command && command.expression) {
23✔
80
        const parser = iotAgentLib.dataPlugins.expressionTransformation;
10✔
81
        // The context for the JEXL expression should be the ID, TYPE, S, SS
82
        let attrList = iotAgentLib.dataPlugins.utils.getIdTypeServSubServiceFromDevice(device);
10✔
83
        attrList = device.staticAttributes
10!
84
            ? attrList.concat(device.staticAttributes).concat(attribute)
85
            : attrList.concat(attribute);
86
        const ctxt = parser.extractContext(attrList, device);
10✔
87
        // expression result will be the full command payload
88
        let payloadRes = null;
10✔
89
        try {
10✔
90
            payloadRes = parser.applyExpression(command.expression, ctxt, device);
10✔
91
        } catch (e) {
92
            // nothing to report
93
        }
94
        payload = payloadRes ? payloadRes : command.expression;
10!
95
    } else {
96
        payload[attribute.name] = attribute.value;
13✔
97
    }
98
    if (device.transport === 'AMQP') {
23✔
99
        // to ensure backward compability
100
        command.payloadType = command.payloadType ? command.payloadType : 'binaryfromjson';
4!
101
    }
102
    const serialized = serializedPayloadCommand(payload, command);
23✔
103
    const contentType = command && command.contentType ? command.contentType : 'application/json';
23!
104
    config
23✔
105
        .getLogger()
106
        .debug(
107
            context,
108
            'Sending command execution to device %s with apikey %s and payload %j ',
109
            device.id,
110
            apiKey,
111
            payload
112
        );
113
    const executions = transportSelector.createExecutionsForBinding(
23✔
114
        [apiKey, group, device, attribute.name, serialized, contentType],
115
        'executeCommand',
116
        device.transport ||
26!
117
            (group && group.transport ? group.transport : undefined) ||
9!
118
            config.getConfig().defaultTransport
119
    );
120
    return executions;
23✔
121
}
122

123
/**
124
 * Handles a command execution request coming from the Context Broker. This handler should:
125
 *  - Identify the device affected by the command.
126
 *  - Send the command to the appropriate MQTT topic.
127
 *  - Update the command status in the Context Broker.
128
 *
129
 * @param {String} id               ID of the entity for which the command execution was issued.
130
 * @param {String} type             Type of the entity for which the command execution was issued.
131
 * @param {String} service          Service ID.
132
 * @param {String} subservice       Subservice ID.
133
 * @param {Array} attributes        List of NGSI attributes of type command to execute.
134
 */
135
function commandHandler(id, type, service, subservice, attributes, callback) {
136
    config
23✔
137
        .getLogger()
138
        .debug(context, 'Handling command %j for device %s in service %s - %s', attributes, id, service, subservice);
139

140
    function concat(previous, current) {
141
        previous = previous.concat(current);
23✔
142
        return previous;
23✔
143
    }
144

145
    iotAgentLib.getDeviceByNameAndType(id, type, service, subservice, function (error, device) {
23✔
146
        if (error) {
23!
147
            config.getLogger().error(
×
148
                context,
149

150
                "COMMAND-001: Command execution could not be handled, as device for entity %s %s wasn't found",
151

152
                id,
153
                type
154
            );
155
            callback(error);
×
156
        } else {
157
            iotaUtils.getEffectiveApiKey(device.service, device.subservice, device, function (error, apiKey) {
23✔
158
                if (error) {
23!
159
                    callback(error);
×
160
                } else {
161
                    let group = {};
23✔
162
                    iotAgentLib.getConfigurationSilently(
23✔
163
                        config.getConfig().iota.defaultResource || '',
23!
164
                        apiKey,
165
                        function (error, foundGroup) {
166
                            if (!error) {
23✔
167
                                group = foundGroup;
4✔
168
                            }
169
                            async.series(
23✔
170
                                attributes
171
                                    .map(generateCommandExecution.bind(null, apiKey, device, group))
172
                                    .reduce(concat, []),
173
                                callback
174
                            );
175
                        }
176
                    );
177
                }
178
            });
179
        }
180
    });
181
}
182

183
/**
184
 * Process an update in the state of a command with information coming from the device.
185
 *
186
 * @param {String} apiKey           API Key corresponding to the Devices configuration.
187
 * @param {String} deviceId         Id of the device to be updated.
188
 * @param {Object} device           Device object containing all the information about a device.
189
 * @param {Object} messageObj       JSON object sent using MQTT.
190
 */
191
function updateCommand(apiKey, deviceId, device, messageObj) {
192
    const commandList = Object.keys(messageObj);
10✔
193
    const commandUpdates = [];
10✔
194

195
    for (let i = 0; i < commandList.length; i++) {
10✔
196
        commandUpdates.push(
10✔
197
            async.apply(
198
                iotAgentLib.setCommandResult,
199
                device.name,
200
                config.getConfig().iota.defaultResource,
201
                apiKey,
202
                commandList[i],
203
                messageObj[commandList[i]],
204
                constants.COMMAND_STATUS_COMPLETED,
205
                device
206
            )
207
        );
208
    }
209

210
    async.series(commandUpdates, function (error) {
10✔
211
        if (error) {
10!
212
            config.getLogger().error(
×
213
                context,
214

215
                "COMMANDS-002: Couldn't update command status in the Context broker " +
216
                    'for device %s with apiKey %s: %s',
217
                device.id,
218
                apiKey,
219
                error
220
            );
221
        } else {
222
            config
10✔
223
                .getLogger()
224
                .debug(context, 'Single measure for device %s with apiKey %s successfully updated', device.id, apiKey);
225
        }
226
    });
227
}
228

229
exports.generateCommandExecution = generateCommandExecution;
1✔
230
exports.updateCommand = updateCommand;
1✔
231
exports.handler = commandHandler;
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

© 2025 Coveralls, Inc