iotagent-node-lib 4.6.0 → 4.8.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.
Files changed (70) hide show
  1. package/.github/workflows/ci.yml +2 -3
  2. package/CHANGES_NEXT_RELEASE +1 -0
  3. package/Changelog +20 -0
  4. package/config.js +3 -1
  5. package/doc/README.md +1 -0
  6. package/doc/admin.md +39 -5
  7. package/doc/api.md +74 -9
  8. package/doc/devel/northboundinteractions.md +122 -15
  9. package/doc/models/models.md +260 -0
  10. package/doc/requirements.txt +1 -1
  11. package/docker/Mosquitto/Dockerfile +1 -1
  12. package/lib/commonConfig.js +21 -2
  13. package/lib/fiware-iotagent-lib.js +15 -11
  14. package/lib/jexlTranformsMap.js +182 -35
  15. package/lib/model/Command.js +11 -2
  16. package/lib/model/Device.js +8 -3
  17. package/lib/model/Group.js +5 -3
  18. package/lib/model/dbConn.js +53 -112
  19. package/lib/services/commands/commandRegistryMongoDB.js +130 -76
  20. package/lib/services/commands/commandService.js +3 -3
  21. package/lib/services/common/iotManagerService.js +3 -1
  22. package/lib/services/devices/deviceRegistryMemory.js +36 -0
  23. package/lib/services/devices/deviceRegistryMongoDB.js +161 -88
  24. package/lib/services/devices/deviceService.js +44 -5
  25. package/lib/services/devices/devices-NGSI-v2.js +6 -1
  26. package/lib/services/groups/groupRegistryMongoDB.js +120 -83
  27. package/lib/services/ngsi/entities-NGSI-v2.js +14 -1
  28. package/lib/services/ngsi/ngsiService.js +32 -1
  29. package/lib/services/northBound/contextServer-NGSI-v2.js +12 -3
  30. package/lib/services/northBound/contextServer.js +2 -1
  31. package/lib/services/northBound/deviceProvisioningServer.js +12 -3
  32. package/lib/services/northBound/northboundServer.js +1 -0
  33. package/lib/templates/createDevice.json +4 -0
  34. package/lib/templates/updateDevice.json +16 -0
  35. package/lib/templates/updateDeviceLax.json +12 -0
  36. package/package.json +4 -4
  37. package/test/functional/config-test.js +3 -2
  38. package/test/functional/testUtils.js +15 -4
  39. package/test/unit/examples/groupProvisioningRequests/provisionFullGroup.json +1 -0
  40. package/test/unit/expressions/jexlExpression-test.js +165 -1
  41. package/test/unit/general/config-multi-core-test.js +1 -2
  42. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +5 -4
  43. package/test/unit/general/deviceService-test.js +6 -5
  44. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +6 -5
  45. package/test/unit/mongodb/mongodb-connectionoptions-test.js +7 -39
  46. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1 -2
  47. package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +1 -2
  48. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +8 -5
  49. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +42 -41
  50. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +11 -10
  51. package/test/unit/ngsiv2/general/deviceService-test.js +6 -5
  52. package/test/unit/ngsiv2/general/https-support-test.js +1 -1
  53. package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +4 -3
  54. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +6 -5
  55. package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +17 -16
  56. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +10 -18
  57. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +21 -20
  58. package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +8 -7
  59. package/test/unit/ngsiv2/plugins/alias-plugin_test.js +12 -11
  60. package/test/unit/ngsiv2/plugins/custom-plugin_test.js +3 -2
  61. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +28 -27
  62. package/test/unit/ngsiv2/provisioning/device-group-api-test.js +6 -4
  63. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -11
  64. package/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js +11 -10
  65. package/test/unit/ngsiv2/provisioning/device-registration_test.js +5 -4
  66. package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +6 -5
  67. package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -1
  68. package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +5 -4
  69. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +8 -7
  70. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +0 -326
@@ -24,7 +24,7 @@
24
24
  */
25
25
 
26
26
  const logger = require('logops');
