iotagent-node-lib 4.3.0 → 4.4.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/doc/api.md CHANGED
@@ -8,6 +8,7 @@
8
8
  - [IoT Agent information model](#iot-agent-information-model)
9
9
  - [Config groups](#config-groups)
10
10
  - [Devices](#devices)
11
+ - [Uniqueness of groups and devices](#uniqueness-of-groups-and-devices)
11
12
  - [Special measures and attributes names](#special-measures-and-attributes-names)
12
13
  - [Entity attributes](#entity-attributes)
13
14
  - [Multientity support)](#multientity-support)
@@ -133,9 +134,9 @@ parameters. The specific parameters that can be configured for a given config gr
133
134
  ### Devices
134
135
 
135
136
  A device contains the information that connects a physical device to a particular entity in the Context Broker. Devices
136
- are identified by a `device_id`, and they are associated to an existing config group based in `apiKey` matching or
137
- `type` matching (in the case `apiKey` matching fails). For instance, let's consider a situation in which a config group
138
- has been provisioned with `type=X`/`apiKey=111` and no other config group has been provisioned.
137
+ are identified by a `device_id`, and they are associated to an existing config group based in `apikey` matching. For
138
+ instance, let's consider a situation in which a config group has been provisioned with `type=X`/`apikey=111` and no
139
+ other config group has been provisioned.
139
140
 
140
141
  The IoT Agents offer a provisioning API where devices can be preregistered, so all the information about service and
141
142
  subservice mapping, security information and attribute configuration can be specified in a per device way instead of
@@ -143,7 +144,7 @@ relaying on the config group configuration. The specific parameters that can be
143
144
  described in the [Device datamodel](#device-datamodel) section.
144
145
 
145
146
  If devices are not pre-registered, they will be automatically created when a measure arrives to the IoT Agent - this
146
- process is known as autoprovisioning. The IoT Agent will create an empty device with the group `apiKey` and `type` - the
147
+ process is known as autoprovisioning. The IoT Agent will create an empty device with the group `apikey` and `type` - the
147
148
  associated document created in database doesn't include config group parameters (in particular, `timestamp`,
148
149
  `explicitAttrs`, `active` or `attributes`, `static` and `lazy` attributes and commands). The IoT Agent will also create
149
150
  the entity in the Context Broker if it does not exist yet.
@@ -152,6 +153,13 @@ This behavior allows that autoprovisioned parameters can freely established modi
152
153
  creation using the provisioning API. However, note that if a device (autoprovisioned or not) doesn't have these
153
154
  parameters defined at device level in database, the parameters are inherit from config group parameters.
154
155
 
156
+ ### Uniqueness of groups and devices
157
+
158
+ Group service uniqueness is defined by the combination of: service, subservice and apikey
159
+
160
+ Device uniqueness is defined by the combination of: service, subservice, device_id and apikey. Note that several devices
161
+ with the same device_id are allowed in the same service and subservice as long as their apikeys are different.
162
+
155
163
  ## Special measures and attributes names
156
164
 
157
165
  In case of arriving measures with name `id` or `type`, they are automatically transformed to `measure_id` and
@@ -306,6 +314,43 @@ e.g.:
306
314
  }
307
315
  ```
308
316
 
317
+ Metadata could also has `expression` like attributes in order to expand it:
318
+
319
+ e.g.:
320
+
321
+ ```json
322
+ {
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
+ }
337
+ }
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"}
348
+ }
349
+ },
350
+ ]
351
+ }
352
+ ```
353
+
309
354
  ### NGSI-LD data and metadata considerations
310
355
 
311
356
  When provisioning devices for an NGSI-LD Context Broker, `type` values should typically correspond to one of the
@@ -517,8 +562,8 @@ expression. In all cases the following data is available to all expressions:
517
562
  - `subservice`: device subservice
518
563
  - `staticAttributes`: static attributes defined in the device or config group
519
564
 
520
- Additionally, for attribute expressions (`expression`, `entity_name`) and `entityNameExp` measures are avaiable in the
521
- **context** used to evaluate them.
565
+ Additionally, for attribute expressions (`expression`, `entity_name`), `entityNameExp` and metadata expressions
566
+ (`expression`) measures are available in the **context** used to evaluate them.
522
567
 
523
568
  ### Examples of JEXL expressions
524
569
 
@@ -964,32 +1009,41 @@ Will now generate the following NGSI v2 payload:
964
1009
 
965
1010
  ## Timestamp Processing
966
1011
 
967
- The IOTA processes the entity attributes looking for a `TimeInstant` attribute. If one is found, for NGSI v2, then it
968
- adds a `TimeInstant` attribute as metadata for every other attribute in the same request. With NGSI-LD, the Standard
969
- `observedAt` property-of-a-property is used instead.
1012
+ Timestamp processing done by IOTA is as follows:
970
1013
 
971
- If a `TimeInstant` arrives as measure but not follows [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) then measure is
972
- refused.
1014
+ - An attribute `TimeInstant` is added to updated entities
1015
+ - In the case of NGSI-v2, a `TimeInstant` metadata is added in each updated attribute. With NGSI-LD, the Standard
1016
+ `observedAt` property-of-a-property is used instead.
973
1017
 
974
1018
  Depending on the `timestamp` configuration and if the measure contains a value named `TimeInstant` with a correct value,
975
1019
  the IoTA behaviour is described in the following table:
976
1020
 
977
- | `timestamp` value | measure contains `TimeInstant` | Behaviour |
978
- | ----------------- | ------------------------------ | ------------------------------------------------------ |
979
- | true | Yes | TimeInstant and metadata updated with measure value |
980
- | true | No | TimeInstant and metadata updated with server timestamp |
981
- | false | Yes | TimeInstant and metadata updated with measure value |
982
- | false | No | TimeInstant and metadata updated with server timestamp |
983
- | Not defined | Yes | TimeInstant and metadata updated with measure value |
984
- | Not defined | No | TimeInstant and metadata updated with server timestamp |
1021
+ | `timestamp` conf value | measure contains `TimeInstant` | Behaviour |
1022
+ | ---------------------- | ------------------------------ | ------------------------------------------------------ |
1023
+ | true | Yes | TimeInstant and metadata updated with measure value |
1024
+ | true | No | TimeInstant and metadata updated with server timestamp |
1025
+ | false | Yes | TimeInstant and metadata updated with measure value |
1026
+ | false | No | TimeInstant and metadata updated with server timestamp |
1027
+ | Not defined | Yes | TimeInstant and metadata updated with measure value |
1028
+ | Not defined | No | TimeInstant and metadata updated with server timestamp |
985
1029
 
986
- The `timestamp` value used is:
1030
+ The `timestamp` conf value used is:
987
1031
 
988
1032
  - The one defined at device level
989
1033
  - The one defined at group level (if not defined at device level)
990
1034
  - The one defined at [IoTA configuration level](admin.md#timestamp) / `IOTA_TIMESTAMP` env var (if not defined at
991
1035
  group level or device level)
992
1036
 
1037
+ Some additional considerations to take into account:
1038
+
1039
+ - If there is an attribute which maps a measure to `TimeInstant` attribute (after
1040
+ [expression evaluation](#expression-language-support) if any is defined), then that value will be used as
1041
+ `TimeInstant, overwriting the above rules specified in "Behaviour" column. Note that an expression in the could be
1042
+ used in that mapping.
1043
+ - If the resulting `TimeInstant` not follows [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) (either from a direct
1044
+ measure of after a mapping, as described in the previous bullet) then it is refused (so a failover to server
1045
+ timestamp will take place).
1046
+
993
1047
  ## Overriding global Context Broker host
