From c4865f35da468e5fdfdd8c3de095cec7d6950691 Mon Sep 17 00:00:00 2001 From: German Toro del Valle Date: Mon, 24 Oct 2016 19:47:34 +0200 Subject: [PATCH] Include support for conditions in the templating mechanism --- CHANGES_NEXT_RELEASE | 1 + README.md | 153 +++- bin/fiwareDeviceSimulatorCLI | 3 +- bin/fiwareDeviceSimulatorTranspilerCLI | 100 +++ .../fiwareDeviceSimulatorComposer.js | 86 -- lib/fiwareDeviceSimulator.js | 4 +- .../fiwareDeviceSimulatorTranspiler.js | 242 ++++++ test/unit/fiwareDeviceSimulator_test.js | 6 +- .../fiwareDeviceSimulatorComposer_test.js | 152 ---- .../template-attribute-condition.json | 6 + .../templates/template-entity-condition.json | 6 + .../template-schedule-entity-condition.json | 10 + .../template-type-attribute-condition.json | 6 + .../template-type-entity-condition.json | 6 + .../template-value-attribute-condition.json | 14 + .../fiwareDeviceSimulatorTranspiler_test.js | 806 ++++++++++++++++++ 16 files changed, 1356 insertions(+), 245 deletions(-) create mode 100755 bin/fiwareDeviceSimulatorTranspilerCLI delete mode 100644 lib/composers/fiwareDeviceSimulatorComposer.js create mode 100644 lib/transpilers/fiwareDeviceSimulatorTranspiler.js delete mode 100644 test/unit/interpolators/fiwareDeviceSimulatorComposer_test.js create mode 100644 test/unit/templates/template-attribute-condition.json create mode 100644 test/unit/templates/template-entity-condition.json create mode 100644 test/unit/templates/template-schedule-entity-condition.json create mode 100644 test/unit/templates/template-type-attribute-condition.json create mode 100644 test/unit/templates/template-type-entity-condition.json create mode 100644 test/unit/templates/template-value-attribute-condition.json create mode 100644 test/unit/transpilers/fiwareDeviceSimulatorTranspiler_test.js diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index e69de29..11d9090 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -0,0 +1 @@ +- [FEATURE] Include support for conditions in the templating mechanism diff --git a/README.md b/README.md index c8da4e0..9433737 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ To run the FIWARE Device Simulator CLI tool just run: ./bin/fiwareDeviceSimulatorCLI ``` -This will show the FIWARE Device Simulator CLI tool help: +This will show the FIWARE Device Simulator CLI tool help which will guide you to learn how to properly use it: ``` Usage: fiwareDeviceSimulatorCLI [options] @@ -390,8 +390,159 @@ Let's see this `imports()` directive mechanism with an example. The next one is For example, the import directives: `import(contextBroker_NGSIv1)`, `import(every 5 seconds)` and `import(autoincrement_1)` will be substituted by the corresponding values declared in the `exports` property of the simulation configuration file, whereas the `import(authentication)` (since it is not declared in the `exports`) property will be `require`d as the file `authentication.json` from the root of the FIWARE Device Simulator application (this is, it is equivalent to `require(${FIWARE_Device_Simulator_Root_Path}/authentication.json))`. +The previous and preliminary support for importing content into specific parts of the simulation configuration files has been recently extended to support conditional imports. In this case, it is possible to impose conditions which must be satisfied for the import to take place. The format of the conditional imports is the following one: + +```json +"": [ + { + "condition": "${{==}}", + "content": "the-content-to-import-a-string-in-this-case" + }, + { + "condition": "${{==}{==}}", + "content": "the-content-to-import-a-string-in-this-case" + } +] +``` + +As you can see, the templates can now be an array of objects including a `condition` and a `content` properties in which case the import will only take place if the `import()` directive appears inside an entity which satisfies the `==` condition (this is, the `` value satisfies the ``) OR appears inside an attribute which satisfies the `==` condition (this is, the `` value satisfies the ``) inside an entity which satisfies the `==` condition (this is, the `` value satisfies the ``). + +Let's see it in a concrete example. Considering a simulation configuration file such as the following one: + +```json +{ + "exports": { + "every 5 seconds": "*/5 * * * * *", + "parking from 6 to 22": "text-rotation-interpolator({\"units\": \"hours\", \"text\": [[0,\"closed\"],[6,[[40,\"free\"],[60,\"occupied\"]]],[19,[[80,\"free\"],[20,\"occupied\"]]],[22,\"closed\"]]})", + "now": "date-increment-interpolator({\"origin\": \"now\", \"increment\": 0})", + "entity-type": [ + { + "content": "ParkingSpot", + "condition": "${{entity_name==pe-moraleja-01-group-0[0-9]:0[0-9]}}" + } + ], + "attribute-type-1": [ + { + "content": "Text", + "condition": "${{entity_name==pe-moraleja-01-group-02:0[0-9]}}" + }, + { + "content": "DateTime", + "condition": "${{entity_name==pe-moraleja-01-group-01:0[0-9]}{name==dateModifie[a-z]}}" + } + ] + }, + ... + "entities": [ + { + "schedule": "import(every 5 seconds)", + "entity_name": "pe-moraleja-01-group-01:01", + "entity_type": "import(entity-type)", + "active": [ + { + "name": "status", + "type": "Text", + "value": "import(parking from 6 to 22)" + }, + { + "name": "dateModified", + "type": "import(attribute-type-1)", + "value": "import(now)" + } + ] + }, + { + "schedule": "import(every 5 seconds)", + "entity_name": "pe-moraleja-01-group-02:01", + "entity_type": "import(entity-type)", + "active": [ + { + "name": "status", + "type": "import(attribute-type-1)", + "value": "import(parking from 6 to 22)" + }, + { + "name": "dateModified", + "type": "DateTime", + "value": "import(now)" + } + ] + } + ] + ... +} +``` + +After resolving the imports, the simulation configuration file will end up as the following one: + +```json +{ + ... + "entities": [ + { + "schedule": "*/5 * * * * *", // -> IMPORTED + "entity_name": "pe-moraleja-01-group-01:01", + "entity_type": "ParkingSpot", // -> IMPORTED + "active": [ + { + "name": "status", + "type": "Text", + "value": "text-rotation-interpolator({\"units\": \"hours\", \"text\": [[0,\"closed\"],[6,[[40,\"free\"],[60,\"occupied\"]]],[19,[[80,\"free\"],[20,\"occupied\"]]],[22,\"closed\"]]})" // -> IMPORTED + }, + { + "name": "dateModified", + "type": "DateTime", // -> IMPORTED + "value": "date-increment-interpolator({\"origin\": \"now\", \"increment\": 0})" // -> IMPORTED + } + ] + }, + { + "schedule": "*/5 * * * * *", // -> IMPORTED + "entity_name": "pe-moraleja-01-group-02:01", + "entity_type": "ParkingSpot", // -> IMPORTED + "active": [ + { + "name": "status", + "type": "Text", // -> IMPORTED + "value": "text-rotation-interpolator({\"units\": \"hours\", \"text\": [[0,\"closed\"],[6,[[40,\"free\"],[60,\"occupied\"]]],[19,[[80,\"free\"],[20,\"occupied\"]]],[22,\"closed\"]]})" // -> IMPORTED + }, + { + "name": "dateModified", + "type": "DateTime", + "value": "date-increment-interpolator({\"origin\": \"now\", \"increment\": 0})" // -> IMPORTED + } + ] + } + ] + ... +} +``` + +Just as in the case of the textual imports, the conditional imports can be declared in the `exports` property of the simulation configuration file or in external JSON files which can be imported. + Obviously, if an import directive refers to a template not declared either in the `exports` property or in an external JSON file, an error is thrown and the simulation is not run. On the other hand, if all the substitutions take place fine and the resulting simulation configuration file is valid, the simulation is run. +Although the `fiwareDeviceSimulatorCLI` command line tool previously detailed includes support for the import mechanism just described, we have also included a specific command line tool for the import mechanism which transpiles an input simulation configuration file into an output configuration file including the resolved imports. + +To run the FIWARE Device Simulator Transpiler CLI tool just run: + +```bash +./bin/fiwareDeviceSimulatorTranspilerCLI +``` + +This will show the FIWARE Device Simulator Transpiler CLI tool help which will guide you to learn how to properly use it: + +``` +Usage: fiwareDeviceSimulatorTranspilerCLI [options] + + Options: + + -h, --help output usage information + -V, --version output the version number + -c, --configuration Absolute or relative path (from the root of the Node application) to the device simulator configuration input file (mandatory) + -o, --output Absolute or relative path (from the root of the Node application) to the output device simulator configuration file (mandatory) +``` + Following the description of the simulation configuration file accepted properties and leaning on the [FIWARE waste management harmonized data models](http://fiware-datamodels.readthedocs.io/en/latest/WasteManagement/doc/introduction/index.html), we provide a simulation configuration real example file to automatically generate waste management data, more concretely simulating the dynamic filling levels for 8 waste containers spread out at 4 areas (`Oeste` (i.e., West), `Norte` (i.e., North), `Este` (i.e., East) and `Sur` (i.e., South) of the Distrito Telefónica area (where the Telefónica headquarters are located) in Madrid. ```json diff --git a/bin/fiwareDeviceSimulatorCLI b/bin/fiwareDeviceSimulatorCLI index 1fc76ea..179b97a 100755 --- a/bin/fiwareDeviceSimulatorCLI +++ b/bin/fiwareDeviceSimulatorCLI @@ -30,6 +30,7 @@ var commander = require('commander'); var fs = require('fs'); var logops = require('logops'); var humanizeDuration = require('humanize-duration'); +var path = require('path'); var deviceSimulator = require(ROOT_PATH + '/lib/fiwareDeviceSimulator'); process.on('SIGINT', function() { @@ -65,7 +66,7 @@ function executeCommand() { } else if (commander.configuration[0] === '.') { configurationFilePath = ROOT_PATH + commander.configuration.substring(1); } else { - commander.help(); + configurationFilePath = ROOT_PATH + path.sep + commander.configuration; } if (!fs.existsSync(configurationFilePath)) { diff --git a/bin/fiwareDeviceSimulatorTranspilerCLI b/bin/fiwareDeviceSimulatorTranspilerCLI new file mode 100755 index 0000000..418e75b --- /dev/null +++ b/bin/fiwareDeviceSimulatorTranspilerCLI @@ -0,0 +1,100 @@ +#!/usr/bin/env node + +/* + * Copyright 2016 Telefónica Investigación y Desarrollo, S.A.U + * + * This file is part of the Short Time Historic (STH) component + * + * STH is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * STH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with STH. + * If not, see http://www.gnu.org/licenses/. + * + * For those usages not covered by the GNU Affero General Public License + * please contact with: [german.torodelvalle@telefonica.com] + */ + +'use strict'; + +var ROOT_PATH = require('app-root-path'); +var commander = require('commander'); +var fs = require('fs'); +var logops = require('logops'); +var path = require('path'); +var fiwareDeviceSimulatorTranspiler = require(ROOT_PATH + '/lib/transpilers/fiwareDeviceSimulatorTranspiler'); + +process.on('SIGINT', function() { + process.exit(0); +}); + +process.on('uncaughtException', function() { + process.exit(1); +}); + +/** + * Returns the absolute path for a file name or relative path + * @param {String} file File name or file path + * @return {String} The absolute path + */ +function getFilePath(file) { + return ROOT_PATH + (file.charAt(0) === '.' ? file.substring(1) : path.sep + file); +} + +/** + * Executes the requested commander + */ +function executeCommand() { + if (!commander.configuration) { + commander.help(); + } + + var inputConfigurationFilePath = getFilePath(commander.configuration); + if (!fs.existsSync(inputConfigurationFilePath)) { + return logops.error('The input file path (\'' + inputConfigurationFilePath + '\') does not exist'); + } + + if (!commander.output) { + commander.help(); + } + + var outputConfigurationFilePath = getFilePath(commander.output); + if (fs.existsSync(outputConfigurationFilePath)) { + return logops.error('The output file path (\'' + outputConfigurationFilePath + '\') already exists'); + } + + fiwareDeviceSimulatorTranspiler.compose(require(inputConfigurationFilePath), function(err, newConfigurationObj) { + if (err) { + return logops.error('Error when transpiling the simulation configuration file (\'' + + inputConfigurationFilePath + '\'): ' + err); + } + fs.writeFile(outputConfigurationFilePath, JSON.stringify(newConfigurationObj, null, ' '), function(err) { + if (err) { + return logops.error('Error when writing to the output simulation configuration file \'' + + outputConfigurationFilePath + '\'): ' + err); + } + return logops.info('Output simulation configuration file \'' + outputConfigurationFilePath + '\') ' + + 'successfully created'); + }); + }); +} + +commander. + version(require(ROOT_PATH + '/package.json').version). + option('-c, --configuration ', + 'Absolute or relative path (from the root of the Node application) to the device simulator configuration ' + + 'input file (mandatory)'). + option('-o, --output ', + 'Absolute or relative path (from the root of the Node application) to the output device simulator ' + + 'configuration file (mandatory)'). + parse(process.argv); + +executeCommand(); diff --git a/lib/composers/fiwareDeviceSimulatorComposer.js b/lib/composers/fiwareDeviceSimulatorComposer.js deleted file mode 100644 index 648f2d0..0000000 --- a/lib/composers/fiwareDeviceSimulatorComposer.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2016 Telefónica Investigación y Desarrollo, S.A.U - * - * This file is part of the Short Time Historic (STH) component - * - * STH is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * STH is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with STH. - * If not, see http://www.gnu.org/licenses/. - * - * For those usages not covered by the GNU Affero General Public License - * please contact with: [german.torodelvalle@telefonica.com] - */ - -var ROOT_PATH = require('app-root-path'); -var path = require('path'); -var fdsErrors = require(ROOT_PATH + '/lib/errors/fdsErrors'); - -/** - * The original simulation configuration file - */ -var originalConfigurationObj; - -/** - * Error, if any, during the composition - */ -var error; - -/** - * Replaces an import() match by its corresponding value - * @param {String} match The matching String - * @return {String} The replacing String - */ -function replacer(match) { - var templateValue; - var templateTag = match.substring(8, match.length - 2); - if (originalConfigurationObj.exports && originalConfigurationObj.exports[templateTag]) { - templateValue = originalConfigurationObj.exports[templateTag]; - } else { - try { - templateValue = require(ROOT_PATH + path.sep + templateTag); - } catch(exception) { - error = exception; - } - } - return JSON.stringify(templateValue); -} - -/** - * Asynchronously returns a new simulation configuration file after importing the corresponding templates - * @param {Object} configuration The original configuration - * @param {Function} callback The callback - */ -function compose(configuration, callback) { - var configurationStr, - newConfigurationStr, - newConfigurationObj; - error = null; - originalConfigurationObj = configuration; - configurationStr = JSON.stringify(configuration); - newConfigurationStr = configurationStr.replace(/"import\([^\)]+\)"/g, replacer); - if (error) { - return callback(new fdsErrors.SimulationConfigurationNotValid('The configuration information provided is not ' + - 'valid (some error ocurred when importing the templates: ' + error + ')')); - } - try { - newConfigurationObj = JSON.parse(newConfigurationStr); - } catch (exception) { - return callback(new fdsErrors.SimulationConfigurationNotValid('The configuration information provided is not ' + - 'valid (some error ocurred when importing the templates: ' + exception + ')')); - } - return callback(null, newConfigurationObj); -} - -module.exports = { - compose: compose -}; diff --git a/lib/fiwareDeviceSimulator.js b/lib/fiwareDeviceSimulator.js index 6641470..944bfd0 100644 --- a/lib/fiwareDeviceSimulator.js +++ b/lib/fiwareDeviceSimulator.js @@ -40,7 +40,7 @@ var multilinePositionInterpolator = require(ROOT_PATH + '/lib/interpolators/mul var textRotationInterpolator = require(ROOT_PATH + '/lib/interpolators/textRotationInterpolator'); var attributeFunctionInterpolator = require(ROOT_PATH + '/lib/interpolators/attributeFunctionInterpolator'); var fdsErrors = require(ROOT_PATH + '/lib/errors/fdsErrors'); -var fiwareDeviceSimulatorComposer = require(ROOT_PATH + '/lib/composers/fiwareDeviceSimulatorComposer'); +var fiwareDeviceSimulatorTranspiler = require(ROOT_PATH + '/lib/transpilers/fiwareDeviceSimulatorTranspiler'); var fiwareDeviceSimulatorValidator = require(ROOT_PATH + '/lib/validators/fiwareDeviceSimulatorValidator'); /** @@ -1200,7 +1200,7 @@ function start(config, theFromDate, theToDate, interval, margin, theDelay) { realFromDate = null; isEnded = false; cancelAllJobs(); - fiwareDeviceSimulatorComposer.compose(config, function(err, newConfig) { + fiwareDeviceSimulatorTranspiler.compose(config, function(err, newConfig) { if (err) { process.nextTick(function notifySimulationConfigurationError() { emitError(err); diff --git a/lib/transpilers/fiwareDeviceSimulatorTranspiler.js b/lib/transpilers/fiwareDeviceSimulatorTranspiler.js new file mode 100644 index 0000000..a96c801 --- /dev/null +++ b/lib/transpilers/fiwareDeviceSimulatorTranspiler.js @@ -0,0 +1,242 @@ +/* + * Copyright 2016 Telefónica Investigación y Desarrollo, S.A.U + * + * This file is part of the Short Time Historic (STH) component + * + * STH is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * STH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with STH. + * If not, see http://www.gnu.org/licenses/. + * + * For those usages not covered by the GNU Affero General Public License + * please contact with: [german.torodelvalle@telefonica.com] + */ + +var ROOT_PATH = require('app-root-path'); +var path = require('path'); +var fdsErrors = require(ROOT_PATH + '/lib/errors/fdsErrors'); + +/** + * The original simulation configuration file + */ +var originalConfigurationObj; + +/** + * Error, if any, during the composition + */ +var error; + +/** + * Entity condition flag + * @type {Number} + */ +var ENTITY_CONDITION = 1; + +/** + * Attribute condition flag + * @type {Number} + */ +var ATTRIBUTE_CONDITION = 2; + +/** + * Attribute condition regular expresion + * @type {RegExp} + */ +var attributeConditionRegExp = /\$\{\{.+\}\{.+\}\}/g; + +/** + * Entity condition regular expression + * @type {RegExp} + */ +var entityConditionRegExp = /\$\{\{[^\}]+\}\}/g; + +/** + * Map of matches and associated count + * @type {Object} + */ +var matchCounterMap = {}; + +/** + * Returns the type of a condition + * @param {String} condition The condition + * @return {Number} The condition type + */ +function getConditionType(condition) { + if (new RegExp(entityConditionRegExp).test(condition)) { + return ENTITY_CONDITION; + } else if (new RegExp(attributeConditionRegExp).test(condition)) { + return ATTRIBUTE_CONDITION; + } +} + +/** + * Returns true if the match satisfies the condition + * @param {String} match The match + * @param {String} condition The condition + * @return {Boolean} True if the match satisfies the condition, false otherwise + */ +function matchesCondition(match, condition) { + /* jshint maxdepth: 9 */ + var currentEntity, + currentAttribute, + entityProperties, + attributeProperties, + entityProperty, + entityRegExp, + entityValue, + attributeProperty, + attributeRegExp, + attributeValue; + var matchCounter = 0; + var matchWithoutQuotes = match.substring(1, match.length - 1); + var conditionType = getConditionType(condition); + for (var ii = 0; ii < originalConfigurationObj.entities.length; ii++) { + currentEntity = originalConfigurationObj.entities[ii]; + entityProperties = Object.getOwnPropertyNames(originalConfigurationObj.entities[ii]); + for (var jj = 0; jj < entityProperties.length; jj++) { + if (originalConfigurationObj.entities[ii][entityProperties[jj]] === matchWithoutQuotes) { + matchCounter += 1; + if (matchCounter >= matchCounterMap[match]) { + if (conditionType === ENTITY_CONDITION) { + entityProperty = condition.substring(3, condition.indexOf('==')); + entityRegExp = condition.substring(condition.indexOf('==') + 2, condition.length - 2); + entityValue = originalConfigurationObj.entities[ii][entityProperty]; + if (new RegExp(entityRegExp).test(entityValue)) { + return true; + } + return false; + } + return false; + } + } + if (Array.isArray(originalConfigurationObj.entities[ii][entityProperties[jj]])) { + for (var kk = 0; kk < originalConfigurationObj.entities[ii][entityProperties[jj]].length; kk++) { + currentAttribute = originalConfigurationObj.entities[ii][entityProperties[jj]][kk]; + attributeProperties = + Object.getOwnPropertyNames(originalConfigurationObj.entities[ii][entityProperties[jj]][kk]); + for (var ll = 0; ll < attributeProperties.length; ll++) { + if (originalConfigurationObj.entities[ii][entityProperties[jj]][kk][attributeProperties[ll]] === + matchWithoutQuotes) { + matchCounter += 1; + if (matchCounter >= matchCounterMap[match]) { + if (conditionType === ENTITY_CONDITION) { + entityProperty = condition.substring(3, condition.indexOf('==')); + entityRegExp = condition.substring(condition.indexOf('==') + 2, condition.length - 2); + entityValue = originalConfigurationObj.entities[ii][entityProperty]; + if (new RegExp(entityRegExp).test(entityValue)) { + return true; + } + return false; + } else if (conditionType === ATTRIBUTE_CONDITION) { + entityProperty = condition.substring(3, condition.indexOf('==')); + entityRegExp = condition.substring(condition.indexOf('==') + 2, condition.indexOf('}{')); + entityValue = originalConfigurationObj.entities[ii][entityProperty]; + attributeProperty = condition.substring(condition.indexOf('}{') + 2, condition.lastIndexOf('==')); + attributeRegExp = condition.substring(condition.lastIndexOf('==') + 2, condition.length - 2); + attributeValue = originalConfigurationObj.entities[ii][entityProperties[jj]][kk][attributeProperty]; + if (new RegExp(entityRegExp).test(entityValue) && new RegExp(attributeRegExp).test(attributeValue)) { + return true; + } + return false; + } + return false; + } + } + } + } + } + } + } + /* jshint maxdepth: 5 */ +} + +/** + * Returns true if the passed template value is a valid array of objects including the content and condition properties + * @param {Object} templateValue The template value + * @return {Boolean} True if the passed template value is a valid array of objects including the content + * and condition properties, false otherwise + */ +function isTemplateValueArray(templateValue) { + if (Array.isArray(templateValue)) { + for (var ii = 0; ii < templateValue.length; ii++) { + if (typeof templateValue[ii] !== 'object' || Object.getOwnPropertyNames(templateValue[ii]).length !== 2 || + !templateValue[ii].content || !templateValue[ii].condition) { + return false; + } + } + } else { + return false; + } + return true; +} + +/** + * Replaces an import() match by its corresponding value + * @param {String} match The matching String + * @return {String} The replacing String + */ +function replacer(match) { + var templateValue; + var templateTag = match.substring(8, match.length - 2); + matchCounterMap[match] = (matchCounterMap[match] ? matchCounterMap[match] + 1 : 1); + if (originalConfigurationObj.exports && originalConfigurationObj.exports[templateTag]) { + templateValue = originalConfigurationObj.exports[templateTag]; + } else { + try { + templateValue = require(ROOT_PATH + path.sep + templateTag); + } catch(exception) { + error = exception; + } + } + if (isTemplateValueArray(templateValue)) { + for (var ii = 0; ii < templateValue.length; ii++) { + if (matchesCondition(match, templateValue[ii].condition)) { + return JSON.stringify(templateValue[ii].content); + } + } + return match; + } else { + return JSON.stringify(templateValue); + } +} + +/** + * Asynchronously returns a new simulation configuration file after importing the corresponding templates + * @param {Object} configuration The original configuration + * @param {Function} callback The callback + */ +function compose(configuration, callback) { + var configurationStr, + newConfigurationStr, + newConfigurationObj; + error = null; + matchCounterMap = {}; + originalConfigurationObj = configuration; + configurationStr = JSON.stringify(configuration); + newConfigurationStr = configurationStr.replace(/"import\([^\)]+\)"/g, replacer); + if (error) { + return callback(new fdsErrors.SimulationConfigurationNotValid('The configuration information provided is not ' + + 'valid (some error ocurred when importing the templates: ' + error + ')')); + } + try { + newConfigurationObj = JSON.parse(newConfigurationStr); + } catch (exception) { + return callback(new fdsErrors.SimulationConfigurationNotValid('The configuration information provided is not ' + + 'valid (some error ocurred when importing the templates: ' + exception + ')')); + } + delete newConfigurationObj.exports; + return callback(null, newConfigurationObj); +} + +module.exports = { + compose: compose +}; diff --git a/test/unit/fiwareDeviceSimulator_test.js b/test/unit/fiwareDeviceSimulator_test.js index bbc4b5b..e3eeb1e 100644 --- a/test/unit/fiwareDeviceSimulator_test.js +++ b/test/unit/fiwareDeviceSimulator_test.js @@ -35,7 +35,7 @@ var stepAfterInterpolator = require(ROOT_PATH + '/lib/interpolators/stepAfterIn var dateIncrementInterpolator = require(ROOT_PATH + '/lib/interpolators/dateIncrementInterpolator'); var multilinePositionInterpolator = require(ROOT_PATH + '/lib/interpolators/multilinePositionInterpolator'); var textRotationInterpolator = require(ROOT_PATH + '/lib/interpolators/textRotationInterpolator'); -var fiwareDeviceSimulatorComposer = require(ROOT_PATH + '/lib/composers/fiwareDeviceSimulatorComposer'); +var fiwareDeviceSimulatorTranspiler = require(ROOT_PATH + '/lib/transpilers/fiwareDeviceSimulatorTranspiler'); var fiwareDeviceSimulator = require(ROOT_PATH + '/lib/fiwareDeviceSimulator'); var fdsErrors = require(ROOT_PATH + '/lib/errors/fdsErrors'); @@ -5444,7 +5444,7 @@ describe('fiwareDeviceSimulator tests', function() { describe('authorization', function() { beforeEach(function(done) { - fiwareDeviceSimulatorComposer.compose(simulationConfiguration, function(err, newSimulationConfiguration) { + fiwareDeviceSimulatorTranspiler.compose(simulationConfiguration, function(err, newSimulationConfiguration) { if (err) { return done(err); } @@ -5518,7 +5518,7 @@ describe('fiwareDeviceSimulator tests', function() { function simulationTestSuite(type, options){ beforeEach(function(done) { simulationConfiguration = require(ROOT_PATH + '/test/unit/configurations/simulation-configuration.json'); - fiwareDeviceSimulatorComposer.compose(simulationConfiguration, function(err, newSimulationConfiguration) { + fiwareDeviceSimulatorTranspiler.compose(simulationConfiguration, function(err, newSimulationConfiguration) { if (err) { return done(err); } diff --git a/test/unit/interpolators/fiwareDeviceSimulatorComposer_test.js b/test/unit/interpolators/fiwareDeviceSimulatorComposer_test.js deleted file mode 100644 index a2b59af..0000000 --- a/test/unit/interpolators/fiwareDeviceSimulatorComposer_test.js +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2016 Telefónica Investigación y Desarrollo, S.A.U - * - * This file is part of the Short Time Historic (STH) component - * - * STH is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * STH is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with STH. - * If not, see http://www.gnu.org/licenses/. - * - * For those usages not covered by the GNU Affero General Public License - * please contact with: [german.torodelvalle@telefonica.com] - */ - -'use strict'; - -var should = require('should'); - -var ROOT_PATH = require('app-root-path'); -var fdsErrors = require(ROOT_PATH + '/lib/errors/fdsErrors'); -var fiwareDeviceSimulatorComposer = require(ROOT_PATH + '/lib/composers/fiwareDeviceSimulatorComposer'); - -describe('fiwareDeviceSimulatorComposer tests', function() { - it('should not transform anything if no template is included', function() { - fiwareDeviceSimulatorComposer.compose( - { - should: 'not-transform-anything' - }, - function(err, newConfiguration) { - should(err).equal(null); - should(newConfiguration).containEql({should: 'not-transform-anything'}); - } - ); - }); - - it('should throw an error if a template cannot be resolved', function(done) { - try{ - fiwareDeviceSimulatorComposer.compose( - { - property: 'import(inexistent-template)' - }, - function(err) { - should(err).be.an.instanceof(fdsErrors.SimulationConfigurationNotValid); - done(); - } - ); - } catch (exception) { - done(exception); - } - }); - - it('should tranform a template if defined in the exports property', function(done) { - try{ - fiwareDeviceSimulatorComposer.compose( - { - exports: { - template: 'template-value' - }, - property: 'import(template)' - }, - function(err, newConfiguration) { - should(err).equal(null); - should(newConfiguration).containEql({property: 'template-value'}); - done(); - } - ); - } catch (exception) { - done(exception); - } - }); - - it('should tranform a number template if defined in an external template file', function(done) { - try{ - fiwareDeviceSimulatorComposer.compose( - { - property: 'import(test/unit/templates/template-number)' - }, - function(err, newConfiguration) { - should(err).equal(null); - should(newConfiguration).containEql({property: 666}); - done(); - } - ); - } catch (exception) { - done(exception); - } - }); - - it('should tranform a string template if defined in an external template file', function(done) { - try{ - fiwareDeviceSimulatorComposer.compose( - { - property: 'import(test/unit/templates/template-string)' - }, - function(err, newConfiguration) { - should(err).equal(null); - should(newConfiguration).containEql({property: 'template-value'}); - done(); - } - ); - } catch (exception) { - done(exception); - } - }); - - it('should tranform an array template if defined in an external template file', function(done) { - try{ - fiwareDeviceSimulatorComposer.compose( - { - property: 'import(test/unit/templates/template-array)' - }, - function(err, newConfiguration) { - should(err).equal(null); - should(newConfiguration.property).containEql(1); - should(newConfiguration.property).containEql(2); - should(newConfiguration.property).containEql(3); - done(); - } - ); - } catch (exception) { - done(exception); - } - }); - - it('should tranform an object template if defined in an external template file', function(done) { - try{ - fiwareDeviceSimulatorComposer.compose( - { - property: 'import(test/unit/templates/template-object)' - }, - function(err, newConfiguration) { - should(err).equal(null); - should(newConfiguration.property).containEql({property1: 'value1'}); - should(newConfiguration.property).containEql({property2: 'value2'}); - should(newConfiguration.property).containEql({property3: 'value3'}); - done(); - } - ); - } catch (exception) { - done(exception); - } - }); -}); diff --git a/test/unit/templates/template-attribute-condition.json b/test/unit/templates/template-attribute-condition.json new file mode 100644 index 0000000..9c9a01a --- /dev/null +++ b/test/unit/templates/template-attribute-condition.json @@ -0,0 +1,6 @@ +[ + { + "content": "the-content", + "condition": "${{entity_name==the-.*-name}{name==the-.*-name}}" + } +] diff --git a/test/unit/templates/template-entity-condition.json b/test/unit/templates/template-entity-condition.json new file mode 100644 index 0000000..16a0885 --- /dev/null +++ b/test/unit/templates/template-entity-condition.json @@ -0,0 +1,6 @@ +[ + { + "content": "the-content", + "condition": "${{entity_name==the-.*-name}}" + } +] diff --git a/test/unit/templates/template-schedule-entity-condition.json b/test/unit/templates/template-schedule-entity-condition.json new file mode 100644 index 0000000..0efafcb --- /dev/null +++ b/test/unit/templates/template-schedule-entity-condition.json @@ -0,0 +1,10 @@ +[ + { + "content": "not-matching", + "condition": "${{entity_name==the-.*-NAME}}" + }, + { + "content": "the-entity-schedule", + "condition": "${{entity_name==the-.*-name}}" + } +] diff --git a/test/unit/templates/template-type-attribute-condition.json b/test/unit/templates/template-type-attribute-condition.json new file mode 100644 index 0000000..293450e --- /dev/null +++ b/test/unit/templates/template-type-attribute-condition.json @@ -0,0 +1,6 @@ +[ + { + "content": "the-attribute-type", + "condition": "${{entity_name==the-.*-name}{name==the-.*-name}}" + } +] diff --git a/test/unit/templates/template-type-entity-condition.json b/test/unit/templates/template-type-entity-condition.json new file mode 100644 index 0000000..7e6fa0e --- /dev/null +++ b/test/unit/templates/template-type-entity-condition.json @@ -0,0 +1,6 @@ +[ + { + "content": "the-entity-type", + "condition": "${{entity_name==the-.*-name}}" + } +] diff --git a/test/unit/templates/template-value-attribute-condition.json b/test/unit/templates/template-value-attribute-condition.json new file mode 100644 index 0000000..c5e3604 --- /dev/null +++ b/test/unit/templates/template-value-attribute-condition.json @@ -0,0 +1,14 @@ +[ + { + "content": "not-matching", + "condition": "${{entity_name==the-.*-NAME}{name==the-.*-name}}" + }, + { + "content": "not-matching", + "condition": "${{entity_name==the-.*-name}{name==the-.*-NAME}}" + }, + { + "content": "the-attribute-value", + "condition": "${{entity_name==the-.*-name}{name==the-.*-name}}" + } +] diff --git a/test/unit/transpilers/fiwareDeviceSimulatorTranspiler_test.js b/test/unit/transpilers/fiwareDeviceSimulatorTranspiler_test.js new file mode 100644 index 0000000..a65b551 --- /dev/null +++ b/test/unit/transpilers/fiwareDeviceSimulatorTranspiler_test.js @@ -0,0 +1,806 @@ +/* + * Copyright 2016 Telefónica Investigación y Desarrollo, S.A.U + * + * This file is part of the Short Time Historic (STH) component + * + * STH is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * STH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with STH. + * If not, see http://www.gnu.org/licenses/. + * + * For those usages not covered by the GNU Affero General Public License + * please contact with: [german.torodelvalle@telefonica.com] + */ + +'use strict'; + +var should = require('should'); + +var ROOT_PATH = require('app-root-path'); +var fdsErrors = require(ROOT_PATH + '/lib/errors/fdsErrors'); +var fiwareDeviceSimulatorTranspiler = require(ROOT_PATH + '/lib/transpilers/fiwareDeviceSimulatorTranspiler'); + +describe('fiwareDeviceSimulatorTranspiler tests', function() { + it('should not import anything if no template is included', function() { + fiwareDeviceSimulatorTranspiler.compose( + { + should: 'not-transform-anything' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration).containEql({should: 'not-transform-anything'}); + } + ); + }); + + it('should throw an error if a template cannot be resolved', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + property: 'import(inexistent-template)' + }, + function(err) { + should(err).be.an.instanceof(fdsErrors.SimulationConfigurationNotValid); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import a string template if defined in the exports property', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: 'template-value' + }, + property: 'import(template)' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration).containEql({property: 'template-value'}); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import a string template if defined in an external template file', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + property: 'import(test/unit/templates/template-string)' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration).containEql({property: 'template-value'}); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import a number template if defined in the exports property', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: 666 + }, + property: 'import(template)' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration).containEql({property: 666}); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import a number template if defined in an external template file', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + property: 'import(test/unit/templates/template-number)' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration).containEql({property: 666}); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import an array template if defined in the exports property', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: [1, 2, 3] + }, + property: 'import(template)' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.property).containEql(1); + should(newConfiguration.property).containEql(2); + should(newConfiguration.property).containEql(3); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import an array template if defined in an external template file', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + property: 'import(test/unit/templates/template-array)' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.property).containEql(1); + should(newConfiguration.property).containEql(2); + should(newConfiguration.property).containEql(3); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import an object template if defined in the exports property', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: { + 'property1': 'value1', + 'property2': 'value2', + 'property3': 'value3' + } + }, + property: 'import(template)' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.property).containEql({property1: 'value1'}); + should(newConfiguration.property).containEql({property2: 'value2'}); + should(newConfiguration.property).containEql({property3: 'value3'}); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import an object template if defined in an external template file', function(done) { + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + property: 'import(test/unit/templates/template-object)' + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.property).containEql({property1: 'value1'}); + should(newConfiguration.property).containEql({property2: 'value2'}); + should(newConfiguration.property).containEql({property3: 'value3'}); + done(); + } + ); + } catch (exception) { + done(exception); + } + }); + + it('should import the template value if the entity condition is satified if defined in the exports property', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: [ + { + 'content': 'the-content', + 'condition': '${{entity_name==the-.*-name}}' + } + ] + }, + entities: [ + { + entity_name: 'the-entity-name', + schedule: 'import(template)' + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].schedule).equal('the-content'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should import the template value if the entity condition is satified if defined in an external template file', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + entities: [ + { + entity_name: 'the-entity-name', + schedule: 'import(test/unit/templates/template-entity-condition)' + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].schedule).equal('the-content'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should not import the template value if the entity condition is not satified if defined in the exports property', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: [ + { + 'content': 'the-content', + 'condition': '${{entity_name==the-.*-name}}' + } + ] + }, + entities: [ + { + entity_name: 'the-entity-NAME', + schedule: 'import(template)' + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].schedule).equal('import(template)'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should not import the template value if the entity condition is not satified if defined in an ' + + 'external template file', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + entities: [ + { + entity_name: 'the-entity-NAME', + schedule: 'import(test/unit/templates/template-entity-condition)' + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].schedule).equal( + 'import(test/unit/templates/template-entity-condition)'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should import the template value of an attribute if the entity condition is satified if defined ' + + 'in the exports property', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: [ + { + 'content': 'the-content', + 'condition': '${{entity_name==the-.*-name}}' + } + ] + }, + entities: [ + { + entity_name: 'the-entity-name', + active: [ + { + name: 'the-attribute-name', + value: 'import(template)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].active[0].value).equal('the-content'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should import the template value of an attribute if the entity condition is satified if defined ' + + 'in an external template file', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + entities: [ + { + entity_name: 'the-entity-name', + active: [ + { + name: 'the-attribute-name', + value: 'import(test/unit/templates/template-entity-condition)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].active[0].value).equal('the-content'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should import the template value of an attribute if the attribute condition is satified if defined ' + + 'in the exports property', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: [ + { + 'content': 'the-content', + 'condition': '${{entity_name==the-.*-name}{name==the-.*-name}}' + } + ] + }, + entities: [ + { + entity_name: 'the-entity-name', + active: [ + { + name: 'the-attribute-name', + value: 'import(template)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].active[0].value).equal('the-content'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should import the template value of an attribute if the attribute condition is satified if defined ' + + 'in an external template file', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + entities: [ + { + entity_name: 'the-entity-name', + active: [ + { + name: 'the-attribute-name', + value: 'import(test/unit/templates/template-attribute-condition)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].active[0].value).equal('the-content'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should not import the template value if the attribute condition is not satified in the entity part ' + + 'if defined in the exports property', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: [ + { + 'content': 'the-content', + 'condition': '${{entity_name==the-.*-name}{name==the-.*-name}}' + } + ] + }, + entities: [ + { + entity_name: 'the-entity-NAME', + active: [ + { + name: 'the-attribute-name', + value: 'import(template)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].active[0].value).equal('import(template)'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should not import the template value if the attribute condition is not satified in the entity part ' + + 'if defined in an external template file', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + entities: [ + { + entity_name: 'the-entity-NAME', + active: [ + { + name: 'the-attribute-name', + value: 'import(test/unit/templates/template-attribute-condition)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].active[0].value).equal( + 'import(test/unit/templates/template-attribute-condition)'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should not import the template value if the attribute condition is not satified in the attribute part ' + + 'if defined in the exports property', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + template: [ + { + 'content': 'the-content', + 'condition': '${{entity_name==the-.*-name}{name==the-.*-name}}' + } + ] + }, + entities: [ + { + entity_name: 'the-entity-name', + active: [ + { + name: 'the-attribute-NAME', + value: 'import(template)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].active[0].value).equal('import(template)'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should not import the template value if the attribute condition is not satified in the attribute part ' + + 'if defined in an external template file', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + entities: [ + { + entity_name: 'the-entity-name', + active: [ + { + name: 'the-attribute-NAME', + value: 'import(test/unit/templates/template-attribute-condition)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].active[0].value).equal( + 'import(test/unit/templates/template-attribute-condition)'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should import correctly a complex simulation configuration file using templates if defined ' + + 'in the exports property', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + exports: { + 'template-1': [ + { + 'content': 'the-entity-type', + 'condition': '${{entity_name==the-.*-name}}' + } + ], + 'template-2': [ + { + 'content': 'not-matching', + 'condition': '${{entity_name==the-.*-NAME}}' + }, + { + 'content': 'the-entity-schedule', + 'condition': '${{entity_name==the-.*-name}}' + } + ], + 'template-3': [ + { + 'content': 'the-attribute-type', + 'condition': '${{entity_name==the-.*-name}{name==the-.*-name}}' + } + ], + 'template-4': [ + { + 'content': 'not-matching', + 'condition': '${{entity_name==the-.*-NAME}{name==the-.*-name}}' + }, + { + 'content': 'not-matching', + 'condition': '${{entity_name==the-.*-name}{name==the-.*-NAME}}' + }, + { + 'content': 'the-attribute-value', + 'condition': '${{entity_name==the-.*-name}{name==the-.*-name}}' + } + ] + }, + entities: [ + { + entity_name: 'the-entity-name', + type: 'import(template-1)', + schedule: 'import(template-2)', + active: [ + { + name: 'the-attribute-name', + type: 'import(template-3)', + value: 'import(template-4)' + }, + { + name: 'the-attribute-name', + type: 'import(template-3)', + value: 'import(template-4)' + } + ] + }, + { + entity_name: 'the-entity-name', + type: 'import(template-1)', + schedule: 'import(template-2)', + active: [ + { + name: 'the-attribute-name', + type: 'import(template-3)', + value: 'import(template-4)' + }, + { + name: 'the-attribute-name', + type: 'import(template-3)', + value: 'import(template-4)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].type).equal('the-entity-type'); + should(newConfiguration.entities[0].schedule).equal('the-entity-schedule'); + should(newConfiguration.entities[0].active[0].type).equal('the-attribute-type'); + should(newConfiguration.entities[0].active[0].value).equal('the-attribute-value'); + should(newConfiguration.entities[0].active[1].type).equal('the-attribute-type'); + should(newConfiguration.entities[0].active[1].value).equal('the-attribute-value'); + should(newConfiguration.entities[1].type).equal('the-entity-type'); + should(newConfiguration.entities[1].schedule).equal('the-entity-schedule'); + should(newConfiguration.entities[1].active[0].type).equal('the-attribute-type'); + should(newConfiguration.entities[1].active[0].value).equal('the-attribute-value'); + should(newConfiguration.entities[1].active[1].type).equal('the-attribute-type'); + should(newConfiguration.entities[1].active[1].value).equal('the-attribute-value'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); + + it('should import correctly a complex simulation configuration file using templates if defined ' + + 'in external template files', + function (done) { + /* jshint camelcase: false */ + try{ + fiwareDeviceSimulatorTranspiler.compose( + { + entities: [ + { + entity_name: 'the-entity-name', + type: 'import(test/unit/templates/template-type-entity-condition)', + schedule: 'import(test/unit/templates/template-schedule-entity-condition)', + active: [ + { + name: 'the-attribute-name', + type: 'import(test/unit/templates/template-type-attribute-condition)', + value: 'import(test/unit/templates/template-value-attribute-condition)' + }, + { + name: 'the-attribute-name', + type: 'import(test/unit/templates/template-type-attribute-condition)', + value: 'import(test/unit/templates/template-value-attribute-condition)' + } + ] + }, + { + entity_name: 'the-entity-name', + type: 'import(test/unit/templates/template-type-entity-condition)', + schedule: 'import(test/unit/templates/template-schedule-entity-condition)', + active: [ + { + name: 'the-attribute-name', + type: 'import(test/unit/templates/template-type-attribute-condition)', + value: 'import(test/unit/templates/template-value-attribute-condition)' + }, + { + name: 'the-attribute-name', + type: 'import(test/unit/templates/template-type-attribute-condition)', + value: 'import(test/unit/templates/template-value-attribute-condition)' + } + ] + } + ] + }, + function(err, newConfiguration) { + should(err).equal(null); + should(newConfiguration.entities[0].type).equal('the-entity-type'); + should(newConfiguration.entities[0].schedule).equal('the-entity-schedule'); + should(newConfiguration.entities[0].active[0].type).equal('the-attribute-type'); + should(newConfiguration.entities[0].active[0].value).equal('the-attribute-value'); + should(newConfiguration.entities[0].active[1].type).equal('the-attribute-type'); + should(newConfiguration.entities[0].active[1].value).equal('the-attribute-value'); + should(newConfiguration.entities[1].type).equal('the-entity-type'); + should(newConfiguration.entities[1].schedule).equal('the-entity-schedule'); + should(newConfiguration.entities[1].active[0].type).equal('the-attribute-type'); + should(newConfiguration.entities[1].active[0].value).equal('the-attribute-value'); + should(newConfiguration.entities[1].active[1].type).equal('the-attribute-type'); + should(newConfiguration.entities[1].active[1].value).equal('the-attribute-value'); + done(); + } + ); + } catch (exception) { + done(exception); + } + /* jshint camelcase: true */ + } + ); +});