iotagent-node-lib 2.18.0 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +1 -2
- package/.nyc_output/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +1 -0
- package/.nyc_output/processinfo/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.readthedocs.yml +3 -1
- package/CHANGES_NEXT_RELEASE +1 -0
- package/README.md +5 -56
- package/doc/advanced-topics.md +121 -85
- package/doc/api.md +63 -54
- package/doc/development.md +8 -9
- package/doc/expressionLanguage.md +517 -316
- package/doc/installationguide.md +66 -64
- package/doc/northboundinteractions.md +40 -33
- package/doc/requirements.txt +4 -0
- package/{docs → doc}/roadmap.md +21 -6
- package/doc/usermanual.md +50 -18
- package/docker/Mosquitto/Dockerfile +28 -11
- package/docker/Mosquitto/README.md +8 -6
- package/docker/Mosquitto/startMosquitto.sh +14 -4
- package/lib/fiware-iotagent-lib.js +4 -2
- package/lib/jexlTranformsMap.js +11 -1
- package/lib/model/Device.js +4 -1
- package/lib/model/Group.js +19 -1
- package/lib/plugins/expressionParser.js +6 -4
- package/lib/plugins/expressionPlugin.js +63 -22
- package/lib/plugins/jexlParser.js +3 -1
- package/lib/plugins/multiEntity.js +2 -21
- package/lib/request-shim.js +111 -0
- package/lib/services/common/domain.js +6 -2
- package/lib/services/common/genericMiddleware.js +6 -2
- package/lib/services/common/iotManagerService.js +1 -1
- package/lib/services/common/securityServiceKeystone.js +1 -1
- package/lib/services/common/securityServiceOAuth2.js +3 -2
- package/lib/services/devices/deviceRegistryMemory.js +13 -2
- package/lib/services/devices/deviceRegistryMongoDB.js +16 -7
- package/lib/services/devices/deviceService.js +26 -2
- package/lib/services/devices/devices-NGSI-LD.js +1 -1
- package/lib/services/devices/devices-NGSI-v2.js +2 -6
- package/lib/services/devices/registrationUtils.js +0 -2
- package/lib/services/ngsi/entities-NGSI-LD.js +97 -11
- package/lib/services/ngsi/entities-NGSI-v2.js +95 -8
- package/lib/services/ngsi/ngsiService.js +5 -4
- package/lib/services/northBound/contextServer-NGSI-LD.js +3 -2
- package/lib/services/northBound/contextServer-NGSI-v2.js +32 -27
- package/lib/services/northBound/contextServerUtils.js +1 -1
- package/lib/services/northBound/deviceProvisioningServer.js +31 -6
- package/lib/services/northBound/northboundServer.js +2 -0
- package/lib/services/northBound/restUtils.js +1 -1
- package/lib/templates/createDevice.json +12 -0
- package/lib/templates/updateDevice.json +12 -0
- package/package.json +9 -15
- package/test/tools/utils.js +2 -0
- package/test/unit/expressions/jexlExpression-test.js +5 -5
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +1 -1
- package/test/unit/general/deviceService-test.js +2 -5
- package/test/unit/general/loglevel-api_test.js +6 -11
- package/test/unit/general/startup-test.js +1 -0
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -0
- package/test/unit/mongodb/mongodb-group-registry-test.js +1 -1
- package/test/unit/mongodb/mongodb-registry-test.js +2 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin12a.json +7 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin13.json +13 -13
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +18 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +18 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin31.json +15 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin32.json +17 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin33.json +18 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin34.json +17 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +36 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin7.json +16 -16
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin8a.json +18 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1018 -0
- package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
- package/test/unit/ngsi-ld/general/deviceService-test.js +1 -1
- package/test/unit/ngsi-ld/general/https-support-test.js +2 -1
- package/test/unit/ngsi-ld/general/iotam-autoregistration-test.js +2 -1
- package/test/unit/ngsi-ld/general/startup-test.js +1 -0
- package/test/unit/ngsi-ld/lazyAndCommands/active-devices-attribute-update-test.js +3 -1
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +2 -1
- package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +2 -6
- package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/active-devices-test.js +1 -0
- package/test/unit/ngsi-ld/ngsiService/autocast-test.js +1 -0
- package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +1 -0
- package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +4 -3
- package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +1 -0
- package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +3 -2
- package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +61 -0
- package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +2 -1
- package/test/unit/ngsi-ld/provisioning/device-registration_test.js +3 -2
- package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +42 -54
- package/test/unit/ngsi-ld/provisioning/provisionDeviceMultientity-test.js +2 -1
- package/test/unit/ngsi-ld/provisioning/removeProvisionedDevice-test.js +4 -4
- package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +3 -5
- package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +12 -18
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +3 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin17.json +1 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +16 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin33.json +22 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +12 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +2 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +25 -0
- package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +4 -4
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +561 -0
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +3 -2
- package/test/unit/ngsiv2/general/deviceService-test.js +9 -8
- package/test/unit/ngsiv2/general/https-support-test.js +2 -1
- package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +2 -1
- package/test/unit/ngsiv2/general/startup-test.js +1 -0
- package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +3 -1
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +2 -1
- package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +14 -18
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +3 -1
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +1 -0
- package/test/unit/ngsiv2/ngsiService/queryDeviceInformationInCb-test.js +0 -1
- package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +4 -3
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +3 -2
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +210 -0
- package/test/unit/ngsiv2/plugins/translation-inPlugins_test.js +1 -1
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +3 -2
- package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +2 -1
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +2 -1
- package/test/unit/ngsiv2/provisioning/device-registration_test.js +3 -2
- package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +4 -3
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +42 -53
- package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +2 -1
- package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +4 -4
- package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +3 -4
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +13 -19
- package/test/unit/plugins/capture-configuration-inPlugins_test.js +3 -1
- package/test/unit/plugins/capture-provision-inPlugins_test.js +2 -1
- package/bin/agentConsole.js +0 -257
- package/bin/iotAgentTester.js +0 -44
- package/lib/command/commandLine.js +0 -918
- package/lib/command/migration.js +0 -176
- package/test/unit/general/migration-test.js +0 -256
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin20.json +0 -25
|
@@ -37,6 +37,7 @@ const iotManager = require('./services/common/iotManagerService');
|
|
|
37
37
|
const contextServer = require('./services/northBound/northboundServer');
|
|
38
38
|
const errors = require('./errors');
|
|
39
39
|
const constants = require('./constants');
|
|
40
|
+
const request = require('./request-shim');
|
|
40
41
|
const logger = require('logops');
|
|
41
42
|
const config = require('./commonConfig');
|
|
42
43
|
const cluster = require('cluster');
|
|
@@ -313,6 +314,7 @@ exports.listDevices = deviceService.listDevices;
|
|
|
313
314
|
exports.getDevice = deviceService.getDevice;
|
|
314
315
|
exports.getDeviceSilently = deviceService.getDeviceSilently;
|
|
315
316
|
exports.getDeviceByName = deviceService.getDeviceByName;
|
|
317
|
+
exports.getDeviceByNameAndType = deviceService.getDeviceByNameAndType;
|
|
316
318
|
exports.getDevicesByAttribute = deviceService.getDevicesByAttribute;
|
|
317
319
|
exports.mergeDeviceWithConfiguration = deviceService.mergeDeviceWithConfiguration;
|
|
318
320
|
exports.retrieveDevice = deviceService.retrieveDevice;
|
|
@@ -325,6 +327,7 @@ exports.setDataQueryHandler = contextServer.setQueryHandler;
|
|
|
325
327
|
exports.setConfigurationHandler = contextServer.setConfigurationHandler;
|
|
326
328
|
exports.setRemoveConfigurationHandler = contextServer.setRemoveConfigurationHandler;
|
|
327
329
|
exports.setProvisioningHandler = contextServer.setProvisioningHandler;
|
|
330
|
+
exports.setUpdatingHandler = contextServer.setUpdatingHandler;
|
|
328
331
|
exports.setRemoveDeviceHandler = contextServer.setRemoveDeviceHandler;
|
|
329
332
|
exports.setNotificationHandler = contextServer.setNotificationHandler;
|
|
330
333
|
exports.addUpdateMiddleware = ngsi.addUpdateMiddleware;
|
|
@@ -353,8 +356,6 @@ exports.fillService = domainUtils.fillService;
|
|
|
353
356
|
|
|
354
357
|
exports.middlewares = middlewares;
|
|
355
358
|
|
|
356
|
-
exports.commandLine = require('./command/commandLine');
|
|
357
|
-
|
|
358
359
|
exports.dataPlugins = {
|
|
359
360
|
compressTimestamp: require('./plugins/compressTimestamp'),
|
|
360
361
|
attributeAlias: require('./plugins/attributeAlias'),
|
|
@@ -372,3 +373,4 @@ exports.constants = constants;
|
|
|
372
373
|
exports.logModule = logger;
|
|
373
374
|
exports.configModule = config;
|
|
374
375
|
exports.startServer = startServer;
|
|
376
|
+
exports.request = request;
|
package/lib/jexlTranformsMap.js
CHANGED
|
@@ -57,7 +57,17 @@ const map = {
|
|
|
57
57
|
bitwisemask: (i, mask, op, shf) =>
|
|
58
58
|
(op === '&' ? parseInt(i) & mask : op === '|' ? parseInt(i) | mask : op === '^' ? parseInt(i) ^ mask : i) >>
|
|
59
59
|
shf,
|
|
60
|
-
slice: (arr, init, end) => arr.slice(init, end)
|
|
60
|
+
slice: (arr, init, end) => arr.slice(init, end),
|
|
61
|
+
addset: (arr, x) => {
|
|
62
|
+
return Array.from(new Set(arr).add(x));
|
|
63
|
+
},
|
|
64
|
+
removeset: (arr, x) => {
|
|
65
|
+
let s = new Set(arr);
|
|
66
|
+
s.delete(x);
|
|
67
|
+
return Array.from(s);
|
|
68
|
+
},
|
|
69
|
+
touppercase: (val) => String(val).toUpperCase(),
|
|
70
|
+
tolowercase: (val) => String(val).toLowerCase()
|
|
61
71
|
};
|
|
62
72
|
|
|
63
73
|
exports.map = map;
|
package/lib/model/Device.js
CHANGED
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
|
|
24
24
|
const mongoose = require('mongoose');
|
|
25
25
|
const Schema = mongoose.Schema;
|
|
26
|
+
const Group = require('./Group');
|
|
27
|
+
|
|
28
|
+
mongoose.Schema.Types.ExplicitAttrsType = Group.ExplicitAttrsType;
|
|
26
29
|
|
|
27
30
|
const Device = new Schema({
|
|
28
31
|
id: String,
|
|
@@ -49,7 +52,7 @@ const Device = new Schema({
|
|
|
49
52
|
internalAttributes: Object,
|
|
50
53
|
autoprovision: Boolean,
|
|
51
54
|
expressionLanguage: String,
|
|
52
|
-
explicitAttrs:
|
|
55
|
+
explicitAttrs: Group.ExplicitAttrsType,
|
|
53
56
|
ngsiVersion: String
|
|
54
57
|
});
|
|
55
58
|
|
package/lib/model/Group.js
CHANGED
|
@@ -24,6 +24,23 @@
|
|
|
24
24
|
const mongoose = require('mongoose');
|
|
25
25
|
const Schema = mongoose.Schema;
|
|
26
26
|
|
|
27
|
+
class ExplicitAttrsType extends mongoose.SchemaType {
|
|
28
|
+
constructor(key, options) {
|
|
29
|
+
super(key, options, 'ExplicitAttrsType');
|
|
30
|
+
}
|
|
31
|
+
// `cast()` takes a parameter that can be anything. You need to
|
|
32
|
+
// validate the provided `val` and throw a `CastError` if you
|
|
33
|
+
// can't convert it.
|
|
34
|
+
cast(val) {
|
|
35
|
+
if (!(typeof val === 'boolean' || typeof val === 'string')) {
|
|
36
|
+
throw new Error('ExplicitAttrsType: ' + val + ' is not Boolean or String');
|
|
37
|
+
}
|
|
38
|
+
return val;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
mongoose.Schema.Types.ExplicitAttrsType = ExplicitAttrsType;
|
|
43
|
+
|
|
27
44
|
const Group = new Schema({
|
|
28
45
|
url: String,
|
|
29
46
|
resource: String,
|
|
@@ -43,7 +60,7 @@ const Group = new Schema({
|
|
|
43
60
|
internalAttributes: Array,
|
|
44
61
|
autoprovision: Boolean,
|
|
45
62
|
expressionLanguage: String,
|
|
46
|
-
explicitAttrs:
|
|
63
|
+
explicitAttrs: ExplicitAttrsType,
|
|
47
64
|
defaultEntityNameConjunction: String,
|
|
48
65
|
ngsiVersion: String
|
|
49
66
|
});
|
|
@@ -55,3 +72,4 @@ function load(db) {
|
|
|
55
72
|
}
|
|
56
73
|
|
|
57
74
|
module.exports.load = load;
|
|
75
|
+
module.exports.ExplicitAttrsType = ExplicitAttrsType;
|
|
@@ -28,15 +28,16 @@
|
|
|
28
28
|
const Parser = require('jison').Parser;
|
|
29
29
|
const errors = require('../errors');
|
|
30
30
|
const logger = require('logops');
|
|
31
|
+
const fillService = require('../services/common/domain').fillService;
|
|
31
32
|
const _ = require('underscore');
|
|
32
|
-
|
|
33
|
+
let logContext = {
|
|
33
34
|
op: 'IoTAgentNGSI.Expression'
|
|
34
35
|
};
|
|
35
36
|
const grammar = {
|
|
36
37
|
lex: {
|
|
37
38
|
rules: [
|
|
38
39
|
['\\s+', '/* skip whitespace */'],
|
|
39
|
-
['@[a-zA-Z0-
|
|
40
|
+
['@[a-zA-Z0-9_]+\\b', 'return "VARIABLE";'],
|
|
40
41
|
['[0-9]+(?:\\.[0-9]+)?\\b', 'return "NUMBER";'],
|
|
41
42
|
['\\*', 'return "*";'],
|
|
42
43
|
['\\/', 'return "/";'],
|
|
@@ -156,6 +157,7 @@ function processExpression(context) {
|
|
|
156
157
|
|
|
157
158
|
/* eslint-disable-next-line no-unused-vars */
|
|
158
159
|
function applyExpression(expression, context, typeInformation) {
|
|
160
|
+
logContext = fillService(logContext, typeInformation);
|
|
159
161
|
const expressionList = expression.match(/\$\{.*?\}/g) || [];
|
|
160
162
|
const substitutions = expressionList.map(processExpression(context));
|
|
161
163
|
let expressionResult = expression;
|
|
@@ -214,14 +216,14 @@ function expressionApplier(context, typeInformation) {
|
|
|
214
216
|
function contextAvailable(expression, context) {
|
|
215
217
|
let error;
|
|
216
218
|
try {
|
|
217
|
-
const variablesList = expression.match(/@[a-zA-Z0-
|
|
219
|
+
const variablesList = expression.match(/@[a-zA-Z0-9_]+/g) || [];
|
|
218
220
|
const variables = variablesList.map(function (item) {
|
|
219
221
|
return item.substr(1);
|
|
220
222
|
});
|
|
221
223
|
const keys = Object.keys(context);
|
|
222
224
|
let validContext = _.difference(variables, keys).length === 0;
|
|
223
225
|
if (!validContext) {
|
|
224
|
-
logger.
|
|
226
|
+
logger.info(
|
|
225
227
|
logContext,
|
|
226
228
|
'For expression "[%s]" context "[%j]" does not have element to match',
|
|
227
229
|
expression,
|
|
@@ -30,6 +30,8 @@ const jexlParser = require('./jexlParser');
|
|
|
30
30
|
const config = require('../commonConfig');
|
|
31
31
|
/* eslint-disable no-unused-vars */
|
|
32
32
|
const logger = require('logops');
|
|
33
|
+
const errors = require('../errors');
|
|
34
|
+
const constants = require('../constants');
|
|
33
35
|
const context = {
|
|
34
36
|
op: 'IoTAgentNGSI.expressionPlugin'
|
|
35
37
|
};
|
|
@@ -42,6 +44,22 @@ function setJEXLTransforms(transformationMap) {
|
|
|
42
44
|
jexlParser.setTransforms(transformationMap);
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
function applyExpression(expression, context, typeInformation) {
|
|
48
|
+
let parser = legacyParser;
|
|
49
|
+
if (checkJexl(typeInformation)) {
|
|
50
|
+
parser = jexlParser;
|
|
51
|
+
}
|
|
52
|
+
return parser.applyExpression(expression, context, typeInformation);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function extractContext(attributeList, typeInformation) {
|
|
56
|
+
let parser = legacyParser;
|
|
57
|
+
if (checkJexl(typeInformation)) {
|
|
58
|
+
parser = jexlParser;
|
|
59
|
+
}
|
|
60
|
+
return parser.extractContext(attributeList);
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
function mergeAttributes(attrList1, attrList2) {
|
|
46
64
|
const finalCollection = _.clone(attrList1);
|
|
47
65
|
const additionalItems = [];
|
|
@@ -70,33 +88,39 @@ function mergeAttributes(attrList1, attrList2) {
|
|
|
70
88
|
return finalCollection.concat(additionalItems);
|
|
71
89
|
}
|
|
72
90
|
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
return false;
|
|
91
|
+
function checkJexl(typeInformation) {
|
|
92
|
+
if (
|
|
93
|
+
config.getConfig().defaultExpressionLanguage === 'jexl' &&
|
|
94
|
+
typeInformation.expressionLanguage &&
|
|
95
|
+
typeInformation.expressionLanguage !== 'legacy'
|
|
96
|
+
) {
|
|
97
|
+
return true;
|
|
98
|
+
} else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
|
|
99
|
+
return true;
|
|
100
|
+
} else if (
|
|
101
|
+
config.getConfig().defaultExpressionLanguage === 'legacy' &&
|
|
102
|
+
typeInformation.expressionLanguage &&
|
|
103
|
+
typeInformation.expressionLanguage === 'jexl'
|
|
104
|
+
) {
|
|
105
|
+
return true;
|
|
91
106
|
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
92
109
|
|
|
110
|
+
function update(entity, typeInformation, callback) {
|
|
93
111
|
function processEntityUpdateNgsi2(attributes) {
|
|
94
112
|
let parser = legacyParser;
|
|
95
113
|
if (checkJexl(typeInformation)) {
|
|
96
114
|
parser = jexlParser;
|
|
97
115
|
}
|
|
98
116
|
let expressionAttributes = [];
|
|
99
|
-
|
|
117
|
+
let attributesCtxt = [...attributes]; // just copy
|
|
118
|
+
if (typeInformation.static) {
|
|
119
|
+
typeInformation.static.forEach(function (att) {
|
|
120
|
+
attributesCtxt.push(att);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const ctx = parser.extractContext(attributesCtxt);
|
|
100
124
|
|
|
101
125
|
if (typeInformation.active) {
|
|
102
126
|
expressionAttributes = parser.processExpressionAttributes(typeInformation, typeInformation.active, ctx);
|
|
@@ -107,15 +131,32 @@ function update(entity, typeInformation, callback) {
|
|
|
107
131
|
}
|
|
108
132
|
|
|
109
133
|
try {
|
|
110
|
-
|
|
111
|
-
attsArray =
|
|
112
|
-
|
|
134
|
+
logger.debug(context, 'expressionPlugin entity %j', entity);
|
|
135
|
+
const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
|
|
136
|
+
// Exclude processing all attr expressions when current attr is of type 'commandStatus' or 'commandResult'
|
|
137
|
+
const attsArrayFiltered = attsArray.filter((obj) => {
|
|
138
|
+
return ![constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
|
|
139
|
+
});
|
|
140
|
+
const attsArrayCmd = attsArray.filter((obj) => {
|
|
141
|
+
// just attr of type 'commandStatus' or 'commandResult'
|
|
142
|
+
return [constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
|
|
143
|
+
});
|
|
144
|
+
let attsArrayFinal = [];
|
|
145
|
+
if (attsArrayFiltered.length > 0) {
|
|
146
|
+
attsArrayFinal = processEntityUpdateNgsi2(attsArrayFiltered);
|
|
147
|
+
}
|
|
148
|
+
attsArrayFinal = attsArrayFinal.concat(attsArrayCmd);
|
|
149
|
+
entity = utils.createNgsi2Entity(entity.id, entity.type, attsArrayFinal, true);
|
|
113
150
|
|
|
114
151
|
callback(null, entity, typeInformation);
|
|
115
152
|
} catch (e) {
|
|
153
|
+
logger.error(context, 'expressionPlugin error %j procesing entity %j', e, entity);
|
|
116
154
|
callback(e);
|
|
117
155
|
}
|
|
118
156
|
}
|
|
119
157
|
|
|
120
158
|
exports.update = update;
|
|
121
159
|
exports.setJEXLTransforms = setJEXLTransforms;
|
|
160
|
+
exports.applyExpression = applyExpression;
|
|
161
|
+
exports.extractContext = extractContext;
|
|
162
|
+
exports.checkJexl = checkJexl;
|
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
const jexl = require('jexl');
|
|
31
31
|
const errors = require('../errors');
|
|
32
32
|
const logger = require('logops');
|
|
33
|
+
const fillService = require('../services/common/domain').fillService;
|
|
33
34
|
const config = require('../commonConfig');
|
|
34
35
|
const baseTranformsMap = require('../jexlTranformsMap.js').map;
|
|
35
|
-
|
|
36
|
+
let logContext = {
|
|
36
37
|
op: 'IoTAgentNGSI.JEXL'
|
|
37
38
|
};
|
|
38
39
|
|
|
@@ -96,6 +97,7 @@ function extractContext(attributeList) {
|
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
function applyExpression(expression, context, typeInformation) {
|
|
100
|
+
logContext = fillService(logContext, typeInformation);
|
|
99
101
|
const result = parse(expression, context);
|
|
100
102
|
logger.debug(logContext, 'applyExpression "[%j]" over "[%j]" result "[%j]" ', expression, context, result);
|
|
101
103
|
const expressionResult = result !== undefined ? result : expression;
|
|
@@ -30,7 +30,7 @@ const _ = require('underscore');
|
|
|
30
30
|
const constants = require('../constants');
|
|
31
31
|
const legacyParser = require('./expressionParser');
|
|
32
32
|
const jexlParser = require('./jexlParser');
|
|
33
|
-
const
|
|
33
|
+
const expressionPlugin = require('./expressionPlugin');
|
|
34
34
|
/* eslint-disable-next-line no-unused-vars */
|
|
35
35
|
const logger = require('logops');
|
|
36
36
|
/* eslint-disable-next-line no-unused-vars */
|
|
@@ -41,25 +41,6 @@ const utils = require('./pluginUtils');
|
|
|
41
41
|
/* eslint-disable-next-line no-unused-vars */
|
|
42
42
|
const aliasPlugin = require('./attributeAlias');
|
|
43
43
|
|
|
44
|
-
function checkJexl(typeInformation) {
|
|
45
|
-
if (
|
|
46
|
-
config.getConfig().defaultExpressionLanguage === 'jexl' &&
|
|
47
|
-
typeInformation.expressionLanguage &&
|
|
48
|
-
typeInformation.expressionLanguage !== 'legacy'
|
|
49
|
-
) {
|
|
50
|
-
return true;
|
|
51
|
-
} else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
|
|
52
|
-
return true;
|
|
53
|
-
} else if (
|
|
54
|
-
config.getConfig().defaultExpressionLanguage === 'legacy' &&
|
|
55
|
-
typeInformation.expressionLanguage &&
|
|
56
|
-
typeInformation.expressionLanguage === 'jexl'
|
|
57
|
-
) {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
44
|
function hasEntityName(item) {
|
|
64
45
|
return item.entity_name;
|
|
65
46
|
}
|
|
@@ -226,7 +207,7 @@ function propagateTimestamp(entity, entities) {
|
|
|
226
207
|
|
|
227
208
|
function updateAttribute(entity, typeInformation, callback) {
|
|
228
209
|
let parser = legacyParser;
|
|
229
|
-
if (checkJexl(typeInformation)) {
|
|
210
|
+
if (expressionPlugin.checkJexl(typeInformation)) {
|
|
230
211
|
parser = jexlParser;
|
|
231
212
|
}
|
|
232
213
|
const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
|
|
3
|
+
*
|
|
4
|
+
* This file is part of fiware-iotagent-lib
|
|
5
|
+
*
|
|
6
|
+
* fiware-iotagent-lib is free software: you can redistribute it and/or
|
|
7
|
+
* modify it under the terms of the GNU Affero General Public License as
|
|
8
|
+
* published by the Free Software Foundation, either version 3 of the License,
|
|
9
|
+
* or (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* fiware-iotagent-lib is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
14
|
+
* See the GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public
|
|
17
|
+
* License along with fiware-iotagent-lib.
|
|
18
|
+
* If not, see http://www.gnu.org/licenses/.
|
|
19
|
+
*
|
|
20
|
+
* For those usages not covered by the GNU Affero General Public License
|
|
21
|
+
* please contact with::daniel.moranjimenez@telefonica.com
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const got = require('got');
|
|
25
|
+
const logger = require('logops');
|
|
26
|
+
const context = {
|
|
27
|
+
op: 'IoTAgentNGSI.Request'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Transform the "request" options into "got" options and add additional "got" defaults
|
|
32
|
+
*
|
|
33
|
+
* The following options are currently exposed:
|
|
34
|
+
* - `method` - HTTP Method
|
|
35
|
+
* - `searchParams` - query string params
|
|
36
|
+
* - `qs` - alias for query string params
|
|
37
|
+
* - `headers`
|
|
38
|
+
* - `responseType` - either `text` or `json`. `json` is the default
|
|
39
|
+
* - `json` - a supplied JSON object as the request body
|
|
40
|
+
* - `body` - any ASCII text as the request body
|
|
41
|
+
* - `url` - the request URL
|
|
42
|
+
* - `uri` - alternative alias for the request URL.
|
|
43
|
+
*
|
|
44
|
+
* @param {Object} options Original definition of the request using the request library
|
|
45
|
+
* @return {Object} Updated definition of the request using the got library
|
|
46
|
+
*
|
|
47
|
+
*/
|
|
48
|
+
function getOptions(options) {
|
|
49
|
+
const httpOptions = {
|
|
50
|
+
method: options.method,
|
|
51
|
+
searchParams: options.searchParams || options.qs,
|
|
52
|
+
headers: options.headers,
|
|
53
|
+
throwHttpErrors: options.throwHttpErrors || false,
|
|
54
|
+
retry: options.retry || 0,
|
|
55
|
+
responseType: options.responseType || 'json'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// got library is not properly documented, so it is not clear which takes precedence
|
|
59
|
+
// among body, json and form (see https://stackoverflow.com/q/70754880/1485926).
|
|
60
|
+
// Thus, we are enforcing our own precedence with the "else if" chain below.
|
|
61
|
+
// Behaviour is consistent with the one described at usermanual.md#iotagentlibrequest
|
|
62
|
+
|
|
63
|
+
if (options.method === 'GET' || options.method === 'HEAD' || options.method === 'OPTIONS') {
|
|
64
|
+
// Do nothing - Never add a body
|
|
65
|
+
} else if (options.body) {
|
|
66
|
+
// body takes precedence over json or form
|
|
67
|
+
httpOptions.body = options.body;
|
|
68
|
+
} else if (options.json) {
|
|
69
|
+
// json takes precedence over form
|
|
70
|
+
httpOptions.json = options.json;
|
|
71
|
+
} else if (options.form) {
|
|
72
|
+
// Note that we don't consider 'form' part of the function API (check usermanual.md#iotagentlibrequest)
|
|
73
|
+
// but we are preparing the code anyway as a safe measure
|
|
74
|
+
httpOptions.form = options.form;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return httpOptions;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
*
|
|
82
|
+
* Make a direct HTTP request using the underlying request library
|
|
83
|
+
* (currently [got](https://github.com/sindresorhus/got)),
|
|
84
|
+
*
|
|
85
|
+
* This function mimics the interface of the obsolete request library and switches
|
|
86
|
+
* back from promises to callbacks to avoid re-writing large chunks of code.
|
|
87
|
+
* This centralizes all HTTP requests in a single location and is useful
|
|
88
|
+
* when creating agents which use an HTTP transport for their southbound
|
|
89
|
+
* commands, and removes the need for the custom IoT Agent to import its own
|
|
90
|
+
* additonal request library.
|
|
91
|
+
*
|
|
92
|
+
* @param {Object} options Definition of the request .
|
|
93
|
+
* @param {Function} callback The callback function.
|
|
94
|
+
*
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
function request(options, callback) {
|
|
98
|
+
const httpOptions = getOptions(options);
|
|
99
|
+
logger.debug(context, 'Options: %s', JSON.stringify(options, null, 4));
|
|
100
|
+
got(options.url || options.uri, httpOptions)
|
|
101
|
+
.then((response) => {
|
|
102
|
+
logger.debug(context, 'Response %s', JSON.stringify(response.body, null, 4));
|
|
103
|
+
return callback(null, response, response.body);
|
|
104
|
+
})
|
|
105
|
+
.catch((error) => {
|
|
106
|
+
logger.debug(context, 'Error: %s', JSON.stringify(error, null, 4));
|
|
107
|
+
return callback(error);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = request;
|
|
@@ -47,6 +47,8 @@ function cleanDomain(domainToClean) {
|
|
|
47
47
|
delete domainToClean.from;
|
|
48
48
|
delete domainToClean.op;
|
|
49
49
|
delete domainToClean.path;
|
|
50
|
+
delete domainToClean.service;
|
|
51
|
+
delete domainToClean.subservice;
|
|
50
52
|
domainToClean.exit();
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -139,8 +141,10 @@ function ensureSouthboundTransaction(context, callback) {
|
|
|
139
141
|
reqDomain.corr = reqDomain.trans;
|
|
140
142
|
}
|
|
141
143
|
|
|
142
|
-
if (context
|
|
143
|
-
|
|
144
|
+
if (context) {
|
|
145
|
+
if (context.op) {
|
|
146
|
+
reqDomain.op = context.op;
|
|
147
|
+
}
|
|
144
148
|
|
|
145
149
|
if (context.srv) {
|
|
146
150
|
reqDomain.service = context.srv;
|
|
@@ -71,7 +71,7 @@ function traceRequest(req, res, next) {
|
|
|
71
71
|
|
|
72
72
|
/* eslint-disable-next-line no-unused-vars */
|
|
73
73
|
function changeLogLevel(req, res, next) {
|
|
74
|
-
const levels = ['INFO', 'ERROR', 'FATAL', 'DEBUG', 'WARNING'];
|
|
74
|
+
const levels = ['INFO', 'ERROR', 'FATAL', 'DEBUG', 'WARN', 'WARNING'];
|
|
75
75
|
|
|
76
76
|
if (!req.query.level) {
|
|
77
77
|
res.status(400).json({
|
|
@@ -82,7 +82,11 @@ function changeLogLevel(req, res, next) {
|
|
|
82
82
|
error: 'invalid log level'
|
|
83
83
|
});
|
|
84
84
|
} else {
|
|
85
|
-
|
|
85
|
+
let newLevel = req.query.level.toUpperCase();
|
|
86
|
+
if (newLevel === 'WARNING') {
|
|
87
|
+
newLevel = 'WARN';
|
|
88
|
+
}
|
|
89
|
+
logger.setLevel(newLevel);
|
|
86
90
|
res.status(200).send('');
|
|
87
91
|
}
|
|
88
92
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* Modified by: Federico M. Facca - Martel Innovate
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
const request = require('request');
|
|
26
|
+
const request = require('../../request-shim');
|
|
27
27
|
const async = require('async');
|
|
28
28
|
const errors = require('../../errors');
|
|
29
29
|
const constants = require('../../constants');
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* please contact with::daniel.moranjimenez@telefonica.com
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
const request = require('request');
|
|
24
|
+
const request = require('../../request-shim');
|
|
25
25
|
const errors = require('../../errors');
|
|
26
26
|
const config = require('../../commonConfig');
|
|
27
27
|
const intoTrans = require('../common/domain').intoTrans;
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
/* eslint-disable consistent-return */
|
|
28
28
|
|
|
29
|
-
const request = require('request');
|
|
29
|
+
const request = require('../../request-shim');
|
|
30
30
|
const queryString = require('query-string');
|
|
31
31
|
const errors = require('../../errors');
|
|
32
32
|
const config = require('../../commonConfig');
|
|
@@ -64,7 +64,8 @@ function auth(trust, callback) {
|
|
|
64
64
|
'Content-Length': formData.length,
|
|
65
65
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
66
66
|
},
|
|
67
|
-
body: formData
|
|
67
|
+
body: formData,
|
|
68
|
+
responseType: 'text'
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
logger.debug(context, 'Authentication on the OAuth2 provider [%s]', url);
|
|
@@ -149,13 +149,19 @@ function getDevice(id, service, subservice, callback) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function
|
|
152
|
+
function getByNameAndType(name, type, service, subservice, callback) {
|
|
153
153
|
const devices = _.values(registeredDevices[service]);
|
|
154
154
|
let device;
|
|
155
155
|
|
|
156
156
|
for (let i = 0; i < devices.length; i++) {
|
|
157
157
|
if (devices[i].name === name) {
|
|
158
|
-
|
|
158
|
+
if (type) {
|
|
159
|
+
if (devices[i].type === type) {
|
|
160
|
+
device = devices[i];
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
device = devices[i];
|
|
164
|
+
}
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
167
|
|
|
@@ -166,6 +172,10 @@ function getByName(name, service, subservice, callback) {
|
|
|
166
172
|
}
|
|
167
173
|
}
|
|
168
174
|
|
|
175
|
+
function getByName(name, service, subservice, callback) {
|
|
176
|
+
getByNameAndType(name, null, service, subservice, callback);
|
|
177
|
+
}
|
|
178
|
+
|
|
169
179
|
function update(device, callback) {
|
|
170
180
|
registeredDevices[device.service][device.id] = deepClone(device);
|
|
171
181
|
callback(null, device);
|
|
@@ -208,4 +218,5 @@ exports.list = listDevices;
|
|
|
208
218
|
exports.get = getDevice;
|
|
209
219
|
exports.getSilently = getDevice;
|
|
210
220
|
exports.getByName = getByName;
|
|
221
|
+
exports.getByNameAndType = getByNameAndType;
|
|
211
222
|
exports.clear = clear;
|
|
@@ -243,15 +243,18 @@ function getDevice(id, service, subservice, callback) {
|
|
|
243
243
|
});
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
function
|
|
246
|
+
function getByNameAndType(name, type, service, servicepath, callback) {
|
|
247
247
|
context = fillService(context, { service, subservice: servicepath });
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
name,
|
|
252
|
-
service,
|
|
248
|
+
let optionsQuery = {
|
|
249
|
+
name: name,
|
|
250
|
+
service: service,
|
|
253
251
|
subservice: servicepath
|
|
254
|
-
}
|
|
252
|
+
};
|
|
253
|
+
if (type) {
|
|
254
|
+
optionsQuery.type = type;
|
|
255
|
+
}
|
|
256
|
+
logger.debug(context, 'Looking for device with [%j].', optionsQuery);
|
|
257
|
+
const query = Device.model.findOne(optionsQuery);
|
|
255
258
|
|
|
256
259
|
query.select({ __v: 0 });
|
|
257
260
|
|
|
@@ -270,6 +273,10 @@ function getByName(name, service, servicepath, callback) {
|
|
|
270
273
|
});
|
|
271
274
|
}
|
|
272
275
|
|
|
276
|
+
function getByName(name, service, servicepath, callback) {
|
|
277
|
+
getByNameAndType(name, null, service, servicepath, callback);
|
|
278
|
+
}
|
|
279
|
+
|
|
273
280
|
/**
|
|
274
281
|
* Updates the given device into the database.
|
|
275
282
|
* updated.
|
|
@@ -289,6 +296,7 @@ function update(device, callback) {
|
|
|
289
296
|
data.internalAttributes = device.internalAttributes;
|
|
290
297
|
data.commands = device.commands;
|
|
291
298
|
data.endpoint = device.endpoint;
|
|
299
|
+
data.polling = device.polling;
|
|
292
300
|
data.name = device.name;
|
|
293
301
|
data.type = device.type;
|
|
294
302
|
data.apikey = device.apikey;
|
|
@@ -360,4 +368,5 @@ exports.list = alarmsInt(constants.MONGO_ALARM, listDevices);
|
|
|
360
368
|
exports.get = alarmsInt(constants.MONGO_ALARM, getDevice);
|
|
361
369
|
exports.getSilently = getDevice;
|
|
362
370
|
exports.getByName = alarmsInt(constants.MONGO_ALARM, getByName);
|
|
371
|
+
exports.getByNameAndType = alarmsInt(constants.MONGO_ALARM, getByNameAndType);
|
|
363
372
|
exports.clear = alarmsInt(constants.MONGO_ALARM, clear);
|