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

telefonicaid / iotagent-json / 3713096713

pending completion
3713096713

push

github

GitHub
Merge pull request #683 from telefonicaid/task/set_apikey_group_autoprovision_device

372 of 551 branches covered (67.51%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

840 of 1012 relevant lines covered (83.0%)

66.68 hits per line

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

77.66
/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) {
19✔
44
        switch (command.payloadType.toLowerCase()) {
4!
45
            case 'binaryfromstring':
46
                serialized = Buffer.from(payload.toString());
×
47
                break;
×
48
            case 'binaryfromhex':
49
                serialized = Buffer.from(payload, 'HEX');
×
50
                break;
×
51
            case 'binaryfromjson': // used by AMQP transport
52
                serialized = Buffer.from(JSON.stringify(payload));
4✔
53
                break;
4✔
54
            case 'json': // passthrough
55
            default:
56
                serialized = JSON.stringify(payload);
×
57
        }
58
    } else {
59
        serialized = JSON.stringify(payload);
15✔
60
    }
61
    return serialized;
19✔
62
}
63

64
/**
65
 * Generate a function that executes the given command in the device.
66
 *
67
 * @param {String} apiKey           APIKey of the device's service or default APIKey.
68
 * @param {Object} device           Object containing all the information about a device.
69
 * @param {Object} attribute        Attribute in NGSI format.
70
 * @return {Function}               Command execution function ready to be called with async.series.
71
 */
72
function generateCommandExecution(apiKey, device, attribute) {
73
    let payload = {};
19✔
74
    let command = device && device.commands.find((att) => att.name === attribute.name);
19✔
75
    if (command && command.expression) {
19✔
76
        let parser = iotAgentLib.dataPlugins.expressionTransformation;
3✔
77
        // The context for the JEXL expression should be the ID, TYPE, S, SS
78
        let attrList = iotAgentLib.dataPlugins.utils.getIdTypeServSubServiceFromDevice(device);
3✔
79
        attrList = device.staticAttributes
3!
80
            ? attrList.concat(device.staticAttributes).concat(attribute)
81
            : attrList.concat(attribute);
82
        let ctxt = parser.extractContext(attrList, device);
3✔
83
        // expression result will be the full command payload
84
        let payloadRes = null;
3✔
85
        try {
3✔
86
            payloadRes = parser.applyExpression(command.expression, ctxt, device);
3✔
87
        } catch (e) {
88
            // nothing to report
89
        }
90
        payload = payloadRes ? payloadRes : command.expression;
3!
91
    } else {
92
        payload[attribute.name] = attribute.value;
16✔
93
    }
94
    if (device.transport === 'AMQP') {
19✔
95
        // to ensure backward compability
96
        command.payloadType = command.payloadType ? command.payloadType : 'binaryfromjson';
4!
97
    }
98
    const serialized = serializedPayloadCommand(payload, command);
19✔
99
    const contentType = command && command.contentType ? command.contentType : 'application/json';
19!
100
    config
19✔
101
        .getLogger()
102
        .debug(
103
            context,
104
            'Sending command execution to device [%s] with apikey [%s] and payload [%j] ',
105
            device.id,
106
            apiKey,
107
            payload
108
        );
109

110
    const executions = transportSelector.createExecutionsForBinding(
19✔
111
        [apiKey, device, attribute.name, serialized, contentType],
112
        'executeCommand',
113
        device.transport || config.getConfig().defaultTransport
19!
114
    );
115

116
    return executions;
19✔
117
}
118

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

143
    function concat(previous, current) {
144
        previous = previous.concat(current);
13✔
145
        return previous;
13✔
146
    }
147

148
    iotAgentLib.getDeviceByNameAndType(id, type, service, subservice, function (error, device) {
13✔
149
        if (error) {
13!
150
            config.getLogger().error(
×
151
                context,
152

153
                "COMMAND-001: Command execution could not be handled, as device for entity [%s] [%s] wasn't found",
154

155
                id,
156
                type
157
            );
158
            callback(error);
×
159
        } else {
160
            iotaUtils.getEffectiveApiKey(device.service, device.subservice, device, function (error, apiKey) {
13✔
161
                if (error) {
13!
162
                    callback(error);
×
163
                } else {
164
                    async.series(
13✔
165
                        attributes.map(generateCommandExecution.bind(null, apiKey, device)).reduce(concat, []),
166
                        callback
167
                    );
168
                }
169
            });
170
        }
171
    });
172
}
173

174
/**
175
 * Process an update in the state of a command with information coming from the device.
176
 *
177
 * @param {String} apiKey           API Key corresponding to the Devices configuration.
178
 * @param {String} deviceId         Id of the device to be updated.
179
 * @param {Object} device           Device object containing all the information about a device.
180
 * @param {Object} messageObj       JSON object sent using MQTT.
181
 */
182
function updateCommand(apiKey, deviceId, device, messageObj) {
183
    const commandList = Object.keys(messageObj);
8✔
184
    const commandUpdates = [];
8✔
185

186
    for (let i = 0; i < commandList.length; i++) {
8✔
187
        commandUpdates.push(
8✔
188
            async.apply(
189
                iotAgentLib.setCommandResult,
190
                device.name,
191
                config.getConfig().iota.defaultResource,
192
                apiKey,
193
                commandList[i],
194
                messageObj[commandList[i]],
195
                constants.COMMAND_STATUS_COMPLETED,
196
                device
197
            )
198
        );
199
    }
200

201
    async.series(commandUpdates, function (error) {
8✔
202
        if (error) {
8!
203
            config.getLogger().error(
×
204
                context,
205

206
                "COMMANDS-002: Couldn't update command status in the Context broker " +
207
                    'for device [%s] with apiKey [%s]: %s',
208
                device.id,
209
                apiKey,
210
                error
211
            );
212
        } else {
213
            config
8✔
214
                .getLogger()
215
                .debug(
216
                    context,
217
                    'Single measure for device [%s] with apiKey [%s] successfully updated',
218
                    device.id,
219
                    apiKey
220
                );
221
        }
222
    });
223
}
224

225
exports.generateCommandExecution = generateCommandExecution;
1✔
226
exports.updateCommand = updateCommand;
1✔
227
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