iotagent-node-lib 4.4.0 → 4.5.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/config.js CHANGED
@@ -76,7 +76,8 @@ var config = {
76
76
  subservice: '/gardens',
77
77
  providerUrl: 'http://192.168.56.1:4041',
78
78
  deviceRegistrationDuration: 'P1M',
79
- defaultType: 'Thing'
79
+ defaultType: 'Thing',
80
+ expressLimit: '1Mb'
80
81
  };
81
82
 
82
83
  module.exports = config;
package/doc/admin.md CHANGED
@@ -260,13 +260,13 @@ the `mongob` section (as described bellow). E.g.:
260
260
 
261
261
  It configures the MongoDB driver for those repositories with 'mongodb' type. If the `host` parameter is a list of
262
262
  comma-separated IPs, they will be considered to be part of a Replica Set. In that case, the optional property
263
- `replicaSet` should contain the Replica Set name. If the database requires authentication, username (`user`),
264
- password (`password`) and authSource (`authSource`) can be set. If the database requires TLS/SSL connection but any
265
- validation of the certificate chain is not mandatory, all you need is to set the ssl (`ssl`) option as `true` to connect
266
- the database. If you need to add more complex option(s) such as `retryWrites=true` or `w=majority` when connection
267
- database, extraArgs (`extraArgs`) can be used to perform it. For The MongoBD driver will retry the connection at startup
268
- time `retries` times, waiting `retryTime` seconds between attempts, if those attributes are present (default values are
269
- 5 and 5 respectively). E.g.:
263
+ `replicaSet` should contain the Replica Set name. If the database requires authentication, username (`user`), password
264
+ (`password`) and authSource (`authSource`) can be set. If the database requires TLS/SSL connection but any validation of
265
+ the certificate chain is not mandatory, all you need is to set the ssl (`ssl`) option as `true` to connect the database.
266
+ If you need to add more complex option(s) such as `retryWrites=true` or `w=majority` when connection database, extraArgs
267
+ (`extraArgs`) can be used to perform it. For The MongoBD driver will retry the connection at startup time `retries`
268
+ times, waiting `retryTime` seconds between attempts, if those attributes are present (default values are 5 and 5
269
+ respectively). E.g.:
270
270
 
271
271
  ```javascript
272
272
  {
@@ -421,6 +421,14 @@ characters (such as semi-colons) which are
421
421
  specification. When provisioning devices, it is necessary that the developer provides valid `objectId`-`name` mappings
422
422
  whenever relaxed mode is used, to prevent the consumption of forbidden characters.
423
423
 
424
+ #### `expressLimit`
425
+
426
+ IotAgents, as all Express applications that use the body-parser middleware, have a default limit to the request body
427
+ size that the application will handle. This default limit for ioiotagnets are 1Mb. So, if your IotAgent receives a
428
+ request with a body that exceeds this limit, the application will throw a “Error: Request entity too large”.
429
+
430
+ The 1Mb default can be changed setting the `expressLimit` configuration parameter (or equivalente `IOTA_EXPRESS_LIMIT` environment variable).
431
+
424
432
  ### Configuration using environment variables
425
433
 
426
434
  Some of the configuration parameters can be overriden with environment variables, to ease the use of those parameters
@@ -482,6 +490,7 @@ overrides.
482
490
  | IOTA_EXPLICIT_ATTRS | `explicitAttrs` |
483
491
  | IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION | `defaultEntityNameConjunction` |
484
492
  | IOTA_RELAX_TEMPLATE_VALIDATION | `relaxTemplateValidation` |
493
+ | IOTA_EXPRESS_LIMIT | `expressLimit` |
485
494
 
486
495
  Note:
487
496
 
package/doc/api.md CHANGED
@@ -11,7 +11,7 @@
11
11
  - [Uniqueness of groups and devices](#uniqueness-of-groups-and-devices)
12
12
  - [Special measures and attributes names](#special-measures-and-attributes-names)
13
13
  - [Entity attributes](#entity-attributes)
14
- - [Multientity support)](#multientity-support)
14
+ - [Multientity support](#multientity-support)
15
15
  - [Metadata support](#metadata-support)
16
16
  - [NGSI LD data and metadata considerations](#ngsi-ld-data-and-metadata-considerations)
17
17
  - [Advice on Attribute definitions](#advice-on-attribute-definitions)
@@ -32,6 +32,7 @@
32
32
  - [Measurement transformation order](#measurement-transformation-order)
33
33
  - [Multientity measurement transformation support (`object_id`)](#multientity-measurement-transformation-support-object_id)
34
34
  - [Timestamp Processing](#timestamp-processing)
35
+ - [Multimeasure support](#multimeasure-support)
35
36
  - [Overriding global Context Broker host](#overriding-global-context-broker-host)
36
37
  - [Multitenancy, FIWARE Service and FIWARE ServicePath](#multitenancy-fiware-service-and-fiware-servicepath)
37
38
  - [Secured access to the Context Broker](#secured-access-to-the-context-broker)
@@ -286,32 +287,37 @@ e.g.:
286
287
 
287
288
  ```json
