iotagent-node-lib 2.15.1 → 2.19.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 (448) hide show
  1. package/.github/workflows/ci.yml +17 -1
  2. package/.readthedocs.yml +7 -0
  3. package/CHANGES_NEXT_RELEASE +0 -1
  4. package/README.md +1 -0
  5. package/doc/Contribution.md +60 -1
  6. package/doc/advanced-topics.md +136 -14
  7. package/doc/api.md +38 -22
  8. package/doc/apiary/iotagent.apib +1 -3
  9. package/doc/architecture.md +2 -2
  10. package/doc/config-basic-example.js +0 -1
  11. package/doc/deprecated.md +2 -2
  12. package/doc/development.md +13 -6
  13. package/doc/expressionLanguage.md +519 -277
  14. package/doc/getting-started.md +0 -1
  15. package/doc/howto.md +13 -17
  16. package/doc/installationguide.md +67 -69
  17. package/doc/usermanual.md +48 -16
  18. package/docker/Mosquitto/Dockerfile +4 -9
  19. package/docker/Mosquitto/README.md +2 -0
  20. package/docker/Mosquitto/startMosquitto.sh +18 -1
  21. package/lib/command/commandLine.js +1 -1
  22. package/lib/commonConfig.js +5 -14
  23. package/lib/errors.js +0 -16
  24. package/lib/fiware-iotagent-lib.js +5 -0
  25. package/lib/jexlTranformsMap.js +71 -0
  26. package/lib/model/Command.js +1 -1
  27. package/lib/model/Device.js +5 -2
  28. package/lib/model/Group.js +20 -2
  29. package/lib/model/dbConn.js +7 -1
  30. package/lib/plugins/attributeAlias.js +6 -13
  31. package/lib/plugins/bidirectionalData.js +2 -27
  32. package/lib/plugins/expressionParser.js +41 -17
  33. package/lib/plugins/expressionPlugin.js +21 -27
  34. package/lib/plugins/jexlParser.js +71 -67
  35. package/lib/plugins/multiEntity.js +68 -147
  36. package/lib/plugins/pluginUtils.js +12 -48
  37. package/lib/plugins/timestampProcessPlugin.js +1 -46
  38. package/lib/request-shim.js +111 -0
  39. package/lib/services/common/domain.js +2 -2
  40. package/lib/services/common/genericMiddleware.js +6 -2
  41. package/lib/services/common/iotManagerService.js +1 -1
  42. package/lib/services/common/securityServiceKeystone.js +1 -1
  43. package/lib/services/common/securityServiceOAuth2.js +3 -2
  44. package/lib/services/devices/deviceRegistryMongoDB.js +29 -22
  45. package/lib/services/devices/deviceService.js +56 -33
  46. package/lib/services/devices/devices-NGSI-LD.js +42 -14
  47. package/lib/services/devices/devices-NGSI-mixed.js +3 -3
  48. package/lib/services/devices/devices-NGSI-v2.js +48 -24
  49. package/lib/services/devices/registrationUtils.js +1 -134
  50. package/lib/services/groups/groupRegistryMemory.js +2 -0
  51. package/lib/services/groups/groupRegistryMongoDB.js +58 -76
  52. package/lib/services/groups/groupService.js +1 -1
  53. package/lib/services/ngsi/entities-NGSI-LD.js +205 -124
  54. package/lib/services/ngsi/entities-NGSI-v2.js +142 -32
  55. package/lib/services/ngsi/ngsiService.js +4 -6
  56. package/lib/services/ngsi/ngsiUtils.js +0 -21
  57. package/lib/services/ngsi/subscription-NGSI-LD.js +14 -5
  58. package/lib/services/ngsi/subscriptionService.js +1 -4
  59. package/lib/services/northBound/contextServer-NGSI-LD.js +7 -2
  60. package/lib/services/northBound/contextServer-NGSI-v2.js +25 -13
  61. package/lib/services/northBound/contextServer.js +1 -4
  62. package/lib/services/northBound/contextServerUtils.js +0 -81
  63. package/lib/services/northBound/deviceProvisioningServer.js +36 -9
  64. package/lib/services/northBound/northboundServer.js +2 -0
  65. package/lib/services/northBound/restUtils.js +2 -39
  66. package/lib/templates/updateDevice.json +4 -0
  67. package/lib/templates/updateDeviceLax.json +4 -0
  68. package/package.json +13 -13
  69. package/test/tools/utils.js +2 -0
  70. package/test/unit/examples/deviceProvisioningRequests/provisionAnotherDevice.json +1 -1
  71. package/test/unit/examples/deviceProvisioningRequests/provisionDeviceMissingParameters.json +1 -1
  72. package/test/unit/examples/deviceProvisioningRequests/provisionDuplicatedDev.json +1 -1
  73. package/test/unit/examples/deviceProvisioningRequests/provisionFullDevice.json +1 -1
  74. package/test/unit/examples/deviceProvisioningRequests/provisionNewDeviceMalformed1.json +1 -1
  75. package/test/unit/examples/groupProvisioningRequests/multipleGroupsCreation.json +1 -1
  76. package/test/unit/examples/iotamRequests/registrationEmpty.json +1 -1
  77. package/test/unit/examples/iotamRequests/registrationWithGroups.json +2 -2
  78. package/test/unit/examples/iotamRequests/registrationWithGroupsWithoutCB.json +2 -2
  79. package/test/unit/examples/iotamRequests/registrationWithStaticGroups.json +2 -2
  80. package/test/unit/expressions/jexlExpression-test.js +119 -0
  81. package/test/unit/general/config-multi-core-test.js +1 -1
  82. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +30 -77
  83. package/test/unit/general/deviceService-test.js +19 -35
  84. package/test/unit/general/loglevel-api_test.js +8 -13
  85. package/test/unit/general/migration-test.js +1 -0
  86. package/test/unit/general/startup-test.js +3 -2
  87. package/test/unit/general/statistics-persistence_test.js +2 -2
  88. package/test/unit/general/statistics-service_test.js +2 -2
  89. package/test/unit/lazyAndCommands/commandRegistry_test.js +18 -18
  90. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +16 -24
  91. package/test/unit/mongodb/mongodb-connectionoptions-test.js +2 -2
  92. package/test/unit/mongodb/mongodb-group-registry-test.js +11 -11
  93. package/test/unit/mongodb/mongodb-registry-test.js +55 -131
  94. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent1.json +1 -1
  95. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent2.json +1 -1
  96. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent4.json +1 -1
  97. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +1 -1
  98. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice.json +1 -1
  99. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice2.json +1 -1
  100. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup.json +1 -1
  101. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup2.json +1 -1
  102. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup3.json +1 -1
  103. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateCommands1.json +1 -1
  104. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent1.json +1 -1
  105. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent2.json +1 -1
  106. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent3.json +1 -1
  107. package/test/unit/ngsi-ld/examples/contextRequests/createMinimumProvisionedDevice.json +0 -7
  108. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDevice.json +0 -7
  109. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceMultientity.json +0 -7
  110. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json +0 -14
  111. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json +0 -14
  112. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic3.json +1 -15
  113. package/test/unit/ngsi-ld/examples/contextRequests/createTimeInstantMinimumDevice.json +0 -7
  114. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin6.json +0 -7
  115. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast5.json +0 -7
  116. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin12a.json +7 -0
  117. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin13.json +13 -13
  118. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin16.json +0 -7
  119. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin17.json +0 -7
  120. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +18 -0
  121. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +18 -0
  122. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin31.json +15 -0
  123. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin32.json +17 -0
  124. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin33.json +18 -0
  125. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin34.json +17 -0
  126. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +36 -0
  127. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin5.json +0 -7
  128. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin7.json +16 -16
  129. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin8a.json +18 -0
  130. package/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties3.json +0 -7
  131. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
  132. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +6 -6
  133. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +3 -3
  134. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionActiveAttributes1.json +0 -7
  135. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionCommands1.json +0 -7
  136. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionDeviceStatic.json +0 -7
  137. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionMinimumDevice.json +0 -7
  138. package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalSubscriptionRequest.json +20 -17
  139. package/test/unit/ngsi-ld/examples/subscriptionRequests/simpleSubscriptionRequest.json +18 -15
  140. package/test/unit/ngsi-ld/examples/subscriptionRequests/simpleSubscriptionRequest2.json +18 -15
  141. package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +21 -21
  142. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1018 -0
  143. package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +2 -2
  144. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +24 -24
  145. package/test/unit/ngsi-ld/general/deviceService-test.js +7 -7
  146. package/test/unit/ngsi-ld/general/https-support-test.js +14 -13
  147. package/test/unit/ngsi-ld/general/iotam-autoregistration-test.js +8 -7
  148. package/test/unit/ngsi-ld/general/startup-test.js +2 -1
  149. package/test/unit/ngsi-ld/lazyAndCommands/active-devices-attribute-update-test.js +10 -8
  150. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +11 -12
  151. package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +25 -29
  152. package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +16 -15
  153. package/test/unit/ngsi-ld/ngsiService/active-devices-test.js +18 -17
  154. package/test/unit/ngsi-ld/ngsiService/autocast-test.js +13 -12
  155. package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +10 -9
  156. package/test/unit/ngsi-ld/ngsiService/staticAttributes-test.js +3 -3
  157. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +25 -24
  158. package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +13 -12
  159. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +27 -26
  160. package/test/unit/ngsi-ld/plugins/compress-timestamp-plugin_test.js +5 -5
  161. package/test/unit/ngsi-ld/plugins/event-plugin_test.js +3 -3
  162. package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +75 -14
  163. package/test/unit/ngsi-ld/plugins/timestamp-processing-plugin_test.js +3 -3
  164. package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +54 -53
  165. package/test/unit/ngsi-ld/provisioning/device-registration_test.js +20 -19
  166. package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +23 -22
  167. package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +58 -70
  168. package/test/unit/ngsi-ld/provisioning/provisionDeviceMultientity-test.js +8 -7
  169. package/test/unit/ngsi-ld/provisioning/removeProvisionedDevice-test.js +19 -19
  170. package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +18 -20
  171. package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +43 -49
  172. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +16 -14
  173. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent1.json +1 -1
  174. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent2.json +1 -1
  175. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent3.json +19 -0
  176. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent4.json +1 -1
  177. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgent5.json +19 -0
  178. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +1 -1
  179. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerProvisionedDevice.json +1 -1
  180. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerProvisionedDevice2.json +1 -1
  181. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup.json +1 -1
  182. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup2.json +1 -1
  183. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup3.json +1 -1
  184. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/updateCommands1.json +3 -3
  185. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/updateIoTAgent1.json +3 -3
  186. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/updateIoTAgent2.json +1 -1
  187. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/updateIoTAgent3.json +1 -1
  188. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin1.json +17 -1
  189. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin11.json +5 -1
  190. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin12.json +2 -2
  191. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin15.json +6 -0
  192. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin16.json +14 -0
  193. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin17.json +14 -0
  194. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin18.json +6 -0
  195. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin19.json +6 -0
  196. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin2.json +13 -1
  197. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin20.json +6 -0
  198. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin21.json +6 -0
  199. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin22.json +6 -0
  200. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin23.json +6 -0
  201. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin24.json +6 -0
  202. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin25.json +6 -0
  203. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin26.json +6 -0
  204. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin27.json +6 -0
  205. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin28.json +6 -0
  206. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin29.json +22 -0
  207. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin3.json +5 -1
  208. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin30.json +6 -0
  209. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin31.json +18 -0
  210. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +16 -0
  211. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin33.json +22 -0
  212. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +12 -0
  213. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin4.json +12 -0
  214. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin5.json +5 -1
  215. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin6.json +5 -1
  216. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin7.json +5 -1
  217. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin8.json +17 -1
  218. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin9.json +5 -1
  219. package/test/unit/ngsiv2/examples/contextRequests/updateContextMiddleware1.json +10 -0
  220. package/test/unit/ngsiv2/examples/contextRequests/updateContextMiddleware2.json +10 -0
  221. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin1.json +2 -2
  222. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin10.json +1 -1
  223. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin11.json +13 -0
  224. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin12.json +33 -0
  225. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin13.json +33 -0
  226. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin14.json +33 -0
  227. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
  228. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +25 -0
  229. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin2.json +2 -2
  230. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin3.json +1 -1
  231. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin4.json +1 -1
  232. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin5.json +1 -1
  233. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin6.json +1 -1
  234. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin7.json +1 -1
  235. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin8.json +2 -2
  236. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin9.json +1 -1
  237. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +8 -8
  238. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +4 -4
  239. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -1
  240. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +2 -2
  241. package/test/unit/ngsiv2/examples/contextRequests/updateProvisionDevice.json +20 -0
  242. package/test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalSubscriptionRequest.json +1 -1
  243. package/test/unit/ngsiv2/examples/subscriptionRequests/simpleSubscriptionRequest.json +1 -1
  244. package/test/unit/ngsiv2/examples/subscriptionRequests/simpleSubscriptionRequest2.json +1 -1
  245. package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +93 -60
  246. package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +16 -16
  247. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +548 -164
  248. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +34 -33
  249. package/test/unit/ngsiv2/general/deviceService-test.js +15 -14
  250. package/test/unit/ngsiv2/general/https-support-test.js +14 -13
  251. package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +8 -7
  252. package/test/unit/ngsiv2/general/startup-test.js +2 -1
  253. package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +9 -7
  254. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +13 -12
  255. package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +323 -51
  256. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +22 -20
  257. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +114 -26
  258. package/test/unit/ngsiv2/ngsiService/autocast-test.js +16 -16
  259. package/test/unit/ngsiv2/ngsiService/geoproperties-test.js +39 -38
  260. package/test/unit/{ngsiService → ngsiv2/ngsiService}/queryDeviceInformationInCb-test.js +25 -54
  261. package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +5 -5
  262. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +25 -24
  263. package/test/unit/ngsiv2/plugins/alias-plugin_test.js +22 -22
  264. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +27 -26
  265. package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +7 -7
  266. package/test/unit/ngsiv2/plugins/event-plugin_test.js +4 -4
  267. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +549 -24
  268. package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +4 -4
  269. package/test/unit/{plugins → ngsiv2/plugins}/translation-inPlugins_test.js +25 -25
  270. package/test/unit/{provisioning → ngsiv2/provisioning}/device-group-api-test.js +44 -75
  271. package/test/unit/{provisioning/device-group-utils_test.js → ngsiv2/provisioning/device-group-utils-test.js} +15 -14
  272. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +74 -73
  273. package/test/unit/ngsiv2/provisioning/device-registration_test.js +20 -19
  274. package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +30 -29
  275. package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +58 -69
  276. package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +8 -7
  277. package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +19 -19
  278. package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +18 -19
  279. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +49 -52
  280. package/test/unit/plugins/capture-configuration-inPlugins_test.js +6 -4
  281. package/test/unit/plugins/capture-provision-inPlugins_test.js +13 -23
  282. package/.nyc_output/016aca19-69bf-4681-9b41-2835c5131681.json +0 -1
  283. package/.nyc_output/processinfo/016aca19-69bf-4681-9b41-2835c5131681.json +0 -1
  284. package/.nyc_output/processinfo/index.json +0 -1
  285. package/lib/services/devices/devices-NGSI-v1.js +0 -293
  286. package/lib/services/devices/registrationUtils.js_avega +0 -589
  287. package/lib/services/ngsi/entities-NGSI-v1.js +0 -300
  288. package/lib/services/ngsi/subscription-NGSI-v1.js +0 -234
  289. package/lib/services/northBound/contextServer-NGSI-v1.js +0 -526
  290. package/lib/templates/notificationTemplateNgsi1.json +0 -75
  291. package/lib/templates/updateContextNgsi1.json +0 -50
  292. package/test/unit/examples/contextAvailabilityRequests/registerIoTAgent1.json +0 -21
  293. package/test/unit/examples/contextAvailabilityRequests/registerIoTAgent2.json +0 -21
  294. package/test/unit/examples/contextAvailabilityRequests/registerIoTAgent3.json +0 -25
  295. package/test/unit/examples/contextAvailabilityRequests/registerIoTAgent4.json +0 -22
  296. package/test/unit/examples/contextAvailabilityRequests/registerIoTAgentAttributeUpdates.json +0 -16
  297. package/test/unit/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +0 -21
  298. package/test/unit/examples/contextAvailabilityRequests/registerNewDevice1.json +0 -21
  299. package/test/unit/examples/contextAvailabilityRequests/registerNewDevice2.json +0 -21
  300. package/test/unit/examples/contextAvailabilityRequests/registerProvisionedDevice.json +0 -25
  301. package/test/unit/examples/contextAvailabilityRequests/registerProvisionedDevice2.json +0 -21
  302. package/test/unit/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup.json +0 -33
  303. package/test/unit/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup2.json +0 -33
  304. package/test/unit/examples/contextAvailabilityRequests/unregisterDevice1.json +0 -22
  305. package/test/unit/examples/contextAvailabilityRequests/unregisterDevice3.json +0 -26
  306. package/test/unit/examples/contextAvailabilityRequests/unregisterProvisionedDevice.json +0 -26
  307. package/test/unit/examples/contextAvailabilityRequests/updateCommands1.json +0 -22
  308. package/test/unit/examples/contextAvailabilityRequests/updateIoTAgent1.json +0 -22
  309. package/test/unit/examples/contextAvailabilityRequests/updateIoTAgent2.json +0 -26
  310. package/test/unit/examples/contextAvailabilityRequests/updateIoTAgent3.json +0 -22
  311. package/test/unit/examples/contextAvailabilityResponses/registerIoTAgent1Failed.json +0 -8
  312. package/test/unit/examples/contextAvailabilityResponses/registerIoTAgent1Success.json +0 -4
  313. package/test/unit/examples/contextAvailabilityResponses/registerNewDevice1Success.json +0 -4
  314. package/test/unit/examples/contextAvailabilityResponses/registerNewDevice2Success.json +0 -4
  315. package/test/unit/examples/contextAvailabilityResponses/registerProvisionedDeviceSuccess.json +0 -4
  316. package/test/unit/examples/contextAvailabilityResponses/unregisterDevice1Failed.json +0 -8
  317. package/test/unit/examples/contextAvailabilityResponses/unregisterDevice1Success.json +0 -4
  318. package/test/unit/examples/contextAvailabilityResponses/updateCommands1Success.json +0 -4
  319. package/test/unit/examples/contextAvailabilityResponses/updateIoTAgent1Success.json +0 -6
  320. package/test/unit/examples/contextProviderResponses/queryInformationResponse.json +0 -22
  321. package/test/unit/examples/contextProviderResponses/queryInformationResponseEmptyAttributes.json +0 -22
  322. package/test/unit/examples/contextProviderResponses/queryInformationStaticAttributesResponse.json +0 -27
  323. package/test/unit/examples/contextProviderResponses/updateInformationResponse.json +0 -22
  324. package/test/unit/examples/contextProviderResponses/updateInformationResponse2.json +0 -22
  325. package/test/unit/examples/contextRequests/contextSubscriptionRequest.json +0 -20
  326. package/test/unit/examples/contextRequests/contextSubscriptionRequest2.json +0 -20
  327. package/test/unit/examples/contextRequests/createBidirectionalDevice.json +0 -17
  328. package/test/unit/examples/contextRequests/createDatetimeProvisionedDevice.json +0 -17
  329. package/test/unit/examples/contextRequests/createGeopointProvisionedDevice.json +0 -17
  330. package/test/unit/examples/contextRequests/createMinimumProvisionedDevice.json +0 -17
  331. package/test/unit/examples/contextRequests/createProvisionedDevice.json +0 -32
  332. package/test/unit/examples/contextRequests/createProvisionedDeviceMultientity.json +0 -32
  333. package/test/unit/examples/contextRequests/createProvisionedDeviceWithGroup.json +0 -47
  334. package/test/unit/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json +0 -52
  335. package/test/unit/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json +0 -52
  336. package/test/unit/examples/contextRequests/createTimeinstantDevice.json +0 -17
  337. package/test/unit/examples/contextRequests/queryContext1.json +0 -13
  338. package/test/unit/examples/contextRequests/queryContext2.json +0 -13
  339. package/test/unit/examples/contextRequests/queryContextAliasPlugin1.json +0 -10
  340. package/test/unit/examples/contextRequests/queryContextCompressTimestamp1.json +0 -13
  341. package/test/unit/examples/contextRequests/updateActiveAttributes.json +0 -11
  342. package/test/unit/examples/contextRequests/updateContext1.json +0 -22
  343. package/test/unit/examples/contextRequests/updateContext2.json +0 -22
  344. package/test/unit/examples/contextRequests/updateContext3.json +0 -17
  345. package/test/unit/examples/contextRequests/updateContext3WithStatic.json +0 -22
  346. package/test/unit/examples/contextRequests/updateContext4.json +0 -22
  347. package/test/unit/examples/contextRequests/updateContext5.json +0 -19
  348. package/test/unit/examples/contextRequests/updateContextAliasPlugin.json +0 -22
  349. package/test/unit/examples/contextRequests/updateContextAliasPlugin2.json +0 -17
  350. package/test/unit/examples/contextRequests/updateContextAppendMode.json +0 -22
  351. package/test/unit/examples/contextRequests/updateContextCommandError.json +0 -22
  352. package/test/unit/examples/contextRequests/updateContextCommandExpired.json +0 -22
  353. package/test/unit/examples/contextRequests/updateContextCommandFinish.json +0 -22
  354. package/test/unit/examples/contextRequests/updateContextCommandStatus.json +0 -17
  355. package/test/unit/examples/contextRequests/updateContextCompressTimestamp1.json +0 -22
  356. package/test/unit/examples/contextRequests/updateContextCompressTimestamp2.json +0 -29
  357. package/test/unit/examples/contextRequests/updateContextExpressionPlugin1.json +0 -17
  358. package/test/unit/examples/contextRequests/updateContextExpressionPlugin2.json +0 -27
  359. package/test/unit/examples/contextRequests/updateContextExpressionPlugin3.json +0 -17
  360. package/test/unit/examples/contextRequests/updateContextExpressionPlugin4.json +0 -17
  361. package/test/unit/examples/contextRequests/updateContextExpressionPlugin5.json +0 -27
  362. package/test/unit/examples/contextRequests/updateContextMiddleware1.json +0 -22
  363. package/test/unit/examples/contextRequests/updateContextMultiEntityPlugin3.json +0 -35
  364. package/test/unit/examples/contextRequests/updateContextMultientityPlugin1.json +0 -30
  365. package/test/unit/examples/contextRequests/updateContextMultientityPlugin2.json +0 -30
  366. package/test/unit/examples/contextRequests/updateContextProcessTimestamp1.json +0 -29
  367. package/test/unit/examples/contextRequests/updateContextStaticAttributes.json +0 -22
  368. package/test/unit/examples/contextRequests/updateContextTimestamp.json +0 -41
  369. package/test/unit/examples/contextRequests/updateContextTimestampOverride.json +0 -22
  370. package/test/unit/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +0 -22
  371. package/test/unit/examples/contextRequests/updateContextTimestampTimezone.json +0 -41
  372. package/test/unit/examples/contextRequests/updateProvisionActiveAttributes1.json +0 -17
  373. package/test/unit/examples/contextRequests/updateProvisionCommands1.json +0 -27
  374. package/test/unit/examples/contextRequests/updateProvisionDeviceStatic.json +0 -38
  375. package/test/unit/examples/contextRequests/updateProvisionMinimumDevice.json +0 -17
  376. package/test/unit/examples/contextResponses/contextSubscriptionRequest2Success.json +0 -7
  377. package/test/unit/examples/contextResponses/contextSubscriptionRequestSuccess.json +0 -7
  378. package/test/unit/examples/contextResponses/createBidirectionalDeviceSuccess.json +0 -22
  379. package/test/unit/examples/contextResponses/createDatetimeProvisionedDeviceSuccess.json +0 -22
  380. package/test/unit/examples/contextResponses/createGeopointProvisionedDeviceSuccess.json +0 -22
  381. package/test/unit/examples/contextResponses/createProvisionedDeviceSuccess.json +0 -22
  382. package/test/unit/examples/contextResponses/createTimeinstantSuccess.json +0 -22
  383. package/test/unit/examples/contextResponses/queryContext1Success.json +0 -27
  384. package/test/unit/examples/contextResponses/queryContext2Error.json +0 -7
  385. package/test/unit/examples/contextResponses/queryContext2UnknownError.json +0 -6
  386. package/test/unit/examples/contextResponses/queryContext3Error.json +0 -34
  387. package/test/unit/examples/contextResponses/queryContextAliasPlugin1Success.json +0 -27
  388. package/test/unit/examples/contextResponses/queryContextCompressTimestamp1Success.json +0 -27
  389. package/test/unit/examples/contextResponses/updateActiveAttributesSuccess.json +0 -16
  390. package/test/unit/examples/contextResponses/updateContext1Failed.json +0 -7
  391. package/test/unit/examples/contextResponses/updateContext1Success.json +0 -27
  392. package/test/unit/examples/contextResponses/updateContext2Failed.json +0 -35
  393. package/test/unit/examples/contextResponses/updateContextAliasPlugin2Success.json +0 -27
  394. package/test/unit/examples/contextResponses/updateContextAliasPluginSuccess.json +0 -27
  395. package/test/unit/examples/contextResponses/updateContextCommandFinishSuccess.json +0 -27
  396. package/test/unit/examples/contextResponses/updateContextCommandStatusSuccess.json +0 -22
  397. package/test/unit/examples/contextResponses/updateContextCompressTimestamp1Success.json +0 -27
  398. package/test/unit/examples/contextResponses/updateContextCompressTimestamp2Success.json +0 -34
  399. package/test/unit/examples/contextResponses/updateContextEvents1Success.json +0 -27
  400. package/test/unit/examples/contextResponses/updateContextExpressionPlugin1Success.json +0 -22
  401. package/test/unit/examples/contextResponses/updateContextExpressionPlugin2Success.json +0 -22
  402. package/test/unit/examples/contextResponses/updateContextExpressionPlugin3Success.json +0 -22
  403. package/test/unit/examples/contextResponses/updateContextExpressionPlugin5Success.json +0 -32
  404. package/test/unit/examples/contextResponses/updateContextMultientityPlugin1Success.json +0 -40
  405. package/test/unit/examples/contextResponses/updateContextMultientityPlugin2Success.json +0 -40
  406. package/test/unit/examples/contextResponses/updateContextMultientityPlugin3Success.json +0 -40
  407. package/test/unit/examples/contextResponses/updateContextProcessTimestamp1Success.json +0 -27
  408. package/test/unit/examples/contextResponses/updateProvisionActiveAttributes1Success.json +0 -22
  409. package/test/unit/examples/contextResponses/updateProvisionCommands1Success.json +0 -32
  410. package/test/unit/examples/contextResponses/updateProvisionMinimumDeviceSuccess.json +0 -22
  411. package/test/unit/examples/subscriptionRequests/bidirectionalNotification.json +0 -24
  412. package/test/unit/examples/subscriptionRequests/bidirectionalSubscriptionRemove.json +0 -3
  413. package/test/unit/examples/subscriptionRequests/bidirectionalSubscriptionRequest.json +0 -22
  414. package/test/unit/examples/subscriptionRequests/errorNotification.json +0 -24
  415. package/test/unit/examples/subscriptionRequests/simpleNotification.json +0 -24
  416. package/test/unit/examples/subscriptionRequests/simpleSubscriptionRemove.json +0 -3
  417. package/test/unit/examples/subscriptionRequests/simpleSubscriptionRequest.json +0 -19
  418. package/test/unit/examples/subscriptionRequests/simpleSubscriptionRequest1.json +0 -19
  419. package/test/unit/examples/subscriptionResponses/bidirectionalSubscriptionSuccess.json +0 -6
  420. package/test/unit/examples/subscriptionResponses/simpleSubscriptionSuccess.json +0 -6
  421. package/test/unit/expressions/expressionBasedTransformations-test.js +0 -328
  422. package/test/unit/expressions/expressionCombinedTransformations-test.js +0 -306
  423. package/test/unit/expressions/jexlBasedTransformations-test.js +0 -334
  424. package/test/unit/general/contextBrokerOAuthSecurityAccess-test.js +0 -902
  425. package/test/unit/general/https-support-test.js +0 -286
  426. package/test/unit/general/iotam-autoregistration-test.js +0 -368
  427. package/test/unit/lazyAndCommands/active-devices-attribute-update-test.js +0 -168
  428. package/test/unit/lazyAndCommands/command-test.js +0 -341
  429. package/test/unit/lazyAndCommands/lazy-devices-test.js +0 -878
  430. package/test/unit/lazyAndCommands/polling-commands-test.js +0 -421
  431. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin20.json +0 -25
  432. package/test/unit/ngsiService/active-devices-test.js +0 -692
  433. package/test/unit/ngsiService/staticAttributes-test.js +0 -148
  434. package/test/unit/ngsiService/subscriptions-test.js +0 -345
  435. package/test/unit/plugins/alias-plugin_test.js +0 -158
  436. package/test/unit/plugins/bidirectional-plugin_test.js +0 -564
  437. package/test/unit/plugins/compress-timestamp-plugin_test.js +0 -257
  438. package/test/unit/plugins/event-plugin_test.js +0 -119
  439. package/test/unit/plugins/multientity-plugin_test.js +0 -248
  440. package/test/unit/plugins/timestamp-processing-plugin_test.js +0 -119
  441. package/test/unit/provisioning/device-provisioning-api_test.js +0 -748
  442. package/test/unit/provisioning/device-registration_test.js +0 -432
  443. package/test/unit/provisioning/device-update-registration_test.js +0 -326
  444. package/test/unit/provisioning/listProvisionedDevices-test.js +0 -488
  445. package/test/unit/provisioning/provisionDeviceMultientity-test.js +0 -123
  446. package/test/unit/provisioning/removeProvisionedDevice-test.js +0 -244
  447. package/test/unit/provisioning/singleConfigurationMode-test.js +0 -346
  448. package/test/unit/provisioning/updateProvisionedDevices-test.js +0 -436
