iotagent-node-lib 2.24.0 → 2.26.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/.github/workflows/ci.yml +6 -7
- package/config.js +6 -1
- package/doc/advanced-topics.md +23 -1
- package/doc/apiary/iotagent.apib +5 -5
- package/doc/deprecated.md +13 -7
- package/doc/expressionLanguage.md +44 -34
- package/doc/getting-started.md +1 -1
- package/doc/howto.md +8 -0
- package/doc/installationguide.md +18 -0
- package/doc/usermanual.md +77 -0
- package/examples/TTOpen-service.json +1 -1
- package/lib/commonConfig.js +15 -1
- package/lib/constants.js +1 -0
- package/lib/fiware-iotagent-lib.js +3 -1
- package/lib/jexlTranformsMap.js +12 -1
- package/lib/plugins/jexlParser.js +2 -2
- package/lib/services/devices/deviceService.js +20 -1
- package/lib/services/devices/devices-NGSI-v2.js +3 -1
- package/lib/services/devices/registrationUtils.js +21 -1
- 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/lib/services/northBound/restUtils.js +3 -5
- package/lib/templates/deviceGroup.json +1 -1
- package/package.json +2 -2
- package/test/unit/examples/deviceProvisioningRequests/provisionNewDeviceEmpty.json +43 -0
- package/test/unit/examples/mongoCollections/configurations.json +3 -3
- package/test/unit/expressions/jexlExpression-test.js +29 -8
- package/test/unit/general/deviceService-test.js +31 -29
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +26 -25
- package/test/unit/mongodb/mongodb-group-registry-test.js +3 -3
- package/test/unit/mongodb/mongodb-registry-test.js +30 -21
- 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/updateContextExpressionPlugin30.json +11 -0
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +18 -4
- package/test/unit/ngsi-ld/general/deviceService-test.js +31 -29
- 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/unsupported-endpoints-test.js +171 -0
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +0 -3
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityJexlExpressionPlugin1.json +22 -0
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +21 -6
- package/test/unit/ngsiv2/general/deviceService-test.js +25 -23
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +58 -0
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +25 -6
|
@@ -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: {
|
|
@@ -31,6 +31,7 @@ const async = require('async');
|
|
|
31
31
|
const apply = async.apply;
|
|
32
32
|
const logger = require('logops');
|
|
33
33
|
const errors = require('../../errors');
|
|
34
|
+
const constants = require('../../constants');
|
|
34
35
|
const deviceService = require('../devices/deviceService');
|
|
35
36
|
const middlewares = require('../common/genericMiddleware');
|
|
36
37
|
const _ = require('underscore');
|
|
@@ -41,11 +42,98 @@ const updateContextTemplateNgsiLD = require('../../templates/updateContextNgsiLD
|
|
|
41
42
|
const notificationTemplateNgsiLD = require('../../templates/notificationTemplateNgsiLD.json');
|
|
42
43
|
const contextServerUtils = require('./contextServerUtils');
|
|
43
44
|
const ngsiLD = require('../ngsi/entities-NGSI-LD');
|
|
45
|
+
const config = require('../../commonConfig');
|
|
44
46
|
|
|
45
47
|
const overwritePaths = ['/ngsi-ld/v1/entities/:entity/attrs', '/ngsi-ld/v1/entities/:entity/attrs/:attr'];
|
|
46
48
|
const updatePaths = ['/ngsi-ld/v1/entities/:entity/attrs', '/ngsi-ld/v1/entities/:entity/attrs/:attr'];
|
|
47
49
|
const queryPaths = ['/ngsi-ld/v1/entities/:entity'];
|
|
48
50
|
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Replacement of NGSI-LD Null placeholders with real null values
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
function replaceNGSILDNull(payload){
|
|
57
|
+
Object.keys(payload).forEach((key) =>{
|
|
58
|
+
const value = payload[key];
|
|
59
|
+
if ( value === constants.NGSI_LD_NULL){
|
|
60
|
+
payload[key] = null;
|
|
61
|
+
} else if (typeof value === 'object' &&
|
|
62
|
+
!Array.isArray(value) &&
|
|
63
|
+
value !== null){
|
|
64
|
+
payload[key] = replaceNGSILDNull(payload[key]);
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
return payload;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check to see if the payload or its subattributes contain null values
|
|
72
|
+
*
|
|
73
|
+
*/
|
|
74
|
+
function containsNulls(payload, result){
|
|
75
|
+
Object.keys(payload).forEach((key) =>{
|
|
76
|
+
const value = payload[key];
|
|
77
|
+
if ( value === null){
|
|
78
|
+
result.nulls = true;
|
|
79
|
+
} else if (typeof value === 'object' &&
|
|
80
|
+
!Array.isArray(value) &&
|
|
81
|
+
value !== null){
|
|
82
|
+
containsNulls(payload[key], result);
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* An Express middleware for preprocessing NGSI-LD payloads. Converts NGSI-LD Nulls
|
|
90
|
+
* to real nulls and checks for the presence of null and datasetId
|
|
91
|
+
*
|
|
92
|
+
*/
|
|
93
|
+
function preprocessNGSILD(req, res, next){
|
|
94
|
+
res.locals.hasDatasetId = false;
|
|
95
|
+
const payload = req.body
|
|
96
|
+
if (payload && typeof payload === 'object'){
|
|
97
|
+
Object.keys(payload).forEach((key) =>{
|
|
98
|
+
if (_.isArray(payload[key])){
|
|
99
|
+
payload[key].forEach((obj) => {
|
|
100
|
+
if (obj.datasetId){
|
|
101
|
+
res.locals.hasDatasetId = true;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
} else if (payload[key] && payload[key].datasetId && payload[key].datasetId !== '@none'){
|
|
105
|
+
res.locals.hasDatasetId = true;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
req.body = replaceNGSILDNull(payload);
|
|
109
|
+
const result = { nulls: false }
|
|
110
|
+
containsNulls(payload, result);
|
|
111
|
+
res.locals.hasNulls = result.nulls;
|
|
112
|
+
}
|
|
113
|
+
next();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A configurable Middleware that makes additional NGSI-LD checks within the payload.
|
|
118
|
+
|
|
119
|
+
*
|
|
120
|
+
* @param {Boolean} supportNull Whether to support NGSI-LD nulls in the payload
|
|
121
|
+
* @param {Boolean} supportDatasetId Whether to support multiattributes in the payload.
|
|
122
|
+
* @return {Object} Express middleware used in request validation.
|
|
123
|
+
*/
|
|
124
|
+
function validateNGSILD(supportNull, supportDatasetId) {
|
|
125
|
+
return function validate(req, res, next) {
|
|
126
|
+
if (!supportNull && res.locals.hasNulls) {
|
|
127
|
+
next(new errors.BadRequest('NGSI-LD Null found within the payload. This IoT Agent does not support nulls for this endpoint.'));
|
|
128
|
+
} else if (!supportDatasetId && res.locals.hasDatasetId) {
|
|
129
|
+
next(new errors.BadRequest('datasetId found within the payload. This IoT Agent does not support multi-attribute requests.'));
|
|
130
|
+
} else {
|
|
131
|
+
next();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
49
137
|
/**
|
|
50
138
|
* Extract metadata attributes from input.
|
|
51
139
|
*
|
|
@@ -318,6 +406,104 @@ function defaultQueryHandlerNgsiLD(id, type, service, subservice, attributes, ca
|
|
|
318
406
|
});
|
|
319
407
|
}
|
|
320
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Generate a merge-patch action corresponding to the request using NGSI-LD.
|
|
411
|
+
* Merge-patch is an NGSI-LD specific action.
|
|
412
|
+
*
|
|
413
|
+
* @param {Object} req Update request to generate Actions from
|
|
414
|
+
*/
|
|
415
|
+
function generateMergePatchActionNgsiLD(req, callback) {
|
|
416
|
+
|
|
417
|
+
const entityId = req.params.entity;
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
function addAttributes(deviceData, body, attributes){
|
|
421
|
+
const keys = Object.keys(body);
|
|
422
|
+
|
|
423
|
+
for (const j in deviceData) {
|
|
424
|
+
if (keys.includes(deviceData[j].name)) {
|
|
425
|
+
const obj = body[deviceData[j].name]
|
|
426
|
+
if ( obj === null) {
|
|
427
|
+
attributes.push({
|
|
428
|
+
type: deviceData[j].type,
|
|
429
|
+
value: null,
|
|
430
|
+
name: deviceData[j].name
|
|
431
|
+
});
|
|
432
|
+
} else {
|
|
433
|
+
attributes.push({
|
|
434
|
+
type: deviceData[j].type,
|
|
435
|
+
value: obj.value,
|
|
436
|
+
name: deviceData[j].name
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return attributes;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
deviceService.getDeviceByName(
|
|
446
|
+
entityId,
|
|
447
|
+
contextServerUtils.getLDTenant(req),
|
|
448
|
+
contextServerUtils.getLDPath(req),
|
|
449
|
+
function (error, deviceObj) {
|
|
450
|
+
if (error) {
|
|
451
|
+
callback(error);
|
|
452
|
+
} else {
|
|
453
|
+
const attributes = [];
|
|
454
|
+
addAttributes(deviceObj.commands, req.body, attributes)
|
|
455
|
+
addAttributes(deviceObj.lazy, req.body, attributes)
|
|
456
|
+
const executeMergePatchHandler = apply(
|
|
457
|
+
contextServerUtils.mergePatchHandler,
|
|
458
|
+
entityId,
|
|
459
|
+
deviceObj.type,
|
|
460
|
+
contextServerUtils.getLDTenant(req),
|
|
461
|
+
contextServerUtils.getLDPath(req),
|
|
462
|
+
attributes
|
|
463
|
+
);
|
|
464
|
+
async.waterfall(
|
|
465
|
+
[executeMergePatchHandler],
|
|
466
|
+
callback()
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Express middleware to manage incoming merge-patch requests using NGSI-LD.
|
|
475
|
+
*
|
|
476
|
+
* @param {Object} req Request that was handled in first place.
|
|
477
|
+
* @param {Object} res Response that will be sent.
|
|
478
|
+
*/
|
|
479
|
+
function handleMergePatchNgsiLD(req, res, next) {
|
|
480
|
+
|
|
481
|
+
function handleMergePatchRequest(error, result) {
|
|
482
|
+
if (error) {
|
|
483
|
+
logger.debug(context, 'There was an error handling the merge-patch: %s.', error);
|
|
484
|
+
next(error);
|
|
485
|
+
} else {
|
|
486
|
+
logger.debug(context, 'Merge-patch from [%s] handled successfully.', req.get('host'));
|
|
487
|
+
res.status(200).json(result);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
logger.debug(context, 'Handling merge-patch from [%s]', req.get('host'));
|
|
492
|
+
if ((req.is('json') || req.is('application/ld+json')) === false) {
|
|
493
|
+
return handleMergePatchRequest(new errors.UnsupportedContentType(req.header('content-type')));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (req.body) {
|
|
497
|
+
logger.debug(context, JSON.stringify(req.body, null, 4));
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (contextServerUtils.mergePatchHandler){
|
|
501
|
+
generateMergePatchActionNgsiLD(req, handleMergePatchRequest);
|
|
502
|
+
} else {
|
|
503
|
+
return handleMergePatchRequest(new errors.MethodNotSupported(req.method, req.path))
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
321
507
|
/**
|
|
322
508
|
* Express middleware to manage incoming query context requests using NGSI-LD.
|
|
323
509
|
*
|
|
@@ -489,19 +675,21 @@ function handleQueryNgsiLD(req, res, next) {
|
|
|
489
675
|
* @param {Object} req Request that was handled in first place.
|
|
490
676
|
* @param {Object} res Response that will be sent.
|
|
491
677
|
*/
|
|
492
|
-
function
|
|
493
|
-
|
|
678
|
+
function ErrorHandlingNgsiLD(action) {
|
|
679
|
+
return function errorHandle(error, req, res, next) {
|
|
680
|
+
let code = 500;
|
|
494
681
|
|
|
495
|
-
|
|
682
|
+
logger.debug(context, action + ' NGSI-LD error [%s] handling request: %s', error.name, error.message);
|
|
496
683
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
684
|
+
if (error.code && String(error.code).match(/^[2345]\d\d$/)) {
|
|
685
|
+
code = error.code;
|
|
686
|
+
}
|
|
500
687
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
688
|
+
res.status(code).json({
|
|
689
|
+
error: error.name,
|
|
690
|
+
description: error.message.replace(/[<>\"\'=;\(\)]/g, '')
|
|
691
|
+
});
|
|
692
|
+
}
|
|
505
693
|
}
|
|
506
694
|
|
|
507
695
|
/**
|
|
@@ -601,28 +789,6 @@ function handleNotificationNgsiLD(req, res, next) {
|
|
|
601
789
|
}
|
|
602
790
|
}
|
|
603
791
|
|
|
604
|
-
/**
|
|
605
|
-
* Error handler for NGSI-LD update requests.
|
|
606
|
-
*
|
|
607
|
-
* @param {Object} error Incoming error
|
|
608
|
-
* @param {Object} req Request that was handled in first place.
|
|
609
|
-
* @param {Object} res Response that will be sent.
|
|
610
|
-
*/
|
|
611
|
-
function updateErrorHandlingNgsiLD(error, req, res, next) {
|
|
612
|
-
let code = 500;
|
|
613
|
-
|
|
614
|
-
logger.debug(context, 'Update NGSI-LD error [%s] handing request: %s', error.name, error.message);
|
|
615
|
-
|
|
616
|
-
if (error.code && String(error.code).match(/^[2345]\d\d$/)) {
|
|
617
|
-
code = error.code;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
res.status(code).json({
|
|
621
|
-
error: error.name,
|
|
622
|
-
description: error.message.replace(/[<>\"\'=;\(\)]/g, '')
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
|
|
626
792
|
/**
|
|
627
793
|
* Load unsupported NGSI-LD entity routes and return proper NGSI-LD not supported responses
|
|
628
794
|
*
|
|
@@ -645,36 +811,53 @@ function loadUnsupportedEndpointsNGSILD(router) {
|
|
|
645
811
|
*/
|
|
646
812
|
function loadContextRoutesNGSILD(router) {
|
|
647
813
|
// In a more evolved implementation, more endpoints could be added to queryPathsNgsi2
|
|
648
|
-
// according to
|
|
649
|
-
|
|
814
|
+
// according to https://www.etsi.org/standards-search#page=1&search=GS%20CIM%20009
|
|
815
|
+
|
|
816
|
+
const support = config.getConfig().server.ldSupport;
|
|
650
817
|
let i;
|
|
651
818
|
|
|
652
819
|
logger.info(context, 'Loading NGSI-LD Context server routes');
|
|
820
|
+
// Update Patch endpoints - this endpoint may accept NGSI-LD Null
|
|
653
821
|
for (i = 0; i < updatePaths.length; i++) {
|
|
654
822
|
router.patch(updatePaths[i], [
|
|
655
823
|
middlewares.ensureType,
|
|
656
824
|
middlewares.validateJson(updateContextTemplateNgsiLD),
|
|
825
|
+
preprocessNGSILD,
|
|
826
|
+
validateNGSILD(support.null, support.datasetId),
|
|
657
827
|
handleUpdateNgsiLD,
|
|
658
|
-
|
|
828
|
+
ErrorHandlingNgsiLD('Partial Update')
|
|
659
829
|
]);
|
|
660
830
|
}
|
|
831
|
+
// Merge Patch endpoints - this endpoint may accept NGSI-LD Null
|
|
832
|
+
router.patch('/ngsi-ld/v1/entities/:entity', [
|
|
833
|
+
preprocessNGSILD,
|
|
834
|
+
validateNGSILD(support.null, support.datasetId),
|
|
835
|
+
handleMergePatchNgsiLD,
|
|
836
|
+
ErrorHandlingNgsiLD('Merge-Patch')
|
|
837
|
+
]);
|
|
661
838
|
|
|
839
|
+
// Overwrite/PUT endpoints - this endpoint does not accept NGSI-LD Null
|
|
662
840
|
for (i = 0; i < overwritePaths.length; i++) {
|
|
663
841
|
router.put(overwritePaths[i], [
|
|
664
842
|
middlewares.ensureType,
|
|
665
843
|
middlewares.validateJson(updateContextTemplateNgsiLD),
|
|
844
|
+
preprocessNGSILD,
|
|
845
|
+
validateNGSILD(false, support.datasetId),
|
|
666
846
|
handleUpdateNgsiLD,
|
|
667
|
-
|
|
847
|
+
ErrorHandlingNgsiLD('Overwrite')
|
|
668
848
|
]);
|
|
669
849
|
}
|
|
850
|
+
// Query/GET endpoints - no payload to check.
|
|
670
851
|
for (i = 0; i < queryPaths.length; i++) {
|
|
671
|
-
router.get(queryPaths[i], [handleQueryNgsiLD,
|
|
852
|
+
router.get(queryPaths[i], [handleQueryNgsiLD, ErrorHandlingNgsiLD('Query')]);
|
|
672
853
|
}
|
|
673
854
|
router.post('/notify', [
|
|
674
855
|
middlewares.ensureType,
|
|
675
856
|
middlewares.validateJson(notificationTemplateNgsiLD),
|
|
857
|
+
preprocessNGSILD,
|
|
858
|
+
validateNGSILD(false, support.datasetId),
|
|
676
859
|
handleNotificationNgsiLD,
|
|
677
|
-
|
|
860
|
+
ErrorHandlingNgsiLD('Notify')
|
|
678
861
|
]);
|
|
679
862
|
loadUnsupportedEndpointsNGSILD(router);
|
|
680
863
|
}
|
|
@@ -64,7 +64,7 @@ function setUpdateHandler(newHandler) {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* Sets the new user handler for
|
|
67
|
+
* Sets the new user handler for command execution requests. This handler will be called whenever an update request
|
|
68
68
|
* arrives to a with the following parameters: (id, type, attributes, callback). The callback is in charge of updating
|
|
69
69
|
* the corresponding values in the devices with the appropriate protocol.
|
|
70
70
|
*
|
|
@@ -77,6 +77,18 @@ function setCommandHandler(newHandler) {
|
|
|
77
77
|
contextServerUtils.commandHandler = newHandler;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Sets the new user handler for NGSI-LD merge-patch requests. This handler will be called whenever an update request
|
|
82
|
+
* arrives to a with the following parameters: (id, type, attributes, callback). The callback is in charge of updating
|
|
83
|
+
* the corresponding values in the devices with the appropriate protocol.
|
|
84
|
+
*
|
|
85
|
+
*
|
|
86
|
+
* @param {Function} newHandler User handler for update requests
|
|
87
|
+
*/
|
|
88
|
+
function setMergePatchHandler(newHandler) {
|
|
89
|
+
contextServerUtils.mergePatchHandler = newHandler;
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
/**
|
|
81
93
|
* Sets the new user handler for Entity query requests. This handler will be called whenever an update request arrives
|
|
82
94
|
* with the following parameters: (id, type, attributes, callback). The handler must retrieve all the corresponding
|
|
@@ -140,6 +152,7 @@ function clear(callback) {
|
|
|
140
152
|
exports.clear = clear;
|
|
141
153
|
exports.loadContextRoutes = intoTrans(context, loadContextRoutes);
|
|
142
154
|
exports.setUpdateHandler = intoTrans(context, setUpdateHandler);
|
|
155
|
+
exports.setMergePatchHandler = intoTrans(context, setMergePatchHandler);
|
|
143
156
|
exports.setCommandHandler = intoTrans(context, setCommandHandler);
|
|
144
157
|
exports.setNotificationHandler = intoTrans(context, setNotificationHandler);
|
|
145
158
|
exports.addNotificationMiddleware = intoTrans(context, addNotificationMiddleware);
|
|
@@ -110,6 +110,7 @@ function clear(callback) {
|
|
|
110
110
|
async.series([deviceProvisioning.clear, groupProvisioning.clear, contextServer.clear], callback);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
exports.setMergePatchHandler = intoTrans(context, contextServer.setMergePatchHandler);
|
|
113
114
|
exports.setUpdateHandler = intoTrans(context, contextServer.setUpdateHandler);
|
|
114
115
|
exports.setQueryHandler = intoTrans(context, contextServer.setQueryHandler);
|
|
115
116
|
exports.setCommandHandler = intoTrans(context, contextServer.setCommandHandler);
|
|
@@ -53,11 +53,9 @@ function checkMandatoryQueryParams(mandatoryAttributes, body, callback) {
|
|
|
53
53
|
for (const p in mandatoryAttributes) {
|
|
54
54
|
let found = false;
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
if (body
|
|
58
|
-
|
|
59
|
-
found = true;
|
|
60
|
-
}
|
|
56
|
+
if (body.hasOwnProperty(mandatoryAttributes[p])) {
|
|
57
|
+
if (body[mandatoryAttributes[p]] !== '' && body[mandatoryAttributes[p]] !== null) {
|
|
58
|
+
found = true;
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "iotagent-node-lib",
|
|
3
3
|
"license": "AGPL-3.0-only",
|
|
4
4
|
"description": "IoT Agent library to interface with NGSI Context Broker",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.26.0",
|
|
6
6
|
"homepage": "https://github.com/telefonicaid/iotagent-node-lib",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"fiware",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"main": "lib/fiware-iotagent-lib",
|
|
25
25
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
26
|
+
"node": ">=14"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"clean": "rm -rf package-lock.json && rm -rf node_modules && rm -rf coverage",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"devices": [
|
|
3
|
+
{
|
|
4
|
+
"device_id": "",
|
|
5
|
+
"protocol": "GENERIC_PROTO",
|
|
6
|
+
"entity_name": "TheFirstLight",
|
|
7
|
+
"entity_type": "TheLightType",
|
|
8
|
+
"timezone": "America/Santiago",
|
|
9
|
+
"endpoint": "http://fakedEndpoint:1234",
|
|
10
|
+
"transport": "MQTT",
|
|
11
|
+
"attributes": [
|
|
12
|
+
{
|
|
13
|
+
"name": "attr_name",
|
|
14
|
+
"type": "string"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"lazy": [
|
|
18
|
+
{
|
|
19
|
+
"name": "luminance",
|
|
20
|
+
"type": "lumens"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"static_attributes": [
|
|
24
|
+
{
|
|
25
|
+
"name": "hardcodedAttr",
|
|
26
|
+
"type": "hardcodedType",
|
|
27
|
+
"value": "hardcodedValue"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"commands": [
|
|
31
|
+
{
|
|
32
|
+
"name": "commandAttr",
|
|
33
|
+
"type": "commandType"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"internal_attributes": [
|
|
37
|
+
{
|
|
38
|
+
"customField": "customValue"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
{
|
|
3
3
|
"apikey" : "7e5721f478220be21f41f4700e50be2",
|
|
4
4
|
"token" : "e50b7781d5e2d39d577e7c15e4a21f470ed39d5777e7c15e2e2d39d5777e7c1e2",
|
|
5
|
-
"
|
|
5
|
+
"cbHost" : "http://10.0.0.2:1026",
|
|
6
6
|
"entity_type" : "device",
|
|
7
7
|
"resource" : "/iot/d",
|
|
8
8
|
"service" : "smart_gondor",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"apikey" : "gdb13gq3f8q4beuk263besr8eh9ri",
|
|
13
|
-
"
|
|
13
|
+
"cbHost" : "http://10.0.0.2:1026",
|
|
14
14
|
"entity_type" : "device",
|
|
15
15
|
"resource" : "/iot/d",
|
|
16
16
|
"service" : "dumb_mordor",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"apikey" : "hs4h2ke7oerfdvrexbyh54w2",
|
|
28
|
-
"
|
|
28
|
+
"cbHost" : "http://10.0.0.2:1026",
|
|
29
29
|
"entity_type" : "device",
|
|
30
30
|
"resource" : "/iot/d",
|
|
31
31
|
"service" : "demo",
|
|
@@ -234,6 +234,26 @@ describe('Jexl expression interpreter', function () {
|
|
|
234
234
|
});
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
+
describe('When an concatarr function is used with an array', function () {
|
|
238
|
+
it('should work on the expression value', function (done) {
|
|
239
|
+
expressionParser.parse('array|concatarr(array)', scope, function (error, result) {
|
|
240
|
+
should.not.exist(error);
|
|
241
|
+
result.should.deepEqual([1, 2, 1, 2]);
|
|
242
|
+
done();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('When an joinarrtostr function is used with an array', function () {
|
|
248
|
+
it('should work on the expression value', function (done) {
|
|
249
|
+
expressionParser.parse('array|joinarrtostr("_")', scope, function (error, result) {
|
|
250
|
+
should.not.exist(error);
|
|
251
|
+
result.should.equal('1_2');
|
|
252
|
+
done();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
237
257
|
describe('When a function is applied to an array', function () {
|
|
238
258
|
it('should work on the expression value', function (done) {
|
|
239
259
|
expressionParser.parse('array[1]+1', scope, function (error, result) {
|
|
@@ -313,14 +333,15 @@ describe('Jexl expression interpreter', function () {
|
|
|
313
333
|
|
|
314
334
|
describe('When a JSON parse transformation is applied', function () {
|
|
315
335
|
it('should work on the expression value', function (done) {
|
|
316
|
-
expressionParser.parse(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
336
|
+
expressionParser.parse(
|
|
337
|
+
'"{\\"name\\":\\"John\\",\\"surname\\":\\"Doe\\"}"|jsonparse',
|
|
338
|
+
scope,
|
|
339
|
+
function (error, result) {
|
|
340
|
+
should.not.exist(error);
|
|
341
|
+
result.should.eql(scope.object);
|
|
342
|
+
done();
|
|
343
|
+
}
|
|
344
|
+
);
|
|
324
345
|
});
|
|
325
346
|
});
|
|
326
347
|
|