diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 9f6103a8..9b70b1f6 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,3 +1,4 @@ +- Add: castType (PERSEO_CAST_TYPE) to enable NGSIv2 type based casting (default is to use JSON native types in values) - Add authentication config env vars (#349) - Add full support for pagination in APIs /rules and /vrules (#364) - Add Fiware-Total-Count header to response when count diff --git a/bin/perseo b/bin/perseo index eb1a2ae0..ae0d4f0b 100755 --- a/bin/perseo +++ b/bin/perseo @@ -73,6 +73,7 @@ function loadConfiguration() { 'PERSEO_SMPP_ENABLED', 'PERSEO_NOTICES_PATH', 'PERSEO_RULES_PATH', + 'PERSEO_CAST_TYPES' 'PERSEO_AUTHENTICATION_HOST', 'PERSEO_AUTHENTICATION_PORT', 'PERSEO_AUTHENTICATION_USER', @@ -157,7 +158,7 @@ function loadConfiguration() { config.smpp.enabled = process.env.PERSEO_SMPP_ENABLED || config.smpp.enabled; config.endpoint.noticesPath = process.env.PERSEO_NOTICES_PATH || config.endpoint.noticesPath; config.endpoint.rulesPath = process.env.PERSEO_RULES_PATH || config.endpoint.rulesPath; - + config.castTypes = process.env.PERSEO_CAST_TYPES || config.castTypes; config.authentication = config.authentication || {}; config.authentication.host = process.env.PERSEO_AUTHENTICATION_HOST || config.authentication.host; config.authentication.port = process.env.PERSEO_AUTHENTICATION_PORT || config.authentication.port; diff --git a/config.js b/config.js index 12e966c0..575aab6a 100644 --- a/config.js +++ b/config.js @@ -214,4 +214,9 @@ config.checkDB = { */ config.restBase = null; +/** + * Cast attribute values in updateAction using NGSIv2 types + */ +config.castTypes = false; + module.exports = config; diff --git a/documentation/configuration.md b/documentation/configuration.md index 12bed5a2..9bf8527b 100644 --- a/documentation/configuration.md +++ b/documentation/configuration.md @@ -48,6 +48,7 @@ The following table shows the environment variables available for Perseo configu | PERSEO_AUTHENTICATION_PORT | Port of the authentication endpoint | | PERSEO_AUTHENTICATION_USER | User to perform authentication | | PERSEO_AUTHENTICATION_PASSWORD | Password for the user to perform authentication | +| PERSEO_CAST_TYPE | If true, enable attribute value casting based in NGSIv2 attribute types if true. If false (default), the JSON native type for the attribute value is used. | ### Basic Configuration @@ -101,5 +102,6 @@ Options for Authentication through PEP (for update action) - `config.authentication.port`: port, - `config.authentication.user`: provisioned user for CEP in Keystone - `config.authentication.password`: provisioned password for CEP in Keystone +- `config.castTypes`: cast or not attribute values to expected type conform NGSIv2 (false by default) URL format for mongoDB could be found at `http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html` diff --git a/lib/models/updateAction.js b/lib/models/updateAction.js index 4d1d9848..087a954b 100644 --- a/lib/models/updateAction.js +++ b/lib/models/updateAction.js @@ -239,31 +239,34 @@ function processOptionParams(action, event) { let theType = myutils.expandVar(attr.type, event); // Metadata should be null or object let theMeta = attr.metadata; - var date; - - switch (theType) { - case 'Text': - theValue = theValue.toString(); - break; - case 'Number': - theValue = parseFloat(theValue); - break; - case 'Boolean': - if (typeof theValue === 'string') { - theValue = theValue.toLowerCase().trim() === 'true'; - } - break; - case 'DateTime': - if (parseInt(theValue).toString() === theValue) { - // Parse String with number (timestamp__ts in Perseo events) - theValue = parseInt(theValue); - } - date = new Date(theValue); - theValue = isNaN(date.getTime()) ? theValue : date.toISOString(); - break; - case 'None': - theValue = null; - break; + + if (config.castTypes) { + // Cast using NGSIv2 types + var date; + switch (theType) { + case 'Text': + theValue = theValue.toString(); + break; + case 'Number': + theValue = parseFloat(theValue); + break; + case 'Boolean': + if (typeof theValue === 'string') { + theValue = theValue.toLowerCase().trim() === 'true'; + } + break; + case 'DateTime': + if (parseInt(theValue).toString() === theValue) { + // Parse String with number (timestamp__ts in Perseo events) + theValue = parseInt(theValue); + } + date = new Date(theValue); + theValue = isNaN(date.getTime()) ? theValue : date.toISOString(); + break; + case 'None': + theValue = null; + break; + } } var key = myutils.expandVar(attr.name, event); changes[key] = { @@ -298,31 +301,34 @@ function processUpdateOptionParams(action, entity) { // Metadata should be null or object let theMeta = attr.metadata; - var date; - - switch (theType) { - case 'Text': - theValue = theValue.toString(); - break; - case 'Number': - theValue = parseFloat(theValue); - break; - case 'Boolean': - if (typeof theValue === 'string') { - theValue = theValue.toLowerCase().trim() === 'true'; - } - break; - case 'DateTime': - if (parseInt(theValue).toString() === theValue) { - // Parse String with number (timestamp__ts in Perseo events) - theValue = parseInt(theValue); - } - date = new Date(theValue); - theValue = isNaN(date.getTime()) ? theValue : date.toISOString(); - break; - case 'None': - theValue = null; - break; + + if (config.castTypes) { + // Cast using NGSIv2 types + var date; + switch (theType) { + case 'Text': + theValue = theValue.toString(); + break; + case 'Number': + theValue = parseFloat(theValue); + break; + case 'Boolean': + if (typeof theValue === 'string') { + theValue = theValue.toLowerCase().trim() === 'true'; + } + break; + case 'DateTime': + if (parseInt(theValue).toString() === theValue) { + // Parse String with number (timestamp__ts in Perseo events) + theValue = parseInt(theValue); + } + date = new Date(theValue); + theValue = isNaN(date.getTime()) ? theValue : date.toISOString(); + break; + case 'None': + theValue = null; + break; + } } var key = attr.name; options[key] = { diff --git a/test/unit/updateAction.js b/test/unit/updateAction.js index 8e64e71f..35cc44eb 100644 --- a/test/unit/updateAction.js +++ b/test/unit/updateAction.js @@ -106,7 +106,7 @@ var action1 = { { name: 'isBool2', type: 'Boolean', - value: 'TRUE' + value: 'true' }, { name: 'isBool3', @@ -116,7 +116,7 @@ var action1 = { { name: 'isBool4', type: 'Boolean', - value: 'False' + value: 'false' }, { name: 'isBool5', @@ -156,7 +156,7 @@ var action1 = { { name: 'refNone', type: '${refNoneType}', - value: 'futureNull' + value: null }, { name: 'refNone2', @@ -182,7 +182,7 @@ var event1 = { addressLocality: 'Stockholm', laststatus: 'allright', powerState: 'on', - stringDate: '2018-12-05T11:31:39.00Z', + stringDate: '2018-12-05T11:31:39.000Z', stringDateMs: '1548843060657', numberDateMs: 1548843229832, subservice: '/', @@ -212,15 +212,15 @@ var expectedChanges = { type: 'Text' }, textBoolLit: { - value: 'false', + value: false, type: 'Text' }, textNumberLit: { - value: '666', + value: 666, type: 'Text' }, textObjLit: { - value: '[object Object]', + value: { a: 1, b: 2 }, type: 'Text' }, refNone: { @@ -228,7 +228,7 @@ var expectedChanges = { type: 'None' }, refNone2: { - value: null, + value: 123, type: 'None' }, refNone3: { @@ -253,7 +253,7 @@ var expectedChanges = { type: 'Boolean' }, isBool5: { - value: false, + value: 'other', type: 'Boolean' }, isBool6: { @@ -293,11 +293,11 @@ var expectedChanges = { type: 'DateTime' }, lastchange2: { - value: '2019-01-30T10:11:00.657Z', + value: 1548843060657, type: 'DateTime' }, lastchange3: { - value: '2019-01-30T10:13:49.832Z', + value: 1548843229832, type: 'DateTime' } }