From 8b475a97b2c40c9780ea6847c7289715c3916345 Mon Sep 17 00:00:00 2001 From: Hack Sparrow Date: Thu, 13 Aug 2015 15:32:31 +0530 Subject: [PATCH] Resolve ${var} values in middleware.json App variables can now be specified in middleware.json using the ${var} format. The value of var is looked up using app.get(). This allows loopback.rest and other loopback middleware which require app variables, to be loaded declaratively using middleware.json. --- lib/executor.js | 40 +++++++++++++ test/executor.test.js | 90 +++++++++++++++++++++++++++++- test/fixtures/simple-middleware.js | 5 ++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/simple-middleware.js diff --git a/lib/executor.js b/lib/executor.js index 84c0605..a8a3031 100644 --- a/lib/executor.js +++ b/lib/executor.js @@ -316,10 +316,50 @@ function setupMiddleware(app, instructions) { } assert(typeof factory === 'function', 'Middleware factory must be a function'); + data.config = getUpdatedConfigObject(data.config, app); app.middlewareFromConfig(factory, data.config); }); } +function getUpdatedConfigObject(config, app) { + + var DYNAMIC_CONFIG_PARAM = /\$\{(\w+)\}$/; + + function getConfigVariable(param) { + var configVariable = param; + var match = configVariable.match(DYNAMIC_CONFIG_PARAM); + if (match) { + var appValue = app.get(match[1]); + if (appValue !== undefined) { + configVariable = appValue; + } else { + console.warn('%s does not resolve to a valid value. ' + + '"%s" must be resolvable by app.get().', param, match[1]); + } + } + return configVariable; + } + + function interpolateVariables(config) { + var interpolated = {}; + Object.keys(config).forEach(function(configKey) { + var value = config[configKey]; + if (Array.isArray(value)) { + interpolated[configKey] = value.map(getConfigVariable); + } else if (typeof value === 'string') { + interpolated[configKey] = getConfigVariable(value); + } else if (typeof value === 'object' && Object.keys(value).length) { + interpolated[configKey] = interpolateVariables(value); + } else { + interpolated[configKey] = value; + } + }); + return interpolated; + } + + return interpolateVariables(config); +} + function setupComponents(app, instructions) { instructions.components.forEach(function(data) { debug('Configuring component %j', data.sourceFile); diff --git a/test/executor.test.js b/test/executor.test.js index 30da00d..3c910ef 100644 --- a/test/executor.test.js +++ b/test/executor.test.js @@ -292,7 +292,7 @@ describe('executor', function() { }); }); - describe ('for mixins', function() { + describe('for mixins', function() { var options; beforeEach(function() { appdir.writeFileSync('custom-mixins/example.js', @@ -425,6 +425,77 @@ describe('executor', function() { }); }); + describe('with middleware.json', function() { + + it('should parse a simple config variable', function(done) { + boot.execute(app, simpleMiddlewareConfig('routes', + { path: '${restApiRoot}' } + )); + + supertest(app).get('/').end(function(err, res) { + if (err) return done(err); + expect(res.body.path).to.equal(app.get('restApiRoot')); + done(); + }); + }); + + it('should parse multiple config variables', function(done) { + boot.execute(app, simpleMiddlewareConfig('routes', + { path: '${restApiRoot}', env: '${env}' } + )); + + supertest(app).get('/').end(function(err, res) { + if (err) return done(err); + expect(res.body.path).to.equal(app.get('restApiRoot')); + expect(res.body.env).to.equal(app.get('env')); + done(); + }); + }); + + it('should parse config variables in an array', function(done) { + boot.execute(app, simpleMiddlewareConfig('routes', + { paths: ['${restApiRoot}'] } + )); + + supertest(app).get('/').end(function(err, res) { + if (err) return done(err); + expect(res.body.paths).to.eql( + [app.get('restApiRoot')] + ); + done(); + }); + }); + + it('should parse config variables in an object', function(done) { + boot.execute(app, simpleMiddlewareConfig('routes', + { info: { path: '${restApiRoot}' } } + )); + + supertest(app).get('/').end(function(err, res) { + if (err) return done(err); + expect(res.body.info).to.eql({ + path: app.get('restApiRoot') + }); + done(); + }); + }); + + it('should parse config variables in a nested object', function(done) { + boot.execute(app, simpleMiddlewareConfig('routes', + { nested: { info: { path: '${restApiRoot}' } } } + )); + + supertest(app).get('/').end(function(err, res) { + if (err) return done(err); + expect(res.body.nested).to.eql({ + info: { path: app.get('restApiRoot') } + }); + done(); + }); + }); + + }); + it('calls function exported by boot/init.js', function() { var file = appdir.writeFileSync('boot/init.js', 'module.exports = function(app) { app.fnCalled = true; };'); @@ -583,6 +654,23 @@ describe('executor', function() { }); }); +function simpleMiddlewareConfig(phase, params) { + return someInstructions({ + middleware: { + phases: [phase], + middleware: [ + { + sourceFile: path.join(__dirname, './fixtures/simple-middleware.js'), + config: { + phase: phase, + params: params + } + } + ] + } + }); +} + function assertValidDataSource(dataSource) { // has methods assert.isFunc(dataSource, 'createModel'); diff --git a/test/fixtures/simple-middleware.js b/test/fixtures/simple-middleware.js new file mode 100644 index 0000000..30079f6 --- /dev/null +++ b/test/fixtures/simple-middleware.js @@ -0,0 +1,5 @@ +module.exports = function(params) { + return function(req, res, next) { + res.send(params); + }; +};