@@ -1,30 +1,118 @@
1
1
  # Measurement Transformation Expression Language
2
2
 
3
3
  - [Overview](#overview)
4
+ - [Comparison between expression languages](#comparison-between-expression-languages)
5
+ - [Configuring expression language used](#configuring-expression-language-used)
4
6
  - [Measurement transformation](#measurement-transformation)
5
7
  - [Expression definition](#expression-definition)
6
- - [Variable values](#variable-values)
7
8
  - [Expression execution](#expression-execution)
8
- - [Language description](#language-description)
9
+ - [Multientity plugin support (`object_id`)](#multientity-plugin-support-object_id)
10
+ - [JEXL Based Transformations](#jexl-based-transformations)
11
+ - [Examples of JEXL expressions](#examples-of-jexl-expressions)
12
+ - [Available functions](#available-functions)
13
+ - [Legacy Expression Language Transformations](#legacy-expression-language-transformations)
14
+ - [Expressions](#expressions)
9
15
  - [Types](#types)
10
16
  - [Values](#values)
17
+ - [Variables](#variables)
18
+ - [Constants](#constants)
11
19
  - [Allowed operations](#allowed-operations)
12
- - [Examples of expressions](#examples-of-expressions)
13
- - [NGSI v2 support](#ngsi-v2-support)
20
+ - [Number operations](#number-operations)
21
+ - [String operations](#string-operations)
22
+ - [Other available operators](#other-available-operators)
23
+ - [Examples of expressions](#examples-of-expressions)
24
+ - [NGSI v2 support](#ngsi-v2-support)
14
25
 
15
26
  ## Overview
16
27
 
17
28
  The IoTAgent Library provides an expression language for measurement transformation, that can be used to adapt the
18
- information coming from the South Bound APIs to the information reported to the Context Broker. Expressions in this
19
- language can be configured for provisioned attributes as explained in the Device Provisioning API section in the main
20
- README.md.
29
+ information coming from the South Bound APIs to the information reported to the Context Broker. This is really useful
30
+ when you need to adapt measure.
31
+
32
+ There are available two differen expression languages `jexl` and `legacy`. The recommended language to use is `jexl`,
33
+ which is newer and most powerful.
34
+
35
+ ## Comparison between expression languages
36
+
37
+ JEXL overpasses the legacy language in several aspects:
38
+
39
+ - JEXL supports multiples types: Boolean, String, Number, Object, Array, NULL.
40
+ - JEXL also supports the management and creation of JSON structures (including GeoJSON Objects)
41
+ - JEXL allows to navigate and [filter](https://github.com/TomFrost/jexl#collections) objects and arrays.
42
+ - JEXL supports if..then...else... via [ternary operator](https://github.com/TomFrost/jexl#ternary-operator).
43
+ - JEXL additionally supports the following operations: Divide and floor `//`, Modulus `%`, Logical AND `&&` and
44
+ Logical OR `||`. Negation operator is `!`
45
+ - JEXL supports [comparisons](https://github.com/TomFrost/jexl#comparisons).
46
+ - JEXL supports defining custom transformations (JEXL functions are not currently supported in Perseo).
47
+
48
+ For more details, check JEXL language details [here](https://github.com/TomFrost/jexl#all-the-details).
49
+
50
+ ## Configuring expression language used
51
+
52
+ By default, in order to maintain backward compatibility, `legacy` language is applied. There are different levels to
53
+ configure the expression language used:
54
+
55
+ - At global level.
56
+ - At service group level.
57
+ - At device level.
58
+
59
+ **Setting the expression language at global level**
60
+
61
+ It is possible to set the default language at global level by setting the
62
+ [global configuration parameter](installationguide.md#global-configuration) `defaultExpressionLanguage` or the
63
+ [environment variable](installationguide.md#configuration-using-environment-variables)
64
+ `IOTA_DEFAULT_EXPRESSION_LANGUAGE`. This option configures the default expression language used to compute expressions,
65
+ possible values are: `legacy` or `jexl`. When not set or wrongly set, `legacy` is used as default value.
66
+
67
+ **Setting the expression language at service group level**
68
+
69
+ It is possible to define the expression language at service group by adding the `expressionLanguage` parameter. It is
70
+ optional and the possible values are: `legacy` or `jexl`. When not set or wrongly set, `legacy` is used as default. You
71
+ can check the following example:
72
+
73
+ ```json
74
+ {
75
+ "services": [
76
+ {
77
+ "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
78
+ "cbroker": "http://orion:1026",
79
+ "entity_type": "Thing",
80
+ "resource": "/iot/d"
81
+ "expressionLanguage": "jexl",
82
+ "attributes": [...]
83
+ }
84
+ ]
85
+ }
86
+ ```
87
+
88
+ **Setting the expression language at device level**
89
+
90
+ Expression language can be configured at device level by adding the `expressionLanguage` parameter to the device
91
+ provisioning payload. It is optional and the possible values are: `legacy` or `jexl`. When not set or wrongly set,
92
+ `legacy` is used as default. The following example shows how to provision a device using `jexl` language.
93
+
94
+ ```json
95
+ {
96
+ "devices":[
97
+ {
98
+ "device_id":"45",
99
+ "protocol":"GENERIC_PROTO",
100
+ "entity_name":"WasteContainer:WC45",
101
+ "entity_type":"WasteContainer",
102
+ "expressionLanguage": "jexl",
103
+ "attributes":[...]
104
+ }
105
+ ]
106
+ }
107
+ ```
21
108
 
22
109
  ## Measurement transformation
23
110
 
24
111
  ### Expression definition
25
112
 
26
113
  Expressions can be defined for Active attributes, either in the Device provisioning or in the Configuration
27
- provisioning. The following example shows a device provisioning payload with defined expressions:
114
+ provisioning. The following example shows a device provisioning payload with defined expressions (using the
115
+ [JEXL Expression language](#jexl-based-transformations)):
28
116
 
29
117
  ```json
30
118
  {
@@ -34,16 +122,17 @@ provisioning. The following example shows a device provisioning payload with def
34
122
  "protocol": "GENERIC_PROTO",
35
123
  "entity_name": "WasteContainer:WC45",
36
124
  "entity_type": "WasteContainer",
125
+ "expressionLanguage": "jexl",
37
126
  "attributes": [
38
127
  {
39
128
  "name": "location",
40
- "type": "geo:point",
41
- "expression": "${@latitude}, ${@longitude}"
129
+ "type": "geo:json",
130
+ "expression": "{coordinates: [longitude,latitude], type: 'Point'}"
42
131
  },
43
132
  {
44
133
  "name": "fillingLevel",
45
134
  "type": "Number",
46
- "expression": "${@level / 100}"
135
+ "expression": "level / 100"
47
136
  },
48
137
  {
49
138
  "name": "level",
@@ -63,11 +152,13 @@ provisioning. The following example shows a device provisioning payload with def
63
152
  }
64
153
  ```
65
154
 
66
- The value of the `expression` attribute is a string that can contain any number of expression patterns. Each expression
67
- pattern is marked with the following secuence: `${<expression>}` where `<expression>` is a valid construction of the
68
- Expression Language (see definition [below](#language-description)). In order for the complete expression to be
69
- evaluated, all the expression patterns must be evaluatable (there must be a value in the measurement for all the
70
- variables of all the expression patterns).
155
+ [Interactive expression `{coordinates: [longitude,latitude], type: 'Point'}`][1]
156
+
157
+ [Interactive expression `level / 100`][2]
158
+
159
+ The value of the `expression` attribute is a string that can contain any number of expression patterns. In order to
160
+ complete expression to be evaluated, all the expression patterns must be evaluable (there must be a value in the
161
+ measurement for all the variables of all the expression patterns).
71
162
 
72
163
  Note that you need to include in the provision operation all the attributes required as inputs for the expressions. In
73
164
  this example, they are `level`, `latitude` and `longitude`. Otherwise the device sending the measures will get
@@ -76,63 +167,52 @@ expression will not be calculated.
76
167
 
77
168
  The exact same syntax works for Configuration and Device provisioning.
78
169
 
79
- ### Variable values
80
-
81
- Attribute expressions can contain values taken from the value of other attributes. Those values have, by default, the
82
- String type. For most arithmetic operations (`*`, `/`, etc...) if a variable is involved, its value will be cast to
83
- Number, regardless of the original type. For, example, if a variable @humidity has the value `'50'` (a String value),
84
- the following expression:
85
-
86
- ```
87
- ${@humidity * 10}
88
- ```
89
-
90
- will give `500` as the result (i.e.: the value `'50'` is cast to number, to get `50`, that is then multiplied by 10). If
91
- this cast fails (because the value of the variable is not a number, e.g.: `'Fifty'`), the overall result will be `NaN`.
92
-
93
170
  ### Expression execution
94
171
 
95
- Whenever a new measurement arrives to the IoTAgent for a device with declared expressions, all of the expressions for
96
- the device will be checked for execution: for all the defined active attributes containing expressions, the IoTAgent
172
+ Whenever a new measurement arrives to the IoT Agent for a device with declared expressions, all of the expressions for
173
+ the device will be checked for execution: for all the defined active attributes containing expressions, the IoT Agent
97
174
  will check which ones contain expressions whose variables are present in the received measurement. For all of those
98
175
  whose variables are covered, their expressions will be executed with the received values, and their values updated in
99
176
  the Context Broker.
100
177
 
101
- E.g.: if a device with the following provisioning information is provisioned in the IoTAgent:
178
+ E.g.: if a device with the following provisioning information is provisioned in the IoT Agent:
102
179
 
103
180
  ```json
104
181
  {
105
182
  "name":"location",
106
183
  "type":"geo:point",
107
- "expression": "${latitude}, ${longitude}"
184
+ "expression": "longitude+', '+latitude"
108
185
  },
109
186
  {
110
187
  "name":"fillingLevel",
111
188
  "type":"Number",
112
- "expression": "${@level / 100}",
113
- "cast": "Number"
189
+ "expression": "level / 100",
114
190
  },
115
191
  ```
116
192
 
117
- and a measurement with the following values arrive to the IoTAgent:
193
+ [Interactive expression `longitude+', '+latitude`][3]
194
+
195
+ [Interactive expression `level / 100`][4]
196
+
197
+ and a measurement with the following values arrive to the IoT Agent:
118
198
 
119
199
  ```text
120
200
  latitude: 1.9
121
201
  level: 85.3
122
202
  ```
123
203
 
124
- The only expression rule that will be executed will be that of the 'fillingLevel' attribute. It will produce the value
125
- '0.853' that will be sent to the Context Broker.
204
+ The only expression rule that will be executed will be that of the `fillingLevel` attribute. It will produce the value
205
+ `0.853` that will be sent to the Context Broker.
126
206
 
127
207
  Note that expressions are only applied if the attribute name (as received by the IoT Agent in the southbound interface)
128
208
  matches the expression variable. Otherwise, the southbound value is used directly. Let's illustrate with the following
129
209
  example:
130
210
 
131
211
  ```json
132
- "consumption": {
212
+ "consumption": {
133
213
  "type": "String",
134
- "value": "${trim(@spaces)}"
135
- }
214
+ "value": "spaces | trim"
215
+ }
136
216
  ```
137
217
 
138
218
  - Case 1: the following measure is received at the southbound interface:
@@ -146,8 +226,8 @@ directly used, so the following is sent to CB:
146
226
 
147
227
  ```json
148
228
  "consumption": {
149
- "type": "String",
150
- "value": "0.44"
229
+ "type": "String",
230
+ "value": "0.44"
151
231
  }
152
232
  ```
153
233
 
@@ -163,27 +243,305 @@ following to CB:
163
243
 
164
244
  ```json
165
245
  "consumption": {
166
- "type": "String",
167
- "value": "foobar"
246
+ "type": "String",
247
+ "value": "foobar"
168
248
  }
169
249
  ```
170
250
 
171
- ## Language description
251
+ [Interactive expression `spaces | trim`][5]
252
+
253
+ ### Multientity plugin support (`object_id`)
254
+
255
+ To allow support for expressions in combination with multi entity plugin, where the same attribute is generated for
256
+ different entities out of different incoming attribute values (i.e. `object_id`), we introduced support for `object_id`
257
+ in the expression context.
258
+
259
+ For example, the following device:
260
+
261
+ ```json
262
+ "WeatherStation": {
263
+ "commands": [],
264
+ "type": "WeatherStation",
265
+ "lazy": [],
266
+ "active": [
267
+ {
268
+ "object_id": "v1",
269
+ "name": "vol",
270
+ "expression" : "v1*100",
271
+ "type": "Number",
272
+ "entity_name": "WeatherStation1"
273
+ },
274
+ {
275
+ "object_id": "v2",
276
+ "name": "vol",
277
+ "expression" : "v2*100",
278
+ "type": "Number",
279
+ "entity_name": "WeatherStation2"
280
+ },
281
+ {
282
+ "object_id": "v",
283
+ "name": "vol",
284
+ "expression" : "v*100",
285
+ "type": "Number"
286
+ }
287
+ ]
288
+ }
289
+ ```
290
+
291
+ When receiving the attributes `v`, `v1` and `v2` in a payload from a message received in the southbound:
292
+
293
+ ```json
294
+ ({
295
+ "name": "v",
296
+ "type": "Number",
297
+ "value": 0
298
+ },
299
+ {
300
+ "name": "v1",
301
+ "type": "Number",
302
+ "value": 1
303
+ },
304
+ {
305
+ "name": "v2",
306
+ "type": "Number",
307
+ "value": 2
308
+ })
309
+ ```
310
+
311
+ Will now generate the following NGSI v2 payload:
312
+
313
+ ```json
314
+ {
315
+ "actionType": "append",
316
+ "entities": [
317
+ {
318
+ "id": "ws9",
319
+ "type": "WeatherStation",
320
+ "vol": {
321
+ "type": "Number",
322
+ "value": 0
323
+ }
324
+ },
325
+ {
326
+ "vol": {
327
+ "type": "Number",
328
+ "value": 100
329
+ },
330
+ "type": "WeatherStation",
331
+ "id": "WeatherStation1"
332
+ },
333
+ {
334
+ "vol": {
335
+ "type": "Number",
336
+ "value": 200
337
+ },
338
+ "type": "WeatherStation",
339
+ "id": "WeatherStation2"
340
+ }
341
+ ]
342
+ }
343
+ ```
344
+
345
+ ## JEXL Based Transformations
346
+
347
+ The recommended expression language for the IoTAgent Library is [JEXL](https://github.com/TomFrost/jexl). To use JEXL,
348
+ you will need to either configure it as default language using the `defaultExpressionLanguage` field to `jexl` (see
349
+ [configuration documentation](installationguide.md)) or configuring the usage of JEXL as expression language for a given
350
+ group or device (as shown above).
351
+
352
+ ```json
353
+ {
354
+ "devices": [
355
+ {
356
+ "device_id": "45",
357
+ "protocol": "GENERIC_PROTO",
358
+ "entity_name": "WasteContainer:WC45",
359
+ "entity_type": "WasteContainer",
360
+ "expressionLanguage": "jexl",
361
+ "attributes": [
362
+ {
363
+ "name": "location",
364
+ "type": "geo:json",
365
+ "expression": "{coordinates: [longitude,latitude], type: 'Point'}"
366
+ },
367
+ {
368
+ "name": "fillingLevel",
369
+ "type": "Number",
370
+ "expression": "level / 100"
371
+ },
372
+ {
373
+ "name": "level",
374
+ "type": "Number"
375
+ },
376
+ {
377
+ "name": "latitude",
378
+ "type": "Number"
379
+ },
380
+ {
381
+ "name": "longitude",
382
+ "type": "Number"
383
+ }
384
+ ]
385
+ }
386
+ ]
387
+ }
388
+ ```
389
+
390
+ In the following we provide examples of using JEXL to apply transformations.
391
+
392
+ ### Examples of JEXL expressions
393
+
394
+ The following table shows expressions and their expected outcomes taking into account the following measures at
395
+ southbound interface:
396
+
397
+ - `value` with value 6 (number)
398
+ - `ts` with value 1637245214901 (unix timestamp)
399
+ - `name` with value `"DevId629"` (string)
400
+ - `object` with value `{name: "John", surname: "Doe"}` (JSON object)
401
+ - `array` with value `[1, 3]` (JSON Array)
402
+
403
+ | Expression | Expected outcome | Format | Playground |
404
+ | :-------------------------------------------- | :---------------------------------------- | ------------------- | ------------- |
405
+ | `5 * value` | `30` | Integer | [Example][6] |
406
+ | `(6 + value) * 3` | `36` | Integer | [Example][7] |
407
+ | `value / 12 + 1` | `1.5` | Float | [Example][8] |
408
+ | `(5 + 2) * (value + 7)` | `91` | Integer | [Example][9] |
409
+ | `value * 5.2` | `31.2` | Float | [Example][10] |
410
+ | `"Pruebas " + "De Strings"` | `"Pruebas De Strings"` | String | [Example][11] |
411
+ | `name + "value is " +value` | `"DevId629 value is 6"` | String | [Example][12] |
412
+ | `{coordinates: [value,value], type: 'Point'}` | `{"coordinates": [6,6], "type": "Point"}` | GeoJSON `Object` | [Example][13] |
413
+ | <code>ts&vert;toisodate</code> | `2021-11-18T14:20:14.901Z` | ISO 8601 `DateTime` | [Example][14] |
414
+
415
+ Support for `trim`, `length`, `substr` and `indexOf` transformations was added.
416
+
417
+ | Expression | Expected outcome | Playground |
418
+ | :-------------------------------------------------------- | :--------------- | ------------- |
419
+ | <code>" a "&vert; trim</code> | `a` | [Example][15] |
420
+ | <code>name&vert;length</code> | `8` | [Example][16] |
421
+ | <code>name&vert;indexOf("e")</code> | `1` | [Example][17] |
422
+ | <code>name&vert;substr(0,name&vert;indexOf("e")+1)</code> | `"De"` | [Example][18] |
423
+
424
+ The following are some examples of **JEXL** expressions not supported by the **legacy** expression language:
425
+
426
+ | Expression | Expected outcome | Format | Playground |
427
+ | :-------------------------------------------------- | :---------------------------------- | ----------- | ------------- |
428
+ | `value == 6? true : false` | `true` | Boolean | [Example][19] |
429
+ | <code>value == 6 && name&vert;indexOf("e")>0</code> | `true` | Boolean | [Example][20] |
430
+ | `array[1]+1` | `3` | Number | [Example][21] |
431
+ | `object.name` | `"John"` | String | [Example][22] |
432
+ | `{type:"Point",coordinates: [value,value]}` | `{type:"Point",coordinates: [6,6]}` | JSON Object | [Example][23] |
433
+
434
+ ### Available functions
435
+
436
+ There are several predefined JEXL transformations available to be used at any JEXL expression. The definition of those
437
+ transformations and their JavaScript implementation can be found at jexlTransformsMap.js.
438
+
439
+ The library module also exports a method `iotAgentLib.dataPlugins.expressionTransformation.setJEXLTransforms(Map)` to be
440
+ used by specific IoT Agent implementations in order to incorporate extra transformations to this set. It is important to
441
+ remark that the lib `jexlTransformsMap` cannot be overwritten by the API additions. The idea behind this is to be able
442
+ to incorporate new transformations from the IoT Agent configuration file in a fast and tactical way.
443
+
444
+ Current common transformation set:
445
+
446
+ | JEXL Transformation | Equivalent JavaScript Function |
447
+ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
448
+ | jsonparse: (str) | `JSON.parse(str);` |
449
+ | jsonstringify: (obj) | `JSON.stringify(obj);` |
450
+ | indexOf: (val, char) | `String(val).indexOf(char);` |
451
+ | length: (val) | `String(val).length;` |
452
+ | trim: (val) | `String(val).trim();` |
453
+ | substr: (val, int1, int2) | `String(val).substr(int1, int2);` |
454
+ | addreduce: (arr) | <code>arr.reduce((i, v) &vert; i + v));</code> |
455
+ | lengtharray: (arr) | `arr.length;` |
456
+ | typeof: (val) | `typeof val;` |
457
+ | isarray: (arr) | `Array.isArray(arr);` |
458
+ | isnan: (val) | `isNaN(val);` |
459
+ | parseint: (val) | `parseInt(val);` |
460
+ | parsefloat: (val) | `parseFloat(val);` |
461
+ | toisodate: (val) | `new Date(val).toISOString();` |
462
+ | timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();` |
463
+ | tostring: (val) | `val.toString();` |
464
+ | urlencode: (val) | `encodeURI(val);` |
465
+ | urldecode: (val) | `decodeURI(val);` |
466
+ | replacestr: (str, from, to) | `str.replace(from, to);` |
467
+ | replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);` |
468
+ | replaceallstr: (str, from, to) | `str.replaceAll(from, to);` |
469
+ | replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);` |
470
+ | split: (str, ch) | `str.split(ch);` |
471
+ | mapper: (val, values, choices) | <code>choices[values.findIndex((target) &vert; target == val)]);</code> |
472
+ | thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) &vert; (acc==0)&vert;&vert;acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
473
+ | bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="&vert;"?parseInt(i)&vert;mask: op==="^"?parseInt(i)^mask:i)>>shf;</code> |
474
+ | slice: (arr, init, end) | `arr.slice(init,end);` |
475
+ | addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
476
+ | removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
477
+
478
+ You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
479
+ test all the functions described above.
480
+
481
+ ## Legacy Expression Language Transformations
482
+
483
+ As described in previous sections, this is the default language just for backward compatibility reasons, but **we
484
+ strongly encourage you not to use this language** for defining expression in order to transform measures, since it is
485
+ not capable to handle JSON native types, it is not extensible and many other reasons as described on the section
486
+ [Comparison between expression languages](#comparison-between-expression-languages). The following example shows a
487
+ device provisioning payload with defined expressions using the Legacy Expression Language:
488
+
489
+ ```json
490
+ {
491
+ "devices": [
492
+ {
493
+ "device_id": "45",
494
+ "protocol": "GENERIC_PROTO",
495
+ "entity_name": "WasteContainer:WC45",
496
+ "entity_type": "WasteContainer",
497
+ "attributes": [
498
+ {
499
+ "name": "location",
500
+ "type": "geo:point",
501
+ "expression": "${@latitude}, ${@longitude}"
502
+ },
503
+ {
504
+ "name": "fillingLevel",
505
+ "type": "Number",
506
+ "expression": "${@level / 100}"
507
+ },
508
+ {
509
+ "name": "level",
510
+ "type": "Number"
511
+ },
512
+ {
513
+ "name": "latitude",
514
+ "type": "Number"
515
+ },
516
+ {
517
+ "name": "longitude",
518
+ "type": "Number"
519
+ }
520
+ ]
521
+ }
522
+ ]
523
+ }
524
+ ```
525
+
526
+ ### Expressions
527
+
528
+ Each expression pattern is marked with the following secuence: `${<expression>}` where `<expression>` is a construction
529
+ with a valid syntax.
172
530
 
173
531
  ### Types
174
532
 
175
- The way the parse() function works (at expressionParser.js) is as follows:
533
+ The way the `parse()` function works (at `expressionParser.js`) is as follows:
176
534
 
177
- - Expressions can have two return types: String or Number. This return type must be configured for each attribute that
178
- is going to be converted. Default value type is String.
535
+ - Expressions can have two return types: `String` or `Number`. This return type must be configured for each attribute
536
+ that is going to be converted. Default value type is `String`.
179
537
  - Whenever an expression is executed without error, its result will be cast to the configured type. If the conversion
180
538
  fails (e.g.: if the expression is null or a String and is cast to Number), the measurement update will fail, and an
181
539
  error will be reported to the device.
182
540
 
183
- However, the usage that the Expression Translation plugin does of that function is using always String type. That means
184
- that at the end, the result of the expression will be always cast to String. However, in NGSI v2 that String result
185
- could be re-cast to the right type (i.e. the one defined for the attribute in the provision operation). Have a look at
186
- the [NGSI v2 support](#ngsiv2) for more information on this.
541
+ However, the usage that the Expression Translation plugin does of that function is using always `String` type. That
542
+ means that at the end, the result of the expression will be always cast to `String`. However, in NGSI v2 that `String`
543
+ result could be re-cast to the right type (i.e. the one defined for the attribute in the provision operation). Have a
544
+ look at the [NGSI v2 support](#ngsiv2) for more information on this.
187
545
 
188
546
  ### Values
189
547
 
@@ -193,11 +551,23 @@ All the information reported in the measurement received by the IoT Agent is ava
193
551
  every attribute coming from the South Bound, a variable with the syntax `@<object_id>` will be created for its use in
194
552
  the expression language.
195
553
 
554
+ Attribute expressions can contain values taken from the value of other attributes. Those values have, by default, the
555
+ String type. For most arithmetic operations (`*`, `/`, etc...) if a variable is involved, its value will be cast to
556
+ Number, regardless of the original type. For, example, if a variable `@humidity` has the value `'50'` (a String value),
557
+ the following expression:
558
+
559
+ ```
560
+ ${@humidity * 10}
561
+ ```
562
+
563
+ will give `500` as the result (i.e.: the value `'50'` is cast to number, to get `50`, that is then multiplied by 10). If
564
+ this cast fails (because the value of the variable is not a number, e.g.: `'Fifty'`), the overall result will be `NaN`.
565
+
196
566
  #### Constants
197
567
 
198
568
  The expression language allows for two kinds of constants:
199
569
 
200
- - Numbers (integer or real)
570
+ - Numbers (integer or float)
201
571
  - Strings (marked with double quotes)
202
572
 
203
573
  Current allowed characters are:
@@ -231,50 +601,49 @@ The following operations are currently available, divided by attribute type
231
601
 
232
602
  Parenthesis can be used to define precedence in the operations. Whitespaces between tokens are generally ignored.
233
603
 
234
- ## Examples of expressions
235
-
236
- The following table shows expressions and their expected outcomes for a measure with two attributes: "@value" with value
237
- 6 and "@name" with value "DevId629".
604
+ ### Examples of expressions
238
605
 
239
- | Expression | Expected outcome |
240
- | :-------------------------- | :-------------------- |
241
- | '5 \* @value' | 30 |
242
- | '(6 + @value) \* 3' | 36 |
243
- | '@value / 12 + 1' | 1.5 |
244
- | '(5 + 2) \* (@value + 7)' | 91 |
245
- | '@value \* 5.2' | 31.2 |
246
- | '"Pruebas " + "De Strings"' | 'Pruebas De Strings' |
247
- | '@name value is @value' | 'DevId629 value is 6' |
606
+ The following table shows expressions and their expected outcomes for a measure with two attributes: `@value` with value
607
+ `6` and `@name` with value `DevId629`.
248
608
 
249
- ## NGSI v2 support
609
+ | Expression | Expected outcome | Format |
610
+ | :-------------------------- | :-------------------- | ------ |
611
+ | `5 \* @value` | `30` | Number |
612
+ | `(6 + @value) \* 3` | `36` | Number |
613
+ | `@value / 12 + 1` | `1.5` | Number |
614
+ | `(5 + 2) \* (@value + 7)` | `91` | Number |
615
+ | `@value \* 5.2` | `31.2` | Number |
616
+ | `"Pruebas " + "De Strings"` | `Pruebas De Strings` | String |
617
+ | `@name value is @value` | `DevId629 value is 6` | String |
250
618
 
251
- As it is explained in previous sections, expressions can have two return types: String or Number, being the former one
252
- the default. Whenever an expression is executed without error, its result will be cast to the configured type.
619
+ ### NGSI v2 support
253
620
 
254
- On one hand, in NGSIv1 since all attributes' values are of type String, in the expression parser the expression type is
255
- set always to String and the transformation of the information coming from the SouthBound is done using replace
256
- instruction. Therefore, values sent to the CB will always be Strings. This can be seen in previous examples.
621
+ As it is explained in previous sections, expressions can have two return types: `String` or `Number`, being the former
622
+ one the default. Whenever an expression is executed without error, its result will be cast to the configured type.
257
623
 
258
- On the other hand, NGSI v2 fully supports all the types described in the JSON specification (string, number, boolean,
259
- object, array and null). Therefore, the result of an expression must be cast to the appropriate type (the type used to
260
- define the attribute) in order to avoid inconsistencies between the type field for an attribute and the type of the
261
- value that is being sent.
624
+ NGSI v2 and NGSI-LD fully supports all the types described in the JSON specification (`string`, `number`, `boolean`,
625
+ `object`, `array` and `null`). Therefore, the result of an expression must be cast to the appropriate type (the type
626
+ used to define the attribute) in order to avoid inconsistencies between the type field for an attribute and the type of
627
+ the value that is being sent.
262
628
 
263
- Currently, the expression parser does not support JSON Arrays and JSON document. A new issue has been created to address
264
- this aspect https://github.com/telefonicaid/iotagent-node-lib/issues/568. For the rest of types the workflow will be the
265
- following:
629
+ The expression parser for legacy does not support JSON Arrays and JSON document. For the rest of types the workflow will
630
+ be the following:
266
631
 
267
632
  1. Variables will be cast to String no matter the expression type (see [comments above](#types) regarding this)
268
633
  2. The expression will be applied
269
634
  3. The output type will be cast again to the original attribute type.
270
635
 
271
- - If attribute type is "Number" and the value is an `Integer`, then the value is casted to integer (JSON number)
272
- - If attribute type is "Number" and the value is a `Float`, then the value is casted to float (JSON number)
273
- - If attribute type is "Boolean" then the value is cast to boolean (JSON boolean). In order to do this conversion,
636
+ - If attribute type is `Number` and the value is an `Integer`, then the value is casted to integer (JSON number)
637
+ - If attribute type is `Number` and the value is a `Float`, then the value is casted to float (JSON number)
638
+ - If attribute type is `Boolean` then the value is cast to boolean (JSON boolean). In order to do this conversion,
274
639
  only `true` or `1` are cast to true.
275
- - If attribute type is "None" then the value is cast to null (JSON null)
640
+ - If attribute type is `None` then the value is cast to `null` (JSON null)
641
+
642
+ > **Note**. All the operations and castings described above are not performed using JELX, because the user has full
643
+ > control on the final value of the attributes, so there is no need of adding a layer of autocast that would interfere
644
+ > and makes things more complicated.
276
645
 
277
- E.g.: if a device with the following provisioning information is provisioned in the IoTAgent:
646
+ E.g.: if a device with the following provisioning information is provisioned in the IoT Agent:
278
647
 
279
648
  ```json
280
649
  {
@@ -284,7 +653,7 @@ E.g.: if a device with the following provisioning information is provisioned in
284
653
  }
285
654
  ```
286
655
 
287
- and a measurement with the following values arrive to the IoTAgent:
656
+ and a measurement with the following values arrive to the IoT Agent:
288
657
 
289
658
  ```
290
659
  status: true
@@ -300,193 +669,66 @@ status: true
300
669
  More examples of this workflow are presented below for the different types of attributes supported in NGSI v2 and the
301
670
  two possible types of expressions: Integer (arithmetic operations) or Strings.
302
671
 
303
- - pressure (of type "Number" and value's type "Integer"): 52 -> ${@pressure * 20} -> ${ 52 \* 20 } -> $ { 1040 } -> $
304
- { "1040"} -> 1040
305
- - pressure (of type "Number" and value's type "Integer"): 52 -> ${trim(@pressure)} -> ${trim("52")} -> $ { "52" } -> $
306
- { "52"} -> 52
307
- - consumption (of type "Number" and value's type "Float"): 0.44 -> ${@consumption * 20} -> ${ 0.44 \* 20 } ->
308
- $ { 8.8 } -> $ { "8.8"} -> 8.8
309
- - consumption (of type "Number" and value's type "Float"): 0.44 -> ${trim(@consumption)} -> ${trim("0.44")} ->
310
- $ { "0.44" } -> $ { "0.44"} -> 0.44
311
- - active (of type "None"): null -> ${@active * 20} -> ${ 0 \* 20 } -> $ { 0 } -> $ { "0"} -> null
312
- - active (of type "None"): null -> ${trim(@active)} -> ${trim("null")} -> $ { "null" } -> $ { "null"} -> null
313
- - update (of type "Boolean"): true -> ${@update * 20} -> ${ 1 \* 20 } -> $ { 20 } -> $ { "20"} -> False
314
- - update (of type "Boolean"): false -> ${@update * 20} -> ${ 0 \* 20 } -> $ { 0 } -> $ { "0"} -> False
315
- - update (of type "Boolean"): true -> ${trim(@updated)} -> ${trim("true")} -> $ { "true" } -> $ { "true"} -> True
316
-
317
-
318
- To allow support for expressions in combination with multi entity plugin, where
319
- the same attribute is generated for different entities out of different
320
- incoming attribute values (i.e. `object_id`), we introduced support for
321
- `object_id` in the expression context.
322
-
323
- For example, the following device:
324
-
325
- ```
326
- WeatherStation: {
327
- commands: [],
328
- type: 'WeatherStation',
329
- lazy: [],
330
- active: [
331
- {
332
- object_id: 'v1',
333
- name: 'vol',
334
- expression : '${@v1*100}',
335
- type: 'Number',
336
- entity_name: 'WeatherStation1'
337
- },
338
- {
339
- object_id: 'v2',
340
- name: 'vol',
341
- expression : '${@v2*100}',
342
- type: 'Number',
343
- entity_name: 'WeatherStation2'
344
- },
345
- {
346
- object_id: 'v',
347
- name: 'vol',
348
- expression : '${@v*100}',
349
- type: 'Number'
350
- }
351
- ]
352
- }
353
- ```
354
-
355
- When receiving the following payloads:
356
-
357
- ```
358
- {
359
- name: 'v',
360
- type: 'Number',
361
- value: 0
362
- },
363
- {
364
- name: 'v1',
365
- type: 'Number',
366
- value: 1
367
- },
368
- {
369
- name: 'v2',
370
- type: 'Number',
371
- value: 2
372
- }
373
- ```
374
-
375
- Will now generate the following NGSI v2 payload:
376
-
377
- ```
378
- {
379
- "actionType": "append",
380
- "entities": [
381
- {
382
- "id": "ws9",
383
- "type": "WeatherStation",
384
- "vol": {
385
- "type": "Number",
386
- "value": 0
387
- }
388
- },
389
- {
390
- "vol": {
391
- "type": "Number",
392
- "value": 100
393
- },
394
- "type": "WeatherStation",
395
- "id": "WeatherStation1"
396
- },
397
- {
398
- "vol": {
399
- "type": "Number",
400
- "value": 200
401
- },
402
- "type": "WeatherStation",
403
- "id": "WeatherStation2"
404
- }
405
- ]
406
- }
407
- ```
408
-
409
- ## JEXL Based Transformations
410
-
411
- As an alternative, the IoTAgent Library supports as well [JEXL](https://github.com/TomFrost/jexl). To use JEXL, you will
412
- need to either configure it as default language using the `defaultExpressionLanguage` field to `jexl` (see
413
- [configuration documentation](installationguide.md)) or configuring the usage of JEXL as expression language for a given
414
- device:
415
-
416
- ```
417
- {
418
- "devices":[
419
- {
420
- "device_id":"45",
421
- "protocol":"GENERIC_PROTO",
422
- "entity_name":"WasteContainer:WC45",
423
- "entity_type":"WasteContainer",
424
- "expressionLanguage": "jexl",
425
- "attributes":[
426
- {
427
- "name":"location",
428
- "type":"geo:point",
429
- "expression": "..."
430
- },
431
- {
432
- "name":"fillingLevel",
433
- "type":"Number",
434
- "expression": "..."
435
- }
436
- ]
437
- }
438
- ]
439
- }
440
- ```
441
-
442
- In the following we provide examples of using JEXL to apply transformations.
443
-
444
- ### Quick comparison to default language
445
-
446
- - JEXL supports the following types: Boolean, String, Number, Object, Array.
447
- - JEXL allows to navigate and [filter](https://github.com/TomFrost/jexl#collections) objects and arrays.
448
- - JEXL supports if..then...else... via [ternary operator](https://github.com/TomFrost/jexl#ternary-operator).
449
- - JEXL additionally supports the following operations: Divide and floor `//`, Modulus `%`, Logical AND `&&` and
450
- Logical OR `||`. Negation operator is `!`
451
- - JEXL supports [comparisons](https://github.com/TomFrost/jexl#comparisons).
452
-
453
- For more details, check JEXL language details [here](https://github.com/TomFrost/jexl#all-the-details).
454
-
455
- ### Examples of expressions
456
-
457
- The following table shows expressions and their expected outcomes taking into account the following measures at
458
- southbound interface:
459
-
460
- - `value` with value 6 (number)
461
- - `name` with value `"DevId629"` (string)
462
- - `object` with value `{name: "John", surname: "Doe"}` (JSON object)
463
- - `array` with value `[1, 3]` (JSON Array)
464
-
465
- | Expression | Expected outcome |
466
- | :-------------------------- | :---------------------- |
467
- | `5 * value` | `30` |
468
- | `(6 + value) * 3` | `36` |
469
- | `value / 12 + 1` | `1.5` |
470
- | `(5 + 2) * (value + 7)` | `91` |
471
- | `value * 5.2` | `31.2` |
472
- | `"Pruebas " + "De Strings"` | `"Pruebas De Strings"` |
473
- | `name + "value is " +value` | `"DevId629 value is 6"` |
474
-
475
- Support for `trim`, `length`, `substr` and `indexOf` transformations was added.
476
-
477
- | Expression | Expected outcome |
478
- | :----------------------------------------------------------- | :--------------- |
479
- | <code>" a "&vert;trim</code> | `a` |
480
- | <code>name&vert;length</code> | `8` |
481
- | <code>name&vert;indexOf("e")</code> | `1` |
482
- | <code>name&vert;substring(0,name&vert;indexOf("e")+1)</code> | `"De"` |
483
-
484
- The following are some expressions not supported by the legacy expression language:
485
-
486
- | Expression | Expected outcome |
487
- | :-------------------------------------------------- | :---------------------------------- |
488
- | `value == 6? true : false` | `true` |
489
- | <code>value == 6 && name&vert;indexOf("e")>0</code> | `true` |
490
- | `array[1]+1` | `3` |
491
- | `object.name` | `"John"` |
492
- | `{type:"Point",coordinates: [value,value]}` | `{type:"Point",coordinates: [6,6]}` |
672
+ - `pressure` with value 52 (integer)
673
+ - `consumption` with value 0.44 (float)
674
+ - `active` with value `null` (None type)
675
+
676
+ | Expression | Expected outcome | Format |
677
+ | :---------------------- | :--------------- | ------- |
678
+ | `${@pressure * 20}` | `1040` | Integer |
679
+ | `${trim(@pressure)}` | `52` | Integer |
680
+ | `${@consumption * 20}` | `8.8` | Float |
681
+ | `${trim(@consumption)}` | `0.44` | Float |
682
+ | `${@pressure * 20}` | `1040` | Integer |
683
+ | `${@active * 20}` | `null` | None |
684
+ | `${trim(@active)` | `null` | None |
685
+ | `${trim(@consumption)}` | `0.44` | Float |
686
+
687
+ [1]:
688
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22longitude%22%3A%205%2C%0A%20%20%22latitude%22%3A%2037%2C%0A%20%20%22level%22%3A223%0A%7D&input=%7Bcoordinates%3A%20%5Blongitude%2Clatitude%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D
689
+ [2]:
690
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22longitude%22%3A%205%2C%0A%20%20%22latitude%22%3A%2037%2C%0A%20%20%22level%22%3A223%0A%7D&input=level%2F100&transforms=%7B%0A%7D
691
+ [3]:
692
+ https://czosel.github.io/jexl-playground/#/?context=%7B%20%0A%20%20%22latitude%22%3A%201.9%2C%0A%20%20%22level%22%3A85.3%0A%7D&input=longitude%2B%22%2C%20%22%2Blatitude&transforms=%7B%0A%7D
693
+ [4]:
694
+ https://czosel.github.io/jexl-playground/#/?context=%7B%20%0A%20%20%22latitude%22%3A%201.9%2C%0A%20%20%22level%22%3A85.3%0A%7D&input=level%2F100&transforms=%7B%0A%7D
695
+ [5]:
696
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22spaces%22%20%3A%20%22%20%20foobar%20%20%22%0A%7D&input=spaces%20%7C%20trim&transforms=%7B%0A%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%0A%7D
697
+ [6]:
698
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=5%20*%20value&transforms=%7B%0A%7D
699
+ [7]:
700
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=(6%20%2B%20value)%20*%203&transforms=%7B%0A%7D
701
+ [8]:
702
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%2F%2012%20%2B%201&transforms=%7B%0A%7D
703
+ [9]:
704
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=(5%20%2B%202)%20*%20(value%20%2B%207)&transforms=%7B%0A%7D
705
+ [10]:
706
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20*%205.2&transforms=%7B%0A%7D
707
+ [11]:
708
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%22Pruebas%20%22%20%2B%20%22De%20Strings%22&transforms=%7B%0A%7D
709
+ [12]:
710
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%20%2B%20%22value%20is%20%22%20%2Bvalue&transforms=%7B%0A%7D
711
+ [13]:
712
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%7Bcoordinates%3A%20%5Bvalue%2Cvalue%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D
713
+ [14]:
714
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=ts%7Ctoisodate&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
715
+ [15]:
716
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%22%20a%20%22%7Ctrim&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
717
+ [16]:
718
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7Clength&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
719
+ [17]:
720
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7CindexOf(%22e%22)&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
721
+ [18]:
722
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7Csubstr(0%2Cname%7CindexOf(%22e%22)%2B1)&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
723
+ [19]:
724
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%3D%3D%206%3F%20true%20%3A%20false&transforms=%7B%7D
725
+ [20]:
726
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%3D%3D%206%20%26%26%20name%7CindexOf(%22e%22)%3E0&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
727
+ [21]:
728
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=array%5B1%5D%2B1&transforms=%7B%7D
729
+ [22]:
730
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=object.name&transforms=%7B%7D
731
+ [23]:
732
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%7Btype%3A%22Point%22%2Ccoordinates%3A%20%5Bvalue%2Cvalue%5D%7D&transforms=%7B%7D
733
+ [99]:
734
+ https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22text%22%20%3A%20%22%20%20foobar%7B%7D%20%20%22%0A%7D&input=text%20%7C%20replacestr(%22foo%22%2C%22FOO%22)%7Ctrim%7Curlencode&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D