27
- const dbService = require('../../model/dbConn');
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
- groupObj.save(function saveHandler(error, groupDAO) {
104
- if (error) {
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
- } else {
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
- async.series(
154
- [query.exec.bind(query), Group.model.countDocuments.bind(Group.model, condition)],
155
- function (error, results) {
156
- callback(error, {
157
- count: results[1],
158
- services: results[0].map(toObjectFn)
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.lean().exec(function handleGet(error, data) {
172
- if (error) {
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
- } else if (data) {
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
- async.series(
208
- [query.exec.bind(query), Group.model.countDocuments.bind(Group.model, condition)],
209
- function (error, results) {
210
- callback(error, {
211
- count: results[1],
212
- services: results[0].map(toObjectFn)
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().exec(function handleGet(error, data) {
222
- if (error) {
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
- } else if (data) {
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.save(saveGroupHandler(groupObj, callback));
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 }, function (error) {
285
- if (error) {
286
- logger.debug(context, 'Internal MongoDB Error getting device: %s', error);
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
- } else {
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
- dbService.db.db.dropDatabase(callback);
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
- entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, newCallback);
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
  /**
@@ -292,9 +292,18 @@ function handleNotificationNgsi2(req, res, next) {
292
292
  }
293
293
  }
294
294
  }
295
+ logger.debug(context, 'extracted atts %j from dataElement %j', atts, dataElement);
296
+ var id = null;
297
+ var type = null;
298
+ if (dataElement.targetEntityId && dataElement.targetEntityId.value) {
299
+ id = dataElement.targetEntityId.value;
300
+ }
301
+ if (dataElement.targetEntityType && dataElement.targetEntityType.value) {
302
+ type = dataElement.targetEntityType.value;
303
+ }
295
304
  deviceService.getDeviceByNameAndType(
296
- dataElement.id,
297
- dataElement.type,
305
+ id,
306
+ type,
298
307
  req.headers['fiware-service'],
299
308
  req.headers['fiware-servicepath'],
300
309
  function (error, device) {
@@ -336,7 +345,7 @@ function handleNotificationNgsi2(req, res, next) {
336
345
  logger.error(context, 'Error found when processing notification: %j', error);
337
346
  next(error);
338
347
  } else {
339
- res.status(200).json({});
348
+ res.status(204).json();
340
349
  }
341
350
  }
342
351
 
@@ -30,7 +30,7 @@ const context = {
30
30
  op: 'IoTAgentNGSI.ContextServer'
31
31
  };
32
32
  const contextServerUtils = require('./contextServerUtils');
33
-
33
+ const executeUpdateSideEffects = contextServerUtils.executeUpdateSideEffects;
34
34
  let contextServerHandler;
35
35
 
36
36
  /**
@@ -157,4 +157,5 @@ exports.setCommandHandler = intoTrans(context, setCommandHandler);
157
157
  exports.setNotificationHandler = intoTrans(context, setNotificationHandler);
158
158
  exports.addNotificationMiddleware = intoTrans(context, addNotificationMiddleware);
159
159
  exports.setQueryHandler = intoTrans(context, setQueryHandler);
160
+ exports.executeUpdateSideEffects = intoTrans(context, executeUpdateSideEffects);
160
161
  exports.init = init;
@@ -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
 
@@ -125,6 +125,7 @@ exports.setRemoveDeviceHandler = intoTrans(context, deviceProvisioning.setRemove
125
125
  exports.addDeviceProvisionMiddleware = deviceProvisioning.addDeviceProvisionMiddleware;
126
126
  exports.addConfigurationProvisionMiddleware = groupProvisioning.addConfigurationProvisionMiddleware;
127
127
  exports.addNotificationMiddleware = contextServer.addNotificationMiddleware;
128
+ exports.executeUpdateSideEffects = contextServer.executeUpdateSideEffects;
128
129
  exports.clear = clear;
129
130
  exports.start = intoTrans(context, start);
130
131
  exports.stop = intoTrans(context, stop);
@@ -167,6 +167,10 @@
167
167
  "description": "Content type",
168
168
  "type": "string"
169
169
  },
170
+ "headers": {
171
+ "description": "Optional headers to include with command",
172
+ "type": "object"
173
+ },
170
174
  "mqtt": {
171
175
  "description": "Mqtt properties",
172
176
  "type": "object",
@@ -145,10 +145,18 @@
145
145
  "description": "Optional expression for command transformation",
146
146
  "type": "string"
147
147
  },
148
+ "headers": {
149
+ "description": "Optional headers to include with command",
150
+ "type": "object"
151
+ },
148
152
  "payloadType": {
149
153
  "description": "Payload type",
150
154
  "type": "string"
151
155
  },
156
+ "useCBflowControl": {
157
+ "description": "use CB flowControl option",
158
+ "type": "boolean"
159
+ },
152
160
  "contentType": {
153
161
  "description": "Content type",
154
162
  "type": "string"
@@ -194,6 +202,14 @@
194
202
  "payloadType": {
195
203
  "description": "Payload type allowed for measures for this device",
196
204
  "type": "string"
205
+ },
206
+ "storeLastMeasure": {
207
+ "description": "Store last measure",
208
+ "type": "boolean"
209
+ },
210
+ "lastMeasure": {
211
+ "description": "last measure",
212
+ "type": "object"
197
213
  }
198
214
  }
199
215
  }
@@ -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.6.0",
5
+ "version": "4.8.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.19.2",
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": "5.13.20",
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.1",
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: 'FATAL',
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';
@@ -74,16 +74,27 @@ function sendMeasureIotaLib(measure, provision) {
74
74
  * This is not a problem for the tests using other transports than Lib, in that case, the type will be retrieved
75
75
  * from the real provision.
76
76
  */
77
+ let typeInformation = {
78
+ service: provision.headers['fiware-service'],
79
+ subservice: provision.headers['fiware-servicepath']
80
+ };
77
81
  let type;
82
+ let staticAttrs;
78
83
  if (Array.isArray(provision.json.services) && provision.json.services.length > 0) {
79
84
  type = provision.json.services[0].entity_type;
85
+ staticAttrs = provision.json.services[0].static_attributes;
80
86
  } else {
81
87
  type = DEF_TYPE;
82
88
  }
89
+ typeInformation.type = type;
90
+ if (staticAttrs) {
91
+ typeInformation.staticAttributes = staticAttrs;
92
+ }
93
+ typeInformation.id = measure.qs.i;
83
94
  iotAgentLib.update(
84
95
  type + ':' + measure.qs.i,
85
96
  type,
86
- '',
97
+ typeInformation,
87
98
  jsonToIotaMeasures(measure.json),
88
99
  function (error, result, body) {
89
100
  error ? reject(error) : resolve(result);
@@ -204,9 +215,9 @@ async function testCase(measure, expectation, provision, env, config, type, tran
204
215
  let cbMockRoute = '';
205
216
  // Set the correct route depending if the test is multientity or not
206
217
  if (type === 'multientity' || type === 'multimeasure') {
207
- cbMockRoute = '/v2/op/update';
218
+ cbMockRoute = '/v2/op/update?options=flowControl';
208
219
  } else {
209
- cbMockRoute = '/v2/entities?options=upsert';
220
+ cbMockRoute = '/v2/entities?options=upsert,flowControl';
210
221
  }
211
222
 
212
223
  // Set the correct mock times depending if the test is multimeasure or not
@@ -230,7 +241,7 @@ async function testCase(measure, expectation, provision, env, config, type, tran
230
241
  if (transport === 'MQTT') {
231
242
  try {
232
243
  let client = await MQTT.connectAsync('mqtt://' + config.mqtt.host);
233
- await client.publish('/' + measure.qs.k + '/' + measure.qs.i + '/attrs', JSON.stringify(measure.json));
244
+ await client.publish('/json/' + measure.qs.k + '/' + measure.qs.i + '/attrs', JSON.stringify(measure.json));
234
245
  await client.end();
235
246
  } catch (error) {
236
247
  expect.fail(ERR_MQTT + error);
@@ -6,6 +6,7 @@
6
6
  "entity_type": "SensorMachine",
7
7
  "trust": "8970A9078A803H3BL98PINEQRW8342HBAMS",
8
8
  "cbHost": "http://unexistentHost:1026",
9
+ "useCBflowControl": true,
9
10
  "commands": [
10
11
  {
11
12
  "name": "wheel1",