994
1048
 
995
1049
  **cbHost**: Context Broker host URL. This option can be used to override the global CB configuration for specific types
@@ -1,4 +1,4 @@
1
- ARG IMAGE_TAG=12.4-slim
1
+ ARG IMAGE_TAG=12.5-slim
2
2
  FROM debian:${IMAGE_TAG}
3
3
 
4
4
  ARG CLEAN_DEV_TOOLS
@@ -485,6 +485,23 @@ function getConfig() {
485
485
  return config;
486
486
  }
487
487
 
488
+ function getConfigForTypeInformation() {
489
+ // Just return relevant configuration flags
490
+ // avoid to include server, authentication, mongodb, orion and iotamanger info
491
+ let conf = {
492
+ timestamp: config.timestamp,
493
+ defaultResource: config.defaultResource,
494
+ explicitAttrs: config.explicitAttrs,
495
+ pollingExpiration: config.pollingExpiration,
496
+ pollingDaemonFrequency: config.pollingDaemonFrequency,
497
+ multiCore: config.multiCore,
498
+ relaxTemplateValidation: config.relaxTemplateValidation,
499
+ defaultEntityNameConjunction: config.defaultEntityNameConjunction,
500
+ defaultType: config.defaultType
501
+ };
502
+ return conf;
503
+ }
504
+
488
505
  function setRegistry(newRegistry) {
489
506
  registry = newRegistry;
490
507
  }