288
289
  {
289
- "entity_type": "Lamp",
290
- "resource": "/iot/d",
291
- "protocol": "PDI-IoTA-UltraLight",
292
- ..etc
293
- "commands": [
294
- {"name": "on","type": "command"},
295
- {"name": "off","type": "command"}
296
- ],
297
- "attributes": [
298
- {"object_id": "s", "name": "state", "type":"Text"},
299
- {"object_id": "l", "name": "luminosity", "type":"Integer",
300
- "metadata":{
301
- "unitCode":{"type": "Text", "value" :"CAL"}
302
- }
290
+ "entity_type": "Lamp",
291
+ "resource": "/iot/d",
292
+ "protocol": "PDI-IoTA-UltraLight",
293
+ "commands": [
294
+ { "name": "on", "type": "command" },
295
+ { "name": "off", "type": "command" }
296
+ ],
297
+ "attributes": [
298
+ { "object_id": "s", "name": "state", "type": "Text" },
299
+ {
300
+ "object_id": "l",
301
+ "name": "luminosity",
302
+ "type": "Integer",
303
+ "metadata": {
304
+ "unitCode": { "type": "Text", "value": "CAL" }
305
+ }
303
306
  }
304
- ],
305
- "static_attributes": [
306
- {"name": "category", "type":"Text", "value": ["actuator","sensor"]},
307
- {"name": "controlledProperty", "type": "Text", "value": ["light"],
308
- "metadata":{
309
- "includes":{"type": "Text", "value" :["state", "luminosity"]},
310
- "alias":{"type": "Text", "value" :"lamp"}
307
+ ],
308
+ "static_attributes": [
309
+ { "name": "category", "type": "Text", "value": ["actuator", "sensor"] },
310
+ {
311
+ "name": "controlledProperty",
312
+ "type": "Text",
313
+ "value": ["light"],
314
+ "metadata": {
315
+ "includes": { "type": "Text", "value": ["state", "luminosity"] },
316
+ "alias": { "type": "Text", "value": "lamp" }
311
317
  }
312
- },
313
- ]
314
- }
318
+ }
319
+ ]
320
+ }
315
321
  ```
316
322
 
317
323
  Metadata could also has `expression` like attributes in order to expand it:
@@ -320,35 +326,37 @@ e.g.:
320
326
 
321
327
  ```json
322
328
  {
323
- "entity_type": "Lamp",
324
- "resource": "/iot/d",
325
- "protocol": "PDI-IoTA-UltraLight",
326
- ..etc
327
- "commands": [
328
- {"name": "on","type": "command"},
329
- {"name": "off","type": "command"}
330
- ],
331
- "attributes": [
332
- {"object_id": "s", "name": "state", "type":"Text"},
333
- {"object_id": "l", "name": "luminosity", "type":"Integer",
334
- "metadata":{
335
- "unitCode":{"type": "Text", "value" :"CAL"}
336
- }
329
+ "entity_type": "Lamp",
330
+ "resource": "/iot/d",
331
+ "protocol": "PDI-IoTA-UltraLight",
332
+ "commands": [
333
+ { "name": "on", "type": "command" },
334
+ { "name": "off", "type": "command" }
335
+ ],
336
+ "attributes": [
337
+ { "object_id": "s", "name": "state", "type": "Text" },
338
+ {
339
+ "object_id": "l",
340
+ "name": "luminosity",
341
+ "type": "Integer",
342
+ "metadata": {
343
+ "unitCode": { "type": "Text", "value": "CAL" }
344
+ }
337
345
  }
338
- ],
339
- "static_attributes": [
340
- {"name": "category", "type":"Text", "value": ["actuator","sensor"]},
341
- {"name": "controlledProperty", "type": "Text", "value": ["light"],
342
- "metadata":{
343
- "includes":{"type": "Text",
344
- "value" :["state", "luminosity"],
345
- "expression": "level / 100"
346
- },
347
- "alias":{"type": "Text", "value" :"lamp"}
346
+ ],
347
+ "static_attributes": [
348
+ { "name": "category", "type": "Text", "value": ["actuator", "sensor"] },
349
+ {
350
+ "name": "controlledProperty",
351
+ "type": "Text",
352
+ "value": ["light"],
353
+ "metadata": {
354
+ "includes": { "type": "Text", "value": ["state", "luminosity"], "expression": "level / 100" },
355
+ "alias": { "type": "Text", "value": "lamp" }
348
356
  }
349
- },
350
- ]
351
- }
357
+ }
358
+ ]
359
+ }
352
360
  ```
353
361
 
354
362
  ### NGSI-LD data and metadata considerations
@@ -458,6 +466,8 @@ mappings of the provision. If `explicitAttrs` is provided both at device and con
458
466
  precedence. Additionally `explicitAttrs` can be used to define which measures (identified by their attribute names, not
459
467
  by their object_id) defined in JSON/JEXL array will be propagated to NGSI interface.
460
468
 
469
+ Note that when `explicitAttrs` is an array or a JEXL expression resulting in to Array, if this array is empty then `TimeInstant` is not propaged to CB.
470
+
461
471
  The different possibilities are summarized below:
462
472
 
463
473
  Case 1 (default):
@@ -1044,6 +1054,127 @@ Some additional considerations to take into account:
1044
1054
  measure of after a mapping, as described in the previous bullet) then it is refused (so a failover to server
1045
1055
  timestamp will take place).
1046
1056
 
1057
+ ## Multimeasure support
1058
+
1059
+ A device could receive several measures at the same time.
1060
+
1061
+ For example:
1062
+
1063
+ ```json
1064
+ [
1065
+ {
1066
+ "vol": 0
1067
+ },
1068
+ {
1069
+ "vol": 1
1070
+ },
1071
+ {
1072
+ "vol": 2
1073
+ }
1074
+ ]
1075
+ ```
1076
+
1077
+ In this case a batch update (`POST /v2/op/update`) to CB will be generated with the following NGSI v2 payload:
1078
+
1079
+ ```json
1080
+ {
1081
+ "actionType": "append",
1082
+ "entities": [
1083
+ {
1084
+ "id": "ws",
1085
+ "type": "WeatherStation",
1086
+ "vol": {
1087
+ "type": "Number",
1088
+ "value": 0
1089
+ }
1090
+ },
1091
+ {
1092
+ "id": "ws",
1093
+ "type": "WeatherStation",
1094
+ "vol": {
1095
+ "type": "Number",
1096
+ "value": 1
1097
+ }
1098
+ },
1099
+ {
1100
+ "id": "ws",
1101
+ "type": "WeatherStation",
1102
+ "vol": {
1103
+ "type": "Number",
1104
+ "value": 1
1105
+ }
1106
+ }
1107
+ ]
1108
+ }
1109
+ ```
1110
+
1111
+ Moreover if a multimeasure contains TimeInstant attribute, then CB update is sorted by attribute TimeInstant:
1112
+
1113
+ For example:
1114
+
1115
+ ```json
1116
+ [
1117
+ {
1118
+ "vol": 0,
1119
+ "TimeInstant": "2024-04-10T10:15:00Z"
1120
+ },
1121
+ {
1122
+ "vol": 1,
1123
+ "TimeInstant": "2024-04-10T10:10:00Z"
1124
+ },
1125
+ {
1126
+ "vol": 2,
1127
+ "TimeInstant": "2024-04-10T10:05:00Z"
1128
+ }
1129
+ ]
1130
+ ```
1131
+
1132
+ In this case a batch update (`POST /v2/op/update`) to CB will be generated with the following NGSI v2 payload:
1133
+
1134
+ ```json
1135
+ {
1136
+ "actionType": "append",
1137
+ "entities": [
1138
+ {
1139
+ "id": "ws",
1140
+ "type": "WeatherStation",
1141
+ "vol": {
1142
+ "type": "Number",
1143
+ "value": 2
1144
+ },
1145
+ "TimeInstant": {
1146
+ "type": "DateTime",
1147
+ "value": "2024-04-10T10:05:00Z"
1148
+ }
1149
+ },
1150
+ {
1151
+ "id": "ws",
1152
+ "type": "WeatherStation",
1153
+ "vol": {
1154
+ "type": "Number",
1155
+ "value": 1
1156
+ },
1157
+ "TimeInstant": {
1158
+ "type": "DateTime",
1159
+ "value": "2024-04-10T10:10:00Z"
1160
+ }
1161
+ },
1162
+ {
1163
+ "id": "ws",
1164
+ "type": "WeatherStation",
1165
+ "vol": {
1166
+ "type": "Number",
1167
+ "value": 0
1168
+ },
1169
+ "TimeInstant": {
1170
+ "type": "DateTime",
1171
+ "value": "2024-04-10T10:15:00Z"
1172
+ }
1173
+ }
1174
+ ]
1175
+ }
1176
+ ```
1177
+
1047
1178
  ## Overriding global Context Broker host
1048
1179
 
1049
1180
  **cbHost**: Context Broker host URL. This option can be used to override the global CB configuration for specific types
@@ -1,4 +1,4 @@
1
1
  mkdocs==1.2.4
2
2
  Pygments==2.15.0
3
3
  Markdown==3.3.4
4
- jinja2==3.1.3
4
+ jinja2==3.1.4
@@ -156,7 +156,8 @@ function processEnvironmentVariables() {
156
156
  'IOTA_FALLBACK_TENANT',
157
157
  'IOTA_FALLBACK_PATH',
158
158
  'IOTA_LD_SUPPORT_NULL',
159
- 'IOTA_LD_SUPPORT_DATASET_ID'
159
+ 'IOTA_LD_SUPPORT_DATASET_ID',
160
+ 'IOTA_EXPRESS_LIMIT'
160
161
  ];
161
162
  const iotamVariables = [
162
163
  'IOTA_IOTAM_URL',
@@ -468,6 +469,11 @@ function processEnvironmentVariables() {
468
469
  ? config.defaultEntityNameConjunction
469
470
  : ':';
470
471
  }
472
+ if (process.env.IOTA_EXPRESS_LIMIT) {
473
+ config.expressLimit = process.env.IOTA_EXPRESS_LIMIT;
474
+ } else {
475
+ config.expressLimit = config.expressLimit ? config.expressLimit : '1mb';
476
+ }
471
477
  }
472
478
 
473
479
  function setConfig(newConfig) {
@@ -176,7 +176,7 @@ function getByName(name, service, subservice, callback) {
176
176
  getByNameAndType(name, null, service, subservice, callback);
177
177
  }
178
178
 
179
- function update(device, callback) {
179
+ function update(previousDevice, device, callback) {
180
180
  registeredDevices[device.service][device.id] = deepClone(device);
181
181
  callback(null, device);
182
182
  }
@@ -290,9 +290,9 @@ function getByName(name, service, servicepath, callback) {
290
290
  *
291
291
  * @param {Object} device Device object with the new values to write.
292
292
  */
293
- function update(device, callback) {
293
+ function update(previousDevice, device, callback) {
294
294
  logger.debug(context, 'Storing updated values for device [%s]:\n%s', device.id, JSON.stringify(device, null, 4));
295
- getDevice(device.id, device.apikey, device.service, device.subservice, function (error, data) {
295
+ getDevice(device.id, previousDevice.apikey, device.service, device.subservice, function (error, data) {
296
296
  if (error) {
297
297
  callback(error);
298
298
  } else {
@@ -444,8 +444,8 @@ function unregisterDevice(id, apikey, service, subservice, callback) {
444
444
  });
445
445
  }
446
446
 
447
- function updateRegisterDevice(deviceObj, entityInfoUpdated, callback) {
448
- deviceHandler.updateRegisterDevice(deviceObj, entityInfoUpdated, callback);
447
+ function updateRegisterDevice(deviceObj, previousDevice, entityInfoUpdated, callback) {
448
+ deviceHandler.updateRegisterDevice(deviceObj, previousDevice, entityInfoUpdated, callback);
449
449
  }
450
450
 
451
451
  /**
@@ -518,7 +518,8 @@ function getDevice(deviceId, apikey, service, subservice, callback) {
518
518
  * @param {String} device JSON object contain the device to update.
519
519
  */
520
520
  function updateDevice(device, callback) {
521
- config.getRegistry().update(device, callback);
521
+ logger.debug(context, 'updateDevice %j', device);
522
+ config.getRegistry().update(device, device, callback);
522
523
  }
523
524
 
524
525
  /**
@@ -182,7 +182,7 @@ function updateEntityNgsiLD(deviceData, updatedDevice, callback) {
182
182
  *
183
183
  * @param {Object} deviceObj Object with all the device information (mandatory).
184
184
  */
185
- function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) {
185
+ function updateRegisterDeviceNgsiLD(deviceObj, previousDevice, entityInfoUpdated, callback) {
186
186
  if (!deviceObj.id || !deviceObj.type) {
187
187
  callback(new errors.MissingAttributes('Id or device missing', deviceObj));
188
188
  return;
@@ -280,7 +280,7 @@ function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) {
280
280
  apply(
281
281
  config.getRegistry().get,
282
282
  deviceObj.id,
283
- deviceObj.apikey,
283
+ previousDevice.apikey,
284
284
  deviceObj.service,
285
285
  deviceObj.subservice
286
286
  ),
@@ -289,7 +289,7 @@ function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) {
289
289
  apply(combineWithNewDevice, deviceObj),
290
290
  apply(registrationUtils.sendRegistrations, false),
291
291
  apply(registrationUtils.processContextRegistration, deviceObj),
292
- config.getRegistry().update
292
+ apply(config.getRegistry().update, previousDevice)
293
293
  ],
294
294
  callback
295
295
  );
@@ -299,7 +299,7 @@ function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) {
299
299
  apply(
300
300
  config.getRegistry().get,
301
301
  deviceObj.id,
302
- deviceObj.apikey,
302
+ previousDevice.apikey,
303
303
  deviceObj.service,
304
304
  deviceObj.subservice
305
305
  ),
@@ -308,7 +308,7 @@ function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) {
308
308
  apply(combineWithNewDevice, deviceObj),
309
309
  apply(registrationUtils.sendRegistrations, false),
310
310
  apply(registrationUtils.processContextRegistration, deviceObj),
311
- config.getRegistry().update
311
+ apply(config.getRegistry().update, previousDevice)
312
312
  ],
313
313
  callback
314
314
  );
@@ -38,11 +38,11 @@ const deviceHandlerV2 = require('./devices-NGSI-v2');
38
38
  *
39
39
  * @param {Object} deviceObj Object with all the device information (mandatory).
40
40
  */
41
- function updateRegisterDeviceNgsiMixed(deviceObj, entityInfoUpdated, callback) {
41
+ function updateRegisterDeviceNgsiMixed(deviceObj, previousDevice, entityInfoUpdated, callback) {
42
42
  if (config.checkNgsiLD(deviceObj)) {
43
- deviceHandlerLD.updateRegisterDevice(deviceObj, entityInfoUpdated, callback);
43
+ deviceHandlerLD.updateRegisterDevice(deviceObj, previousDevice, entityInfoUpdated, callback);
44
44
  } else {
45
- deviceHandlerV2.updateRegisterDevice(deviceObj, entityInfoUpdated, callback);
45
+ deviceHandlerV2.updateRegisterDevice(deviceObj, previousDevice, entityInfoUpdated, callback);
46
46
  }
47
47
  }
48
48
 
@@ -247,7 +247,7 @@ function updateEntityNgsi2(deviceData, updatedDevice, callback) {
247
247
  *
248
248
  * @param {Object} deviceObj Object with all the device information (mandatory).
249
249
  */
250
- function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) {
250
+ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated, callback) {
251
251
  if (!deviceObj.id || !deviceObj.type) {
252
252
  callback(new errors.MissingAttributes('Id or device missing', deviceObj));
253
253
  return;
@@ -352,7 +352,7 @@ function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) {
352
352
  apply(
353
353
  config.getRegistry().get,
354
354
  deviceObj.id,
355
- deviceObj.apikey,
355
+ previousDevice.apikey, // it could be updated
356
356
  deviceObj.service,
357
357
  deviceObj.subservice
358
358
  ),
@@ -361,7 +361,7 @@ function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) {
361
361
  apply(combineWithNewDevice, deviceObj),
362
362
  apply(registrationUtils.sendRegistrations, false),
363
363
  apply(registrationUtils.processContextRegistration, deviceObj),
364
- config.getRegistry().update
364
+ apply(config.getRegistry().update, previousDevice)
365
365
  ],
366
366
  callback
367
367
  );
@@ -371,7 +371,7 @@ function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) {
371
371
  apply(
372
372
  config.getRegistry().get,
373
373
  deviceObj.id,
374
- deviceObj.apikey,
374
+ previousDevice.apikey,
375
375
  deviceObj.service,
376
376
  deviceObj.subservice
377
377
  ),
@@ -380,7 +380,7 @@ function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) {
380
380
  apply(combineWithNewDevice, deviceObj),
381
381
  apply(registrationUtils.sendRegistrations, false),
382
382
  apply(registrationUtils.processContextRegistration, deviceObj),
383
- config.getRegistry().update
383
+ apply(config.getRegistry().update, previousDevice)
384
384
  ],
385
385
  callback
386
386
  );
@@ -341,7 +341,7 @@ function generateNGSILDOperationHandler(operationName, entityName, typeInformati
341
341
  } else if (
342
342
  response &&
343
343
  operationName === 'update' &&
344
- (response.statusCode === 200 || response.statusCode === 204)
344
+ (response.statusCode === 200 || response.statusCode === 204 || response.statusCode === 201)
345
345
  ) {
346
346
  logger.info(context, 'Received the following response from the CB: Value updated successfully\n');
347
347
  alarms.release(constants.ORION_ALARM);