diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 3554ef2a..9bca421d 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,8 +1,11 @@ - Add: new approach to handle trust auth (urbo-deployer#868) +- Add: apply expandVar with JSON.parse to all fields of all actions (sms, smpp, email, post, update) (#746) +- Fix: check domain before access domain - Fix: expandVar return a 'null' instead of null (#746) +- Fix: expandVar for single string values with null, boolean, number (#746) - Fix: smtp and smpp logs (#738) - Add: allow access entities using NGSIv2 API for non_signal rules (new setting nonSignalByAPI / PERSEO_CHECK_NON_SIGNAL_BY_API) (#549) - Remove support for ngsv1 notifications (#714) - Remove ngsiv1 support for updateAction (#714) - Fix: check timer string in lower case in TimedRule checker - +- Fix: dep of chai get-func-name to 2.0.0 to avoid installation errors diff --git a/lib/models/emailAction.js b/lib/models/emailAction.js index 74bcf418..4e42cb8e 100644 --- a/lib/models/emailAction.js +++ b/lib/models/emailAction.js @@ -37,10 +37,10 @@ function buildTransporterOptions(action, event) { options.smtp = {}; options.smtp = action.parameters.smtp; if (action.parameters.smtp.host) { - options.smtp.host = myutils.expandVar(action.parameters.smtp.host, event); + options.smtp.host = myutils.expandVar(action.parameters.smtp.host, event, true); } if (action.parameters.smtp.port) { - options.smtp.port = myutils.expandVar(action.parameters.smtp.port, event); + options.smtp.port = myutils.expandVar(action.parameters.smtp.port, event, true); } } return options; @@ -49,16 +49,16 @@ function buildTransporterOptions(action, event) { function buildMailOptions(action, event) { var options = {}; options = { - from: myutils.expandVar(action.parameters.from, event), - to: myutils.expandVar(action.parameters.to, event), - subject: myutils.expandVar(action.parameters.subject || '', event), - text: myutils.expandVar(action.template, event) + from: myutils.expandVar(action.parameters.from, event, true), + to: myutils.expandVar(action.parameters.to, event, true), + subject: myutils.expandVar(action.parameters.subject || '', event, true), + text: myutils.expandVar(action.template, event, true) }; if (action.parameters.cc) { - options.cc = myutils.expandVar(action.parameters.cc, event); + options.cc = myutils.expandVar(action.parameters.cc, event, true); } if (action.parameters.bcc) { - options.bcc = myutils.expandVar(action.parameters.bcc, event); + options.bcc = myutils.expandVar(action.parameters.bcc, event, true); } return options; } diff --git a/lib/models/smppAction.js b/lib/models/smppAction.js index 16478a20..363f2cd1 100644 --- a/lib/models/smppAction.js +++ b/lib/models/smppAction.js @@ -31,25 +31,25 @@ var logger = require('logops'), function buildSMSOptions(action, event) { var options = { - text: myutils.expandVar(action.template, event), - to: myutils.expandVar(action.parameters.to, event) + text: myutils.expandVar(action.template, event, true), + to: myutils.expandVar(action.parameters.to, event, false) // it is expected be string number }; if (action.parameters.smpp) { options.smpp = {}; if (action.parameters.smpp.from) { - options.smpp.from = myutils.expandVar(action.parameters.smpp.from, event); + options.smpp.from = myutils.expandVar(action.parameters.smpp.from, event, true); } if (action.parameters.smpp.host) { - options.smpp.host = myutils.expandVar(action.parameters.smpp.host, event); + options.smpp.host = myutils.expandVar(action.parameters.smpp.host, event, true); } if (action.parameters.smpp.port) { - options.smpp.port = myutils.expandVar(action.parameters.smpp.port, event); + options.smpp.port = myutils.expandVar(action.parameters.smpp.port, event, true); } if (action.parameters.smpp.systemid) { - options.smpp.systemid = myutils.expandVar(action.parameters.smpp.systemid, event); + options.smpp.systemid = myutils.expandVar(action.parameters.smpp.systemid, event, true); } if (action.parameters.smpp.password) { - options.smpp.password = myutils.expandVar(action.parameters.smpp.password, event); + options.smpp.password = myutils.expandVar(action.parameters.smpp.password, event, true); } } return options; diff --git a/lib/models/smsAction.js b/lib/models/smsAction.js index fac47ba6..e1134aaa 100644 --- a/lib/models/smsAction.js +++ b/lib/models/smsAction.js @@ -31,22 +31,22 @@ var util = require('util'), function buildSMSOptions(action, event) { var options = { - text: myutils.expandVar(action.template, event), - to: myutils.expandVar(action.parameters.to, event) + text: myutils.expandVar(action.template, event, true), + to: myutils.expandVar(action.parameters.to, event, false) // it is expected be string number }; if (action.parameters.sms) { options.sms = {}; if (action.parameters.sms.URL) { - options.sms.URL = myutils.expandVar(action.parameters.sms.URL, event); + options.sms.URL = myutils.expandVar(action.parameters.sms.URL, event, true); } if (action.parameters.sms.API_KEY) { - options.sms.API_KEY = myutils.expandVar(action.parameters.sms.API_KEY, event); + options.sms.API_KEY = myutils.expandVar(action.parameters.sms.API_KEY, event, true); } if (action.parameters.sms.API_SECRET) { - options.sms.API_SECRET = myutils.expandVar(action.parameters.sms.API_SECRET, event); + options.sms.API_SECRET = myutils.expandVar(action.parameters.sms.API_SECRET, event, true); } if (action.parameters.sms.from) { - options.sms.from = myutils.expandVar(action.parameters.sms.from, event); + options.sms.from = myutils.expandVar(action.parameters.sms.from, event, true); } } return options; diff --git a/lib/models/twitterAction.js b/lib/models/twitterAction.js index 035aef5f..3a3ade78 100644 --- a/lib/models/twitterAction.js +++ b/lib/models/twitterAction.js @@ -29,7 +29,7 @@ var myutils = require('../myutils'), metrics = require('./metrics'); function buildTwitterOptions(action, event) { - return { text: myutils.expandVar(action.template, event) }; + return { text: myutils.expandVar(action.template, event, true) }; } function doIt(action, event, callback) { diff --git a/lib/models/updateAction.js b/lib/models/updateAction.js index dae70a58..bcc5c3ef 100644 --- a/lib/models/updateAction.js +++ b/lib/models/updateAction.js @@ -76,7 +76,7 @@ function buildQueryOptionsParams(action, event) { var filter = {}; Object.keys(action.parameters.filter).forEach(function(key) { - filter[key] = myutils.expandVar(action.parameters.filter[key], event); + filter[key] = myutils.expandVar(action.parameters.filter[key], event, true); }); // Translate filter with geojsonpolygon -> georel, geometry, coords @@ -126,7 +126,7 @@ function processOptionParams(action, event) { action.parameters.attributes.forEach(function(attr) { // Direct value for Text, DateTime, and others. Apply expandVar for strings let theValue = myutils.expandVar(attr.value, event, true); - let theType = myutils.expandVar(attr.type, event); + let theType = myutils.expandVar(attr.type, event, true); // Metadata should be null or object let theMeta = attr.metadata; @@ -160,7 +160,7 @@ function processOptionParams(action, event) { //Nothing to do } } - var key = myutils.expandVar(attr.name, event); + var key = myutils.expandVar(attr.name, event, true); changes[key] = { value: theValue, type: theType @@ -169,8 +169,8 @@ function processOptionParams(action, event) { changes[key].metadata = theMeta; } }); - changes.id = myutils.expandVar(action.parameters.id, event); - changes.type = myutils.expandVar(action.parameters.type, event); + changes.id = myutils.expandVar(action.parameters.id, event, true); + changes.type = myutils.expandVar(action.parameters.type, event, true); logger.debug('processOptionParams changes: %j', changes); return changes; } @@ -189,7 +189,7 @@ function processUpdateOptionParams(action, entity, event) { var options = {}; action.parameters.attributes.forEach(function(attr) { let theValue = myutils.expandVar(attr.value, event, true); - let theType = myutils.expandVar(attr.type, event); + let theType = myutils.expandVar(attr.type, event, true); // Metadata should be null or object let theMeta = attr.metadata; @@ -222,7 +222,7 @@ function processUpdateOptionParams(action, entity, event) { break; } } - var key = myutils.expandVar(attr.name, event); + var key = myutils.expandVar(attr.name, event, true); options[key] = { value: theValue, type: theType @@ -254,11 +254,11 @@ function getUpdateConnection(connection, action, event, token) { }; var pep = false; if (action.parameters.service !== undefined) { - options.service = myutils.expandVar(action.parameters.service, event); + options.service = myutils.expandVar(action.parameters.service, event, true); pep = true; } if (action.parameters.subservice !== undefined) { - options.servicepath = myutils.expandVar(action.parameters.subservice, event); + options.servicepath = myutils.expandVar(action.parameters.subservice, event, true); pep = true; } // token headers @@ -318,13 +318,13 @@ function doUpdateActionWithFilter(queryOptions, action, event, token, connection }; if (action.parameters.actionType !== undefined) { - changes.actionType = myutils.expandVar(action.parameters.actionType, event); + changes.actionType = myutils.expandVar(action.parameters.actionType, event, true); } if (response && response.results) { // Process response (array of entities) response.results.forEach(function(entity) { - var updateOptions = processUpdateOptionParams(action, entity, event); + var updateOptions = processUpdateOptionParams(action, entity, event, true); changes.entities.push(updateOptions); }); // For each } @@ -404,7 +404,7 @@ function doUpdateAction(action, event, token, connection, callback) { entities: [processOptionParams(action, event)] }; if (action.parameters.actionType !== undefined) { - changes.actionType = myutils.expandVar(action.parameters.actionType, event); + changes.actionType = myutils.expandVar(action.parameters.actionType, event, true); } metrics.IncMetrics(event.service, event.subservice, metrics.actionEntityUpdate); logger.debug('entity to update: %j', changes); diff --git a/lib/models/visualRules.js b/lib/models/visualRules.js index 652265ca..dadd721e 100644 --- a/lib/models/visualRules.js +++ b/lib/models/visualRules.js @@ -256,12 +256,16 @@ function vr2rule(cardRule) { r.nosignal.idRegexp = idRegexp; r.nosignal.type = type; } else { - r.text = myutils.expandVar(eplTemplate, { - name: r.name, - conditions: conditions.join(' AND '), - service: r.service, - subservice: r.subservice - }); + r.text = myutils.expandVar( + eplTemplate, + { + name: r.name, + conditions: conditions.join(' AND '), + service: r.service, + subservice: r.subservice + }, + true + ); } } catch (ex) { // SHOULD BE ex instanceof TypeError. Do not do anything else inside try diff --git a/lib/myutils.js b/lib/myutils.js index 8408f942..2b170711 100644 --- a/lib/myutils.js +++ b/lib/myutils.js @@ -55,9 +55,13 @@ function expandVar(val, mapping, trycast) { if (typeof val === 'string') { var expanded = false; var valObj = null; + var uniqueString = false; Object.keys(mapping).forEach(function(p) { + if (typeof mapping[p] === 'string' && Object.keys(mapping).length === 1) { + uniqueString = true; + } val = val.replace(new RegExp('\\$\\{' + p + '\\}', 'g'), mapping[p]); - // javascript replace always return a string: we need to keep object value + // javascript replace always return a string: we need to keep object value (#692) if (!expanded && val === '[object Object]') { valObj = mapping[p]; // for cases where mapping[p] is an object like { type: 'Point', coordinates: [] } expanded = true; @@ -66,9 +70,9 @@ function expandVar(val, mapping, trycast) { if (valObj) { val = valObj; } - if (trycast) { + if (trycast && !uniqueString) { try { - // Check if "${num}" and expand it as real value, non string + // Check if "${num}" and expand it as real value, non string (#362, #369) var n = JSON.parse(val); switch (typeof n) { case 'number': @@ -77,15 +81,20 @@ function expandVar(val, mapping, trycast) { val = n; expanded = true; break; + // default: keep val value } } catch (e) { // keep val value } } if (!expanded) { - val = val.replace(/\$\{\w*\}/g, 'null'); - if (val === 'null') { - val = null; + if (val !== 'null' || !uniqueString) { + // Set to 'null' all non expanded values (#469) + val = val.replace(/\$\{\w*\}/g, 'null'); + if (val === 'null') { + // val is just one value (non string value composes of several values) (#746) + val = null; + } } } } @@ -143,7 +152,7 @@ function requestHelperAux(method, options, withMetrics, callback) { } } options.headers = headers; - if (withMetrics && options.json && domain.context) { + if (withMetrics && options.json && domain && domain.context) { try { metrics.IncMetrics( domain.context.srv, @@ -157,7 +166,7 @@ function requestHelperAux(method, options, withMetrics, callback) { } request[method](options, function cbRequest2core(err, response, body) { var bodySz = 0; - if (withMetrics && domain.context) { + if (withMetrics && domain && domain.context) { if (body) { if (typeof body === 'string') { bodySz = Buffer.byteLength(body); @@ -178,7 +187,7 @@ function requestHelperAux(method, options, withMetrics, callback) { } if (err) { logErrorIf(err, util.format('error %s to %s', method, options.url)); - if (withMetrics && domain.context) { + if (withMetrics && domain && domain.context) { metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.outgoingTransactionsErrors); } return callback(err, null); @@ -196,7 +205,7 @@ function requestHelperAux(method, options, withMetrics, callback) { ); localError.httpCode = respObj.code < 500 && respObj.code >= 400 ? respObj.code : 500; logErrorIf(localError, null, domain && domain.context); - if (withMetrics && domain.context) { + if (withMetrics && domain && domain.context) { metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.outgoingTransactionsErrors); } return callback(localError, respObj); diff --git a/package.json b/package.json index 155be63f..24a5a896 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ }, "devDependencies": { "chai": "~4.1.2", + "get-func-name": "2.0.0", "coveralls": "~3.0.2", "husky": "~1.1.0", "nyc": "~15.1.0", diff --git a/test/component/myutils_test.js b/test/component/myutils_test.js index 7a2381f8..b9fdbd52 100644 --- a/test/component/myutils_test.js +++ b/test/component/myutils_test.js @@ -28,7 +28,7 @@ var should = require('should'), describe('Myutils', function() { describe('#ExpandVar()', function() { - describe('When there are not variables', function() { + describe('When there are not variables to expand', function() { it('should return the same string passed', function() { var str = 'a string without vars', map = { without: 'XXXX', vars: 'YYYYY' }, @@ -38,7 +38,7 @@ describe('Myutils', function() { newStr.should.be.equal(str); }); }); - describe('When there is a variable which is a number', function() { + describe('When there is a variable which is a number to expand', function() { it('should return the number', function() { var str = '${a}', map = { a: 23 }, @@ -48,17 +48,61 @@ describe('Myutils', function() { newStr.should.be.equal(23); }); }); - describe('When there is a variable which is a string number', function() { + describe('When there is a variable which is a string number to expand', function() { it('should return the number', function() { var str = '${a}', map = { a: '23' }, newStr; newStr = myutils.expandVar(str, map, true); should.exist(newStr); + newStr.should.be.equal('23'); + }); + }); + describe('When string and there is a variable which is a number to expand', function() { + it('should return the string number', function() { + var str = '"${a}"', + map = { a: 23 }, + newStr; + newStr = myutils.expandVar(str, map, true); + should.exist(newStr); + newStr.should.be.equal('"23"'); + }); + }); + describe('When string and there is a variable which is a string number to expand', function() { + it('should return the string number', function() { + var str = '"${a}"', + map = { a: '23' }, + newStr; + newStr = myutils.expandVar(str, map, true); + should.exist(newStr); + newStr.should.be.equal('"23"'); + }); + }); + describe('When string and there is a variable which is a number to expand', function() { + it('should return the string number', function() { + /*jshint quotmark: double */ + var str = "'${a}'", + /*jshint quotmark: single */ + map = { a: 23 }, + newStr; + newStr = myutils.expandVar(str, map, true); + should.exist(newStr); + /*jshint quotmark: double */ + newStr.should.be.equal("'23'"); + /*jshint quotmark: single */ + }); + }); + describe('When string and there is a variable which is a number to expand', function() { + it('should return the string number', function() { + var str = '${a}', + map = { a: 23 }, + newStr; + newStr = myutils.expandVar(str, map, true); + should.exist(newStr); newStr.should.be.equal(23); }); }); - describe('When there is a variable which is a boolean', function() { + describe('When there is a variable which is a boolean to expand', function() { it('should return the boolean', function() { var str = '${a}', map = { a: true }, @@ -68,17 +112,17 @@ describe('Myutils', function() { newStr.should.be.equal(true); }); }); - describe('When there is a variable which is a string boolean', function() { + describe('When there is a variable which is a string boolean to expand', function() { it('should return the boolean', function() { var str = '${a}', map = { a: 'true' }, newStr; newStr = myutils.expandVar(str, map, true); should.exist(newStr); - newStr.should.be.equal(true); + newStr.should.be.equal('true'); }); }); - describe('When there is a variable which is a string object', function() { + describe('When there is a variable which is a string object to expand', function() { it('should return the object', function() { var str = '{"type":"Point"}', map = { a: true }, @@ -88,7 +132,7 @@ describe('Myutils', function() { newStr.type.should.be.equal('Point'); }); }); - describe('When there is a variable which is a string object', function() { + describe('When there is a variable which is a string object to expand', function() { it('should return the boolean', function() { var str = '${p}', map = { p: { type: 'Point', coordinates: [11, 12] } }, @@ -100,8 +144,8 @@ describe('Myutils', function() { newStr.coordinates[1].should.be.equal(12); }); }); - describe('When there is a variable which is a null', function() { - it('should return the null', function() { + describe('When there is a variable which is a null to expand', function() { + it('should return null', function() { var str = '${a}', map = { a: null }, newStr; @@ -109,16 +153,43 @@ describe('Myutils', function() { should.equal(newStr, null); }); }); - describe('When there is a variable which is a string null', function() { - it('should return the null', function() { + describe('When there is a variable which is a string null to expand', function() { + it('should return null', function() { var str = '${a}', map = { a: 'null' }, newStr; newStr = myutils.expandVar(str, map, true); + should.equal(newStr, 'null'); + }); + }); + describe('When there is a variable which is not expanded', function() { + it('should return null', function() { + var str = '${a}', + map = {}, + newStr; + newStr = myutils.expandVar(str, map, true); should.equal(newStr, null); }); }); - describe('When there is a variable which is a string', function() { + describe('When string and there is a variable which is a null to expand', function() { + it('should return "null"', function() { + var str = '"${a}"', + map = { a: null }, + newStr; + newStr = myutils.expandVar(str, map, true); + should.equal(newStr, '"null"'); + }); + }); + describe('When string and there is a variable which is a string null to expand', function() { + it('should return "null"', function() { + var str = '"${a}"', + map = { a: 'null' }, + newStr; + newStr = myutils.expandVar(str, map, true); + should.equal(newStr, '"null"'); + }); + }); + describe('When there is a variable which is a string to expand', function() { it('should return the string', function() { var str = '${a}', map = { a: 'boniato' }, @@ -128,7 +199,7 @@ describe('Myutils', function() { newStr.should.be.equal('boniato'); }); }); - describe('When there is not variable ', function() { + describe('When there is not variable to expand', function() { it('should return the string', function() { var str = '${a}', map = { b: 'boniato' }, @@ -137,7 +208,7 @@ describe('Myutils', function() { should.equal(newStr, null); }); }); - describe('When there is not some variables', function() { + describe('When there is not some variables to expand', function() { it('should return the string', function() { var str = '${a} and ${b}', map = { b: 'boniato' }, @@ -147,6 +218,16 @@ describe('Myutils', function() { newStr.should.be.equal('null and boniato'); }); }); + describe('When there is a variable which is a string number to expand in a phone number', function() { + it('should return the phone number', function() { + var str = '${a}', + map = { a: '666123123' }, + newStr; + newStr = myutils.expandVar(str, map, false); + should.exist(newStr); + newStr.should.be.equal('666123123'); + }); + }); }); describe('#RequestHelper()', function() { describe('When there is a network problem', function() {