iotagent-node-lib 2.20.0 → 2.21.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 (31) hide show
  1. package/.readthedocs.yml +3 -1
  2. package/README.md +2 -2
  3. package/doc/advanced-topics.md +6 -6
  4. package/doc/api.md +11 -2
  5. package/doc/expressionLanguage.md +3 -0
  6. package/doc/northboundinteractions.md +40 -33
  7. package/doc/requirements.txt +4 -0
  8. package/{docs → doc}/roadmap.md +21 -6
  9. package/doc/usermanual.md +2 -2
  10. package/docker/Mosquitto/Dockerfile +28 -12
  11. package/docker/Mosquitto/README.md +8 -7
  12. package/docker/Mosquitto/startMosquitto.sh +8 -0
  13. package/lib/fiware-iotagent-lib.js +1 -0
  14. package/lib/jexlTranformsMap.js +3 -1
  15. package/lib/plugins/expressionPlugin.js +55 -21
  16. package/lib/plugins/multiEntity.js +2 -21
  17. package/lib/services/devices/deviceRegistryMemory.js +13 -2
  18. package/lib/services/devices/deviceRegistryMongoDB.js +15 -7
  19. package/lib/services/devices/deviceService.js +26 -2
  20. package/lib/services/ngsi/entities-NGSI-LD.js +6 -4
  21. package/lib/services/ngsi/entities-NGSI-v2.js +8 -6
  22. package/lib/services/ngsi/ngsiService.js +2 -2
  23. package/lib/services/northBound/contextServer-NGSI-v2.js +32 -27
  24. package/lib/services/northBound/contextServerUtils.js +1 -1
  25. package/lib/services/northBound/deviceProvisioningServer.js +2 -0
  26. package/lib/templates/createDevice.json +12 -0
  27. package/lib/templates/updateDevice.json +12 -0
  28. package/package.json +5 -5
  29. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +2 -0
  30. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
  31. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +63 -2
package/.readthedocs.yml CHANGED
@@ -4,4 +4,6 @@ mkdocs:
4
4
  configuration: mkdocs.yml
5
5
 
6
6
  python:
