iotagent-node-lib 4.6.0 → 4.7.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 +0 -1
- package/Changelog +12 -0
- package/config.js +3 -1
- package/doc/admin.md +39 -5
- package/doc/api.md +67 -6
- package/doc/requirements.txt +1 -1
- package/lib/commonConfig.js +21 -2
- package/lib/model/Command.js +2 -2
- package/lib/model/Device.js +7 -3
- package/lib/model/Group.js +5 -3
- package/lib/model/dbConn.js +53 -112
- package/lib/services/commands/commandRegistryMongoDB.js +115 -75
- package/lib/services/common/iotManagerService.js +3 -1
- package/lib/services/devices/deviceRegistryMemory.js +36 -0
- package/lib/services/devices/deviceRegistryMongoDB.js +160 -87
- package/lib/services/devices/deviceService.js +33 -3
- package/lib/services/devices/devices-NGSI-v2.js +6 -1
- package/lib/services/groups/groupRegistryMongoDB.js +120 -83
- package/lib/services/ngsi/entities-NGSI-v2.js +14 -1
- package/lib/services/ngsi/ngsiService.js +32 -1
- package/lib/services/northBound/deviceProvisioningServer.js +12 -3
- package/lib/templates/updateDevice.json +12 -0
- package/lib/templates/updateDeviceLax.json +12 -0
- package/package.json +4 -4
- package/test/functional/config-test.js +3 -2
- package/test/functional/testUtils.js +2 -2
- package/test/unit/examples/groupProvisioningRequests/provisionFullGroup.json +1 -0
- package/test/unit/general/config-multi-core-test.js +1 -2
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +5 -4
- package/test/unit/general/deviceService-test.js +6 -5
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +6 -5
- package/test/unit/mongodb/mongodb-connectionoptions-test.js +7 -39
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1 -2
- package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +1 -2
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +8 -5
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +42 -41
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +11 -10
- package/test/unit/ngsiv2/general/deviceService-test.js +6 -5
- package/test/unit/ngsiv2/general/https-support-test.js +1 -1
- package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +4 -3
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +6 -5
- package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +17 -16
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +10 -18
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +21 -20
- package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +8 -7
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +12 -11
- package/test/unit/ngsiv2/plugins/custom-plugin_test.js +3 -2
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +28 -27
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +6 -4
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -11
- package/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js +11 -10
- package/test/unit/ngsiv2/provisioning/device-registration_test.js +5 -4
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +6 -5
- package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -1
- package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +5 -4
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +8 -7
|
@@ -179,6 +179,12 @@ function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuratio
|
|
|
179
179
|
if (configuration && configuration.payloadType !== undefined && deviceData.payloadType === undefined) {
|
|
180
180
|
deviceData.payloadType = configuration.payloadType;
|
|
181
181
|
}
|
|
182
|
+
if (configuration && configuration.useCBflowControl !== undefined && deviceData.useCBflowControl === undefined) {
|
|
183
|
+
deviceData.useCBflowControl = configuration.useCBflowControl;
|
|
184
|
+
}
|
|
185
|
+
if (configuration && configuration.storeLastMeasure !== undefined && deviceData.storeLastMeasure === undefined) {
|
|
186
|
+
deviceData.storeLastMeasure = configuration.storeLastMeasure;
|
|
187
|
+
}
|
|
182
188
|
logger.debug(context, 'deviceData after merge with conf: %j', deviceData);
|
|
183
189
|
callback(null, deviceData);
|
|
184
190
|
}
|
|
@@ -277,7 +283,20 @@ function registerDevice(deviceObj, callback) {
|
|
|
277
283
|
deviceData.ngsiVersion = configuration.ngsiVersion;
|
|
278
284
|
}
|
|
279
285
|
}
|
|
280
|
-
|
|
286
|
+
// Set polling and transport for autoprovisioned devices
|
|
287
|
+
if (!deviceData.transport && config.getConfig().defaultTransport) {
|
|
288
|
+
deviceData.transport =
|
|
289
|
+
configuration && configuration.transport
|
|
290
|
+
? configuration.transport
|
|
291
|
+
: config.getConfig().defaultTransport;
|
|
292
|
+
}
|
|
293
|
+
if (deviceData.transport === 'HTTP') {
|
|
294
|
+
if (deviceData.endpoint) {
|
|
295
|
+
deviceData.polling = false;
|
|
296
|
+
} else {
|
|
297
|
+
deviceData.polling = !(configuration && configuration.endpoint);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
281
300
|
if (!deviceData.name) {
|
|
282
301
|
let entityName = null;
|
|
283
302
|
if (configuration && configuration.entityNameExp !== undefined && configuration.entityNameExp !== '') {
|
|
@@ -285,7 +304,7 @@ function registerDevice(deviceObj, callback) {
|
|
|
285
304
|
let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(deviceData);
|
|
286
305
|
attrList = deviceData.staticAttributes ? attrList.concat(deviceData.staticAttributes) : attrList;
|
|
287
306
|
attrList = configuration.staticAttributes ? attrList.concat(configuration.staticAttributes) : attrList;
|
|
288
|
-
|
|
307
|
+
const ctxt = expressionPlugin.extractContext(attrList);
|
|
289
308
|
try {
|
|
290
309
|
entityName = expressionPlugin.applyExpression(configuration.entityNameExp, ctxt, deviceData);
|
|
291
310
|
} catch (e) {
|
|
@@ -357,6 +376,12 @@ function registerDevice(deviceObj, callback) {
|
|
|
357
376
|
if ('apikey' in deviceData && deviceData.apikey !== undefined) {
|
|
358
377
|
deviceObj.apikey = deviceData.apikey;
|
|
359
378
|
}
|
|
379
|
+
if ('transport' in deviceData && deviceData.transport !== undefined) {
|
|
380
|
+
deviceObj.transport = deviceData.transport;
|
|
381
|
+
}
|
|
382
|
+
if ('polling' in deviceData && deviceData.polling !== undefined) {
|
|
383
|
+
deviceObj.polling = deviceData.polling;
|
|
384
|
+
}
|
|
360
385
|
logger.debug(context, 'Storing device :\n%s', JSON.stringify(deviceObj, null, 4));
|
|
361
386
|
config.getRegistry().store(deviceObj, callback);
|
|
362
387
|
}
|
|
@@ -616,7 +641,7 @@ function findOrCreate(deviceId, apikey, group, callback) {
|
|
|
616
641
|
} else if (error.name === 'DEVICE_NOT_FOUND') {
|
|
617
642
|
const newDevice = {
|
|
618
643
|
id: deviceId,
|
|
619
|
-
apikey
|
|
644
|
+
apikey,
|
|
620
645
|
service: group.service,
|
|
621
646
|
subservice: group.subservice,
|
|
622
647
|
type: group.type
|
|
@@ -692,6 +717,10 @@ function retrieveDevice(deviceId, apiKey, callback) {
|
|
|
692
717
|
}
|
|
693
718
|
}
|
|
694
719
|
|
|
720
|
+
function storeDeviceField(fieldName, fieldValue, typeInformation, callback) {
|
|
721
|
+
config.getRegistry().storeDeviceField(fieldName, fieldValue, typeInformation, callback);
|
|
722
|
+
}
|
|
723
|
+
|
|
695
724
|
exports.listDevices = intoTrans(context, checkRegistry)(listDevices);
|
|
696
725
|
exports.listDevicesWithType = intoTrans(context, checkRegistry)(listDevicesWithType);
|
|
697
726
|
exports.getDevice = intoTrans(context, checkRegistry)(getDevice);
|
|
@@ -708,4 +737,5 @@ exports.retrieveDevice = intoTrans(context, checkRegistry)(retrieveDevice);
|
|
|
708
737
|
exports.mergeDeviceWithConfiguration = mergeDeviceWithConfiguration;
|
|
709
738
|
exports.findOrCreate = findOrCreate;
|
|
710
739
|
exports.findConfigurationGroup = findConfigurationGroup;
|
|
740
|
+
exports.storeDeviceField = storeDeviceField;
|
|
711
741
|
exports.init = init;
|
|
@@ -285,7 +285,12 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
|
|
|
285
285
|
if ('transport' in newDevice && newDevice.transport !== undefined) {
|
|
286
286
|
oldDevice.transport = newDevice.transport;
|
|
287
287
|
}
|
|
288
|
-
|
|
288
|
+
if ('useCBflowControl' in newDevice && newDevice.useCBflowControl !== undefined) {
|
|
289
|
+
oldDevice.useCBflowControl = newDevice.useCBflowControl;
|
|
290
|
+
}
|
|
291
|
+
if ('storeLastMeasure' in newDevice && newDevice.storeLastMeasure !== undefined) {
|
|
292
|
+
oldDevice.storeLastMeasure = newDevice.storeLastMeasure;
|
|
293
|
+
}
|
|
289
294
|
callback(null, oldDevice);
|
|
290
295
|
} else {
|
|
291
296
|
callback(new errors.DeviceNotFound(newDevice.id, newDevice));
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
const logger = require('logops');
|
|
27
|
-
const
|
|
27
|
+
const mongoose = require('mongoose');
|
|
28
28
|
const intoTrans = require('../common/domain').intoTrans;
|
|
29
29
|
const fillService = require('./../common/domain').fillService;
|
|
30
30
|
const alarmsInt = require('../common/alarmManagement').intercept;
|
|
@@ -61,28 +61,11 @@ const attributeList = [
|
|
|
61
61
|
'defaultEntityNameConjunction',
|
|
62
62
|
'ngsiVersion',
|
|
63
63
|
'entityNameExp',
|
|
64
|
-
'payloadType'
|
|
64
|
+
'payloadType',
|
|
65
|
+
'useCBflowControl',
|
|
66
|
+
'storeLastMeasure'
|
|
65
67
|
];
|
|
66
68
|
|
|
67
|
-
/**
|
|
68
|
-
* Generates a handler for the save device group operations. The handler will take the customary error and the saved
|
|
69
|
-
* device group as the parameters (and pass the serialized DAO as the callback value).
|
|
70
|
-
*
|
|
71
|
-
* @return {Function} The generated handler.
|
|
72
|
-
*/
|
|
73
|
-
function saveGroupHandler(groupDAO, callback) {
|
|
74
|
-
/* eslint-disable-next-line no-unused-vars */
|
|
75
|
-
return function saveHandler(error, result) {
|
|
76
|
-
if (error) {
|
|
77
|
-
logger.debug(fillService(context, groupDAO), 'Error storing device group information: %s', error);
|
|
78
|
-
|
|
79
|
-
callback(new errors.InternalDbError(error));
|
|
80
|
-
} else {
|
|
81
|
-
callback(null, groupDAO.toObject());
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
69
|
function createGroup(group, callback) {
|
|
87
70
|
/* eslint-disable-next-line new-cap */
|
|
88
71
|
const groupObj = new Group.model();
|
|
@@ -99,9 +82,12 @@ function createGroup(group, callback) {
|
|
|
99
82
|
groupObj.apikey,
|
|
100
83
|
groupObj.resource
|
|
101
84
|
);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
85
|
+
groupObj
|
|
86
|
+
.save({})
|
|
87
|
+
.then((groupDAO) => {
|
|
88
|
+
callback(null, groupDAO.toObject());
|
|
89
|
+
})
|
|
90
|
+
.catch((error) => {
|
|
105
91
|
if (error.code === 11000) {
|
|
106
92
|
logger.debug(
|
|
107
93
|
context,
|
|
@@ -109,17 +95,12 @@ function createGroup(group, callback) {
|
|
|
109
95
|
group.resource,
|
|
110
96
|
group.apikey
|
|
111
97
|
);
|
|
112
|
-
|
|
113
98
|
callback(new errors.DuplicateGroup(group));
|
|
114
99
|
} else {
|
|
115
100
|
logger.debug(context, 'Error storing device group information: %s', error);
|
|
116
|
-
|
|
117
101
|
callback(new errors.InternalDbError(error));
|
|
118
102
|
}
|
|
119
|
-
}
|
|
120
|
-
callback(null, groupDAO.toObject());
|
|
121
|
-
}
|
|
122
|
-
});
|
|
103
|
+
});
|
|
123
104
|
}
|
|
124
105
|
|
|
125
106
|
/**
|
|
@@ -141,7 +122,7 @@ function listGroups(service, limit, offset, callback) {
|
|
|
141
122
|
}
|
|
142
123
|
|
|
143
124
|
const query = Group.model.find(condition).sort();
|
|
144
|
-
|
|
125
|
+
const queryCount = Group.model.countDocuments(condition);
|
|
145
126
|
if (limit) {
|
|
146
127
|
query.limit(parseInt(limit, 10));
|
|
147
128
|
}
|
|
@@ -149,16 +130,32 @@ function listGroups(service, limit, offset, callback) {
|
|
|
149
130
|
if (offset) {
|
|
150
131
|
query.skip(parseInt(offset, 10));
|
|
151
132
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
133
|
+
function funcQuery(cb) {
|
|
134
|
+
query
|
|
135
|
+
.exec({})
|
|
136
|
+
.then((res) => {
|
|
137
|
+
cb(null, res);
|
|
138
|
+
})
|
|
139
|
+
.catch((error) => {
|
|
140
|
+
cb(error);
|
|
159
141
|
});
|
|
160
|
-
|
|
161
|
-
)
|
|
142
|
+
}
|
|
143
|
+
function funcQueryCount(cb) {
|
|
144
|
+
queryCount
|
|
145
|
+
.exec({})
|
|
146
|
+
.then((res) => {
|
|
147
|
+
cb(null, res);
|
|
148
|
+
})
|
|
149
|
+
.catch((error) => {
|
|
150
|
+
cb(error);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async.series([funcQuery, funcQueryCount], function (error, results) {
|
|
154
|
+
callback(error, {
|
|
155
|
+
count: results[1],
|
|
156
|
+
services: results[0].map(toObjectFn)
|
|
157
|
+
});
|
|
158
|
+
});
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
function getById(id, callback) {
|
|
@@ -168,21 +165,22 @@ function getById(id, callback) {
|
|
|
168
165
|
const query = Group.model.findOne({ _id: id });
|
|
169
166
|
query.select({ __v: 0 });
|
|
170
167
|
|
|
171
|
-
query
|
|
172
|
-
|
|
168
|
+
query
|
|
169
|
+
.exec({})
|
|
170
|
+
.then((data) => {
|
|
171
|
+
if (data) {
|
|
172
|
+
context = fillService(context, data);
|
|
173
|
+
logger.debug(context, 'Device group data found: %j', data);
|
|
174
|
+
callback(null, data);
|
|
175
|
+
} else {
|
|
176
|
+
logger.debug(context, 'Device group [%s] not found.', id);
|
|
177
|
+
callback(new errors.DeviceGroupNotFound(id));
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
.catch((error) => {
|
|
173
181
|
logger.debug(context, 'Internal MongoDB Error getting group: %s', error);
|
|
174
|
-
|
|
175
182
|
callback(new errors.InternalDbError(error));
|
|
176
|
-
}
|
|
177
|
-
context = fillService(context, data);
|
|
178
|
-
logger.debug(context, 'Device group data found: %j', data);
|
|
179
|
-
callback(null, data);
|
|
180
|
-
} else {
|
|
181
|
-
logger.debug(context, 'Device group [%s] not found.', id);
|
|
182
|
-
|
|
183
|
-
callback(new errors.DeviceGroupNotFound(id));
|
|
184
|
-
}
|
|
185
|
-
});
|
|
183
|
+
});
|
|
186
184
|
}
|
|
187
185
|
|
|
188
186
|
/**
|
|
@@ -203,34 +201,56 @@ function find(service, subservice, callback) {
|
|
|
203
201
|
condition.subservice = subservice;
|
|
204
202
|
|
|
205
203
|
const query = Group.model.find(condition).sort();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
204
|
+
const queryCount = Group.model.countDocuments(condition);
|
|
205
|
+
function funcQuery(cb) {
|
|
206
|
+
query
|
|
207
|
+
.exec({})
|
|
208
|
+
.then((res) => {
|
|
209
|
+
cb(null, res);
|
|
210
|
+
})
|
|
211
|
+
.catch((error) => {
|
|
212
|
+
cb(error);
|
|
213
213
|
});
|
|
214
|
-
|
|
215
|
-
)
|
|
214
|
+
}
|
|
215
|
+
function funcQueryCount(cb) {
|
|
216
|
+
queryCount
|
|
217
|
+
.exec({})
|
|
218
|
+
.then((res) => {
|
|
219
|
+
cb(null, res);
|
|
220
|
+
})
|
|
221
|
+
.catch((error) => {
|
|
222
|
+
cb(error);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
async.series([funcQuery, funcQueryCount], function (error, results) {
|
|
226
|
+
callback(error, {
|
|
227
|
+
count: results[1],
|
|
228
|
+
services: results[0].map(toObjectFn)
|
|
229
|
+
});
|
|
230
|
+
});
|
|
216
231
|
}
|
|
217
232
|
|
|
218
233
|
function findOneInMongoDB(queryObj, fields, callback) {
|
|
219
234
|
const query = Group.model.findOne(queryObj);
|
|
220
235
|
query.select({ __v: 0 });
|
|
221
|
-
query.lean()
|
|
222
|
-
|
|
236
|
+
query.lean();
|
|
237
|
+
|
|
238
|
+
query
|
|
239
|
+
.exec({})
|
|
240
|
+
.then((data) => {
|
|
241
|
+
if (data) {
|
|
242
|
+
context = fillService(context, data);
|
|
243
|
+
logger.debug(context, 'Device group data found: %j', data);
|
|
244
|
+
callback(null, data);
|
|
245
|
+
} else {
|
|
246
|
+
logger.debug(context, 'Device group for fields [%j] not found: [%j]', fields, queryObj);
|
|
247
|
+
callback(new errors.DeviceGroupNotFound(fields, queryObj));
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
.catch((error) => {
|
|
223
251
|
logger.debug(context, 'Internal MongoDB Error getting group: %s', error);
|
|
224
252
|
callback(new errors.InternalDbError(error));
|
|
225
|
-
}
|
|
226
|
-
context = fillService(context, data);
|
|
227
|
-
logger.debug(context, 'Device group data found: %j', data);
|
|
228
|
-
callback(null, data);
|
|
229
|
-
} else {
|
|
230
|
-
logger.debug(context, 'Device group for fields [%j] not found: [%j]', fields, queryObj);
|
|
231
|
-
callback(new errors.DeviceGroupNotFound(fields, queryObj));
|
|
232
|
-
}
|
|
233
|
-
});
|
|
253
|
+
});
|
|
234
254
|
}
|
|
235
255
|
|
|
236
256
|
function findBy(fields) {
|
|
@@ -269,7 +289,15 @@ function update(id, body, callback) {
|
|
|
269
289
|
/* eslint-disable-next-line new-cap */
|
|
270
290
|
const groupObj = new Group.model(group);
|
|
271
291
|
groupObj.isNew = false;
|
|
272
|
-
groupObj
|
|
292
|
+
groupObj
|
|
293
|
+
.save({})
|
|
294
|
+
.then((groupDAO) => {
|
|
295
|
+
callback(null, groupDAO.toObject());
|
|
296
|
+
})
|
|
297
|
+
.catch((error) => {
|
|
298
|
+
logger.debug(fillService(context, group), 'Error storing device group information: %s', error);
|
|
299
|
+
callback(new errors.InternalDbError(error));
|
|
300
|
+
});
|
|
273
301
|
}
|
|
274
302
|
});
|
|
275
303
|
}
|
|
@@ -281,16 +309,18 @@ function remove(id, callback) {
|
|
|
281
309
|
if (error) {
|
|
282
310
|
callback(error);
|
|
283
311
|
} else {
|
|
284
|
-
Group.model.deleteOne({ _id: id }
|
|
285
|
-
|
|
286
|
-
|
|
312
|
+
const query = Group.model.deleteOne({ _id: id });
|
|
313
|
+
query
|
|
314
|
+
.exec({})
|
|
315
|
+
.then(() => {
|
|
316
|
+
logger.debug(context, 'Device group [%s] successfully removed.', id);
|
|
317
|
+
callback(null, deviceGroup);
|
|
318
|
+
})
|
|
319
|
+
.catch((error) => {
|
|
320
|
+
logger.debug(context, 'Internal MongoDB Error getting device group: %s', error);
|
|
287
321
|
|
|
288
322
|
callback(new errors.InternalDbError(error));
|
|
289
|
-
}
|
|
290
|
-
logger.debug(context, 'Device [%s] successfully removed.', id);
|
|
291
|
-
callback(null, deviceGroup);
|
|
292
|
-
}
|
|
293
|
-
});
|
|
323
|
+
});
|
|
294
324
|
}
|
|
295
325
|
});
|
|
296
326
|
}
|
|
@@ -300,7 +330,14 @@ function init(newConfig, callback) {
|
|
|
300
330
|
}
|
|
301
331
|
|
|
302
332
|
function clear(callback) {
|
|
303
|
-
|
|
333
|
+
mongoose.connection
|
|
334
|
+
.dropDatabase()
|
|
335
|
+
.then(() => {
|
|
336
|
+
callback(null);
|
|
337
|
+
})
|
|
338
|
+
.catch((error) => {
|
|
339
|
+
callback(error);
|
|
340
|
+
});
|
|
304
341
|
}
|
|
305
342
|
|
|
306
343
|
exports.create = alarmsInt(constants.MONGO_ALARM + '_01', intoTrans(context, createGroup));
|
|
@@ -347,6 +347,10 @@ function sendUpdateValueNgsi2(entityName, originMeasures, originTypeInformation,
|
|
|
347
347
|
//metadata static attributes
|
|
348
348
|
jexlctxt = reduceMetadataAttrToPlainObject(typeInformation.staticAttributes, jexlctxt);
|
|
349
349
|
|
|
350
|
+
//recover oldctxt
|
|
351
|
+
if (typeInformation.oldCtxt) {
|
|
352
|
+
jexlctxt.oldCtxt = typeInformation.oldCtxt;
|
|
353
|
+
}
|
|
350
354
|
logger.debug(
|
|
351
355
|
context,
|
|
352
356
|
'sendUpdateValueNgsi2 loop with: entityName=%s, measures=%j, typeInformation=%j, initial jexlContext=%j, timestamp=%j',
|
|
@@ -652,9 +656,15 @@ function sendUpdateValueNgsi2(entityName, originMeasures, originTypeInformation,
|
|
|
652
656
|
}
|
|
653
657
|
}
|
|
654
658
|
}
|
|
659
|
+
//Add jexlctxt to typeInformation without oldCtxt
|
|
660
|
+
const oldCJexlctxt = { ...jexlctxt };
|
|
661
|
+
delete oldCJexlctxt.oldCtxt;
|
|
662
|
+
originTypeInformation['oldCtxt'] = oldCJexlctxt;
|
|
655
663
|
} // end for (let measures of originMeasures)
|
|
656
|
-
|
|
657
664
|
let url = '/v2/op/update';
|
|
665
|
+
if (originTypeInformation.useCBflowControl) {
|
|
666
|
+
url += '?options=flowControl';
|
|
667
|
+
}
|
|
658
668
|
let options = NGSIUtils.createRequestObject(url, originTypeInformation, token);
|
|
659
669
|
options.json = payload;
|
|
660
670
|
|
|
@@ -680,6 +690,9 @@ function sendUpdateValueNgsi2(entityName, originMeasures, originTypeInformation,
|
|
|
680
690
|
if (!multi) {
|
|
681
691
|
// recreate options object to use single entity update
|
|
682
692
|
url = '/v2/entities?options=upsert';
|
|
693
|
+
if (originTypeInformation.useCBflowControl) {
|
|
694
|
+
url += ',flowControl';
|
|
695
|
+
}
|
|
683
696
|
options = NGSIUtils.createRequestObject(url, originTypeInformation, token);
|
|
684
697
|
delete payload.actionType;
|
|
685
698
|
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
const async = require('async');
|
|
28
28
|
const apply = async.apply;
|
|
29
29
|
const statsRegistry = require('../stats/statsRegistry');
|
|
30
|
+
const deviceService = require('../devices/deviceService');
|
|
30
31
|
const intoTrans = require('../common/domain').intoTrans;
|
|
31
32
|
const fillService = require('./../common/domain').fillService;
|
|
32
33
|
const errors = require('../../errors');
|
|
@@ -69,7 +70,37 @@ function init() {
|
|
|
69
70
|
*/
|
|
70
71
|
function sendUpdateValue(entityName, attributes, typeInformation, token, callback) {
|
|
71
72
|
const newCallback = statsRegistry.withStats('updateEntityRequestsOk', 'updateEntityRequestsError', callback);
|
|
72
|
-
|
|
73
|
+
const additionalCallback = (data, next) => {
|
|
74
|
+
if (typeInformation.oldCtxt) {
|
|
75
|
+
logger.debug(context, 'StoreOldCtxt %j', typeInformation.oldCtxt);
|
|
76
|
+
deviceService.storeDeviceField('oldCtxt', typeInformation.oldCtxt, typeInformation, function () {
|
|
77
|
+
next(null, data);
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
80
|
+
next(null, data);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const wrappedNewCallback = (err, result) => {
|
|
84
|
+
if (err) {
|
|
85
|
+
newCallback(err);
|
|
86
|
+
} else {
|
|
87
|
+
additionalCallback(result, (additionalErr, modifiedResult) => {
|
|
88
|
+
if (additionalErr) {
|
|
89
|
+
newCallback(additionalErr);
|
|
90
|
+
}
|
|
91
|
+
newCallback(null, modifiedResult || result);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
// check config about store last measure
|
|
96
|
+
if (typeInformation.storeLastMeasure) {
|
|
97
|
+
logger.debug(context, 'StoreLastMeasure for %j', typeInformation);
|
|
98
|
+
deviceService.storeDeviceField('lastMeasure', attributes, typeInformation, function () {
|
|
99
|
+
return entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, wrappedNewCallback);
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, wrappedNewCallback);
|
|
103
|
+
}
|
|
73
104
|
}
|
|
74
105
|
|
|
75
106
|
/**
|
|
@@ -64,7 +64,10 @@ const provisioningAPITranslation = {
|
|
|
64
64
|
explicitAttrs: 'explicitAttrs',
|
|
65
65
|
ngsiVersion: 'ngsiVersion',
|
|
66
66
|
entityNameExp: 'entityNameExp',
|
|
67
|
-
payloadType: 'payloadType'
|
|
67
|
+
payloadType: 'payloadType',
|
|
68
|
+
useCBflowControl: 'useCBflowControl',
|
|
69
|
+
storeLastMeasure: 'storeLastMeasure',
|
|
70
|
+
lastMeasure: 'lastMeasure'
|
|
68
71
|
};
|
|
69
72
|
|
|
70
73
|
/**
|
|
@@ -143,7 +146,10 @@ function handleProvision(req, res, next) {
|
|
|
143
146
|
autoprovision: body.autoprovision,
|
|
144
147
|
explicitAttrs: body.explicitAttrs,
|
|
145
148
|
ngsiVersion: body.ngsiVersion,
|
|
146
|
-
payloadType: body.payloadType
|
|
149
|
+
payloadType: body.payloadType,
|
|
150
|
+
useCBflowControl: body.useCBflowControl,
|
|
151
|
+
storeLastMeasure: body.storeLastMeasure,
|
|
152
|
+
lastMeasure: body.lastMeasure
|
|
147
153
|
});
|
|
148
154
|
}
|
|
149
155
|
|
|
@@ -220,7 +226,10 @@ function toProvisioningAPIFormat(device) {
|
|
|
220
226
|
autoprovision: device.autoprovision,
|
|
221
227
|
explicitAttrs: device.explicitAttrs,
|
|
222
228
|
ngsiVersion: device.ngsiVersion,
|
|
223
|
-
payloadType: device.payloadType
|
|
229
|
+
payloadType: device.payloadType,
|
|
230
|
+
useCBflowControl: device.useCBflowControl,
|
|
231
|
+
storeLastMeasure: device.storeLastMeasure,
|
|
232
|
+
lastMeasure: device.lastMeasure
|
|
224
233
|
};
|
|
225
234
|
}
|
|
226
235
|
|
|
@@ -149,6 +149,10 @@
|
|
|
149
149
|
"description": "Payload type",
|
|
150
150
|
"type": "string"
|
|
151
151
|
},
|
|
152
|
+
"useCBflowControl": {
|
|
153
|
+
"description": "use CB flowControl option",
|
|
154
|
+
"type": "boolean"
|
|
155
|
+
},
|
|
152
156
|
"contentType": {
|
|
153
157
|
"description": "Content type",
|
|
154
158
|
"type": "string"
|
|
@@ -194,6 +198,14 @@
|
|
|
194
198
|
"payloadType": {
|
|
195
199
|
"description": "Payload type allowed for measures for this device",
|
|
196
200
|
"type": "string"
|
|
201
|
+
},
|
|
202
|
+
"storeLastMeasure": {
|
|
203
|
+
"description": "Store last measure",
|
|
204
|
+
"type": "boolean"
|
|
205
|
+
},
|
|
206
|
+
"lastMeasure": {
|
|
207
|
+
"description": "last measure",
|
|
208
|
+
"type": "object"
|
|
197
209
|
}
|
|
198
210
|
}
|
|
199
211
|
}
|
|
@@ -146,6 +146,18 @@
|
|
|
146
146
|
"payloadType": {
|
|
147
147
|
"description": "Payload type allowed for measures for this device",
|
|
148
148
|
"type": "string"
|
|
149
|
+
},
|
|
150
|
+
"useCBflowControl": {
|
|
151
|
+
"description": "use CB flowControl option",
|
|
152
|
+
"type": "boolean"
|
|
153
|
+
},
|
|
154
|
+
"storeLastMeasure": {
|
|
155
|
+
"description": "Store last measure",
|
|
156
|
+
"type": "boolean"
|
|
157
|
+
},
|
|
158
|
+
"lastMeasure": {
|
|
159
|
+
"description": "last measure",
|
|
160
|
+
"type": "object"
|
|
149
161
|
}
|
|
150
162
|
}
|
|
151
163
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "iotagent-node-lib",
|
|
3
3
|
"license": "AGPL-3.0-only",
|
|
4
4
|
"description": "IoT Agent library to interface with NGSI Context Broker",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.7.0",
|
|
6
6
|
"homepage": "https://github.com/telefonicaid/iotagent-node-lib",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"fiware",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"async": "2.6.4",
|
|
47
47
|
"body-parser": "~1.20.3",
|
|
48
|
-
"express": "~4.
|
|
48
|
+
"express": "~4.21.2",
|
|
49
49
|
"got": "~11.8.5",
|
|
50
50
|
"jexl": "2.3.0",
|
|
51
51
|
"jison": "0.4.18",
|
|
52
52
|
"logops": "2.1.2",
|
|
53
53
|
"moment": "~2.29.2",
|
|
54
54
|
"moment-timezone": "~0.5.34",
|
|
55
|
-
"mongoose": "
|
|
55
|
+
"mongoose": "8.9.5",
|
|
56
56
|
"query-string": "7.1.1",
|
|
57
57
|
"revalidator": "~0.3.1",
|
|
58
58
|
"underscore": "~1.13.4",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"husky": "~4.2.5",
|
|
70
70
|
"lint-staged": "~12.3.8",
|
|
71
71
|
"mocha": "10.0.0",
|
|
72
|
-
"mongodb": "4.17.
|
|
72
|
+
"mongodb": "4.17.2",
|
|
73
73
|
"nock": "13.2.7",
|
|
74
74
|
"nyc": "~15.1.0",
|
|
75
75
|
"prettier": "~2.7.1",
|
|
@@ -45,7 +45,7 @@ config.amqp = {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
config.iota = {
|
|
48
|
-
logLevel: '
|
|
48
|
+
logLevel: 'DEBUG',
|
|
49
49
|
contextBroker: {
|
|
50
50
|
host: '192.168.1.1',
|
|
51
51
|
port: '1026',
|
|
@@ -61,7 +61,8 @@ config.iota = {
|
|
|
61
61
|
service: 'smartgondor',
|
|
62
62
|
subservice: '/gardens',
|
|
63
63
|
providerUrl: 'http://localhost:4041',
|
|
64
|
-
types: {}
|
|
64
|
+
types: {},
|
|
65
|
+
useCBflowControl: true
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
config.defaultKey = '1234';
|
|
@@ -204,9 +204,9 @@ async function testCase(measure, expectation, provision, env, config, type, tran
|
|
|
204
204
|
let cbMockRoute = '';
|
|
205
205
|
// Set the correct route depending if the test is multientity or not
|
|
206
206
|
if (type === 'multientity' || type === 'multimeasure') {
|
|
207
|
-
cbMockRoute = '/v2/op/update';
|
|
207
|
+
cbMockRoute = '/v2/op/update?options=flowControl';
|
|
208
208
|
} else {
|
|
209
|
-
cbMockRoute = '/v2/entities?options=upsert';
|
|
209
|
+
cbMockRoute = '/v2/entities?options=upsert,flowControl';
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
// Set the correct mock times depending if the test is multimeasure or not
|