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
|
@@ -90,10 +90,10 @@ function isFloat(value) {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
* It casts attribute values which are reported using JSON
|
|
93
|
+
* It casts attribute values which are reported using JSON parsing
|
|
94
94
|
*
|
|
95
95
|
* @param {String} payload The payload
|
|
96
|
-
* @return {String} New payload where attributes's values are casted to the corresponding JSON
|
|
96
|
+
* @return {String} New payload where attributes's values are casted to the corresponding JSON values
|
|
97
97
|
*/
|
|
98
98
|
function castJsonNativeAttributes(payload) {
|
|
99
99
|
if (!config.getConfig().autocast) {
|
|
@@ -105,28 +105,13 @@ function castJsonNativeAttributes(payload) {
|
|
|
105
105
|
/* eslint-disable-next-line no-prototype-builtins */
|
|
106
106
|
payload.hasOwnProperty(key) &&
|
|
107
107
|
payload[key].value &&
|
|
108
|
-
payload[key].type &&
|
|
109
108
|
typeof payload[key].value === 'string'
|
|
110
109
|
) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
payload[key].value = payload[key].value === 'true' || payload[key].value === '1';
|
|
117
|
-
} else if (payload[key].type === 'None') {
|
|
118
|
-
payload[key].value = null;
|
|
119
|
-
} else if (payload[key].type === 'Array' || payload[key].type === 'Object') {
|
|
120
|
-
try {
|
|
121
|
-
const parsedValue = JSON.parse(payload[key].value);
|
|
122
|
-
payload[key].value = parsedValue;
|
|
123
|
-
} catch (e) {
|
|
124
|
-
logger.error(
|
|
125
|
-
context,
|
|
126
|
-
'Bad attribute value type. Expecting JSON Array or JSON Object. Received:%s',
|
|
127
|
-
payload[key].value
|
|
128
|
-
);
|
|
129
|
-
}
|
|
110
|
+
try {
|
|
111
|
+
const parsedValue = JSON.parse(payload[key].value);
|
|
112
|
+
payload[key].value = parsedValue;
|
|
113
|
+
} catch (e) {
|
|
114
|
+
// Keep value as is
|
|
130
115
|
}
|
|
131
116
|
}
|
|
132
117
|
}
|
|
@@ -223,6 +208,14 @@ function getMetaData(typeInformation, name, metadata) {
|
|
|
223
208
|
}
|
|
224
209
|
}
|
|
225
210
|
}
|
|
211
|
+
if (typeInformation.lazy) {
|
|
212
|
+
for (i = 0; i < typeInformation.lazy.length; i++) {
|
|
213
|
+
/* jshint camelcase: false */
|
|
214
|
+
if (name === typeInformation.lazy[i].object_id) {
|
|
215
|
+
return typeInformation.lazy[i].metadata;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
226
219
|
if (typeInformation.staticAttributes) {
|
|
227
220
|
for (i = 0; i < typeInformation.staticAttributes.length; i++) {
|
|
228
221
|
if (name === typeInformation.staticAttributes[i].name) {
|
|
@@ -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);
|
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": "
|
|
5
|
+
"version": "3.0.0",
|
|
6
6
|
"homepage": "https://github.com/telefonicaid/iotagent-node-lib",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"fiware",
|
|
@@ -73,8 +73,6 @@ describe('Log level API', function () {
|
|
|
73
73
|
|
|
74
74
|
iotAgentLib.activate(iotAgentConfig, function () {
|
|
75
75
|
iotAgentLib.clearAll(function () {
|
|
76
|
-
iotAgentLib.addUpdateMiddleware(iotAgentLib.dataPlugins.attributeAlias.update);
|
|
77
|
-
iotAgentLib.addQueryMiddleware(iotAgentLib.dataPlugins.attributeAlias.query);
|
|
78
76
|
done();
|
|
79
77
|
});
|
|
80
78
|
});
|
|
@@ -241,7 +241,7 @@ describe('NGSI-v2 - In memory device registry', function () {
|
|
|
241
241
|
should.exist(device.type);
|
|
242
242
|
device.name.should.equal('name5');
|
|
243
243
|
device.type.should.equal('Light5');
|
|
244
|
-
Object.keys(device).length.should.equal(
|
|
244
|
+
Object.keys(device).length.should.equal(12);
|
|
245
245
|
done();
|
|
246
246
|
});
|
|
247
247
|
});
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
"type": "ContextSourceRegistration",
|
|
3
|
+
"information": [
|
|
4
|
+
{
|
|
5
|
+
"entities": [
|
|
5
6
|
{
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"id": "urn:ngsi-ld:Light:light1",
|
|
9
|
-
"type": "Light"
|
|
10
|
-
}
|
|
11
|
-
],
|
|
12
|
-
"properties": [
|
|
13
|
-
"temperature"
|
|
14
|
-
]
|
|
7
|
+
"type": "Light",
|
|
8
|
+
"id": "urn:ngsi-ld:Light:light1"
|
|
15
9
|
}
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
],
|
|
11
|
+
"propertyNames": [
|
|
12
|
+
"temperature"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"mode": "exclusive",
|
|
17
|
+
"operations": [
|
|
18
|
+
"retrieveOps"
|
|
19
|
+
],
|
|
20
|
+
"endpoint": "http://smartgondor.com",
|
|
21
|
+
"contextSourceInfo": [
|
|
22
|
+
{
|
|
23
|
+
"key": "jsonldContext",
|
|
24
|
+
"value": "http://context.json-ld"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"@context": "http://context.json-ld"
|
|
18
28
|
}
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
{
|
|
5
|
-
"entities": [
|
|
2
|
+
"type": "ContextSourceRegistration",
|
|
3
|
+
"information": [
|
|
6
4
|
{
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
"entities": [
|
|
6
|
+
{
|
|
7
|
+
"type": "Motion",
|
|
8
|
+
"id": "urn:ngsi-ld:Motion:motion1"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"propertyNames": [
|
|
12
|
+
"moving"
|
|
13
|
+
]
|
|
9
14
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
],
|
|
16
|
+
"mode": "exclusive",
|
|
17
|
+
"operations": [
|
|
18
|
+
"retrieveOps"
|
|
19
|
+
],
|
|
20
|
+
"endpoint": "http://smartgondor.com",
|
|
21
|
+
"contextSourceInfo":[
|
|
22
|
+
{
|
|
23
|
+
"key": "jsonldContext",
|
|
24
|
+
"value": "http://context.json-ld"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"@context": "http://context.json-ld"
|
|
28
|
+
}
|
|
@@ -4,15 +4,25 @@
|
|
|
4
4
|
{
|
|
5
5
|
"entities": [
|
|
6
6
|
{
|
|
7
|
-
"type": "
|
|
8
|
-
"id": "urn:ngsi-ld:
|
|
7
|
+
"type": "Light",
|
|
8
|
+
"id": "urn:ngsi-ld:Light:light1"
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
|
-
"
|
|
12
|
-
"
|
|
11
|
+
"propertyNames": [
|
|
12
|
+
"temperature"
|
|
13
13
|
]
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
|
+
"mode": "exclusive",
|
|
17
|
+
"operations": [
|
|
18
|
+
"retrieveOps"
|
|
19
|
+
],
|
|
16
20
|
"endpoint": "http://smartgondor.com",
|
|
21
|
+
"contextSourceInfo": [
|
|
22
|
+
{
|
|
23
|
+
"key": "jsonldContext",
|
|
24
|
+
"value": "http://context.json-ld"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
17
27
|
"@context": "http://context.json-ld"
|
|
18
|
-
}
|
|
28
|
+
}
|
package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@context": "http://context.json-ld",
|
|
3
3
|
"endpoint": "http://smartgondor.com",
|
|
4
|
+
"contextSourceInfo":[
|
|
5
|
+
{
|
|
6
|
+
"key": "jsonldContext",
|
|
7
|
+
"value": "http://context.json-ld"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
4
10
|
"information": [
|
|
5
11
|
{
|
|
6
12
|
"entities": [
|
|
@@ -9,11 +15,13 @@
|
|
|
9
15
|
"type": "Robot"
|
|
10
16
|
}
|
|
11
17
|
],
|
|
12
|
-
"
|
|
18
|
+
"propertyNames": [
|
|
13
19
|
"position",
|
|
14
20
|
"orientation"
|
|
15
21
|
]
|
|
16
22
|
}
|
|
17
23
|
],
|
|
24
|
+
"mode": "exclusive",
|
|
25
|
+
"operations" :["updateOps"],
|
|
18
26
|
"type": "ContextSourceRegistration"
|
|
19
27
|
}
|
package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommandsAndLazy.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "ContextSourceRegistration",
|
|
3
|
+
"information": [
|
|
4
|
+
{
|
|
5
|
+
"entities": [
|
|
6
|
+
{
|
|
7
|
+
"type": "Robot",
|
|
8
|
+
"id": "urn:ngsi-ld:Robot:r2d2"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"propertyNames": [
|
|
12
|
+
"batteryLevel",
|
|
13
|
+
"position",
|
|
14
|
+
"orientation"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"mode": "exclusive",
|
|
19
|
+
"operations": [
|
|
20
|
+
"retrieveOps",
|
|
21
|
+
"updateOps",
|
|
22
|
+
"mergeEntity"
|
|
23
|
+
],
|
|
24
|
+
"endpoint": "http://smartgondor.com",
|
|
25
|
+
"contextSourceInfo": [
|
|
26
|
+
{
|
|
27
|
+
"key": "jsonldContext",
|
|
28
|
+
"value": "http://context.json-ld"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"@context": "http://context.json-ld"
|
|
32
|
+
}
|
package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@context": "http://context.json-ld",
|
|
3
3
|
"endpoint": "http://smartgondor.com",
|
|
4
|
+
"contextSourceInfo":[
|
|
5
|
+
{
|
|
6
|
+
"key": "jsonldContext",
|
|
7
|
+
"value": "http://context.json-ld"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
4
10
|
"information": [
|
|
5
11
|
{
|
|
6
12
|
"entities": [
|
|
@@ -9,11 +15,16 @@
|
|
|
9
15
|
"type": "TheLightType"
|
|
10
16
|
}
|
|
11
17
|
],
|
|
12
|
-
"
|
|
18
|
+
"propertyNames": [
|
|
13
19
|
"luminance",
|
|
14
20
|
"commandAttr"
|
|
15
21
|
]
|
|
16
22
|
}
|
|
17
23
|
],
|
|
24
|
+
"mode": "exclusive",
|
|
25
|
+
"operations": [
|
|
26
|
+
"retrieveOps",
|
|
27
|
+
"updateOps"
|
|
28
|
+
],
|
|
18
29
|
"type": "ContextSourceRegistration"
|
|
19
30
|
}
|