iotagent-node-lib 2.25.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES_NEXT_RELEASE +0 -1
- package/config.js +6 -1
- package/doc/deprecated.md +4 -1
- package/doc/howto.md +8 -0
- package/doc/installationguide.md +18 -0
- package/doc/usermanual.md +77 -0
- package/lib/commonConfig.js +15 -1
- package/lib/constants.js +1 -0
- package/lib/fiware-iotagent-lib.js +1 -5
- package/lib/plugins/compressTimestamp.js +2 -7
- package/lib/plugins/expressionParser.js +0 -47
- package/lib/plugins/expressionPlugin.js +6 -78
- package/lib/plugins/jexlParser.js +0 -36
- package/lib/plugins/pluginUtils.js +0 -119
- package/lib/services/devices/deviceService.js +1 -0
- package/lib/services/devices/registrationUtils.js +21 -1
- package/lib/services/ngsi/entities-NGSI-LD.js +600 -199
- package/lib/services/ngsi/entities-NGSI-v2.js +620 -298
- package/lib/services/ngsi/ngsiUtils.js +15 -22
- package/lib/services/northBound/contextServer-NGSI-LD.js +221 -38
- package/lib/services/northBound/contextServer.js +14 -1
- package/lib/services/northBound/northboundServer.js +1 -0
- package/package.json +1 -1
- package/test/unit/general/loglevel-api_test.js +0 -2
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent1.json +24 -14
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent2.json +25 -15
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent4.json +15 -5
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +9 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommandsAndLazy.json +32 -0
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice.json +12 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice2.json +9 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup.json +12 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup2.json +12 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup3.json +9 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateCommands1.json +10 -2
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent1.json +11 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent2.json +12 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin1.json +0 -5
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json +1 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json +1 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json +1 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin32.json +16 -15
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4.json +8 -8
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +34 -34
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin5.json +8 -8
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin6.json +0 -20
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin7.json +0 -5
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin8.json +10 -10
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +8 -7
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +5 -4
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextProcessTimestamp.json +4 -4
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverride.json +4 -3
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +4 -3
- package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +2 -5
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1 -7
- package/test/unit/ngsi-ld/general/startup-test.js +17 -2
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +1 -12
- package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +249 -0
- package/test/unit/ngsi-ld/ngsiService/autocast-test.js +15 -3
- package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +171 -0
- package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +0 -2
- package/test/unit/ngsi-ld/plugins/compress-timestamp-plugin_test.js +0 -31
- package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +18 -18
- package/test/unit/ngsi-ld/plugins/timestamp-processing-plugin_test.js +19 -6
- package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin1.json +2 -2
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin11.json +5 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin5.json +4 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin6.json +0 -16
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin7.json +0 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin8.json +8 -8
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +42 -42
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +7 -7
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +0 -5
- package/test/unit/ngsiv2/examples/contextRequests/updateContextProcessTimestamp.json +1 -7
- package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -3
- package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -6
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +1 -7
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +0 -2
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +0 -4
- package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +0 -32
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +5 -15
- package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +1 -1
- package/lib/plugins/addEvent.js +0 -32
- package/lib/plugins/attributeAlias.js +0 -107
- package/lib/plugins/multiEntity.js +0 -255
- package/lib/plugins/timestampProcessPlugin.js +0 -95
- package/test/unit/ngsi-ld/plugins/event-plugin_test.js +0 -116
- package/test/unit/ngsiv2/plugins/event-plugin_test.js +0 -118
- package/test/unit/ngsiv2/plugins/translation-inPlugins_test.js +0 -262
package/CHANGES_NEXT_RELEASE
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
package/config.js
CHANGED
package/doc/deprecated.md
CHANGED
|
@@ -18,7 +18,9 @@ A list of deprecated features and the version in which they were deprecated foll
|
|
|
18
18
|
- Support to Node.js v8 in iotagent-node-lib 2.12.0 (finally removed in 2.13.0)
|
|
19
19
|
- Support to Node.js v10 in iotagent-node-lib 2.15.0 (finally removed in 2.16.0)
|
|
20
20
|
- Support to Node.js v12 in iotagent-node-lib 2.24.0 (finally removed in 2.25.0)
|
|
21
|
-
- Support to NGSI-LD v1.3 in iotagent-node-lib 2.25.0
|
|
21
|
+
- Support to NGSI-LD v1.3 in iotagent-node-lib 2.25.0 (finally removed in 2.26.0)
|
|
22
|
+
- Support groups (provision) statically defined by configuration
|
|
23
|
+
- Support to in-memory registry (i.e.`deviceRegistry.type=memory`)
|
|
22
24
|
|
|
23
25
|
The use of Node.js v14 is highly recommended.
|
|
24
26
|
|
|
@@ -46,3 +48,4 @@ The following table provides information about the last iotagent-node-lib versio
|
|
|
46
48
|
| Support to Node.js v8 | 2.12.0 | April 7th, 2020 |
|
|
47
49
|
| Support to Node.js v10 | 2.15.0 | February 18th, 2021 |
|
|
48
50
|
| Support to Node.js v12 | 2.24.0 | September 2nd, 2022 |
|
|
51
|
+
| Support to NGSI-LD 1.3 | 2.25.0 | January 24th, 2023 |
|
package/doc/howto.md
CHANGED
|
@@ -430,6 +430,14 @@ iotAgentLib.setDataUpdateHandler(updateContextHandler);
|
|
|
430
430
|
iotAgentLib.setDataQueryHandler(queryContextHandler);
|
|
431
431
|
```
|
|
432
432
|
|
|
433
|
+
Where necessary, additional handlers to deal with command actuations and merge-patch operations may also be added when
|
|
434
|
+
necessary.
|
|
435
|
+
|
|
436
|
+
```javascript
|
|
437
|
+
iotAgentLib.setCommandHandler(commandHandler);
|
|
438
|
+
iotAgentLib.setMergePatchHandler(mergePatchHandler);
|
|
439
|
+
```
|
|
440
|
+
|
|
433
441
|
#### IOTA Testing
|
|
434
442
|
|
|
435
443
|
In order to test it, we need to create an HTTP server simulating the device. The quickest way to do that may be using
|
package/doc/installationguide.md
CHANGED
|
@@ -72,6 +72,22 @@ overriding the group setting.
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
When connected to an **NGSI-LD** context broker, an IoT Agent is able to indicate whether it is willing to accept `null`
|
|
76
|
+
values and also whether it is able to process the **NGSI-LD** `datasetId` metadata element. Setting these
|
|
77
|
+
values to `false` will cause the IoT Agent to return a 400 **Bad Request** HTTP status code explaining that the IoT
|
|
78
|
+
Agent does not support nulls or multi-attribute requests if they are encountered.
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
{
|
|
82
|
+
baseRoot: '/',
|
|
83
|
+
port: 4041,
|
|
84
|
+
ldSupport : {
|
|
85
|
+
null: true,
|
|
86
|
+
datasetId: true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
75
91
|
- **stats**: configure the periodic collection of statistics. Use `interval` in milliseconds to set the time between
|
|
76
92
|
stats writings.
|
|
77
93
|
|
|
@@ -301,6 +317,8 @@ overrides.
|
|
|
301
317
|
| IOTA_CB_NGSI_VERSION | `contextBroker.ngsiVersion` |
|
|
302
318
|
| IOTA_NORTH_HOST | `server.host` |
|
|
303
319
|
| IOTA_NORTH_PORT | `server.port` |
|
|
320
|
+
| IOTA_LD_SUPPORT_NULL | `server.ldSupport.null` |
|
|
321
|
+
| IOTA_LD_SUPPORT_DATASET_ID | `server.ldSupport.datasetId` |
|
|
304
322
|
| IOTA_PROVIDER_URL | `providerUrl` |
|
|
305
323
|
| IOTA_AUTH_ENABLED | `authentication.enabled` |
|
|
306
324
|
| IOTA_AUTH_TYPE | `authentication.type` |
|
package/doc/usermanual.md
CHANGED
|
@@ -354,6 +354,83 @@ values.
|
|
|
354
354
|
The handler is expected to call its callback once with no parameters (failing to do so may cause unexpected behaviors in
|
|
355
355
|
the IoT Agent).
|
|
356
356
|
|
|
357
|
+
##### iotagentLib.setCommandHandler()
|
|
358
|
+
|
|
359
|
+
###### Signature
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
function setCommandHandler(newHandler)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
###### Description
|
|
366
|
+
|
|
367
|
+
Sets the new user handler for registered entity commands. This handler will be called whenever a command request arrives, with
|
|
368
|
+
the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve
|
|
369
|
+
all the corresponding information from the devices and return a NGSI entity with the requested values.
|
|
370
|
+
|
|
371
|
+
The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
callback(null, {
|
|
375
|
+
type: "TheType",
|
|
376
|
+
isPattern: false,
|
|
377
|
+
id: "EntityID",
|
|
378
|
+
attributes: [
|
|
379
|
+
{
|
|
380
|
+
name: "lumniscence",
|
|
381
|
+
type: "Lumens",
|
|
382
|
+
value: "432"
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
In the case of NGSI requests affecting multiple entities, this handler will be called multiple times, one for each
|
|
389
|
+
entity, and all the results will be combined into a single response. Only IoT Agents which deal with actuator devices will include a handler for commands.
|
|
390
|
+
|
|
391
|
+
###### Params
|
|
392
|
+
|
|
393
|
+
- newHandler: User handler for command requests.
|
|
394
|
+
|
|
395
|
+
##### iotagentLib.setMergePatchHandler()
|
|
396
|
+
|
|
397
|
+
###### Signature
|
|
398
|
+
|
|
399
|
+
```javascript
|
|
400
|
+
function setMergePatchHandler(newHandler)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
###### Description
|
|
404
|
+
|
|
405
|
+
Sets the new user handler for NGSI-LD Entity [merge-patch](https://datatracker.ietf.org/doc/html/rfc7386) requests. This handler will be called whenever a merge-patch request arrives, with
|
|
406
|
+
the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve
|
|
407
|
+
all the corresponding information from the devices and return a NGSI entity with the requested values.
|
|
408
|
+
|
|
409
|
+
The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:
|
|
410
|
+
|
|
411
|
+
```javascript
|
|
412
|
+
callback(null, {
|
|
413
|
+
type: "TheType",
|
|
414
|
+
isPattern: false,
|
|
415
|
+
id: "EntityID",
|
|
416
|
+
attributes: [
|
|
417
|
+
{
|
|
418
|
+
name: "lumniscence",
|
|
419
|
+
type: "Lumens",
|
|
420
|
+
value: "432"
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
});
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
In the case of NGSI-LD requests affecting multiple entities, this handler will be
|
|
427
|
+
called multiple times. Since merge-patch is an advanced function, not all IoT Agents
|
|
428
|
+
will include a handler for merge-patch.
|
|
429
|
+
|
|
430
|
+
###### Params
|
|
431
|
+
|
|
432
|
+
- newHandler: User handler for merge-patch requests.
|
|
433
|
+
|
|
357
434
|
##### iotagentLib.setProvisioningHandler()
|
|
358
435
|
|
|
359
436
|
###### Signature
|
package/lib/commonConfig.js
CHANGED
|
@@ -158,7 +158,9 @@ function processEnvironmentVariables() {
|
|
|
158
158
|
'IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION',
|
|
159
159
|
'IOTA_JSON_LD_CONTEXT',
|
|
160
160
|
'IOTA_FALLBACK_TENANT',
|
|
161
|
-
'IOTA_FALLBACK_PATH'
|
|
161
|
+
'IOTA_FALLBACK_PATH',
|
|
162
|
+
'IOTA_LD_SUPPORT_NULL',
|
|
163
|
+
'IOTA_LD_SUPPORT_DATASET_ID'
|
|
162
164
|
];
|
|
163
165
|
const iotamVariables = [
|
|
164
166
|
'IOTA_IOTAM_URL',
|
|
@@ -263,6 +265,18 @@ function processEnvironmentVariables() {
|
|
|
263
265
|
config.server.port = process.env.IOTA_NORTH_PORT;
|
|
264
266
|
}
|
|
265
267
|
|
|
268
|
+
config.server.ldSupport = config.server.ldSupport || {null: true, datasetId: true, merge: false};
|
|
269
|
+
|
|
270
|
+
if (process.env.IOTA_LD_SUPPORT_NULL) {
|
|
271
|
+
config.server.ldSupport.null = process.env.IOTA_LD_SUPPORT_NULL === 'true';
|
|
272
|
+
}
|
|
273
|
+
if (process.env.IOTA_LD_SUPPORT_DATASET_ID) {
|
|
274
|
+
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_DATASET_ID === 'true';
|
|
275
|
+
}
|
|
276
|
+
if (process.env.IOTA_LD_SUPPORT_MERGE) {
|
|
277
|
+
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_MERGE === 'true';
|
|
278
|
+
}
|
|
279
|
+
|
|
266
280
|
if (process.env.IOTA_PROVIDER_URL) {
|
|
267
281
|
config.providerUrl = process.env.IOTA_PROVIDER_URL;
|
|
268
282
|
}
|
package/lib/constants.js
CHANGED
|
@@ -60,6 +60,7 @@ module.exports = {
|
|
|
60
60
|
SUBSERVICE_HEADER: 'fiware-servicepath',
|
|
61
61
|
NGSI_LD_TENANT_HEADER: 'NGSILD-Tenant',
|
|
62
62
|
NGSI_LD_PATH_HEADER: 'NGSILD-Path',
|
|
63
|
+
NGSI_LD_NULL: 'urn:ngsi-ld:null',
|
|
63
64
|
//FIXME: check Keystone support this in lowercase, then change
|
|
64
65
|
AUTH_HEADER: 'X-Auth-Token',
|
|
65
66
|
X_FORWARDED_FOR_HEADER: 'x-forwarded-for',
|
|
@@ -323,6 +323,7 @@ exports.getConfigurationSilently = groupConfig.getSilently;
|
|
|
323
323
|
exports.findConfiguration = groupConfig.find;
|
|
324
324
|
exports.setDataUpdateHandler = contextServer.setUpdateHandler;
|
|
325
325
|
exports.setCommandHandler = contextServer.setCommandHandler;
|
|
326
|
+
exports.setMergePatchHandler = contextServer.setMergePatchHandler;
|
|
326
327
|
exports.setDataQueryHandler = contextServer.setQueryHandler;
|
|
327
328
|
exports.setConfigurationHandler = contextServer.setConfigurationHandler;
|
|
328
329
|
exports.setRemoveConfigurationHandler = contextServer.setRemoveConfigurationHandler;
|
|
@@ -357,12 +358,7 @@ exports.fillService = domainUtils.fillService;
|
|
|
357
358
|
exports.middlewares = middlewares;
|
|
358
359
|
|
|
359
360
|
exports.dataPlugins = {
|
|
360
|
-
compressTimestamp: require('./plugins/compressTimestamp'),
|
|
361
|
-
attributeAlias: require('./plugins/attributeAlias'),
|
|
362
|
-
addEvents: require('./plugins/addEvent'),
|
|
363
|
-
timestampProcess: require('./plugins/timestampProcessPlugin'),
|
|
364
361
|
expressionTransformation: require('./plugins/expressionPlugin'),
|
|
365
|
-
multiEntity: require('./plugins/multiEntity'),
|
|
366
362
|
bidirectionalData: require('./plugins/bidirectionalData'),
|
|
367
363
|
utils: require('./plugins/pluginUtils')
|
|
368
364
|
};
|
|
@@ -23,9 +23,6 @@
|
|
|
23
23
|
* Modified by: Daniel Calvo - ATOS Research & Innovation
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
const pluginUtils = require('./pluginUtils');
|
|
27
|
-
const constants = require('../constants');
|
|
28
|
-
|
|
29
26
|
/**
|
|
30
27
|
* Takes a string representation of a date in ISO8601 basic calendar format and transforms it into
|
|
31
28
|
* the extended format (with separators).
|
|
@@ -58,7 +55,5 @@ function fromExtendedToBasic(date) {
|
|
|
58
55
|
return null;
|
|
59
56
|
}
|
|
60
57
|
|
|
61
|
-
exports.
|
|
62
|
-
exports.
|
|
63
|
-
exports.updateNgsi2 = pluginUtils.createUpdateFilter(fromBasicToExtended, constants.TIMESTAMP_TYPE_NGSI2);
|
|
64
|
-
exports.queryNgsi2 = pluginUtils.createQueryFilter(fromExtendedToBasic, constants.TIMESTAMP_TYPE_NGSI2);
|
|
58
|
+
exports.fromBasicToExtended = fromBasicToExtended;
|
|
59
|
+
exports.fromExtendedToBasic = fromExtendedToBasic;
|
|
@@ -175,44 +175,6 @@ function applyExpression(expression, context, typeInformation) {
|
|
|
175
175
|
return expressionResult;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
function expressionApplier(context, typeInformation) {
|
|
179
|
-
return function (attribute) {
|
|
180
|
-
/**
|
|
181
|
-
* Determines if a value is of type float
|
|
182
|
-
*
|
|
183
|
-
* @param {String} value Value to be analyzed
|
|
184
|
-
* @return {boolean} True if float, False otherwise.
|
|
185
|
-
*/
|
|
186
|
-
function isFloat(value) {
|
|
187
|
-
return !isNaN(value) && value.toString().indexOf('.') !== -1;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const newAttribute = {
|
|
191
|
-
name: attribute.name,
|
|
192
|
-
type: attribute.type
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
/*jshint camelcase: false */
|
|
196
|
-
if (attribute.object_id) {
|
|
197
|
-
newAttribute.object_id = attribute.object_id;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
newAttribute.value = applyExpression(attribute.expression, context, typeInformation);
|
|
201
|
-
|
|
202
|
-
if (attribute.type === 'Number' && isFloat(newAttribute.value)) {
|
|
203
|
-
newAttribute.value = Number.parseFloat(newAttribute.value);
|
|
204
|
-
} else if (attribute.type === 'Number' && !Number.isNaN(Number.parseInt(newAttribute.value))) {
|
|
205
|
-
newAttribute.value = Number.parseInt(newAttribute.value);
|
|
206
|
-
} else if (attribute.type === 'Boolean') {
|
|
207
|
-
newAttribute.value = newAttribute.value === 'true' || newAttribute.value === '1';
|
|
208
|
-
} else if (attribute.type === 'None') {
|
|
209
|
-
newAttribute.value = null;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return newAttribute;
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
178
|
function contextAvailable(expression, context) {
|
|
217
179
|
let error;
|
|
218
180
|
try {
|
|
@@ -237,16 +199,7 @@ function contextAvailable(expression, context) {
|
|
|
237
199
|
}
|
|
238
200
|
}
|
|
239
201
|
|
|
240
|
-
function processExpressionAttributes(typeInformation, list, context) {
|
|
241
|
-
return list
|
|
242
|
-
.filter(function (item) {
|
|
243
|
-
return item.expression && contextAvailable(item.expression, context);
|
|
244
|
-
})
|
|
245
|
-
.map(expressionApplier(context, typeInformation));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
202
|
exports.parse = parse;
|
|
249
203
|
exports.extractContext = extractContext;
|
|
250
|
-
exports.processExpressionAttributes = processExpressionAttributes;
|
|
251
204
|
exports.contextAvailable = contextAvailable;
|
|
252
205
|
exports.applyExpression = applyExpression;
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
* Modified by: Federico M. Facca - Martel Innovate
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
const _ = require('underscore');
|
|
28
27
|
const legacyParser = require('./expressionParser');
|
|
29
28
|
const jexlParser = require('./jexlParser');
|
|
30
29
|
const config = require('../commonConfig');
|
|
@@ -67,32 +66,12 @@ function parse(expression, context, type, typeInformation) {
|
|
|
67
66
|
return jexlParser.parse(expression, context);
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
for (let i = 0; i < attrList2.length; i++) {
|
|
76
|
-
found = false;
|
|
77
|
-
|
|
78
|
-
for (let j = 0; j < finalCollection.length; j++) {
|
|
79
|
-
if (finalCollection[j].name === attrList2[i].name && attrList2[i].object_id) {
|
|
80
|
-
if (finalCollection[j].object_id === attrList2[i].object_id) {
|
|
81
|
-
finalCollection[j].value = attrList2[i].value;
|
|
82
|
-
found = true;
|
|
83
|
-
}
|
|
84
|
-
} else if (finalCollection[j].name === attrList2[i].name && !attrList2[i].object_id) {
|
|
85
|
-
finalCollection[j].value = attrList2[i].value;
|
|
86
|
-
found = true;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (!found) {
|
|
91
|
-
additionalItems.push(attrList2[i]);
|
|
92
|
-
}
|
|
69
|
+
function contextAvailable(expression, context, typeInformation) {
|
|
70
|
+
let parser = jexlParser;
|
|
71
|
+
if (!checkJexl(typeInformation)) {
|
|
72
|
+
parser = legacyParser;
|
|
93
73
|
}
|
|
94
|
-
|
|
95
|
-
return finalCollection.concat(additionalItems);
|
|
74
|
+
return parser.contextAvailable(expression, context);
|
|
96
75
|
}
|
|
97
76
|
|
|
98
77
|
function checkJexl(typeInformation) {
|
|
@@ -114,60 +93,9 @@ function checkJexl(typeInformation) {
|
|
|
114
93
|
return false;
|
|
115
94
|
}
|
|
116
95
|
|
|
117
|
-
function update(entity, typeInformation, callback) {
|
|
118
|
-
function processEntityUpdateNgsi2(attributes) {
|
|
119
|
-
let parser = legacyParser;
|
|
120
|
-
if (checkJexl(typeInformation)) {
|
|
121
|
-
parser = jexlParser;
|
|
122
|
-
}
|
|
123
|
-
let expressionAttributes = [];
|
|
124
|
-
let attributesCtxt = [...attributes]; // just copy
|
|
125
|
-
if (typeInformation.static) {
|
|
126
|
-
typeInformation.static.forEach(function (att) {
|
|
127
|
-
attributesCtxt.push(att);
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
let idTypeSSSList = utils.getIdTypeServSubServiceFromDevice(typeInformation);
|
|
131
|
-
attributesCtxt = attributesCtxt.concat(idTypeSSSList);
|
|
132
|
-
const ctx = parser.extractContext(attributesCtxt);
|
|
133
|
-
|
|
134
|
-
if (typeInformation.active) {
|
|
135
|
-
expressionAttributes = parser.processExpressionAttributes(typeInformation, typeInformation.active, ctx);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
attributes = mergeAttributes(attributes, expressionAttributes);
|
|
139
|
-
return attributes;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
logger.debug(context, 'expressionPlugin entity %j', entity);
|
|
144
|
-
const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
|
|
145
|
-
// Exclude processing all attr expressions when current attr is of type 'commandStatus' or 'commandResult'
|
|
146
|
-
const attsArrayFiltered = attsArray.filter((obj) => {
|
|
147
|
-
return ![constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
|
|
148
|
-
});
|
|
149
|
-
const attsArrayCmd = attsArray.filter((obj) => {
|
|
150
|
-
// just attr of type 'commandStatus' or 'commandResult'
|
|
151
|
-
return [constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
|
|
152
|
-
});
|
|
153
|
-
let attsArrayFinal = [];
|
|
154
|
-
if (attsArrayFiltered.length > 0) {
|
|
155
|
-
attsArrayFinal = processEntityUpdateNgsi2(attsArrayFiltered);
|
|
156
|
-
}
|
|
157
|
-
attsArrayFinal = attsArrayFinal.concat(attsArrayCmd);
|
|
158
|
-
entity = utils.createNgsi2Entity(entity.id, entity.type, attsArrayFinal, true);
|
|
159
|
-
|
|
160
|
-
callback(null, entity, typeInformation);
|
|
161
|
-
} catch (e) {
|
|
162
|
-
logger.info(context, 'expressionPlugin error %j procesing entity %j', e, entity);
|
|
163
|
-
callback(e);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
96
|
exports.parse = parse;
|
|
169
|
-
exports.update = update;
|
|
170
97
|
exports.setJEXLTransforms = setJEXLTransforms;
|
|
171
98
|
exports.applyExpression = applyExpression;
|
|
172
99
|
exports.extractContext = extractContext;
|
|
100
|
+
exports.contextAvailable = contextAvailable;
|
|
173
101
|
exports.checkJexl = checkJexl;
|
|
@@ -104,33 +104,6 @@ function applyExpression(expression, context, typeInformation) {
|
|
|
104
104
|
return expressionResult;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
function expressionApplier(context, typeInformation) {
|
|
108
|
-
return function (attribute) {
|
|
109
|
-
/**
|
|
110
|
-
* Determines if a value is of type float
|
|
111
|
-
*
|
|
112
|
-
* @param {String} value Value to be analyzed
|
|
113
|
-
* @return {boolean} True if float, False otherwise.
|
|
114
|
-
*/
|
|
115
|
-
function isFloat(value) {
|
|
116
|
-
return !isNaN(value) && value.toString().indexOf('.') !== -1;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const newAttribute = {
|
|
120
|
-
name: attribute.name,
|
|
121
|
-
type: attribute.type
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
/*jshint camelcase: false */
|
|
125
|
-
if (attribute.object_id) {
|
|
126
|
-
newAttribute.object_id = attribute.object_id;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
newAttribute.value = applyExpression(attribute.expression, context, typeInformation);
|
|
130
|
-
return newAttribute;
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
107
|
function isTransform(identifier) {
|
|
135
108
|
return jexl.getTransform(identifier) !== (null || undefined);
|
|
136
109
|
}
|
|
@@ -146,14 +119,6 @@ function contextAvailable(expression, context) {
|
|
|
146
119
|
}
|
|
147
120
|
}
|
|
148
121
|
|
|
149
|
-
function processExpressionAttributes(typeInformation, list, context) {
|
|
150
|
-
return list
|
|
151
|
-
.filter(function (item) {
|
|
152
|
-
return item.expression && contextAvailable(item.expression, context);
|
|
153
|
-
})
|
|
154
|
-
.map(expressionApplier(context, typeInformation));
|
|
155
|
-
}
|
|
156
|
-
|
|
157
122
|
function checkTransformationMap(tranformsMap) {
|
|
158
123
|
let error = null;
|
|
159
124
|
let message = 'No trasformations were added to JEXL Parser';
|
|
@@ -198,7 +163,6 @@ function setTransforms(configMap) {
|
|
|
198
163
|
}
|
|
199
164
|
|
|
200
165
|
exports.extractContext = extractContext;
|
|
201
|
-
exports.processExpressionAttributes = processExpressionAttributes;
|
|
202
166
|
exports.contextAvailable = contextAvailable;
|
|
203
167
|
exports.applyExpression = applyExpression;
|
|
204
168
|
exports.parse = parse;
|
|
@@ -53,120 +53,6 @@ function extractAttributesArrayFromNgsi2Entity(entity) {
|
|
|
53
53
|
return attsArray;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
* Creates a NGSIv2 entity.
|
|
58
|
-
*
|
|
59
|
-
* @param {String} id The identifier
|
|
60
|
-
* @param {String} type The type
|
|
61
|
-
* @param {Object} attsArray The atts array
|
|
62
|
-
* @param {Object} withObjectId The flag to keep object_id
|
|
63
|
-
* @return {Object} A NGSIv2 entity
|
|
64
|
-
*/
|
|
65
|
-
function createNgsi2Entity(id, type, attsArray, withObjectId) {
|
|
66
|
-
const entity = {};
|
|
67
|
-
for (let i = 0; i < attsArray.length; i++) {
|
|
68
|
-
/*jshint camelcase: false */
|
|
69
|
-
if (entity[attsArray[i].name] && withObjectId && attsArray[i].object_id) {
|
|
70
|
-
// Check if multiple measures with multientity attributes with same name(#635)
|
|
71
|
-
if (!entity[attsArray[i].name].multi) {
|
|
72
|
-
entity[attsArray[i].name].multi = [];
|
|
73
|
-
}
|
|
74
|
-
entity[attsArray[i].name].multi.push({
|
|
75
|
-
type: attsArray[i].type,
|
|
76
|
-
value: attsArray[i].value,
|
|
77
|
-
/*jshint camelcase: false */
|
|
78
|
-
object_id: attsArray[i].object_id,
|
|
79
|
-
metadata: attsArray[i].metadata
|
|
80
|
-
});
|
|
81
|
-
} else {
|
|
82
|
-
entity[attsArray[i].name] = {
|
|
83
|
-
type: attsArray[i].type,
|
|
84
|
-
value: attsArray[i].value,
|
|
85
|
-
metadata: attsArray[i].metadata
|
|
86
|
-
};
|
|
87
|
-
if (withObjectId && attsArray[i].object_id) {
|
|
88
|
-
entity[attsArray[i].name].object_id = attsArray[i].object_id;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
entity.id = id;
|
|
93
|
-
entity.type = type;
|
|
94
|
-
return entity;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function createProcessAttribute(fn, attributeType) {
|
|
98
|
-
return function (attribute) {
|
|
99
|
-
if (attribute.type && attribute.type === attributeType) {
|
|
100
|
-
attribute.value = fn(attribute.value);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (attribute.metadata) {
|
|
104
|
-
Object.keys(attribute.metadata).forEach(function (key) {
|
|
105
|
-
const entry = attribute.metadata[key];
|
|
106
|
-
if (entry.type === 'DateTime') {
|
|
107
|
-
entry.value = fn(entry.value);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
return attribute;
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Create a new filter for update requests. The filter will apply the given function to the value
|
|
117
|
-
* of every attribute of the given type.
|
|
118
|
-
*
|
|
119
|
-
* @param {Function} fn Function to apply. Should take one value and return one value.
|
|
120
|
-
* @param {String} attributeType Name of the type of attributes to modify
|
|
121
|
-
* @return {query} Filter ready to be used in data filter plugins.
|
|
122
|
-
*/
|
|
123
|
-
|
|
124
|
-
function createUpdateFilter(fn, attributeType) {
|
|
125
|
-
return function update(entity, typeInformation, callback) {
|
|
126
|
-
function processEntityUpdateNgsi2(entity) {
|
|
127
|
-
let attsArray = extractAttributesArrayFromNgsi2Entity(entity);
|
|
128
|
-
attsArray = attsArray.map(createProcessAttribute(fn, attributeType));
|
|
129
|
-
entity = createNgsi2Entity(entity.id, entity.type, attsArray, true);
|
|
130
|
-
return entity;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
entity = processEntityUpdateNgsi2(entity);
|
|
134
|
-
callback(null, entity, typeInformation);
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Create a new filter for query responses. The filter will apply the given function to the value
|
|
140
|
-
* of every attribute of the given type.
|
|
141
|
-
*
|
|
142
|
-
* @param {Function} fn Function to apply. Should take one value and return one value.
|
|
143
|
-
* @param {String} attributeType Name of the type of attributes to modify
|
|
144
|
-
* @return {query} Filter ready to be used in data filter plugins.
|
|
145
|
-
*/
|
|
146
|
-
function createQueryFilter(fn, attributeType) {
|
|
147
|
-
return function query(entity, typeInformation, callback) {
|
|
148
|
-
function processEntityQueryNgsi2(entity) {
|
|
149
|
-
let attsArray = extractAttributesArrayFromNgsi2Entity(entity);
|
|
150
|
-
attsArray = attsArray.map(createProcessAttribute(fn, attributeType));
|
|
151
|
-
entity = createNgsi2Entity(entity.id, entity.type, attsArray);
|
|
152
|
-
return entity;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
entity = processEntityQueryNgsi2(entity);
|
|
156
|
-
callback(null, entity, typeInformation);
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Creates an empty filter that does not change anything.
|
|
162
|
-
*
|
|
163
|
-
* @param {Object} entity Data identifiying the requesting entity.
|
|
164
|
-
* @param {Object} typeInformation Information about the device corresponding to that entity.
|
|
165
|
-
*/
|
|
166
|
-
function identityFilter(entity, typeInformation, callback) {
|
|
167
|
-
callback(null, entity, typeInformation);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
56
|
/**
|
|
171
57
|
* Creates an array of attributes from an device including id, type, service and subservice
|
|
172
58
|
* @param {Object} device
|
|
@@ -183,10 +69,5 @@ function getIdTypeServSubServiceFromDevice(typeInformation) {
|
|
|
183
69
|
return attrList;
|
|
184
70
|
}
|
|
185
71
|
|
|
186
|
-
exports.createProcessAttribute = createProcessAttribute;
|
|
187
|
-
exports.createUpdateFilter = createUpdateFilter;
|
|
188
|
-
exports.createQueryFilter = createQueryFilter;
|
|
189
|
-
exports.identityFilter = identityFilter;
|
|
190
|
-
exports.createNgsi2Entity = createNgsi2Entity;
|
|
191
72
|
exports.extractAttributesArrayFromNgsi2Entity = extractAttributesArrayFromNgsi2Entity;
|
|
192
73
|
exports.getIdTypeServSubServiceFromDevice = getIdTypeServSubServiceFromDevice;
|
|
@@ -363,6 +363,7 @@ function registerDevice(deviceObj, callback) {
|
|
|
363
363
|
deviceObj.type = deviceData.type;
|
|
364
364
|
deviceObj.staticAttributes = deviceData.staticAttributes;
|
|
365
365
|
deviceObj.commands = deviceData.commands;
|
|
366
|
+
deviceObj.lazy = deviceData.lazy;
|
|
366
367
|
if ('timestamp' in deviceData && deviceData.timestamp !== undefined) {
|
|
367
368
|
deviceObj.timestamp = deviceData.timestamp;
|
|
368
369
|
}
|
|
@@ -310,9 +310,11 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
|
|
|
310
310
|
return sendUnregistrationsNgsiLD(deviceData, callback);
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
const operations = [];
|
|
313
314
|
const properties = [];
|
|
314
315
|
const lazy = deviceData.lazy || [];
|
|
315
316
|
const commands = deviceData.commands || [];
|
|
317
|
+
const supportMerge = config.getConfig().server.ldSupport.merge;
|
|
316
318
|
|
|
317
319
|
lazy.forEach((element) => {
|
|
318
320
|
properties.push(element.name);
|
|
@@ -321,6 +323,16 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
|
|
|
321
323
|
properties.push(element.name);
|
|
322
324
|
});
|
|
323
325
|
|
|
326
|
+
if (lazy.length > 0){
|
|
327
|
+
operations.push('retrieveOps');
|
|
328
|
+
}
|
|
329
|
+
if (commands.length > 0){
|
|
330
|
+
operations.push('updateOps');
|
|
331
|
+
}
|
|
332
|
+
if (supportMerge){
|
|
333
|
+
operations.push('mergeEntity');
|
|
334
|
+
}
|
|
335
|
+
|
|
324
336
|
if (properties.length === 0) {
|
|
325
337
|
logger.debug(context, 'Registration with Context Provider is not needed. Device without lazy atts or commands');
|
|
326
338
|
return callback(null, deviceData);
|
|
@@ -348,10 +360,18 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
|
|
|
348
360
|
information: [
|
|
349
361
|
{
|
|
350
362
|
entities: [{ type: deviceData.type, id }],
|
|
351
|
-
properties
|
|
363
|
+
propertyNames: properties
|
|
352
364
|
}
|
|
353
365
|
],
|
|
366
|
+
mode: 'exclusive',
|
|
367
|
+
operations,
|
|
354
368
|
endpoint: config.getConfig().providerUrl,
|
|
369
|
+
contextSourceInfo: [
|
|
370
|
+
{
|
|
371
|
+
'key': 'jsonldContext',
|
|
372
|
+
'value': config.getConfig().contextBroker.jsonLdContext
|
|
373
|
+
}
|
|
374
|
+
],
|
|
355
375
|
'@context': config.getConfig().contextBroker.jsonLdContext
|
|
356
376
|
},
|
|
357
377
|
headers: {
|