iotagent-node-lib 4.9.0 → 4.10.0
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.
- package/.github/workflows/ci.yml +6 -8
- package/Changelog +6 -0
- package/config.js +2 -1
- package/doc/admin.md +12 -0
- package/doc/api.md +33 -3
- package/doc/devel/northboundinteractions.md +200 -112
- package/lib/commonConfig.js +5 -1
- package/lib/model/Device.js +3 -1
- package/lib/model/Group.js +2 -1
- package/lib/services/common/iotManagerService.js +2 -1
- package/lib/services/devices/deviceRegistryMongoDB.js +5 -1
- package/lib/services/devices/deviceService.js +17 -1
- package/lib/services/devices/devices-NGSI-LD.js +3 -2
- package/lib/services/devices/devices-NGSI-mixed.js +16 -0
- package/lib/services/devices/devices-NGSI-v2.js +122 -8
- package/lib/services/devices/registrationUtils.js +97 -30
- package/lib/services/groups/groupRegistryMongoDB.js +2 -1
- package/lib/services/ngsi/subscription-NGSI-LD.js +2 -2
- package/lib/services/ngsi/subscription-NGSI-mixed.js +3 -3
- package/lib/services/ngsi/subscription-NGSI-v2.js +20 -6
- package/lib/services/ngsi/subscriptionService.js +2 -2
- package/lib/services/northBound/deviceProvisioningServer.js +6 -3
- package/lib/templates/updateDevice.json +12 -0
- package/lib/templates/updateDeviceLax.json +4 -0
- package/package.json +2 -2
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +2 -2
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -1
- package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
- package/test/unit/ngsi-ld/general/https-support-test.js +1 -1
- package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +6 -6
- package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands.json +24 -0
- package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands2.json +24 -0
- package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands3.json +24 -0
- package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands4.json +24 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateEntity.json +5 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateEntity2.json +5 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateEntity2b.json +7 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateEntity3.json +5 -0
- package/test/unit/ngsiv2/examples/subscriptionRequests/simpleSubscriptionRequest.json +4 -2
- package/test/unit/ngsiv2/examples/subscriptionRequests/simpleSubscriptionRequest2.json +4 -2
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
- package/test/unit/ngsiv2/general/https-support-test.js +1 -1
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +245 -2
|
@@ -59,7 +59,9 @@ const attributeList = [
|
|
|
59
59
|
'subscriptions',
|
|
60
60
|
'payloadType',
|
|
61
61
|
'useCBflowControl',
|
|
62
|
-
'storeLastMeasure'
|
|
62
|
+
'storeLastMeasure',
|
|
63
|
+
'cmdMode',
|
|
64
|
+
'subscriptionId'
|
|
63
65
|
];
|
|
64
66
|
|
|
65
67
|
/**
|
|
@@ -324,6 +326,8 @@ function update(previousDevice, device, callback) {
|
|
|
324
326
|
data.useCBflowControl = device.useCBflowControl;
|
|
325
327
|
data.storeLastMeasure = device.storeLastMeasure;
|
|
326
328
|
data.lastMeasure = device.lastMeasure;
|
|
329
|
+
data.cmdMode = device.cmdMode;
|
|
330
|
+
data.subscriptionId = device.subscriptionId;
|
|
327
331
|
|
|
328
332
|
/* eslint-disable-next-line new-cap */
|
|
329
333
|
const deviceObj = new Device.model(data);
|
|
@@ -66,6 +66,17 @@ function init() {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Creates the initial entity representing the device in the Context Broker. This is important mainly to allow the
|
|
71
|
+
* rest of the updateContext operations to be performed using an UPDATE action instead of an APPEND one.
|
|
72
|
+
*
|
|
73
|
+
* @param {Object} deviceData Object containing all the deviceData needed to send the registration.
|
|
74
|
+
* @param {Object} newDevice Device object that will be stored in the database.
|
|
75
|
+
*/
|
|
76
|
+
function createInitialEntity(deviceData, newDevice, callback) {
|
|
77
|
+
deviceHandler.createInitialEntity(deviceData, newDevice, callback);
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
/**
|
|
70
81
|
* If the object_id or the name of the attribute is missing, complete it with the other piece of data.
|
|
71
82
|
*
|
|
@@ -187,6 +198,9 @@ function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuratio
|
|
|
187
198
|
if (configuration && configuration.storeLastMeasure !== undefined && deviceData.storeLastMeasure === undefined) {
|
|
188
199
|
deviceData.storeLastMeasure = configuration.storeLastMeasure;
|
|
189
200
|
}
|
|
201
|
+
if (configuration && configuration.cmdMode !== undefined && deviceData.cmdMode === undefined) {
|
|
202
|
+
deviceData.cmdMode = configuration.cmdMode;
|
|
203
|
+
}
|
|
190
204
|
logger.debug(context, 'deviceData after merge with conf: %j', deviceData);
|
|
191
205
|
callback(null, deviceData);
|
|
192
206
|
}
|
|
@@ -361,13 +375,15 @@ function registerDevice(deviceObj, callback) {
|
|
|
361
375
|
async.waterfall(
|
|
362
376
|
[
|
|
363
377
|
apply(registrationUtils.sendRegistrations, false, deviceData),
|
|
364
|
-
apply(registrationUtils.processContextRegistration, deviceData)
|
|
378
|
+
apply(registrationUtils.processContextRegistration, deviceData),
|
|
379
|
+
apply(createInitialEntity, deviceData)
|
|
365
380
|
],
|
|
366
381
|
function (error, results) {
|
|
367
382
|
if (error) {
|
|
368
383
|
callback(error);
|
|
369
384
|
} else {
|
|
370
385
|
deviceObj.registrationId = results.registrationId;
|
|
386
|
+
deviceObj.subscriptionId = results.subscriptionId;
|
|
371
387
|
deviceObj.name = deviceData.name;
|
|
372
388
|
deviceObj.service = deviceData.service;
|
|
373
389
|
deviceObj.subservice = deviceData.subservice;
|
|
@@ -106,7 +106,7 @@ function updateEntityHandlerNgsiLD(deviceData, updatedDevice, callback) {
|
|
|
106
106
|
* @param {Object} deviceData Object containing all the deviceData needed to send the registration.
|
|
107
107
|
* @param {Object} newDevice Device object that will be stored in the database.
|
|
108
108
|
*/
|
|
109
|
-
function
|
|
109
|
+
function createInitialEntityNgsiLD(deviceData, newDevice, callback) {
|
|
110
110
|
callback(null, newDevice);
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -284,7 +284,7 @@ function updateRegisterDeviceNgsiLD(deviceObj, previousDevice, entityInfoUpdated
|
|
|
284
284
|
deviceObj.subservice
|
|
285
285
|
),
|
|
286
286
|
apply(extractDeviceDifference, deviceObj),
|
|
287
|
-
|
|
287
|
+
createInitialEntityNgsiLD,
|
|
288
288
|
apply(combineWithNewDevice, deviceObj),
|
|
289
289
|
apply(registrationUtils.sendRegistrations, false),
|
|
290
290
|
apply(registrationUtils.processContextRegistration, deviceObj),
|
|
@@ -314,4 +314,5 @@ function updateRegisterDeviceNgsiLD(deviceObj, previousDevice, entityInfoUpdated
|
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
exports.createInitialEntity = createInitialEntityNgsiLD;
|
|
317
318
|
exports.updateRegisterDevice = updateRegisterDeviceNgsiLD;
|
|
@@ -27,6 +27,21 @@ const config = require('../../commonConfig');
|
|
|
27
27
|
const deviceHandlerLD = require('./devices-NGSI-LD');
|
|
28
28
|
const deviceHandlerV2 = require('./devices-NGSI-v2');
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Creates the initial entity representing the device in the Context Broker using both NGSI-LD and NGSI-v2
|
|
32
|
+
* This is important mainly to allow the rest of the updateContext operations to be performed.
|
|
33
|
+
*
|
|
34
|
+
* @param {Object} deviceData Object containing all the deviceData needed to send the registration.
|
|
35
|
+
* @param {Object} newDevice Device object that will be stored in the database.
|
|
36
|
+
*/
|
|
37
|
+
function createInitialEntityNgsiMixed(deviceData, newDevice, callback) {
|
|
38
|
+
if (config.checkNgsiLD(deviceData)) {
|
|
39
|
+
deviceHandlerLD.createInitialEntity(deviceData, newDevice, callback);
|
|
40
|
+
} else {
|
|
41
|
+
deviceHandlerV2.createInitialEntity(deviceData, newDevice, callback);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
/**
|
|
31
46
|
* Updates the register of an existing device identified by the Id and Type in the Context Broker, and the internal
|
|
32
47
|
* registry. It uses both NGSI-LD and NGSI-v2
|
|
@@ -46,4 +61,5 @@ function updateRegisterDeviceNgsiMixed(deviceObj, previousDevice, entityInfoUpda
|
|
|
46
61
|
}
|
|
47
62
|
}
|
|
48
63
|
|
|
64
|
+
exports.createInitialEntity = createInitialEntityNgsiMixed;
|
|
49
65
|
exports.updateRegisterDevice = updateRegisterDeviceNgsiMixed;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright 2020 Telefonica
|
|
2
|
+
* Copyright 2020 Telefonica Investigación y Desarrollo, S.A.U
|
|
3
3
|
*
|
|
4
4
|
* This file is part of fiware-iotagent-lib
|
|
5
5
|
*
|
|
@@ -164,12 +164,102 @@ function formatCommandsNgsi2(originalVector) {
|
|
|
164
164
|
return attributeList;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
function formatCommandsBySubsNgsi2(originalVector) {
|
|
168
|
+
const attributeList = {};
|
|
169
|
+
|
|
170
|
+
if (originalVector && originalVector.length) {
|
|
171
|
+
for (let i = 0; i < originalVector.length; i++) {
|
|
172
|
+
attributeList[originalVector[i].name] = {
|
|
173
|
+
type: 'command',
|
|
174
|
+
value: null
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return attributeList;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function createInitialEntityHandlerNgsi2(deviceData, newDevice, callback) {
|
|
183
|
+
return function handleInitialEntityResponse(error, response, body) {
|
|
184
|
+
if (error) {
|
|
185
|
+
logger.error(
|
|
186
|
+
context,
|
|
187
|
+
'ORION-001: Connection error creating inital entity in the Context Broker: %s',
|
|
188
|
+
error
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
alarms.raise(constants.ORION_ALARM, error);
|
|
192
|
+
|
|
193
|
+
callback(error);
|
|
194
|
+
} else if (response && response.statusCode === 204) {
|
|
195
|
+
alarms.release(constants.ORION_ALARM);
|
|
196
|
+
logger.debug(context, 'Initial entity created successfully.');
|
|
197
|
+
callback(null, newDevice);
|
|
198
|
+
} else {
|
|
199
|
+
logger.error(
|
|
200
|
+
context,
|
|
201
|
+
'Protocol error connecting to the Context Broker [%d]: %s',
|
|
202
|
+
response.statusCode,
|
|
203
|
+
body
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const errorObj = new errors.EntityGenericError(newDevice.id, newDevice.type, body, response.statusCode);
|
|
207
|
+
|
|
208
|
+
callback(errorObj);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
167
213
|
/*
|
|
168
214
|
* This methods makes a bypass in updateRegisterDeviceNgsi2 to allow not change
|
|
169
215
|
* extractDeviceDifference and combineWithNewDevice methods
|
|
170
216
|
*/
|
|
171
|
-
function
|
|
172
|
-
|
|
217
|
+
function createInitialEntityNgsi2(newDevice, deviceObj, callback) {
|
|
218
|
+
logger.debug(context, 'createInitialEntityNgsiv2 called with newDevice: %j', newDevice);
|
|
219
|
+
|
|
220
|
+
const cmdModeConfig = config.getConfig().cmdMode;
|
|
221
|
+
|
|
222
|
+
const isLegacy =
|
|
223
|
+
(!newDevice.cmdMode && (!cmdModeConfig || cmdModeConfig === 'legacy')) || newDevice.cmdMode === 'legacy';
|
|
224
|
+
if (isLegacy) {
|
|
225
|
+
logger.debug(context, 'Old cmdMode with newDevice: %j', deviceObj);
|
|
226
|
+
return callback(null, deviceObj);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!Array.isArray(newDevice.commands) || newDevice.commands.length === 0) {
|
|
230
|
+
logger.debug(context, 'No initial entity due to no commands by subs in newDevice: %j', newDevice);
|
|
231
|
+
return callback(null, deviceObj);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const entityPayload = {
|
|
235
|
+
id: String(newDevice.name),
|
|
236
|
+
type: newDevice.type
|
|
237
|
+
};
|
|
238
|
+
jsonConcat(entityPayload, formatCommandsBySubsNgsi2(newDevice.commands));
|
|
239
|
+
|
|
240
|
+
let url = config.getConfig().contextBroker.url + '/v2/entities?options=upsert';
|
|
241
|
+
if (newDevice.cbHost) {
|
|
242
|
+
url = newDevice.cbHost.includes('://')
|
|
243
|
+
? newDevice.cbHost + '/v2/entities?options=upsert'
|
|
244
|
+
: 'http://' + newDevice.cbHost + '/v2/entities?options=upsert';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const headers = {
|
|
248
|
+
'fiware-correlator': (domain.active && domain.active.corr) || uuid.v4()
|
|
249
|
+
};
|
|
250
|
+
if (newDevice.service) headers['fiware-service'] = newDevice.service;
|
|
251
|
+
if (newDevice.subservice) headers['fiware-servicepath'] = newDevice.subservice;
|
|
252
|
+
|
|
253
|
+
const options = { url, method: 'POST', json: entityPayload, headers };
|
|
254
|
+
|
|
255
|
+
logger.debug(context, 'Creating initial entity due to commands by subs with newDevice: %j', newDevice);
|
|
256
|
+
logger.debug(context, 'Creating initial entity in the Context Broker:\n %s', JSON.stringify(options, null, 4));
|
|
257
|
+
|
|
258
|
+
return utils.executeWithSecurity(
|
|
259
|
+
options,
|
|
260
|
+
newDevice,
|
|
261
|
+
createInitialEntityHandlerNgsi2(newDevice, deviceObj, callback)
|
|
262
|
+
);
|
|
173
263
|
}
|
|
174
264
|
|
|
175
265
|
/**
|
|
@@ -179,6 +269,7 @@ function createInitialEntityNgsi2Fake(deviceData, newDevice, callback) {
|
|
|
179
269
|
* @param {Object} updatedDevice Device object that will be stored in the database.
|
|
180
270
|
*/
|
|
181
271
|
function updateEntityNgsi2(deviceData, updatedDevice, callback) {
|
|
272
|
+
logger.debug(context, 'updateEntityNgsi2 called with deviceData: %j updatedDevice: %j', deviceData, updatedDevice);
|
|
182
273
|
const options = {
|
|
183
274
|
url: config.getConfig().contextBroker.url + '/v2/entities/' + String(deviceData.name) + '/attrs',
|
|
184
275
|
method: 'POST',
|
|
@@ -202,7 +293,14 @@ function updateEntityNgsi2(deviceData, updatedDevice, callback) {
|
|
|
202
293
|
|
|
203
294
|
jsonConcat(options.json, formatAttributesNgsi2(deviceData.active, false));
|
|
204
295
|
jsonConcat(options.json, formatAttributesNgsi2(deviceData.staticAttributes, true));
|
|
205
|
-
|
|
296
|
+
if (
|
|
297
|
+
(!updatedDevice.cmdMode && (!config.getConfig().cmdMode || config.getConfig().cmdMode === 'legacy')) ||
|
|
298
|
+
(updatedDevice && updatedDevice.cmdMode === 'legacy')
|
|
299
|
+
) {
|
|
300
|
+
jsonConcat(options.json, formatCommandsNgsi2(deviceData.commands));
|
|
301
|
+
} else {
|
|
302
|
+
jsonConcat(options.json, formatCommandsBySubsNgsi2(deviceData.commands));
|
|
303
|
+
}
|
|
206
304
|
|
|
207
305
|
for (const att in options.json) {
|
|
208
306
|
try {
|
|
@@ -252,7 +350,13 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
|
|
|
252
350
|
return;
|
|
253
351
|
}
|
|
254
352
|
|
|
255
|
-
logger.debug(
|
|
353
|
+
logger.debug(
|
|
354
|
+
context,
|
|
355
|
+
'Update provisioned v2 device %j with Device %j entityInfoUpdated %j',
|
|
356
|
+
previousDevice,
|
|
357
|
+
deviceObj,
|
|
358
|
+
entityInfoUpdated
|
|
359
|
+
);
|
|
256
360
|
|
|
257
361
|
function combineWithNewDevice(newDevice, oldDevice, callback) {
|
|
258
362
|
logger.debug(context, 'combineWithNewDevice %j %j', newDevice, oldDevice);
|
|
@@ -290,6 +394,9 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
|
|
|
290
394
|
if ('storeLastMeasure' in newDevice && newDevice.storeLastMeasure !== undefined) {
|
|
291
395
|
oldDevice.storeLastMeasure = newDevice.storeLastMeasure;
|
|
292
396
|
}
|
|
397
|
+
if ('cmdMode' in newDevice && newDevice.cmdMode !== undefined) {
|
|
398
|
+
oldDevice.cmdMode = newDevice.cmdMode;
|
|
399
|
+
}
|
|
293
400
|
callback(null, oldDevice);
|
|
294
401
|
} else {
|
|
295
402
|
callback(new errors.DeviceNotFound(newDevice.id, newDevice));
|
|
@@ -337,7 +444,7 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
|
|
|
337
444
|
if (entityInfoUpdated) {
|
|
338
445
|
jsonConcat(deviceData.active, oldDevice.active);
|
|
339
446
|
jsonConcat(deviceData.lazy, oldDevice.lazy);
|
|
340
|
-
|
|
447
|
+
deviceData.commands = deviceData.commands.concat(oldDevice.commands);
|
|
341
448
|
jsonConcat(deviceData.staticAttributes, oldDevice.staticAttributes);
|
|
342
449
|
if (oldDevice.name !== newDevice.name) {
|
|
343
450
|
deviceData.name = newDevice.name;
|
|
@@ -346,7 +453,13 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
|
|
|
346
453
|
deviceData.type = newDevice.type;
|
|
347
454
|
}
|
|
348
455
|
}
|
|
349
|
-
|
|
456
|
+
logger.debug(
|
|
457
|
+
context,
|
|
458
|
+
'extractDeviceDifference newDevice %j oldDevice %j difference %j',
|
|
459
|
+
newDevice,
|
|
460
|
+
oldDevice,
|
|
461
|
+
deviceData
|
|
462
|
+
);
|
|
350
463
|
callback(null, deviceData, oldDevice);
|
|
351
464
|
}
|
|
352
465
|
|
|
@@ -361,7 +474,7 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
|
|
|
361
474
|
deviceObj.subservice
|
|
362
475
|
),
|
|
363
476
|
apply(extractDeviceDifference, deviceObj),
|
|
364
|
-
|
|
477
|
+
createInitialEntityNgsi2,
|
|
365
478
|
apply(combineWithNewDevice, deviceObj),
|
|
366
479
|
apply(registrationUtils.sendRegistrations, false),
|
|
367
480
|
apply(registrationUtils.processContextRegistration, deviceObj),
|
|
@@ -391,6 +504,7 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
|
|
|
391
504
|
}
|
|
392
505
|
}
|
|
393
506
|
|
|
507
|
+
exports.createInitialEntity = createInitialEntityNgsi2;
|
|
394
508
|
exports.updateRegisterDevice = updateRegisterDeviceNgsi2;
|
|
395
509
|
exports.formatCommands = formatCommandsNgsi2;
|
|
396
510
|
exports.formatAttributes = formatAttributesNgsi2;
|
|
@@ -36,6 +36,7 @@ const context = {
|
|
|
36
36
|
};
|
|
37
37
|
const async = require('async');
|
|
38
38
|
const utils = require('../northBound/restUtils');
|
|
39
|
+
const subscriptionService = require('../ngsi/subscriptionService');
|
|
39
40
|
|
|
40
41
|
const NGSI_LD_URN = 'urn:ngsi-ld:';
|
|
41
42
|
|
|
@@ -203,6 +204,23 @@ function sendUnregistrationsNgsiLD(deviceData, callback) {
|
|
|
203
204
|
return callback(null, deviceData);
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
function formatAttributes(originalVector) {
|
|
208
|
+
const attributeList = [];
|
|
209
|
+
if (originalVector && originalVector.length) {
|
|
210
|
+
for (let i = 0; i < originalVector.length; i++) {
|
|
211
|
+
attributeList.push(originalVector[i].name);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return attributeList;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function mergeWithSameName(old, current) {
|
|
218
|
+
if (old.indexOf(current) < 0) {
|
|
219
|
+
old.push(current);
|
|
220
|
+
}
|
|
221
|
+
return old;
|
|
222
|
+
}
|
|
223
|
+
|
|
206
224
|
/**
|
|
207
225
|
* Sends a Context Provider registration or unregistration request to the Context Broker using NGSIv2.
|
|
208
226
|
*
|
|
@@ -210,26 +228,6 @@ function sendUnregistrationsNgsiLD(deviceData, callback) {
|
|
|
210
228
|
* @param {Object} deviceData Object containing all the deviceData needed to send the registration.
|
|
211
229
|
*/
|
|
212
230
|
function sendRegistrationsNgsi2(unregister, deviceData, callback) {
|
|
213
|
-
function formatAttributes(originalVector) {
|
|
214
|
-
const attributeList = [];
|
|
215
|
-
|
|
216
|
-
if (originalVector && originalVector.length) {
|
|
217
|
-
for (let i = 0; i < originalVector.length; i++) {
|
|
218
|
-
attributeList.push(originalVector[i].name);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return attributeList;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function mergeWithSameName(old, current) {
|
|
226
|
-
if (old.indexOf(current) < 0) {
|
|
227
|
-
old.push(current);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return old;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
231
|
// FIXME: When https://github.com/telefonicaid/fiware-orion/issues/3007 is merged into master branch,
|
|
234
232
|
// this function should use the new API. This is just a temporary solution which implies deleting the
|
|
235
233
|
// registration and creating a new one.
|
|
@@ -299,6 +297,61 @@ function sendRegistrationsNgsi2(unregister, deviceData, callback) {
|
|
|
299
297
|
utils.executeWithSecurity(options, deviceData, createRegistrationHandlerNgsi2(unregister, deviceData, callback));
|
|
300
298
|
}
|
|
301
299
|
|
|
300
|
+
function sendUnsubscriptionsNgsi2(deviceData, callback) {
|
|
301
|
+
if (deviceData.subscriptionId) {
|
|
302
|
+
logger.debug(
|
|
303
|
+
context,
|
|
304
|
+
'Sending v2 device unsubscriptions to Context Broker at [%s]',
|
|
305
|
+
config.getConfig().contextBroker.url
|
|
306
|
+
);
|
|
307
|
+
logger.debug(context, 'Using the following subscriptionId %j', deviceData.subscriptionId);
|
|
308
|
+
subscriptionService.unsubscribe(deviceData, deviceData.subscriptionId, callback);
|
|
309
|
+
} else {
|
|
310
|
+
logger.debug(context, 'No subscription found for unregister');
|
|
311
|
+
return callback(null, deviceData);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function sendSubscriptionsNgsi2(unregister, deviceData, callback) {
|
|
316
|
+
function updateSubscriptionNgsi2(deviceData, callback) {
|
|
317
|
+
const functions = [];
|
|
318
|
+
|
|
319
|
+
function removeSubscriptionId(deviceData, unregistrationResult, callback) {
|
|
320
|
+
delete deviceData.subscriptionId;
|
|
321
|
+
return callback(null, deviceData);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
functions.push(async.apply(sendSubscriptionsNgsi2, true, deviceData));
|
|
325
|
+
functions.push(async.apply(removeSubscriptionId, deviceData));
|
|
326
|
+
functions.push(async.apply(sendSubscriptionsNgsi2, false));
|
|
327
|
+
async.waterfall(functions, callback);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (unregister) {
|
|
331
|
+
return sendUnsubscriptionsNgsi2(deviceData, callback);
|
|
332
|
+
}
|
|
333
|
+
if (deviceData.subscriptionId) {
|
|
334
|
+
return updateSubscriptionNgsi2(deviceData, callback);
|
|
335
|
+
}
|
|
336
|
+
const attrs = [].concat(formatAttributes(deviceData.commands)).reduce(mergeWithSameName, []);
|
|
337
|
+
|
|
338
|
+
if (attrs.length === 0) {
|
|
339
|
+
logger.debug(context, 'Subscription is not needed. Device without commands');
|
|
340
|
+
return callback(null, deviceData);
|
|
341
|
+
}
|
|
342
|
+
const trigger = attrs; // one subscription for all commands
|
|
343
|
+
const content = attrs;
|
|
344
|
+
const attrsFormat = 'simplifiedNormalized';
|
|
345
|
+
|
|
346
|
+
logger.debug(
|
|
347
|
+
context,
|
|
348
|
+
'Sending v2 device subscriptions to Context Broker at [%s]',
|
|
349
|
+
config.getConfig().contextBroker.url
|
|
350
|
+
);
|
|
351
|
+
logger.debug(context, 'Using the following trigger %j and content %j', trigger, content);
|
|
352
|
+
subscriptionService.subscribe(deviceData, trigger, content, attrsFormat, callback);
|
|
353
|
+
}
|
|
354
|
+
|
|
302
355
|
/**
|
|
303
356
|
* Sends a Context Provider registration or unregistration request to the Context Broker using NGSI-LD.
|
|
304
357
|
*
|
|
@@ -323,14 +376,14 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
|
|
|
323
376
|
properties.push(element.name);
|
|
324
377
|
});
|
|
325
378
|
|
|
326
|
-
if (lazy.length > 0){
|
|
327
|
-
operations.push('retrieveOps');
|
|
379
|
+
if (lazy.length > 0) {
|
|
380
|
+
operations.push('retrieveOps');
|
|
328
381
|
}
|
|
329
|
-
if (commands.length > 0){
|
|
330
|
-
|
|
382
|
+
if (commands.length > 0) {
|
|
383
|
+
operations.push('updateOps');
|
|
331
384
|
}
|
|
332
|
-
if (supportMerge){
|
|
333
|
-
operations.push('mergeEntity');
|
|
385
|
+
if (supportMerge) {
|
|
386
|
+
operations.push('mergeEntity');
|
|
334
387
|
}
|
|
335
388
|
|
|
336
389
|
if (properties.length === 0) {
|
|
@@ -368,8 +421,8 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
|
|
|
368
421
|
endpoint: config.getConfig().providerUrl,
|
|
369
422
|
contextSourceInfo: [
|
|
370
423
|
{
|
|
371
|
-
|
|
372
|
-
|
|
424
|
+
key: 'jsonldContext',
|
|
425
|
+
value: config.getConfig().contextBroker.jsonLdContext
|
|
373
426
|
}
|
|
374
427
|
],
|
|
375
428
|
'@context': config.getConfig().contextBroker.jsonLdContext
|
|
@@ -405,7 +458,14 @@ function sendRegistrations(unregister, deviceData, callback) {
|
|
|
405
458
|
sendRegistrationsNgsiLD(unregister, deviceData, callback);
|
|
406
459
|
break;
|
|
407
460
|
default:
|
|
408
|
-
|
|
461
|
+
if (
|
|
462
|
+
(!deviceData.cmdMode && (!config.getConfig().cmdMode || config.getConfig().cmdMode === 'legacy')) ||
|
|
463
|
+
(deviceData && deviceData.cmdMode === 'legacy')
|
|
464
|
+
) {
|
|
465
|
+
sendRegistrationsNgsi2(unregister, deviceData, callback);
|
|
466
|
+
} else {
|
|
467
|
+
sendSubscriptionsNgsi2(unregister, deviceData, callback);
|
|
468
|
+
}
|
|
409
469
|
break;
|
|
410
470
|
}
|
|
411
471
|
}
|
|
@@ -421,7 +481,14 @@ function processContextRegistration(deviceData, body, callback) {
|
|
|
421
481
|
const newDevice = _.clone(deviceData);
|
|
422
482
|
|
|
423
483
|
if (body) {
|
|
424
|
-
|
|
484
|
+
if (
|
|
485
|
+
(!deviceData.cmdMode && (!config.getConfig().cmdMode || config.getConfig().cmdMode === 'legacy')) ||
|
|
486
|
+
(deviceData && deviceData.cmdMode === 'legacy')
|
|
487
|
+
) {
|
|
488
|
+
newDevice.registrationId = body.registrationId;
|
|
489
|
+
} else {
|
|
490
|
+
newDevice.subscriptionId = body.subscriptionId;
|
|
491
|
+
}
|
|
425
492
|
}
|
|
426
493
|
|
|
427
494
|
callback(null, newDevice);
|
|
@@ -105,7 +105,7 @@ function createSubscriptionHandlerNgsiLD(device, triggers, store, callback) {
|
|
|
105
105
|
* @param {Object} triggers Array with the names of the attributes that would trigger the subscription
|
|
106
106
|
* @param {Object} content Array with the names of the attributes to retrieve in the notification.
|
|
107
107
|
*/
|
|
108
|
-
function subscribeNgsiLD(device, triggers, content, callback) {
|
|
108
|
+
function subscribeNgsiLD(device, triggers, content, attrsFormat, callback) {
|
|
109
109
|
const options = {
|
|
110
110
|
method: 'POST',
|
|
111
111
|
headers: {
|
|
@@ -132,7 +132,7 @@ function subscribeNgsiLD(device, triggers, content, callback) {
|
|
|
132
132
|
accept: 'application/json'
|
|
133
133
|
},
|
|
134
134
|
attributes: content || [],
|
|
135
|
-
format:
|
|
135
|
+
format: attrsFormat
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
};
|
|
@@ -36,11 +36,11 @@ const subscriptionHandlerV2 = require('./subscription-NGSI-v2');
|
|
|
36
36
|
* @param {Object} triggers Array with the names of the attributes that would trigger the subscription
|
|
37
37
|
* @param {Object} content Array with the names of the attributes to retrieve in the notification.
|
|
38
38
|
*/
|
|
39
|
-
function subscribeNgsiMixed(device, triggers, content, callback) {
|
|
39
|
+
function subscribeNgsiMixed(device, triggers, content, attrsFormat, callback) {
|
|
40
40
|
if (config.checkNgsiLD(device)) {
|
|
41
|
-
subscriptionHandlerLD.subscribe(device, triggers, content, callback);
|
|
41
|
+
subscriptionHandlerLD.subscribe(device, triggers, content, attrsFormat, callback);
|
|
42
42
|
} else {
|
|
43
|
-
subscriptionHandlerV2.subscribe(device, triggers, content, callback);
|
|
43
|
+
subscriptionHandlerV2.subscribe(device, triggers, content, attrsFormat, callback);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -93,7 +93,9 @@ function createSubscriptionHandlerNgsi2(device, triggers, store, callback) {
|
|
|
93
93
|
|
|
94
94
|
config.getRegistry().update(device, device, callback);
|
|
95
95
|
} else {
|
|
96
|
-
callback(null,
|
|
96
|
+
callback(null, {
|
|
97
|
+
subscriptionId: response.headers.location.substr(response.headers.location.lastIndexOf('/') + 1)
|
|
98
|
+
});
|
|
97
99
|
}
|
|
98
100
|
};
|
|
99
101
|
}
|
|
@@ -107,7 +109,7 @@ function createSubscriptionHandlerNgsi2(device, triggers, store, callback) {
|
|
|
107
109
|
* @param {Object} triggers Array with the names of the attributes that would trigger the subscription
|
|
108
110
|
* @param {Object} content Array with the names of the attributes to retrieve in the notification.
|
|
109
111
|
*/
|
|
110
|
-
function subscribeNgsi2(device, triggers, content, callback) {
|
|
112
|
+
function subscribeNgsi2(device, triggers, content, attrsFormat, callback) {
|
|
111
113
|
const options = {
|
|
112
114
|
method: 'POST',
|
|
113
115
|
headers: {
|
|
@@ -115,6 +117,7 @@ function subscribeNgsi2(device, triggers, content, callback) {
|
|
|
115
117
|
'fiware-servicepath': device.subservice
|
|
116
118
|
},
|
|
117
119
|
json: {
|
|
120
|
+
description: 'Managed by IOTA: ' + device.name + ' ' + device.type + ' ' + triggers.join(','),
|
|
118
121
|
subject: {
|
|
119
122
|
entities: [
|
|
120
123
|
{
|
|
@@ -132,7 +135,8 @@ function subscribeNgsi2(device, triggers, content, callback) {
|
|
|
132
135
|
url: config.getConfig().providerUrl + '/notify'
|
|
133
136
|
},
|
|
134
137
|
attrs: content || [],
|
|
135
|
-
attrsFormat:
|
|
138
|
+
attrsFormat: attrsFormat,
|
|
139
|
+
onlyChangedAttrs: true
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
142
|
};
|
|
@@ -167,7 +171,7 @@ function createUnsubscribeHandlerNgsi2(device, id, callback) {
|
|
|
167
171
|
if (error) {
|
|
168
172
|
logger.debug(
|
|
169
173
|
context,
|
|
170
|
-
'Transport error found
|
|
174
|
+
'Transport error found unsubscribing device with id [%s] to entity [%s]',
|
|
171
175
|
device.id,
|
|
172
176
|
device.name
|
|
173
177
|
);
|
|
@@ -176,7 +180,9 @@ function createUnsubscribeHandlerNgsi2(device, id, callback) {
|
|
|
176
180
|
} else if (response.statusCode !== 204) {
|
|
177
181
|
logger.debug(
|
|
178
182
|
context,
|
|
179
|
-
'Unknown error
|
|
183
|
+
'Unknown error unsubscribing device with id [%s] to entity [%s]: %s',
|
|
184
|
+
device.id,
|
|
185
|
+
device.name,
|
|
180
186
|
response.statusCode
|
|
181
187
|
);
|
|
182
188
|
|
|
@@ -202,7 +208,15 @@ function createUnsubscribeHandlerNgsi2(device, id, callback) {
|
|
|
202
208
|
|
|
203
209
|
callback(new errors.BadRequest(body.orionError.details));
|
|
204
210
|
} else {
|
|
205
|
-
|
|
211
|
+
logger.debug(context, 'removing subscription %s from device %j', id, device);
|
|
212
|
+
if (device.subscriptions) {
|
|
213
|
+
// check before try to eliminates
|
|
214
|
+
const index = device.subscriptions.indexOf(id);
|
|
215
|
+
if (index !== -1) {
|
|
216
|
+
// only eliminates if exits
|
|
217
|
+
device.subscriptions.splice(index, 1);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
206
220
|
config.getRegistry().update(device, device, callback);
|
|
207
221
|
}
|
|
208
222
|
};
|
|
@@ -57,8 +57,8 @@ function init() {
|
|
|
57
57
|
* @param {Object} triggers Array with the names of the attributes that would trigger the subscription
|
|
58
58
|
* @param {Object} content Array with the names of the attributes to retrieve in the notification.
|
|
59
59
|
*/
|
|
60
|
-
function subscribe(device, triggers, content, callback) {
|
|
61
|
-
subscriptionHandler.subscribe(device, triggers, content, callback);
|
|
60
|
+
function subscribe(device, triggers, content, attrsFormat, callback) {
|
|
61
|
+
subscriptionHandler.subscribe(device, triggers, content, attrsFormat, callback);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -67,7 +67,8 @@ const provisioningAPITranslation = {
|
|
|
67
67
|
payloadType: 'payloadType',
|
|
68
68
|
useCBflowControl: 'useCBflowControl',
|
|
69
69
|
storeLastMeasure: 'storeLastMeasure',
|
|
70
|
-
lastMeasure: 'lastMeasure'
|
|
70
|
+
lastMeasure: 'lastMeasure',
|
|
71
|
+
cmdMode: 'cmdMode'
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
/**
|
|
@@ -149,7 +150,8 @@ function handleProvision(req, res, next) {
|
|
|
149
150
|
payloadType: body.payloadType,
|
|
150
151
|
useCBflowControl: body.useCBflowControl,
|
|
151
152
|
storeLastMeasure: body.storeLastMeasure,
|
|
152
|
-
lastMeasure: body.lastMeasure
|
|
153
|
+
lastMeasure: body.lastMeasure,
|
|
154
|
+
cmdMode: body.cmdMode
|
|
153
155
|
});
|
|
154
156
|
}
|
|
155
157
|
|
|
@@ -229,7 +231,8 @@ function toProvisioningAPIFormat(device) {
|
|
|
229
231
|
payloadType: device.payloadType,
|
|
230
232
|
useCBflowControl: device.useCBflowControl,
|
|
231
233
|
storeLastMeasure: device.storeLastMeasure,
|
|
232
|
-
lastMeasure: device.lastMeasure
|
|
234
|
+
lastMeasure: device.lastMeasure,
|
|
235
|
+
cmdMode: device.cmdMode
|
|
233
236
|
};
|
|
234
237
|
}
|
|
235
238
|
|