7
- version: 3.6
7
+ version: 3.8
8
+ install:
9
+ - requirements: doc/requirements.txt
package/README.md CHANGED
@@ -20,7 +20,7 @@ platform (authentication and authorization of the channel) and provide other com
20
20
  This project is part of [FIWARE](https://www.fiware.org/). For more information check the FIWARE Catalogue entry for the
21
21
  [IoT Agents](https://github.com/Fiware/catalogue/tree/master/iot-agents).
22
22
 
23
- | :books: [Documentation](https://iotagent-node-lib.rtfd.io) | :mortar_board: [Academy](https://fiware-academy.readthedocs.io/en/latest/iot-agents/idas) | :dart: [Roadmap](https://github.com/telefonicaid/iotagent-node-lib/blob/master/docs/roadmap.md) |
23
+ | :books: [Documentation](https://iotagent-node-lib.rtfd.io) | :mortar_board: [Academy](https://fiware-academy.readthedocs.io/en/latest/iot-agents/idas) | :dart: [Roadmap](https://github.com/telefonicaid/iotagent-node-lib/blob/master/doc/roadmap.md) |
24
24
  | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
25
25
 
26
26
 
@@ -304,7 +304,7 @@ deleted upon startup.
304
304
 
305
305
  The IoT Agent Node Library is licensed under [Affero General Public License (GPL) version 3](./LICENSE).
306
306
 
307
- © 2019 Telefonica Investigación y Desarrollo, S.A.U
307
+ © 2022 Telefonica Investigación y Desarrollo, S.A.U
308
308
 
309
309
  ### Are there any legal issues with AGPL 3.0? Is it safe for me to use?
310
310
 
@@ -305,8 +305,8 @@ stored in the Context Broker by adding a new attribute to the entity with the sa
305
305
  element. By adding the field `explicitAttrs` with `true` value to device or group provision, the IoTAgent rejects the
306
306
  measure elements that are not defined in the mappings of device or group provision, persisting only the one defined in
307
307
  the mappings of the provision. If `explicitAttrs` is provided both at device and group level, the device level takes
308
- precedence. Additionally `explicitAttrs` can be used to define which meassures defined in JSON/JEXL array will be
309
- propagated to NGSI interface.
308
+ precedence. Additionally `explicitAttrs` can be used to define which meassures (identified by their attribute names, not
309
+ by their object_id) defined in JSON/JEXL array will be propagated to NGSI interface.
310
310
 
311
311
  The different possibilities are summarized below:
312
312
 
@@ -332,8 +332,8 @@ Case 3:
332
332
  "explicitAttrs": "['attr1','atrr2']"
333
333
  ```
334
334
 
335
- just measures defined in the array will be will be propagated to NGSI interface (note that in this case the value of
336
- `explicitAttrs` is not a JSON but a string that looks likes a JSON).
335
+ just measures defined in the array (identified by their attribute names, not by their object_id) will be will be
336
+ propagated to NGSI interface (note that in this case the value of `explicitAttrs` is not a JSON but a string that looks likes a JSON).
337
337
 
338
338
  Case 4:
339
339
 
@@ -346,8 +346,8 @@ depending on the JEXL expression evaluation:
346
346
  - If it evaluates to `true` every measure will be propagated to NGSI interface (as in case 1)
347
347
  - If it evaluates to `false` just measures defined in active, static (plus conditionally TimeInstant) will be
348
348
  propagated to NGSI interface (as in case 2)
349
- - If it evaluates to an array just measures defined in the array will be will be propagated to NGSI interface (as in
350
- case 3)
349
+ - If it evaluates to an array just measures defined in the array (identified by their attribute names, not by their object_id)
350
+ will be will be propagated to NGSI interface (as in case 3)
351
351
 
352
352
  ### Configuring operation to persist the data in Context Broker (appendMode)
353
353
 
package/doc/api.md CHANGED
@@ -247,7 +247,7 @@ the API resource fields and the same fields in the database model.
247
247
 
248
248
  #### Attribute lists
249
249
 
250
- In the device model there are three list of attributes that can be declared: attributes, lazy and commands. All of them
250
+ In the group/device model there are three list of attributes that can be declared: attributes, lazy and commands. All of them
251
251
  have the same syntax, an object containing the following attributes:
252
252
 
253
253
  - **object_id** (optional): name of the attribute as coming from the device.
@@ -255,7 +255,7 @@ have the same syntax, an object containing the following attributes:
255
255
  - **type** (mandatory): name of the type of the attribute in the target entity.
256
256
  - **metadata** (optional): additional static metadata for the attribute in the target entity. (e.g. `unitCode`)
257
257
 
258
- Some transformation plugins also allow the use of the following optional attributes:
258
+ Some transformation plugins also allow the use of the following optional fields:
259
259
 
260
260
  - **expression**: indicates that the value of the target attribute will not be the plain value or the measurement, but
261
261
  an expression based on a combination of the reported values. See the
@@ -268,6 +268,15 @@ Some transformation plugins also allow the use of the following optional attribu
268
268
  - **reverse**: add bidirectionality expressions to the attribute. See the **bidirectionality** transformation plugin
269
269
  in the [Data Mapping Plugins section](advanced-topics.md#bidirectionality-plugin-bidirectional) for details.
270
270
 
271
+ Additionally for commands (which are attributes of type `command`) the following fields are optional:
272
+
273
+ - **expression** indicates that the value of the target command will not be the plain value or the command, but an
274
+ expression based on a combination of the returned values. See the
275
+ [Expression Language definition](expressionLanguage.md) for details
276
+ - **payloadType**: indicates how command payload will be transformed before be sent to device. Please have a look to particular
277
+ IOTAs documentation for allowed values of this field in each case.
278
+ - **contentType**: `content-type` header used when send command by HTTP transport (ignored in other kinds of transports)
279
+
271
280
  See the transformation plugins Section for more details.
272
281
 
273
282
  #### Advice on Attribute defintions
@@ -474,7 +474,10 @@ Current common transformation set:
474
474
  | slice: (arr, init, end) | `arr.slice(init,end);` |
475
475
  | addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
476
476
  | removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
477
+ | touppercase: (val) | `String(val).toUpperCase()` |
478
+ | tolowercase: (val) | `String(val).toLowerCase()` |
477
479
 
480
+
478
481
  You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
479
482
  test all the functions described above.
480
483
 
@@ -163,16 +163,15 @@ The equivalent **NGSI-LD** payload is associated to an update operation (PATCH `
163
163
 
164
164
  ```json
165
165
  {
166
- "temperature": {
167
- "type": "Property",
168
- "value": "23"
169
- },
170
- "pressure" :{
171
- "type": "Property",
172
- "value": "720"
173
- }
166
+ "temperature": {
167
+ "type": "Property",
168
+ "value": "23"
169
+ },
170
+ "pressure": {
171
+ "type": "Property",
172
+ "value": "720"
173
+ }
174
174
  }
175
-
176
175
  ```
177
176
 
178
177
  #### R1 - UpdateContext (response)
@@ -285,7 +284,8 @@ This is an **NGSI-LD** response to `/ngsi-ld/v1/entities/<entity>`
285
284
  ```
286
285
 
287
286
  In this case, the response to the QueryContext is a list of responses, one for each requested entity, indicating whether
288
- the information has been retrieved successfully (in the HTTP Status code) and the requested Context information in the body of the response.
287
+ the information has been retrieved successfully (in the HTTP Status code) and the requested Context information in the
288
+ body of the response.
289
289
 
290
290
  Application level errors can be specified for each entity in this payload.
291
291
 
@@ -305,7 +305,7 @@ Context Element, but with the request as a whole.
305
305
 
306
306
  ### Scenario 1: active attributes
307
307
 
308
- ![General ](./img/scenario1.png "Scenario 1: active attributes")
308
+ ![General ](./img/scenario1.png 'Scenario 1: active attributes')
309
309
 
310
310
  In this scenario, the interaction is started by the device, that is going to actively send a piece of data to the
311
311
  platform. When the IoTAgent receives the data, it sends it to the Context Broker through a P1 request. The Context
@@ -319,7 +319,7 @@ updating process, and can occur at any time (they are to completely different pr
319
319
 
320
320
  ### Scenario 2: lazy attributes
321
321
 
322
- ![General ](./img/scenario2.png "Scenario 2: lazy attributes")
322
+ ![General ](./img/scenario2.png 'Scenario 2: lazy attributes')
323
323
 
324
324
  This scenario requires that the attributes that are going to be requested are marked as provided by the IoT Agent,
325
325
  through a registration process (NGSIv9). Examples of this registration process will be provided in the practical section
@@ -345,7 +345,7 @@ queries (and thus P2 and R2 payloads).
345
345
 
346
346
  ### Scenario 3: commands
347
347
 
348
- ![General ](./img/scenario3.png "Scenario 3: commands")
348
+ ![General ](./img/scenario3.png 'Scenario 3: commands')
349
349
 
350
350
  This scenario requires that the attributes that are going to be requested are marked as provided by the IoT Agent,
351
351
  through a registration process (NGSIv9). Examples of this registration process will be provided in the practical section
@@ -359,15 +359,26 @@ use three kinds of attributes:
359
359
  - An attribute will be used as the _input attribute_ (the attribute registered in the Context Provider). This input
360
360
  attribute can be thought of as a command issued to the IoTAgent (from here the name of the scenario) whose value is
361
361
  the set of arguments of the command. Only updateContext operations will be used to interact with this attributes.
362
+ Typically this attribute is of type `command`.
362
363
 
363
364
  - Another attribute will be used as the _result attribute_. This attribute will be updated from the IoTAgent, and its
364
365
  value stored in the Context Broker. This attribute will contain the result of the command (this result can be
365
366
  information in case the command was a "information retrieval" command or the result of an action if it was an
366
- "actuator command"). Typically, the name of this attribute will be the same of the input attribute, with an
367
- additional sufix (`_info`).
367
+ "actuator command"). Initially its value is empty. Typically, the name of this attribute will be the same of the
368
+ input attribute, with an additional sufix (`_info`) and the type is `commandResult`.
368
369
 
369
370
  - Another attribute with the same characteristics as the later will be used to indicate whether the command has ended
370
- successfully or whether an error has been reported.
371
+ successfully or whether an error has been reported. Typically, the name of this attribute will be the same of the
372
+ input attribute, with an additional sufix (`_status`) and the type is `commandStatus`. The possible values of this
373
+ attribute are: `ERROR`, `EXPIRED`, `PENDING`, `DELIVERED`, `UNKNOWN` with the following meanings:
374
+ - ERROR: There is a kind of error.
375
+ - EXPIRED: This meens that pull command has been expired without be delivered to device according with
376
+ `pollingExpiration` time defined by config.
377
+ - PENDING: In a PUSH command means that command has been sent to device but not device has still not respond. In a
378
+ PULL command means that command has been stored and device still has no ask for it.
379
+ - DELIVERED: The command has been delivered to phisical device.
380
+ - OK: The command has been delivered and device has respond.
381
+ - UNKNOWN: This is the initial value.
371
382
 
372
383
  In this scenario, the interaction is also initiated by the User. The user starts the scenario by sending an update
373
384
  request P1 to the Context Broker, to the input attribute (1). The Context Broker redirects this same payload to the
@@ -835,7 +846,6 @@ The IoT Agent detects the selected attribute is a command, and replies to the Co
835
846
  ```json
836
847
  [
837
848
  {
838
-
839
849
  "type": "device",
840
850
  "id": "Dev0001",
841
851
  "switch": {
@@ -854,7 +864,6 @@ The Context Broker, forwards the same response to the user, thus replying the or
854
864
  ```json
855
865
  [
856
866
  {
857
-
858
867
  "type": "device",
859
868
  "id": "Dev0001",
860
869
  "switch": {
@@ -882,11 +891,11 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
882
891
  "isPattern": "false",
883
892
  "id": "Dev0001",
884
893
  "switch_info": {
885
- "type": "command_info",
894
+ "type": "commandResult",
886
895
  "value": "Switched successfully!"
887
896
  },
888
897
  "switch_status": {
889
- "type": "command_status",
898
+ "type": "commandStatus",
890
899
  "value": "OK"
891
900
  }
892
901
  }
@@ -906,11 +915,11 @@ The Context Broker replies to the IoT Agent with a R1 payload (200 OK):
906
915
  "type": "device",
907
916
  "id": "Dev0001",
908
917
  "switch_info": {
909
- "type": "command_info",
918
+ "type": "commandResult",
910
919
  "value": ""
911
920
  },
912
- "switch_status": {
913
- "type": "command_status",
921
+ "switch_status": {
922
+ "type": "commandStatus",
914
923
  "value": ""
915
924
  }
916
925
  }
@@ -936,8 +945,8 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
936
945
  }
937
946
  ],
938
947
  "attributes": [
939
- "switch_info",
940
- "switch_status"
948
+ "switch_info",
949
+ "switch_status"
941
950
  ]
942
951
  }' "https://<platform-ip>:10027/v2/op/query"
943
952
  ```
@@ -947,20 +956,18 @@ The Context Broker replies with all the desired data, in R2 format (200 OK):
947
956
  ```json
948
957
  [
949
958
  {
950
-
951
959
  "type": "device",
952
960
  "id": "Dev0001",
953
961
  "switch_info": {
954
- "type": "command_info",
962
+ "type": "commandResult",
955
963
  "value": "Switched successfully!"
956
964
  },
957
965
  "switch_status": {
958
- "type": "command_status",
966
+ "type": "commandStatus",
959
967
  "value": "OK"
960
968
  }
961
969
  }
962
970
  ]
963
-
964
971
  ```
965
972
 
966
973
  ### Scenario 3: commands (error)
@@ -978,11 +985,11 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
978
985
  "isPattern": "false",
979
986
  "id": "Dev0001",
980
987
  "switch_info":{
981
- "type": "command_info",
988
+ "type": "commandResult",
982
989
  "value": "The switch could not be switched due to the following error: switch blocked"
983
990
  },
984
991
  "switch_status":{
985
- "type": "command_status",
992
+ "type": "commandStatus",
986
993
  "value": "ERROR"
987
994
  }
988
995
  }
@@ -999,11 +1006,11 @@ In this case, the Context Broker reply with the following response (200 OK):
999
1006
  "type": "device",
1000
1007
  "id": "Dev0001",
1001
1008
  "switch_info": {
1002
- "type": "command_info",
1009
+ "type": "commandResult",
1003
1010
  "value": ""
1004
1011
  },
1005
1012
  "switch_status": {
1006
- "type": "command_status",
1013
+ "type": "commandStatus",
1007
1014
  "value": ""
1008
1015
  }
1009
1016
  }
@@ -0,0 +1,4 @@
1
+ mkdocs==1.2.3
2
+ Pygments==2.9.0
3
+ Markdown==3.3.4
4
+ jinja2==3.0.0
@@ -13,7 +13,7 @@ only, and this section may be revised to provide newer information at any time.
13
13
 
14
14
  Disclaimer:
15
15
 
16
- - This section has been last updated in March 2020. Please take into account its content could be obsolete.
16
+ - This section has been last updated in March 2022. Please take into account its content could be obsolete.
17
17
  - Note we develop this software in Agile way, so development plan is continuously under review. Thus, this roadmap has
18
18
  to be understood as rough plan of features to be done along time which is fully valid only at the time of writing
19
19
  it. This roadmap has not be understood as a commitment on features and/or dates.
@@ -25,19 +25,17 @@ Disclaimer:
25
25
  The following list of features are planned to be addressed in the short term, and incorporated in a release of the
26
26
  product:
27
27
 
28
- - Selectively ignore measure in the southbound interface (community)
29
- - JEXL support in expressions (community)
30
28
  - cgroup literal in configuration groups management API (community)
31
29
  - Metadata processing improvements
32
- - Add MongoDB authentication support
30
+ - Improve command functionalities (binary data + expression + mapping)
33
31
 
34
32
  ### Medium term
35
33
 
36
34
  The following list of features are planned to be addressed in the medium term, typically within the subsequent
37
35
  release(s) generated in the next 9 months after the next planned release:
38
36
 
39
- - Support for "delta" measures (i.e. "temperature _increased_ in 5 degress" instead of "temperature _is_ 25")
40
- - Removal support for NGSIv1 (which currently is deprecated)
37
+ - Accept JEXL Expressions for entity name in autoprovisioned devices (#1145)
38
+ - Refactor entities-NGSI-v2.js module (#1166)
41
39
 
42
40
  ### Long term
43
41
 
@@ -48,3 +46,20 @@ us if you wish to get involved in the implementation or influence the roadmap:
48
46
  - Incremental introduccion of ECMAScript6 syntax (previous analysis of which sub-set of interesting aspect we want to
49
47
  take)
50
48
  - Use the lightweight ingestion mechanism for connection oriented updates implemented in Context Broker
49
+ - Add support to other transport protocols (BacNET, Modbus, etc)
50
+
51
+ ### Features already completed
52
+
53
+ The following list contains all features that were in the roadmap and have already been implemented.
54
+
55
+ - Support for "delta" measures (i.e. "temperature _increased_ in 5 degress" instead of "temperature _is_ 25")
56
+ - Allow to handle binary messages ([iota-ul#530](https://github.com/telefonicaid/iotagent-ul/issues/530))
57
+ - Removal support for NGSIv1 (#966) ([2.18.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.18.0))
58
+ - Selectively ignore measure in the southbound interface
59
+ ([iotagent-json#416](https://github.com/telefonicaid/iotagent-json/issues/416),
60
+ [iotagent-ul#372](https://github.com/telefonicaid/iotagent-ul/issues/372))
61
+ ([2.13.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.13.0))
62
+ - JEXL support in expressions (#801, #687, #868)
63
+ ([2.13.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.13.0))
64
+ - Add MongoDB authentication support (#844)
65
+ ([2.12.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.12.0))
package/doc/usermanual.md CHANGED
@@ -209,8 +209,8 @@ function setCommandResult(entityName, resource, apikey, commandName, commandResu
209
209
  ###### Description
210
210
 
211
211
  Update the result of a command in the Context Broker. The result of the command has two components: the result of the
212
- command itself will be represented with the suffix `_result` in the entity while the status is updated in the attribute
213
- with the `_info` suffix.
212
+ command itself will be represented with the suffix `_info` in the entity while the status is updated in the attribute
213
+ with the `_status` suffix.
214
214
 
215
215
  ###### Params
216
216
 
@@ -1,19 +1,35 @@
1
- FROM centos:7.9.2009
1
+ ARG IMAGE_TAG=11.2-slim
2
+ FROM debian:${IMAGE_TAG}
2
3
 
3
- COPY aclfile /root/
4
- COPY startMosquitto.sh /bin
4
+ ARG CLEAN_DEV_TOOLS
5
+ ENV CLEAN_DEV_TOOLS ${CLEAN_DEV_TOOLS:-1}
5
6
 
6
7
  ENV CONGIF_FROM_ENV true
7
8
 
8
- RUN yum update -y && yum install -y wget \
9
- && yum install -y epel-release \
10
- && yum update -y epel-release \
11
- && yum install -y mosquitto \
12
- && cp /etc/mosquitto/mosquitto.conf /etc/mosquitto/mosquitto.conf.orig \
13
- && chmod 755 /bin/startMosquitto.sh \
14
- && mkdir /var/log/mosquitto \
15
- && chown mosquitto:mosquitto /var/log/mosquitto \
16
- && yum clean all
9
+ COPY aclfile /root/
10
+ COPY startMosquitto.sh /bin
11
+
12
+ RUN \
13
+ # Install security updates
14
+ apt-get -y update && \
15
+ apt-get -y upgrade && \
16
+ # Install dependencies
17
+ apt-get -y install \
18
+ wget \
19
+ mosquitto && \
20
+ cp /etc/mosquitto/mosquitto.conf /etc/mosquitto/mosquitto.conf.orig && \
21
+ chmod 755 /bin/startMosquitto.sh && \
22
+ mkdir -p /var/log/mosquitto && \
23
+ chown mosquitto:mosquitto /var/log/mosquitto && \
24
+ mkdir -p /var/run/mosquitto/ && \
25
+ chown mosquitto:mosquitto /var/run/mosquitto && \
26
+ echo "INFO: Cleaning unused software..." && \
27
+ apt-get clean && \
28
+ apt-get -y autoremove --purge && \
29
+ if [ ${CLEAN_DEV_TOOLS} -eq 0 ] ; then exit 0 ; fi && \
30
+ # remove the same packages we installed at the beginning to build Orch
31
+ apt-get -y autoremove --purge \
32
+ wget
17
33
 
18
34
 
19
35
  EXPOSE 1883
@@ -2,10 +2,11 @@ Thi directory containts the Dockerfile (and associated files) for a container of
2
2
  This container is provide as a help for users to test with MQTT, but it is just an auxiliary material in this repository.
3
3
 
4
4
  The following releases matches with eclipse-mosquitto version:
5
- - 1.6.0 uses mosquitto-1.6.10-1.el7.x86_64
6
- - 1.5.0 uses mosquitto-1.6.10-1.el7.x86_64
7
- - 1.4.0 uses mosquitto-1.6.10-1.el7.x86_64
8
- - 1.3.0 uses mosquitto-1.6.8-1.el7.x86_64
9
- - 1.2.0 uses mosquitto-1.6.7-1.el7.x86_64
10
- - 1.1.0 uses mosquitto-1.5.8-1.el7.x86_64
11
- - 1.0.0 uses mosquitto-1.4.8-1.el7.x86_64
5
+ - 2.0.0 uses mosquitto-2.0.11 from Debian 11
6
+ - 1.6.0 uses mosquitto-1.6.10-1.el7.x86_64 (from Centos7)
7
+ - 1.5.0 uses mosquitto-1.6.10-1.el7.x86_64 (from Centos7)
8
+ - 1.4.0 uses mosquitto-1.6.10-1.el7.x86_64 (from Centos7)
9
+ - 1.3.0 uses mosquitto-1.6.8-1.el7.x86_64 (from Centos7)
10
+ - 1.2.0 uses mosquitto-1.6.7-1.el7.x86_64 (from Centos7)
11
+ - 1.1.0 uses mosquitto-1.5.8-1.el7.x86_64 (from Centos7)
12
+ - 1.0.0 uses mosquitto-1.4.8-1.el7.x86_64 (from Centos7)
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
+ echo "INFO: startMosquitto..."
4
+
3
5
  if [ "${CONGIF_FROM_ENV}" = true ] ; then
4
6
  cp /etc/mosquitto/mosquitto.conf.orig /etc/mosquitto/mosquitto.conf
7
+ sed -i 's/log_dest file \/var\/log\/mosquitto\/mosquitto.log/log_dest stderr/g' /etc/mosquitto/mosquitto.conf
5
8
  echo "log_timestamp true" >> /etc/mosquitto/mosquitto.conf
6
9
  echo "log_timestamp_format %Y-%m-%dT%H:%M:%S" >> /etc/mosquitto/mosquitto.conf
7
10
  echo 'listener 9001' >> /etc/mosquitto/mosquitto.conf
@@ -18,4 +21,9 @@ if [ "${CONGIF_FROM_ENV}" = true ] ; then
18
21
  fi
19
22
  fi
20
23
 
24
+ echo "INFO: content /etc/mosquitto/mosquitto.conf: "
25
+ cat /etc/mosquitto/mosquitto.conf
26
+
27
+ echo "INFO: start: startMosquitto -c /etc/mosquitto/mosquitto.conf"
28
+
21
29
  /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
@@ -314,6 +314,7 @@ exports.listDevices = deviceService.listDevices;
314
314
  exports.getDevice = deviceService.getDevice;
315
315
  exports.getDeviceSilently = deviceService.getDeviceSilently;
316
316
  exports.getDeviceByName = deviceService.getDeviceByName;
317
+ exports.getDeviceByNameAndType = deviceService.getDeviceByNameAndType;
317
318
  exports.getDevicesByAttribute = deviceService.getDevicesByAttribute;
318
319
  exports.mergeDeviceWithConfiguration = deviceService.mergeDeviceWithConfiguration;
319
320
  exports.retrieveDevice = deviceService.retrieveDevice;
@@ -65,7 +65,9 @@ const map = {
65
65
  let s = new Set(arr);
66
66
  s.delete(x);
67
67
  return Array.from(s);
68
- }
68
+ },
69
+ touppercase: (val) => String(val).toUpperCase(),
70
+ tolowercase: (val) => String(val).toLowerCase()
69
71
  };
70
72
 
71
73
  exports.map = map;
@@ -30,6 +30,8 @@ const jexlParser = require('./jexlParser');
30
30
  const config = require('../commonConfig');
31
31
  /* eslint-disable no-unused-vars */
32
32
  const logger = require('logops');
33
+ const errors = require('../errors');
34
+ const constants = require('../constants');
33
35
  const context = {
34
36
  op: 'IoTAgentNGSI.expressionPlugin'
35
37
  };
@@ -42,6 +44,22 @@ function setJEXLTransforms(transformationMap) {
42
44
  jexlParser.setTransforms(transformationMap);
43
45
  }
44
46
 
47
+ function applyExpression(expression, context, typeInformation) {
48
+ let parser = legacyParser;
49
+ if (checkJexl(typeInformation)) {
50
+ parser = jexlParser;
51
+ }
52
+ return parser.applyExpression(expression, context, typeInformation);
53
+ }
54
+
55
+ function extractContext(attributeList, typeInformation) {
56
+ let parser = legacyParser;
57
+ if (checkJexl(typeInformation)) {
58
+ parser = jexlParser;
59
+ }
60
+ return parser.extractContext(attributeList);
61
+ }
62
+
45
63
  function mergeAttributes(attrList1, attrList2) {
46
64
  const finalCollection = _.clone(attrList1);
47
65
  const additionalItems = [];
@@ -70,26 +88,26 @@ function mergeAttributes(attrList1, attrList2) {
70
88
  return finalCollection.concat(additionalItems);
71
89
  }
72
90
 
73
- function update(entity, typeInformation, callback) {
74
- function checkJexl(typeInformation) {
75
- if (
76
- config.getConfig().defaultExpressionLanguage === 'jexl' &&
77
- typeInformation.expressionLanguage &&
78
- typeInformation.expressionLanguage !== 'legacy'
79
- ) {
80
- return true;
81
- } else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
82
- return true;
83
- } else if (
84
- config.getConfig().defaultExpressionLanguage === 'legacy' &&
85
- typeInformation.expressionLanguage &&
86
- typeInformation.expressionLanguage === 'jexl'
87
- ) {
88
- return true;
89
- }
90
- return false;
91
+ function checkJexl(typeInformation) {
92
+ if (
93
+ config.getConfig().defaultExpressionLanguage === 'jexl' &&
94
+ typeInformation.expressionLanguage &&
95
+ typeInformation.expressionLanguage !== 'legacy'
96
+ ) {
97
+ return true;
98
+ } else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
99
+ return true;
100
+ } else if (
101
+ config.getConfig().defaultExpressionLanguage === 'legacy' &&
102
+ typeInformation.expressionLanguage &&
103
+ typeInformation.expressionLanguage === 'jexl'
104
+ ) {
105
+ return true;
91
106
  }
107
+ return false;
108
+ }
92
109
 
110
+ function update(entity, typeInformation, callback) {
93
111
  function processEntityUpdateNgsi2(attributes) {
94
112
  let parser = legacyParser;
95
113
  if (checkJexl(typeInformation)) {
@@ -114,15 +132,31 @@ function update(entity, typeInformation, callback) {
114
132
 
115
133
  try {
116
134
  logger.debug(context, 'expressionPlugin entity %j', entity);
117
- let attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
118
- attsArray = processEntityUpdateNgsi2(attsArray);
119
- entity = utils.createNgsi2Entity(entity.id, entity.type, attsArray, true);
135
+ const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
136
+ // Exclude processing all attr expressions when current attr is of type 'commandStatus' or 'commandResult'
137
+ const attsArrayFiltered = attsArray.filter((obj) => {
138
+ return ![constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
139
+ });
140
+ const attsArrayCmd = attsArray.filter((obj) => {
141
+ // just attr of type 'commandStatus' or 'commandResult'
142
+ return [constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
143
+ });
144
+ let attsArrayFinal = [];
145
+ if (attsArrayFiltered.length > 0) {
146
+ attsArrayFinal = processEntityUpdateNgsi2(attsArrayFiltered);
147
+ }
148
+ attsArrayFinal = attsArrayFinal.concat(attsArrayCmd);
149
+ entity = utils.createNgsi2Entity(entity.id, entity.type, attsArrayFinal, true);
120
150
 
121
151
  callback(null, entity, typeInformation);
122
152
  } catch (e) {
153
+ logger.error(context, 'expressionPlugin error %j procesing entity %j', e, entity);
123
154
  callback(e);
124
155
  }
125
156
  }
126
157
 
127
158
  exports.update = update;
128
159
  exports.setJEXLTransforms = setJEXLTransforms;
160
+ exports.applyExpression = applyExpression;
161
+ exports.extractContext = extractContext;
162
+ exports.checkJexl = checkJexl;
@@ -30,7 +30,7 @@ const _ = require('underscore');
30
30
  const constants = require('../constants');
31
31
  const legacyParser = require('./expressionParser');
32
32
  const jexlParser = require('./jexlParser');
33
- const config = require('../commonConfig');
33
+ const expressionPlugin = require('./expressionPlugin');
34
34
  /* eslint-disable-next-line no-unused-vars */
35
35
  const logger = require('logops');
36
36
  /* eslint-disable-next-line no-unused-vars */
@@ -41,25 +41,6 @@ const utils = require('./pluginUtils');
41
41
  /* eslint-disable-next-line no-unused-vars */
42
42
  const aliasPlugin = require('./attributeAlias');
43
43
 
44
- function checkJexl(typeInformation) {
45
- if (
46
- config.getConfig().defaultExpressionLanguage === 'jexl' &&
47
- typeInformation.expressionLanguage &&
48
- typeInformation.expressionLanguage !== 'legacy'
49
- ) {
50
- return true;
51
- } else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
52
- return true;
53
- } else if (
54
- config.getConfig().defaultExpressionLanguage === 'legacy' &&
55
- typeInformation.expressionLanguage &&
56
- typeInformation.expressionLanguage === 'jexl'
57
- ) {
58
- return true;
59
- }
60
- return false;
61
- }
62
-
63
44
  function hasEntityName(item) {
64
45
  return item.entity_name;
65
46
  }
@@ -226,7 +207,7 @@ function propagateTimestamp(entity, entities) {
226
207
 
227
208
  function updateAttribute(entity, typeInformation, callback) {
228
209
  let parser = legacyParser;
229
- if (checkJexl(typeInformation)) {
210
+ if (expressionPlugin.checkJexl(typeInformation)) {
230
211
  parser = jexlParser;
231
212
  }
232
213
  const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
@@ -149,13 +149,19 @@ function getDevice(id, service, subservice, callback) {
149
149
  }
150
150
  }
151
151
 
152
- function getByName(name, service, subservice, callback) {
152
+ function getByNameAndType(name, type, service, subservice, callback) {
153
153
  const devices = _.values(registeredDevices[service]);
154
154
  let device;
155
155
 
156
156
  for (let i = 0; i < devices.length; i++) {
157
157
  if (devices[i].name === name) {
158
- device = devices[i];
158
+ if (type) {
159
+ if (devices[i].type === type) {
160
+ device = devices[i];
161
+ }
162
+ } else {
163
+ device = devices[i];
164
+ }
159
165
  }
160
166
  }
161
167
 
@@ -166,6 +172,10 @@ function getByName(name, service, subservice, callback) {
166
172
  }
167
173
  }
168
174
 
175
+ function getByName(name, service, subservice, callback) {
176
+ getByNameAndType(name, null, service, subservice, callback);
177
+ }
178
+
169
179
  function update(device, callback) {
170
180
  registeredDevices[device.service][device.id] = deepClone(device);
171
181
  callback(null, device);
@@ -208,4 +218,5 @@ exports.list = listDevices;
208
218
  exports.get = getDevice;
209
219
  exports.getSilently = getDevice;
210
220
  exports.getByName = getByName;
221
+ exports.getByNameAndType = getByNameAndType;
211
222
  exports.clear = clear;
@@ -243,15 +243,18 @@ function getDevice(id, service, subservice, callback) {
243
243
  });
244
244
  }
245
245
 
246
- function getByName(name, service, servicepath, callback) {
246
+ function getByNameAndType(name, type, service, servicepath, callback) {
247
247
  context = fillService(context, { service, subservice: servicepath });
248
- logger.debug(context, 'Looking for device with name [%s].', name);
249
-
250
- const query = Device.model.findOne({
251
- name,
252
- service,
248
+ let optionsQuery = {
249
+ name: name,
250
+ service: service,
253
251
  subservice: servicepath
254
- });
252
+ };
253
+ if (type) {
254
+ optionsQuery.type = type;
255
+ }
256
+ logger.debug(context, 'Looking for device with [%j].', optionsQuery);
257
+ const query = Device.model.findOne(optionsQuery);
255
258
 
256
259
  query.select({ __v: 0 });
257
260
 
@@ -270,6 +273,10 @@ function getByName(name, service, servicepath, callback) {
270
273
  });
271
274
  }
272
275
 
276
+ function getByName(name, service, servicepath, callback) {
277
+ getByNameAndType(name, null, service, servicepath, callback);
278
+ }
279
+
273
280
  /**
274
281
  * Updates the given device into the database.
275
282
  * updated.
@@ -361,4 +368,5 @@ exports.list = alarmsInt(constants.MONGO_ALARM, listDevices);
361
368
  exports.get = alarmsInt(constants.MONGO_ALARM, getDevice);
362
369
  exports.getSilently = getDevice;
363
370
  exports.getByName = alarmsInt(constants.MONGO_ALARM, getByName);
371
+ exports.getByNameAndType = alarmsInt(constants.MONGO_ALARM, getByNameAndType);
364
372
  exports.clear = alarmsInt(constants.MONGO_ALARM, clear);
@@ -214,7 +214,13 @@ function findConfigurationGroup(deviceObj, callback) {
214
214
  } else {
215
215
  config
216
216
  .getGroupRegistry()
217
- .findTypeSilently(deviceObj.service, deviceObj.subservice, deviceObj.type, deviceObj.apikey, handlerGroupFindByType);
217
+ .findTypeSilently(
218
+ deviceObj.service,
219
+ deviceObj.subservice,
220
+ deviceObj.type,
221
+ deviceObj.apikey,
222
+ handlerGroupFindByType
223
+ );
218
224
  }
219
225
  }
220
226
 
@@ -513,6 +519,18 @@ function getDeviceByName(deviceName, service, subservice, callback) {
513
519
  config.getRegistry().getByName(deviceName, service, subservice, callback);
514
520
  }
515
521
 
522
+ /**
523
+ * Retrieve a device from the registry based on its entity name and type
524
+ *
525
+ * @param {String} deviceName Name of the entity associated to a device.
526
+ * @param {String} deviceType Type of the entity associated to a device.
527
+ * @param {String} service Service the device belongs to.
528
+ * @param {String} subservice Division inside the service.
529
+ */
530
+ function getDeviceByNameAndType(deviceName, deviceType, service, subservice, callback) {
531
+ config.getRegistry().getByNameAndType(deviceName, deviceType, service, subservice, callback);
532
+ }
533
+
516
534
  /**
517
535
  * Retrieve a device from the registry based on the value of a given attribute.
518
536
  *
@@ -581,7 +599,12 @@ function findOrCreate(deviceId, group, callback) {
581
599
  callback(error, device, group);
582
600
  });
583
601
  } else {
584
- logger.info(context, 'Device %j not provisioned due autoprovision is disabled by its conf %j', newDevice, group);
602
+ logger.info(
603
+ context,
604
+ 'Device %j not provisioned due autoprovision is disabled by its conf %j',
605
+ newDevice,
606
+ group
607
+ );
585
608
  callback(new errors.DeviceNotFound(deviceId));
586
609
  }
587
610
  } else {
@@ -632,6 +655,7 @@ exports.getDevice = intoTrans(context, checkRegistry)(getDevice);
632
655
  exports.getDeviceSilently = intoTrans(context, checkRegistry)(getDeviceSilently);
633
656
  exports.getDevicesByAttribute = intoTrans(context, checkRegistry)(getDevicesByAttribute);
634
657
  exports.getDeviceByName = intoTrans(context, checkRegistry)(getDeviceByName);
658
+ exports.getDeviceByNameAndType = intoTrans(context, checkRegistry)(getDeviceByNameAndType);
635
659
  exports.register = intoTrans(context, registerDevice);
636
660
  exports.updateRegister = intoTrans(context, updateRegisterDevice);
637
661
  exports.unregister = intoTrans(context, unregisterDevice);
@@ -410,8 +410,9 @@ function addLinkedEntities(typeInformation, json) {
410
410
  * @param {Object} typeInformation Configuration information for the device.
411
411
  */
412
412
  function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
413
- var explicitAttrsList = [];
413
+ let explicitAttrsList;
414
414
  if (typeInformation.explicitAttrs) {
415
+ explicitAttrsList = [];
415
416
  explicitAttrsList.push('type');
416
417
  explicitAttrsList.push('id');
417
418
  if (typeof typeInformation.explicitAttrs === 'boolean') {
@@ -437,7 +438,7 @@ function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
437
438
  });
438
439
  }
439
440
  }
440
- if (explicitAttrsList.length > 0) {
441
+ if (explicitAttrsList && explicitAttrsList.length >= 0) {
441
442
  entities.forEach((entity) => {
442
443
  const hidden = _.difference(_.keys(entity), explicitAttrsList);
443
444
  hidden.forEach((attr) => {
@@ -457,8 +458,9 @@ function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
457
458
  function removeHiddenAttrs(result, typeInformation) {
458
459
  delete result.id;
459
460
  delete result.type;
460
- var explicitAttrsList = [];
461
+ let explicitAttrsList;
461
462
  if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
463
+ explicitAttrsList = [];
462
464
  if (typeInformation.timestamp) {
463
465
  explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
464
466
  }
@@ -478,7 +480,7 @@ function removeHiddenAttrs(result, typeInformation) {
478
480
  const res = jexlParser.parse(typeInformation.explicitAttrs, ctx);
479
481
  explicitAttrsList = res;
480
482
  }
481
- if (explicitAttrsList.length > 0) {
483
+ if (explicitAttrsList && explicitAttrsList.length >= 0) {
482
484
  const hidden = _.difference(_.keys(result), explicitAttrsList);
483
485
  hidden.forEach((attr) => {
484
486
  delete result[attr];
@@ -321,8 +321,9 @@ function sendQueryValueNgsi2(entityName, attributes, typeInformation, token, cal
321
321
  * @param {Object} typeInformation Configuration information for the device.
322
322
  */
323
323
  function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
324
- var explicitAttrsList = [];
324
+ let explicitAttrsList;
325
325
  if (typeInformation.explicitAttrs) {
326
+ explicitAttrsList = [];
326
327
  explicitAttrsList.push('type');
327
328
  explicitAttrsList.push('id');
328
329
  if (typeof typeInformation.explicitAttrs === 'boolean') {
@@ -349,10 +350,10 @@ function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
349
350
  });
350
351
  }
351
352
  }
352
- if (explicitAttrsList.length > 0) {
353
- logger.debug(context, 'removeHiddenAttrsFromMultiEntity %s', explicitAttrsList);
353
+ if (explicitAttrsList && explicitAttrsList.length >= 0) {
354
354
  entities.forEach((entity) => {
355
355
  const hidden = _.difference(_.keys(entity), explicitAttrsList);
356
+ logger.debug(context, 'removeHiddenAttrsFromMultiEntity %s from entity %s', hidden, entity);
356
357
  hidden.forEach((attr) => {
357
358
  delete entity[attr];
358
359
  });
@@ -369,8 +370,9 @@ function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
369
370
  function removeHiddenAttrs(result, typeInformation) {
370
371
  delete result.id;
371
372
  delete result.type;
372
- var explicitAttrsList = [];
373
+ let explicitAttrsList;
373
374
  if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
375
+ explicitAttrsList = [];
374
376
  if (typeInformation.timestamp) {
375
377
  explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
376
378
  }
@@ -390,9 +392,9 @@ function removeHiddenAttrs(result, typeInformation) {
390
392
  const res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctx, typeInformation);
391
393
  explicitAttrsList = res;
392
394
  }
393
- if (explicitAttrsList.length > 0) {
394
- logger.debug(context, 'removeHiddenAttrs %s', explicitAttrsList);
395
+ if (explicitAttrsList && explicitAttrsList.length >= 0) {
395
396
  const hidden = _.difference(_.keys(result), explicitAttrsList);
397
+ logger.debug(context, 'removeHiddenAttrs %s', hidden);
396
398
  hidden.forEach((attr) => {
397
399
  delete result[attr];
398
400
  });
@@ -177,7 +177,7 @@ function executeWithDeviceInformation(operationFunction) {
177
177
 
178
178
  /**
179
179
  * Update the result of a command in the Context Broker. The result of the command has two components: the result
180
- * of the command itself will be represented with the sufix '_result' in the entity while the status is updated in the
180
+ * of the command itself will be represented with the sufix '_info' in the entity while the status is updated in the
181
181
  * attribute with the '_status' sufix.
182
182
  *
183
183
  * @param {String} entityName Name of the entity holding the command.
@@ -234,7 +234,7 @@ function setCommandResult(
234
234
  }
235
235
 
236
236
  if (commandInfo.length === 1) {
237
- exports.update(entityName, resource, apikey, attributes, typeInformation, callback);
237
+ exports.update(entityName, typeInformation.type, apikey, attributes, typeInformation, callback);
238
238
  } else {
239
239
  callback(new errors.CommandNotFound(commandName));
240
240
  }
@@ -159,29 +159,32 @@ function generateUpdateActionsNgsi2(req, contextElement, callback) {
159
159
  callback(null, updateActions);
160
160
  }
161
161
 
162
- deviceService.getDeviceByName(entityId, req.headers['fiware-service'], req.headers['fiware-servicepath'], function (
163
- error,
164
- deviceObj
165
- ) {
166
- if (error) {
167
- callback(error);
168
- } else {
169
- async.waterfall(
170
- [
171
- apply(deviceService.findConfigurationGroup, deviceObj),
172
- apply(
173
- deviceService.mergeDeviceWithConfiguration,
174
- ['lazy', 'internalAttributes', 'active', 'staticAttributes', 'commands', 'subscriptions'],
175
- [null, null, [], [], [], [], []],
176
- deviceObj
177
- ),
178
- splitUpdates,
179
- createActionsArray
180
- ],
181
- callback
182
- );
162
+ deviceService.getDeviceByNameAndType(
163
+ entityId,
164
+ entityType,
165
+ req.headers['fiware-service'],
166
+ req.headers['fiware-servicepath'],
167
+ function (error, deviceObj) {
168
+ if (error) {
169
+ callback(error);
170
+ } else {
171
+ async.waterfall(
172
+ [
173
+ apply(deviceService.findConfigurationGroup, deviceObj),
174
+ apply(
175
+ deviceService.mergeDeviceWithConfiguration,
176
+ ['lazy', 'internalAttributes', 'active', 'staticAttributes', 'commands', 'subscriptions'],
177
+ [null, null, [], [], [], [], []],
178
+ deviceObj
179
+ ),
180
+ splitUpdates,
181
+ createActionsArray
182
+ ],
183
+ callback
184
+ );
185
+ }
183
186
  }
184
- });
187
+ );
185
188
  }
186
189
 
187
190
  /** Express middleware to manage incoming update requests using NGSIv2.
@@ -238,8 +241,7 @@ function defaultQueryHandlerNgsi2(id, type, service, subservice, attributes, cal
238
241
  type,
239
242
  id
240
243
  };
241
-
242
- deviceService.getDeviceByName(id, service, subservice, function (error, ngsiDevice) {
244
+ deviceService.getDeviceByNameAndType(id, type, service, subservice, function (error, ngsiDevice) {
243
245
  if (error) {
244
246
  callback(error);
245
247
  } else {
@@ -285,8 +287,9 @@ function handleNotificationNgsi2(req, res, next) {
285
287
  }
286
288
  }
287
289
  }
288
- deviceService.getDeviceByName(
290
+ deviceService.getDeviceByNameAndType(
289
291
  dataElement.id,
292
+ dataElement.type,
290
293
  req.headers['fiware-service'],
291
294
  req.headers['fiware-servicepath'],
292
295
  function (error, device) {
@@ -448,6 +451,7 @@ function handleQueryNgsi2(req, res, next) {
448
451
  }
449
452
 
450
453
  deviceService.findConfigurationGroup(device, function (error, group) {
454
+ logger.debug(context, 'finishQueryForDevice %j', group);
451
455
  const executeCompleteAttributes = apply(completeAttributes, attributes, group);
452
456
  const executeQueryHandler = apply(
453
457
  actualHandler,
@@ -474,8 +478,9 @@ function handleQueryNgsi2(req, res, next) {
474
478
 
475
479
  if (contextEntity.id) {
476
480
  getFunction = apply(
477
- deviceService.getDeviceByName,
481
+ deviceService.getDeviceByNameAndType,
478
482
  contextEntity.id,
483
+ contextEntity.type,
479
484
  req.headers['fiware-service'],
480
485
  req.headers['fiware-servicepath']
481
486
  );
@@ -504,7 +509,7 @@ function handleQueryNgsi2(req, res, next) {
504
509
  } else {
505
510
  deviceList = [innerDevice];
506
511
  }
507
-
512
+ logger.debug(context, 'handleFindDevice from %j', deviceList);
508
513
  async.map(
509
514
  deviceList,
510
515
  async.apply(finishQueryForDevice, attributes, contextEntity, actualHandler),
@@ -91,7 +91,7 @@ function executeUpdateSideEffects(device, id, type, service, subservice, attribu
91
91
  ];
92
92
 
93
93
  sideEffects.push(
94
- apply(ngsi.update, device.name, device.resource, device.apikey, newAttributes, device)
94
+ apply(ngsi.update, device.name, device.type, device.apikey, newAttributes, device)
95
95
  );
96
96
  }
97
97
  }
@@ -184,6 +184,8 @@ function attributeToProvisioningAPIFormat(attribute) {
184
184
  entity_name: attribute.entity_name,
185
185
  entity_type: attribute.entity_type,
186
186
  mqtt: attribute.mqtt,
187
+ payloadType: attribute.payloadType,
188
+ contentType: attribute.contentType,
187
189
  metadata: attribute.metadata
188
190
  };
189
191
  }
@@ -173,6 +173,18 @@
173
173
  "type": "string",
174
174
  "pattern": "^([^<>();'=\"]+)*$"
175
175
  },
176
+ "expression": {
177
+ "description": "Optional expression for command transformation",
178
+ "type": "string"
179
+ },
180
+ "payloadType": {
181
+ "description": "Payload type",
182
+ "type": "string"
183
+ },
184
+ "contentType": {
185
+ "description": "Content type",
186
+ "type": "string"
187
+ },
176
188
  "mqtt": {
177
189
  "description": "Mqtt properties",
178
190
  "type": "object",
@@ -158,6 +158,18 @@
158
158
  "type": "string",
159
159
  "pattern": "^([^<>();'=\"]+)*$"
160
160
  },
161
+ "expression": {
162
+ "description": "Optional expression for command transformation",
163
+ "type": "string"
164
+ },
165
+ "payloadType": {
166
+ "description": "Payload type",
167
+ "type": "string"
168
+ },
169
+ "contentType": {
170
+ "description": "Content type",
171
+ "type": "string"
172
+ },
161
173
  "mqtt": {
162
174
  "description": "Mqtt properties",
163
175
  "type": "object",
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": "2.20.0",
5
+ "version": "2.21.0",
6
6
  "homepage": "https://github.com/telefonicaid/iotagent-node-lib",
7
7
  "keywords": [
8
8
  "fiware",
@@ -42,17 +42,17 @@
42
42
  "watch": "watch 'npm test && npm run lint' ./lib ./test"
43
43
  },
44
44
  "dependencies": {
45
- "async": "2.6.2",
45
+ "async": "2.6.4",
46
46
  "body-parser": "~1.19.0",
47
47
  "express": "~4.16.4",
48
48
  "got": "~11.8.2",
49
49
  "jexl": "2.3.0",
50
50
  "jison": "0.4.18",
51
51
  "logops": "2.1.2",
52
- "moment": "~2.24.0",
52
+ "moment": "~2.29.2",
53
53
  "moment-timezone": "~0.5.25",
54
- "mongodb": "3.6.8",
55
- "mongoose": "5.7.7",
54
+ "mongodb": "3.6.12",
55
+ "mongoose": "5.7.14",
56
56
  "query-string": "6.5.0",
57
57
  "revalidator": "~0.3.1",
58
58
  "underscore": "~1.12.1",
@@ -0,0 +1 @@
1
+ {"myattr":{"type": "String","value": "location"}}
@@ -319,7 +319,7 @@ const iotAgentConfig = {
319
319
  expression: "{coordinates: [lon,lat], type: 'Point'}"
320
320
  }
321
321
  ],
322
- explicitAttrs: '[ myattr ]'
322
+ explicitAttrs: "[ 'myattr' ]"
323
323
  },
324
324
  GPS6: {
325
325
  commands: [],
@@ -345,6 +345,30 @@ const iotAgentConfig = {
345
345
  }
346
346
  ],
347
347
  explicitAttrs: true
348
+ },
349
+ GPS7: {
350
+ commands: [],
351
+ type: 'GPS',
352
+ lazy: [],
353
+ static: [
354
+ {
355
+ name: 'color',
356
+ type: 'string',
357
+ value: 'blue'
358
+ }
359
+ ],
360
+ active: [
361
+ {
362
+ name: 'price',
363
+ type: 'number'
364
+ },
365
+ {
366
+ name: 'location',
367
+ type: 'geo:json',
368
+ expression: "{coordinates: [lon,lat], type: 'Point'}"
369
+ }
370
+ ],
371
+ explicitAttrs: '[ ]'
348
372
  }
349
373
  },
350
374
  service: 'smartgondor',
@@ -1180,7 +1204,7 @@ describe('Java expression language (JEXL) based transformations plugin', functio
1180
1204
  .patch(
1181
1205
  '/v2/entities/gps1/attrs',
1182
1206
  utils.readExampleFile(
1183
- './test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json'
1207
+ './test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json'
1184
1208
  )
1185
1209
  )
1186
1210
  .query({ type: 'GPS' })
@@ -1230,6 +1254,43 @@ describe('Java expression language (JEXL) based transformations plugin', functio
1230
1254
  });
1231
1255
  });
1232
1256
  });
1257
+
1258
+ describe('When there is an extra TimeInstant sent by the device to be removed by jexl expression with context but with empty explicitAttrs', function () {
1259
+ // Case: Expression which results is sent as a new attribute
1260
+ const values = [
1261
+ {
1262
+ name: 'lat',
1263
+ type: 'Number',
1264
+ value: 52
1265
+ },
1266
+ {
1267
+ name: 'lon',
1268
+ type: 'Number',
1269
+ value: 13
1270
+ },
1271
+ {
1272
+ name: 'myattr',
1273
+ type: 'String',
1274
+ value: 'location'
1275
+ },
1276
+ {
1277
+ name: 'TimeInstant',
1278
+ type: 'DateTime',
1279
+ value: '2015-08-05T07:35:01.468+00:00'
1280
+ }
1281
+ ];
1282
+
1283
+ beforeEach(function () {
1284
+ nock.cleanAll();
1285
+ });
1286
+
1287
+ it('should calculate them and remove non-explicitAttrs by jexl expression with context from the payload ', function (done) {
1288
+ iotAgentLib.update('gps1', 'GPS7', '', values, function (error) {
1289
+ should.not.exist(error);
1290
+ done();
1291
+ });
1292
+ });
1293
+ });
1233
1294
  });
1234
1295
 
1235
1296
  describe('Java expression language (JEXL) based transformations plugin - Timestamps', function () {