@@ -541,6 +558,7 @@ function getSecurityService() {
541
558
 
542
559
  exports.setConfig = setConfig;
543
560
  exports.getConfig = getConfig;
561
+ exports.getConfigForTypeInformation = getConfigForTypeInformation;
544
562
  exports.setRegistry = setRegistry;
545
563
  exports.getRegistry = getRegistry;
546
564
  exports.setGroupRegistry = setGroupRegistry;
package/lib/errors.js CHANGED
@@ -107,7 +107,10 @@ class DuplicateDeviceId {
107
107
  constructor(device) {
108
108
  this.name = 'DUPLICATE_DEVICE_ID';
109
109
  this.message =
110
- 'A device with the same pair (Service, DeviceId) was found:' + device.id + ' and ' + JSON.stringify(device);
110
+ 'A device with the same info (DeviceId, ApiKey, Service, Subservice) was found:' +
111
+ device.id +
112
+ ' and ' +
113
+ JSON.stringify(device);
111
114
  this.code = 409;
112
115
  }
113
116
  }
@@ -243,14 +243,7 @@ function getDeviceById(id, apikey, service, subservice, callback) {
243
243
  function getDevice(id, apikey, service, subservice, callback) {
244
244
  getDeviceById(id, apikey, service, subservice, function (error, data) {
245
245
  if (error) {
246
- // Try without apikey: apikey will be added to device later
247
- getDeviceById(id, null, service, subservice, function (error, data) {
248
- if (error) {
249
- callback(error);
250
- } else {
251
- callback(null, data);
252
- }
253
- });
246
+ callback(error);
254
247
  } else {
255
248
  callback(null, data);
256
249
  }
@@ -615,6 +615,7 @@ function findOrCreate(deviceId, apikey, group, callback) {
615
615
  } else if (error.name === 'DEVICE_NOT_FOUND') {
616
616
  const newDevice = {
617
617
  id: deviceId,
618
+ apikey: apikey,
618
619
  service: group.service,
619
620
  subservice: group.subservice,
620
621
  type: group.type
@@ -277,7 +277,6 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
277
277
  let entities = {}; //{entityName:{entityType:[attrs]}} //SubGoal Populate entoties data striucture
278
278
  let jexlctxt = {}; //will store the whole context (not just for JEXL)
279
279
  let payload = {}; //will store the final payload
280
- let timestamp = { type: constants.TIMESTAMP_TYPE_NGSI2 }; //timestamp scafold-attr for insertions.
281
280
  let plainMeasures = null; //will contain measures POJO
282
281
  let idTypeSSSList = pluginUtils.getIdTypeServSubServiceFromDevice(typeInformation);
283
282
 
@@ -308,43 +307,16 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
308
307
  jexlctxt = reduceAttrToPlainObject(idTypeSSSList, jexlctxt);
309
308
 
310
309
  //Managing timestamp (mustInsertTimeInstant flag to decide if we should insert Timestamp later on)
311
- const mustInsertTimeInstant =
312
- typeInformation.timestamp !== undefined
313
- ? typeInformation.timestamp
314
- : false;
315
-
316
- if (mustInsertTimeInstant) {
317
- //remove TimeInstant from measures
318
- measures = measures.filter((item) => item.name !== constants.TIMESTAMP_ATTRIBUTE);
319
-
320
- if (plainMeasures[constants.TIMESTAMP_ATTRIBUTE]) {
321
- //if it comes from a measure
322
- if (moment(plainMeasures[constants.TIMESTAMP_ATTRIBUTE], moment.ISO_8601, true).isValid()) {
323
- timestamp.value = plainMeasures[constants.TIMESTAMP_ATTRIBUTE];
324
- } else {
325
- callback(
326
- new errors.BadTimestamp(plainMeasures[constants.TIMESTAMP_ATTRIBUTE], entityName, typeInformation)
327
- );
328
- return;
329
- }
330
- } else if (!typeInformation.timezone) {
331
- timestamp.value = new Date().toISOString();
332
- jexlctxt[constants.TIMESTAMP_ATTRIBUTE] = timestamp.value;
333
- } else {
334
- timestamp.value = moment().tz(typeInformation.timezone).format('YYYY-MM-DD[T]HH:mm:ss.SSSZ');
335
- jexlctxt[constants.TIMESTAMP_ATTRIBUTE] = timestamp.value;
336
- }
337
- }
310
+ const mustInsertTimeInstant = typeInformation.timestamp !== undefined ? typeInformation.timestamp : false;
338
311
 
339
312
  logger.debug(
340
313
  context,
341
- 'sendUpdateValueNgsi2 called with: entityName=%s, measures=%j, typeInformation=%j, initial jexlContext=%j, timestamp=%j with value=%j',
314
+ 'sendUpdateValueNgsi2 called with: entityName=%s, measures=%j, typeInformation=%j, initial jexlContext=%j, timestamp=%j',
342
315
  entityName,
343
316
  plainMeasures,
344
317
  typeInformation,
345
318
  jexlctxt,
346
- mustInsertTimeInstant,
347
- timestamp.value
319
+ mustInsertTimeInstant
348
320
  );
349
321
 
350
322
  //Now we can calculate the EntityName of primary entity
@@ -479,14 +451,6 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
479
451
  currentAttr.hitted = hitted;
480
452
  currentAttr.value = valueExpression;
481
453
 
482
- //add TimeInstant to attr metadata
483
- if (mustInsertTimeInstant) {
484
- if (!currentAttr.metadata) {
485
- currentAttr.metadata = {};
486
- }
487
- currentAttr.metadata[constants.TIMESTAMP_ATTRIBUTE] = timestamp;
488
- }
489
-
490
454
  //store de New Attributte in entity data structure
491
455
  if (hitted === true) {
492
456
  if (entities[attrEntityName] === undefined) {
@@ -501,6 +465,38 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
501
465
 
502
466
  //RE-Populate de JEXLcontext (except for null or NaN we preffer undefined)
503
467
  jexlctxt[currentAttr.name] = valueExpression;
468
+
469
+ // Expand metadata value expression
470
+ if (currentAttr.metadata) {
471
+ for (var metaKey in currentAttr.metadata) {
472
+ if (currentAttr.metadata[metaKey].expression && metaKey !== constants.TIMESTAMP_ATTRIBUTE) {
473
+ let newAttrMeta = {};
474
+ if (currentAttr.metadata[metaKey].type) {
475
+ newAttrMeta['type'] = currentAttr.metadata[metaKey].type;
476
+ }
477
+ let metaValueExpression;
478
+ try {
479
+ metaValueExpression = jexlParser.applyExpression(
480
+ currentAttr.metadata[metaKey].expression,
481
+ jexlctxt,
482
+ typeInformation
483
+ );
484
+ //we fallback to null if anything unexpecte happend
485
+ if (
486
+ metaValueExpression === null ||
487
+ metaValueExpression === undefined ||
488
+ Number.isNaN(metaValueExpression)
489
+ ) {
490
+ metaValueExpression = null;
491
+ }
492
+ } catch (e) {
493
+ metaValueExpression = null;
494
+ }
495
+ newAttrMeta['value'] = metaValueExpression;
496
+ currentAttr.metadata[metaKey] = newAttrMeta;
497
+ }
498
+ }
499
+ }
504
500
  }
505
501
 
506
502
  //now we can compute explicit (Bool or Array) with the complete JexlContext
@@ -527,16 +523,6 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
527
523
 
528
524
  //more mesures may be added to the attribute list (unnhandled/left mesaures) l
529
525
  if (explicit === false && Object.keys(measures).length > 0) {
530
- //add Timestamp to measures if needed
531
- if (mustInsertTimeInstant) {
532
- for (let currentMeasure of measures) {
533
- if (!currentMeasure.metadata) {
534
- currentMeasure.metadata = {};
535
- }
536
- currentMeasure.metadata[constants.TIMESTAMP_ATTRIBUTE] = timestamp;
537
- }
538
- //If just measures in the principal entity we missed the Timestamp.
539
- }
540
526
  entities[entityName][typeInformation.type] = entities[entityName][typeInformation.type].concat(measures);
541
527
  }
542
528
 
@@ -547,11 +533,40 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
547
533
  payload.actionType = 'append';
548
534
 
549
535
  payload.entities = [];
536
+ const currentIsoDate = new Date().toISOString();
537
+ const currentMoment = moment(currentIsoDate);
550
538
  for (let ename in entities) {
551
539
  for (let etype in entities[ename]) {
552
540
  let e = {};
553
541
  e.id = String(ename);
554
542
  e.type = String(etype);
543
+ let timestamp = { type: constants.TIMESTAMP_TYPE_NGSI2 }; //timestamp scafold-attr for insertions.
544
+ let timestampAttrs = null;
545
+ if (mustInsertTimeInstant) {
546
+ // get timestamp for current entity
547
+
548
+ timestampAttrs = entities[ename][etype].filter((item) => item.name === constants.TIMESTAMP_ATTRIBUTE);
549
+ if (timestampAttrs && timestampAttrs.length > 0) {
550
+ timestamp.value = timestampAttrs[0]['value'];
551
+ }
552
+
553
+ if (timestamp.value) {
554
+ if (!moment(timestamp.value, moment.ISO_8601, true).isValid()) {
555
+ callback(new errors.BadTimestamp(timestamp.value, entityName, typeInformation));
556
+ return;
557
+ }
558
+ } else {
559
+ if (!typeInformation.timezone) {
560
+ timestamp.value = currentIsoDate;
561
+ jexlctxt[constants.TIMESTAMP_ATTRIBUTE] = timestamp.value;
562
+ } else {
563
+ timestamp.value = currentMoment
564
+ .tz(typeInformation.timezone)
565
+ .format('YYYY-MM-DD[T]HH:mm:ss.SSSZ');
566
+ jexlctxt[constants.TIMESTAMP_ATTRIBUTE] = timestamp.value;
567
+ }
568
+ }
569
+ }
555
570
  //extract attributes
556
571
  let isEmpty = true;
557
572
  for (let attr of entities[ename][etype]) {
@@ -568,6 +583,15 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
568
583
  ))))
569
584
  ) {
570
585
  isEmpty = false;
586
+ if (mustInsertTimeInstant) {
587
+ // Add TimeInstant to all attribute metadata of all entities
588
+ if (attr.name !== constants.TIMESTAMP_ATTRIBUTE) {
589
+ if (!attr.metadata) {
590
+ attr.metadata = {};
591
+ }
592
+ attr.metadata[constants.TIMESTAMP_ATTRIBUTE] = timestamp;
593
+ }
594
+ }
571
595
  e[attr.name] = { type: attr.type, value: attr.value, metadata: attr.metadata };
572
596
  }
573
597
  }
@@ -144,9 +144,9 @@ function executeWithDeviceInformation(operationFunction) {
144
144
  // For preregistered devices, augment the existing deviceInformation with selected attributes.
145
145
  if (!callback) {
146
146
  callback = deviceInformation;
147
- typeInformation = deviceGroup || { ...config.getConfig(), ...configDeviceInfo };
147
+ typeInformation = deviceGroup || { ...config.getConfigForTypeInformation(), ...configDeviceInfo };
148
148
  } else {
149
- typeInformation = { ...config.getConfig(), ...deviceInformation };
149
+ typeInformation = { ...config.getConfigForTypeInformation(), ...deviceInformation };
150
150
  attributeList.forEach((key) => {
151
151
  typeInformation[key] =
152
152
  typeInformation[key] || (deviceGroup || {})[key] || (configDeviceInfo || {})[key];
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.3.0",
5
+ "version": "4.4.0",
6
6
  "homepage": "https://github.com/telefonicaid/iotagent-node-lib",
7
7
  "keywords": [
8
8
  "fiware",
@@ -45,7 +45,7 @@
45
45
  "dependencies": {
46
46
  "async": "2.6.4",
47
47
  "body-parser": "~1.20.0",
48
- "express": "~4.18.1",
48
+ "express": "~4.19.2",
49
49
  "got": "~11.8.5",
50
50
  "jexl": "2.3.0",
51
51
  "jison": "0.4.18",
@@ -1,3 +1,3 @@
1
1
  matplotlib==3.7.1
2
2
  pandas==2.0.2
3
- pymongo==4.3.3
3
+ pymongo==4.6.3
@@ -445,6 +445,74 @@ const testCases = [
445
445
  }
446
446
  ]
447
447
  },
448
+ {
449
+ describeName: '0021 Simple group with active attributes with metadata',
450
+ provision: {
451
+ url: 'http://localhost:' + config.iota.server.port + '/iot/services',
452
+ method: 'POST',
453
+ json: {
454
+ services: [
455
+ {
456
+ resource: '/iot/json',
457
+ apikey: globalEnv.apikey,
458
+ entity_type: globalEnv.entity_type,
459
+ commands: [],
460
+ lazy: [],
461
+ attributes: [
462
+ {
463
+ object_id: 'a',
464
+ name: 'attr_a',
465
+ type: 'Boolean',
466
+ metadata: {
467
+ accuracy: {
468
+ value: 0.8,
469
+ type: 'Float'
470
+ }
471
+ }
472
+ }
473
+ ],
474
+ static_attributes: []
475
+ }
476
+ ]
477
+ },
478
+ headers: {
479
+ 'fiware-service': globalEnv.service,
480
+ 'fiware-servicepath': globalEnv.servicePath
481
+ }
482
+ },
483
+ should: [
484
+ {
485
+ shouldName:
486
+ 'A - WHEN sending defined object_ids (measures) through http IT should send measures to Context Broker preserving value types, name mappings and metadatas',
487
+ type: 'single',
488
+ measure: {
489
+ url: 'http://localhost:' + config.http.port + '/iot/json',
490
+ method: 'POST',
491
+ qs: {
492
+ i: globalEnv.deviceId,
493
+ k: globalEnv.apikey
494
+ },
495
+ json: {
496
+ a: false
497
+ }
498
+ },
499
+ expectation: {
500
+ id: globalEnv.entity_name,
501
+ type: globalEnv.entity_type,
502
+ attr_a: {
503
+ value: false,
504
+ type: 'Boolean',
505
+ metadata: {
506
+ accuracy: {
507
+ value: 0.8,
508
+ type: 'Float'
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+ ]
515
+ },
448
516
  // 0100 - JEXL TESTS
449
517
  {
450
518
  describeName: '0100 - Simple group with active attribute + JEXL expression boolean (!)',
@@ -1036,7 +1104,8 @@ const testCases = [
1036
1104
  ]
1037
1105
  },
1038
1106
  {
1039
- describeName: '0140 - Simple group with active attribute + chained JEXL expression text (a|substr(0,2))',
1107
+ describeName:
1108
+ '0140 - Simple group with active attribute + chained JEXL expression text (a | trim | replacestr("hello","hi"))',
1040
1109
  provision: {
1041
1110
  url: 'http://localhost:' + config.iota.server.port + '/iot/services',
1042
1111
  method: 'POST',
@@ -1476,6 +1545,75 @@ const testCases = [
1476
1545
  }
1477
1546
  ]
1478
1547
  },
1548
+ {
1549
+ describeName: '0191 - Simple group with JEXL expression in metadata',
1550
+ provision: {
1551
+ url: 'http://localhost:' + config.iota.server.port + '/iot/services',
1552
+ method: 'POST',
1553
+ json: {
1554
+ services: [
1555
+ {
1556
+ resource: '/iot/json',
1557
+ apikey: globalEnv.apikey,
1558
+ entity_type: globalEnv.entity_type,
1559
+ commands: [],
1560
+ lazy: [],
1561
+ attributes: [
1562
+ {
1563
+ object_id: 'a',
1564
+ name: 'attr_a',
1565
+ type: 'Boolean',
1566
+ expression: 'a?threshold[90|tostring].max:true',
1567
+ metadata: {
1568
+ unit: {
1569
+ type: 'Text',
1570
+ expression: '"hola" + "adios"'
1571
+ }
1572
+ }
1573
+ }
1574
+ ]
1575
+ }
1576
+ ]
1577
+ },
1578
+ headers: {
1579
+ 'fiware-service': globalEnv.service,
1580
+ 'fiware-servicepath': globalEnv.servicePath
1581
+ }
1582
+ },
1583
+ should: [
1584
+ {
1585
+ shouldName:
1586
+ 'A - WHEN sending a measure through http IT should send to Context Broker expanded metadata value ',
1587
+ type: 'single',
1588
+ isRegex: true,
1589
+ measure: {
1590
+ url: 'http://localhost:' + config.http.port + '/iot/json',
1591
+ method: 'POST',
1592
+ qs: {
1593
+ i: globalEnv.deviceId,
1594
+ k: globalEnv.apikey
1595
+ },
1596
+ json: {
1597
+ a: false
1598
+ }
1599
+ },
1600
+ expectation: {
1601
+ id: globalEnv.entity_name,
1602
+ type: globalEnv.entity_type,
1603
+ attr_a: {
1604
+ value: true,
1605
+ type: 'Boolean',
1606
+ metadata: {
1607
+ unit: {
1608
+ type: 'Text',
1609
+ value: 'holaadios'
1610
+ }
1611
+ }
1612
+ }
1613
+ }
1614
+ }
1615
+ ]
1616
+ },
1479
1617
  // 0200 - COMMANDS TESTS
1480
1618
  {
1481
1619
  describeName: '0200 - Simple group with commands',
@@ -1704,6 +1842,78 @@ const testCases = [
1704
1842
  }
1705
1843
  ]
1706
1844
  },
1845
+ {
1846
+ describeName: '0320 - Simple group with active attributes + metadata in Static attributes ',
1847
+ provision: {
1848
+ url: 'http://localhost:' + config.iota.server.port + '/iot/services',
1849
+ method: 'POST',
1850
+ json: {
1851
+ services: [
1852
+ {
1853
+ resource: '/iot/json',
1854
+ apikey: globalEnv.apikey,
1855
+ entity_type: globalEnv.entity_type,
1856
+ commands: [],
1857
+ lazy: [],
1858
+ attributes: [],
1859
+ static_attributes: [
1860
+ {
1861
+ name: 'static_a',
1862
+ type: 'Number',
1863
+ value: 4,
1864
+ metadata: {
1865
+ accuracy: {
1866
+ value: 0.8,
1867
+ type: 'Float'
1868
+ }
1869
+ }
1870
+ }
1871
+ ]
1872
+ }
1873
+ ]
1874
+ },
1875
+ headers: {
1876
+ 'fiware-service': globalEnv.service,
1877
+ 'fiware-servicepath': globalEnv.servicePath
1878
+ }
1879
+ },
1880
+ should: [
1881
+ {
1882
+ shouldName:
1883
+ 'A - WHEN sending measures through http IT should store metatada static attributes into Context Broker',
1884
+ type: 'single',
1885
+ measure: {
1886
+ url: 'http://localhost:' + config.http.port + '/iot/json',
1887
+ method: 'POST',
1888
+ qs: {
1889
+ i: globalEnv.deviceId,
1890
+ k: globalEnv.apikey
1891
+ },
1892
+ json: {
1893
+ a: 10
1894
+ }
1895
+ },
1896
+ expectation: {
1897
+ id: globalEnv.entity_name,
1898
+ type: globalEnv.entity_type,
1899
+ a: {
1900
+ value: 10,
1901
+ type: 'Text'
1902
+ },
1903
+ static_a: {
1904
+ value: 4,
1905
+ type: 'Number',
1906
+ metadata: {
1907
+ accuracy: {
1908
+ value: 0.8,
1909
+ type: 'Float'
1910
+ }
1911
+ }
1912
+ }
1913
+ }
1914
+ }
1915
+ ]
1916
+ },
1707
1917
  // 0400 - TIMESTAMP TESTS
1708
1918
  {
1709
1919
  describeName: '0400 - Simple group with active attribute + timestamp:false',
@@ -1976,6 +2186,527 @@ const testCases = [
1976
2186
  }
1977
2187
  ]
1978
2188
  },
2189
+ {
2190
+ describeName: '0430 - Simple group with active attribute + timestamp mapping defined',
2191
+ provision: {
2192
+ url: 'http://localhost:' + config.iota.server.port + '/iot/services',
2193
+ method: 'POST',
2194
+ json: {
2195
+ services: [
2196
+ {
2197
+ resource: '/iot/json',
2198
+ apikey: globalEnv.apikey,
2199
+ timestamp: true,
2200
+ entity_type: globalEnv.entity_type,
2201
+ commands: [],
2202
+ lazy: [],
2203
+ attributes: [
2204
+ {
2205
+ object_id: 'mydatetime',
2206
+ name: 'TimeInstant',
2207
+ type: 'DateTime'
2208
+ }
2209
+ ]
2210
+ }
2211
+ ]
2212
+ },
2213
+ headers: {
2214
+ 'fiware-service': globalEnv.service,
2215
+ 'fiware-servicepath': globalEnv.servicePath
2216
+ }
2217
+ },
2218
+ should: [
2219
+ {
2220
+ shouldName:
2221
+ 'A - WHEN sending a measure through http IT should map the measure to timestamp attribute and use it for timestmap and other metadata attributes sent to Context Broker',
2222
+ type: 'single',
2223
+ measure: {
2224
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2225
+ method: 'POST',
2226
+ qs: {
2227
+ i: globalEnv.deviceId,
2228
+ k: globalEnv.apikey
2229
+ },
2230
+ json: {
2231
+ mydatetime: '2022-02-02T02:22:22.222Z',
2232
+ a: 23
2233
+ }
2234
+ },
2235
+ expectation: {
2236
+ id: globalEnv.entity_name,
2237
+ type: globalEnv.entity_type,
2238
+ a: {
2239
+ value: 23,
2240
+ type: 'Text',
2241
+ metadata: {
2242
+ TimeInstant: {
2243
+ value: '2022-02-02T02:22:22.222Z',
2244
+ type: 'DateTime'
2245
+ }
2246
+ }
2247
+ },
2248
+ TimeInstant: {
2249
+ value: '2022-02-02T02:22:22.222Z',
2250
+ type: 'DateTime'
2251
+ }
2252
+ }
2253
+ },
2254
+ {
2255
+ shouldName:
2256
+ 'A - WHEN sending a measure without timestamp through http IT should use system timestamp for mapped attribute and use it for timestmap and other metadata attributes sent to Context Broker',
2257
+ type: 'single',
2258
+ isRegex: true,
2259
+ measure: {
2260
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2261
+ method: 'POST',
2262
+ qs: {
2263
+ i: globalEnv.deviceId,
2264
+ k: globalEnv.apikey
2265
+ },
2266
+ json: {
2267
+ a: 23
2268
+ }
2269
+ },
2270
+ expectation: {
2271
+ id: globalEnv.entity_name,
2272
+ type: globalEnv.entity_type,
2273
+ a: {
2274
+ value: 23,
2275
+ type: 'Text',
2276
+ metadata: {
2277
+ TimeInstant: {
2278
+ value: _.isDateString,
2279
+ type: 'DateTime'
2280
+ }
2281
+ }
2282
+ },
2283
+ TimeInstant: {
2284
+ value: _.isDateString,
2285
+ type: 'DateTime'
2286
+ }
2287
+ }
2288
+ },
2289
+ {
2290
+ shouldName:
2291
+ 'A - WHEN sending a measure with timestamp through http IT should map the measure to timestamp attribute and use it for timestmap and other metadata attributes sent to Context Broker',
2292
+ type: 'single',
2293
+ measure: {
2294
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2295
+ method: 'POST',
2296
+ qs: {
2297
+ i: globalEnv.deviceId,
2298
+ k: globalEnv.apikey
2299
+ },
2300
+ json: {
2301
+ mydatetime: '2022-02-02T02:22:22.222Z',
2302
+ TimeInstant: '2033-03-03T03:33:33.333Z',
2303
+ a: 23
2304
+ }
2305
+ },
2306
+ expectation: {
2307
+ id: globalEnv.entity_name,
2308
+ type: globalEnv.entity_type,
2309
+ a: {
2310
+ value: 23,
2311
+ type: 'Text',
2312
+ metadata: {
2313
+ TimeInstant: {
2314
+ value: '2022-02-02T02:22:22.222Z',
2315
+ type: 'DateTime'
2316
+ }
2317
+ }
2318
+ },
2319
+ TimeInstant: {
2320
+ value: '2022-02-02T02:22:22.222Z',
2321
+ type: 'DateTime'
2322
+ }
2323
+ }
2324
+ }
2325
+ ]
2326
+ },
2327
+ {
2328
+ describeName: '0431 - Simple group with active attribute + timestamp mapping defined + explicitAttrs',
2329
+ provision: {
2330
+ url: 'http://localhost:' + config.iota.server.port + '/iot/services',
2331
+ method: 'POST',
2332
+ json: {
2333
+ services: [
2334
+ {
2335
+ resource: '/iot/json',
2336
+ apikey: globalEnv.apikey,
2337
+ timestamp: true,
2338
+ explicitAttrs: true,
2339
+ entity_type: globalEnv.entity_type,
2340
+ commands: [],
2341
+ lazy: [],
2342
+ attributes: [
2343
+ {
2344
+ object_id: 'mydatetime',
2345
+ name: 'TimeInstant',
2346
+ type: 'DateTime'
2347
+ },
2348
+ {
2349
+ object_id: 'a',
2350
+ name: 'a',
2351
+ type: 'Text'
2352
+ }
2353
+ ]
2354
+ }
2355
+ ]
2356
+ },
2357
+ headers: {
2358
+ 'fiware-service': globalEnv.service,
2359
+ 'fiware-servicepath': globalEnv.servicePath
2360
+ }
2361
+ },
2362
+ should: [
2363
+ {
2364
+ shouldName:
2365
+ 'A - WHEN sending a measure through http IT should map the measure to timestamp attribute and use it for timestmap and other metadata attributes sent to Context Broker',
2366
+ type: 'single',
2367
+ measure: {
2368
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2369
+ method: 'POST',
2370
+ qs: {
2371
+ i: globalEnv.deviceId,
2372
+ k: globalEnv.apikey
2373
+ },
2374
+ json: {
2375
+ mydatetime: '2022-02-02T02:22:22.222Z',
2376
+ a: 23
2377
+ }
2378
+ },
2379
+ expectation: {
2380
+ id: globalEnv.entity_name,
2381
+ type: globalEnv.entity_type,
2382
+ a: {
2383
+ value: 23,
2384
+ type: 'Text',
2385
+ metadata: {
2386
+ TimeInstant: {
2387
+ value: '2022-02-02T02:22:22.222Z',
2388
+ type: 'DateTime'
2389
+ }
2390
+ }
2391
+ },
2392
+ TimeInstant: {
2393
+ value: '2022-02-02T02:22:22.222Z',
2394
+ type: 'DateTime'
2395
+ }
2396
+ }
2397
+ },
2398
+ {
2399
+ shouldName:
2400
+ 'A - WHEN sending a measure without timestamp through http IT should use system timestamp for mapped attribute and use it for timestmap and other metadata attributes sent to Context Broker',
2401
+ type: 'single',
2402
+ isRegex: true,
2403
+ measure: {
2404
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2405
+ method: 'POST',
2406
+ qs: {
2407
+ i: globalEnv.deviceId,
2408
+ k: globalEnv.apikey
2409
+ },
2410
+ json: {
2411
+ a: 23
2412
+ }
2413
+ },
2414
+ expectation: {
2415
+ id: globalEnv.entity_name,
2416
+ type: globalEnv.entity_type,
2417
+ a: {
2418
+ value: 23,
2419
+ type: 'Text',
2420
+ metadata: {
2421
+ TimeInstant: {
2422
+ value: _.isDateString,
2423
+ type: 'DateTime'
2424
+ }
2425
+ }
2426
+ },
2427
+ TimeInstant: {
2428
+ value: _.isDateString,
2429
+ type: 'DateTime'
2430
+ }
2431
+ }
2432
+ },
2433
+ {
2434
+ shouldName:
2435
+ 'A - WHEN sending a measure with timestamp through http IT should map the measure to timestamp attribute and use it for timestmap and other metadata attributes sent to Context Broker',
2436
+ type: 'single',
2437
+ measure: {
2438
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2439
+ method: 'POST',
2440
+ qs: {
2441
+ i: globalEnv.deviceId,
2442
+ k: globalEnv.apikey
2443
+ },
2444
+ json: {
2445
+ mydatetime: '2022-02-02T02:22:22.222Z',
2446
+ TimeInstant: '2033-03-03T03:33:33.333Z',
2447
+ a: 23
2448
+ }
2449
+ },
2450
+ expectation: {
2451
+ id: globalEnv.entity_name,
2452
+ type: globalEnv.entity_type,
2453
+ a: {
2454
+ value: 23,
2455
+ type: 'Text',
2456
+ metadata: {
2457
+ TimeInstant: {
2458
+ value: '2022-02-02T02:22:22.222Z',
2459
+ type: 'DateTime'
2460
+ }
2461
+ }
2462
+ },
2463
+ TimeInstant: {
2464
+ value: '2022-02-02T02:22:22.222Z',
2465
+ type: 'DateTime'
2466
+ }
2467
+ }
2468
+ }
2469
+ ]
2470
+ },
2471
+ {
2472
+ describeName: '0432 - Simple group with active attribute + bad timestamp mapping defined',
2473
+ provision: {
2474
+ url: 'http://localhost:' + config.iota.server.port + '/iot/services',
2475
+ method: 'POST',
2476
+ json: {
2477
+ services: [
2478
+ {
2479
+ resource: '/iot/json',
2480
+ apikey: globalEnv.apikey,
2481
+ timestamp: true,
2482
+ entity_type: globalEnv.entity_type,
2483
+ commands: [],
2484
+ lazy: [],
2485
+ attributes: [
2486
+ {
2487
+ object_id: 'mydatetime',
2488
+ name: 'TimeInstant',
2489
+ type: 'DateTime',
2490
+ expression: '"2033-03-03T" + "03:33:33.333Z"'
2491
+ }
2492
+ ]
2493
+ }
2494
+ ]
2495
+ },
2496
+ headers: {
2497
+ 'fiware-service': globalEnv.service,
2498
+ 'fiware-servicepath': globalEnv.servicePath
2499
+ }
2500
+ },
2501
+ should: [
2502
+ {
2503
+ shouldName:
2504
+ 'A - WHEN sending a measure through http IT should map the measure to fixed timestamp attribute and use it for timestmap sent to Context Broker',
2505
+ type: 'single',
2506
+ measure: {
2507
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2508
+ method: 'POST',
2509
+ qs: {
2510
+ i: globalEnv.deviceId,
2511
+ k: globalEnv.apikey
2512
+ },
2513
+ json: {
2514
+ mydatetime: '2022-02-02T02:22:22.222Z',
2515
+ a: 23
2516
+ }
2517
+ },
2518
+ expectation: {
2519
+ id: globalEnv.entity_name,
2520
+ type: globalEnv.entity_type,
2521
+ a: {
2522
+ value: 23,
2523
+ type: 'Text',
2524
+ metadata: {
2525
+ TimeInstant: {
2526
+ value: '2033-03-03T03:33:33.333Z',
2527
+ type: 'DateTime'
2528
+ }
2529
+ }
2530
+ },
2531
+ TimeInstant: {
2532
+ value: '2033-03-03T03:33:33.333Z',
2533
+ type: 'DateTime'
2534
+ }
2535
+ }
2536
+ }
2537
+ ]
2538
+ },
2539
+ {
2540
+ describeName: '0433 - Simple group with active attribute + several timestamp mappings defined in multientity',
2541
+ provision: {
2542
+ url: 'http://localhost:' + config.iota.server.port + '/iot/services',
2543
+ method: 'POST',
2544
+ json: {
2545
+ services: [
2546
+ {
2547
+ resource: '/iot/json',
2548
+ apikey: globalEnv.apikey,
2549
+ timestamp: true,
2550
+ entity_type: globalEnv.entity_type,
2551
+ commands: [],
2552
+ lazy: [],
2553
+ attributes: [
2554
+ {
2555
+ object_id: 'a',
2556
+ name: 'a',
2557
+ type: 'Text'
2558
+ },
2559
+ {
2560
+ object_id: 'mydatetime1',
2561
+ name: 'TimeInstant',
2562
+ type: 'DateTime',
2563
+ entity_name: 'TestType:TestDevice1',
2564
+ entity_type: 'TestType'
2565
+ },
2566
+ {
2567
+ object_id: 'mydatetime2',
2568
+ name: 'TimeInstant',
2569
+ type: 'DateTime',
2570
+ entity_name: 'TestType:TestDevice2',
2571
+ entity_type: 'TestType'
2572
+ },
2573
+ {
2574
+ object_id: 'a1',
2575
+ name: 'a1',
2576
+ type: 'Text',
2577
+ expression: 'a',
2578
+ entity_name: 'TestType:TestDevice1',
2579
+ entity_type: 'TestType'
2580
+ },
2581
+ {
2582
+ object_id: 'a2',
2583
+ name: 'a2',
2584
+ type: 'Text',
2585
+ expression: 'a',
2586
+ entity_name: 'TestType:TestDevice2',
2587
+ entity_type: 'TestType'
2588
+ }
2589
+ ]
2590
+ }
2591
+ ]
2592
+ },
2593
+ headers: {
2594
+ 'fiware-service': globalEnv.service,
2595
+ 'fiware-servicepath': globalEnv.servicePath
2596
+ }
2597
+ },
2598
+ should: [
2599
+ {
2600
+ shouldName:
2601
+ 'A - WHEN sending a measure through http IT should map the measure to timestamp attributes and use it for timestmap and other metadata attributes sent to Context Broker',
2602
+ type: 'multientity',
2603
+ isRegex: true,
2604
+ measure: {
2605
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2606
+ method: 'POST',
2607
+ qs: {
2608
+ i: globalEnv.deviceId,
2609
+ k: globalEnv.apikey
2610
+ },
2611
+ json: {
2612
+ a: 23,
2613
+ mydatetime1: '2011-01-01T01:11:11.111Z',
2614
+ mydatetime2: '2022-02-02T02:22:22.222Z'
2615
+ }
2616
+ },
2617
+ expectation: {
2618
+ actionType: 'append',
2619
+ entities: [
2620
+ {
2621
+ id: globalEnv.entity_name,
2622
+ type: globalEnv.entity_type,
2623
+ a: {
2624
+ value: 23,
2625
+ type: 'Text',
2626
+ metadata: {
2627
+ TimeInstant: {
2628
+ value: _.isDateString,
2629
+ type: 'DateTime'
2630
+ }
2631
+ }
2632
+ },
2633
+ TimeInstant: {
2634
+ value: _.isDateString,
2635
+ type: 'DateTime'
2636
+ }
2637
+ },
2638
+ {
2639
+ id: 'TestType:TestDevice1',
2640
+ type: globalEnv.entity_type,
2641
+ a1: {
2642
+ value: 23,
2643
+ type: 'Text',
2644
+ metadata: {
2645
+ TimeInstant: {
2646
+ value: '2011-01-01T01:11:11.111Z',
2647
+ type: 'DateTime'
2648
+ }
2649
+ }
2650
+ },
2651
+ TimeInstant: {
2652
+ value: '2011-01-01T01:11:11.111Z',
2653
+ type: 'DateTime'
2654
+ }
2655
+ },
2656
+ {
2657
+ id: 'TestType:TestDevice2',
2658
+ type: globalEnv.entity_type,
2659
+ a2: {
2660
+ value: 23,
2661
+ type: 'Text',
2662
+ metadata: {
2663
+ TimeInstant: {
2664
+ value: '2022-02-02T02:22:22.222Z',
2665
+ type: 'DateTime'
2666
+ }
2667
+ }
2668
+ },
2669
+ TimeInstant: {
2670
+ value: '2022-02-02T02:22:22.222Z',
2671
+ type: 'DateTime'
2672
+ }
2673
+ }
2674
+ ]
2675
+ }
2676
+ },
2677
+ {
2678
+ shouldName:
2679
+ 'A - WHEN sending a measure through http IT should map the measure to timestamp attributes for just mapped entity and sent to Context Broker',
2680
+ type: 'multientity',
2681
+ isRegex: true,
2682
+ measure: {
2683
+ url: 'http://localhost:' + config.http.port + '/iot/json',
2684
+ method: 'POST',
2685
+ qs: {
2686
+ i: globalEnv.deviceId,
2687
+ k: globalEnv.apikey
2688
+ },
2689
+ json: {
2690
+ mydatetime1: '2011-01-01T01:11:11.111Z'
2691
+ }
2692
+ },
2693
+ expectation: {
2694
+ actionType: 'append',
2695
+ entities: [
2696
+ {
2697
+ id: 'TestType:TestDevice1',
2698
+ type: globalEnv.entity_type,
2699
+ TimeInstant: {
2700
+ value: '2011-01-01T01:11:11.111Z',
2701
+ type: 'DateTime'
2702
+ }
2703
+ }
2704
+ ]
2705
+ }
2706
+ }
2707
+ ]
2708
+ },
2709
+
1979
2710
  // 0500 - EXPLICIT ATTRIBUTES TESTS
1980
2711
  {
1981
2712
  describeName: '0500 - Group with explicit attrs:false (boolean) + active atributes',
@@ -307,6 +307,38 @@ const iotAgentConfig = {
307
307
  }
308
308
  ]
309
309
  },
310
+ WeatherStation10: {
311
+ commands: [],
312
+ type: 'WeatherStation',
313
+ lazy: [],
314
+ active: [
315
+ {
316
+ object_id: 'p',
317
+ name: 'pressure',
318
+ type: 'Hgmm'
319
+ },
320
+ {
321
+ object_id: 'h',
322
+ name: 'humidity',
323
+ type: 'Percentage',
324
+ entity_name: 'Higro2000',
325
+ entity_type: 'Higrometer',
326
+ metadata: {
327
+ unitCode: {
328
+ type: 'Text',
329
+ value: 'Hgmm'
330
+ }
331
+ }
332
+ },
333
+ {
334
+ object_id: 'TimeInstant',
335
+ name: 'TimeInstant',
336
+ type: 'DateTime',
337
+ entity_name: 'Higro2000',
338
+ entity_type: 'Higrometer'
339
+ }
340
+ ]
341
+ },
310
342
  Sensor001: {
311
343
  commands: [],
312
344
  type: 'Sensor',
@@ -1488,7 +1520,7 @@ describe('NGSI-v2 - Multi-entity plugin', function () {
1488
1520
 
1489
1521
  describe('NGSI-v2 - Multi-entity plugin is executed before timestamp process plugin', function () {
1490
1522
  beforeEach(function (done) {
1491
- logger.setLevel('FATAL');
1523
+ logger.setLevel('DEBUG');
1492
1524
  iotAgentConfig.timestamp = true;
1493
1525
  iotAgentLib.activate(iotAgentConfig, function () {
1494
1526
  iotAgentLib.clearAll(function () {
@@ -1635,7 +1667,7 @@ describe('NGSI-v2 - Multi-entity plugin is executed before timestamp process plu
1635
1667
  value: '2018-06-13T13:28:34.611Z'
1636
1668
  }
1637
1669
  ];
1638
- iotAgentLib.update('ws5', 'WeatherStation', '', tsValue, function (error) {
1670
+ iotAgentLib.update('ws5', 'WeatherStation10', '', tsValue, function (error) {
1639
1671
  should.not.exist(error);
1640
1672
  contextBrokerMock.done();
1641
1673
  done();