From 4583affba95b77689172876ae8e493fdb8b3af3a Mon Sep 17 00:00:00 2001 From: wwalser Date: Tue, 27 Sep 2016 03:09:25 -0400 Subject: [PATCH 1/5] Add async configuration support. --- lib/helpers/config-helper.js | 130 ++++++++++++++++++++++------------- lib/helpers/gulp-helper.js | 99 ++++++++++++++------------ lib/tasks/model.js | 4 +- 3 files changed, 140 insertions(+), 93 deletions(-) diff --git a/lib/helpers/config-helper.js b/lib/helpers/config-helper.js index 2839502d..ddb6b313 100644 --- a/lib/helpers/config-helper.js +++ b/lib/helpers/config-helper.js @@ -1,13 +1,49 @@ 'use strict'; -var args = require('yargs').argv; -var path = require('path'); -var fs = require('fs'); -var helpers = require(__dirname); -var url = require('url'); -var _ = require('lodash'); - -module.exports = { +var args = require('yargs').argv; +var Bluebird = require('bluebird'); +var path = require('path'); +var fs = require('fs'); +var helpers = require(__dirname); +var url = require('url'); +var _ = require('lodash'); + +var api = { + config: undefined, + error: undefined, + init: function () { + return Bluebird.resolve() + .then(function () { + var config; + + if (args.url) { + config = api.parseDbUrl(args.url); + } else { + try { + config = require(api.getConfigFile()); + } catch (e) { + api.error = e; + } + } + return config; + }) + .then(function (config) { + if (typeof config === 'object' || config === undefined) { + return config; + } else if (config.length === 1) { + return Bluebird.promisify(config)(); + } else { + return config(); + } + }) + .then(function (config) { + api.config = config; + }) + .then(function () { + // Always return the full config api + return api; + }); + }, getConfigFile: function () { if (args.config) { return path.resolve(process.cwd(), args.config); @@ -20,11 +56,11 @@ module.exports = { }, relativeConfigFile: function () { - return path.relative(process.cwd(), this.getConfigFile()); + return path.relative(process.cwd(), api.getConfigFile()); }, configFileExists: function () { - return helpers.path.existsSync(this.getConfigFile()); + return helpers.path.existsSync(api.getConfigFile()); }, getDefaultConfig: function () { @@ -54,13 +90,13 @@ module.exports = { }, writeDefaultConfig: function () { - var configPath = path.dirname(this.getConfigFile()); + var configPath = path.dirname(api.getConfigFile()); if (!helpers.path.existsSync(configPath)) { fs.mkdirSync(configPath); } - fs.writeFileSync(this.getConfigFile(), this.getDefaultConfig()); + fs.writeFileSync(api.getConfigFile(), api.getDefaultConfig()); }, readConfig: function (options) { @@ -70,53 +106,51 @@ module.exports = { logging: true }, options || {}); - if (!this.config) { + if (api.config === undefined) { + throw new Error( + 'Error reading "' + + api.relativeConfigFile() + + '". Error: ' + api.error + ); + } + + if (typeof api.config !== 'object') { + throw new Error( + 'Config must be an object or a promise for and object: ' + + api.relativeConfigFile() + ); + } + + if (options.logging) { if (args.url) { - this.config = this.parseDbUrl(args.url); + console.log('Parsed url ' + api.filteredUrl(args.url)); } else { - try { - this.config = require(this.getConfigFile()); - } catch (e) { - throw new Error('Error reading "' + this.relativeConfigFile() + '". Error: ' + e.message); - } + console.log('Loaded configuration file "' + api.relativeConfigFile() + '".'); } + } - if (typeof this.config !== 'object') { - throw new Error('Config must be an object: ' + this.relativeConfigFile()); - } + // in case url is present - we overwrite the configuration + if (api.config.url) { + api.config = _.merge(api.config, api.parseDbUrl(api.config.url)); + } + if (api.config[env]) { if (options.logging) { - if (args.url) { - console.log('Parsed url ' + this.filteredUrl(args.url)); - } else { - console.log('Loaded configuration file "' + this.relativeConfigFile() + '".'); - } + console.log('Using environment "' + env + '".'); } - if (this.config[env]) { - if (options.logging) { - console.log('Using environment "' + env + '".'); - } - - // The Sequelize library needs a function passed in to its logging option - if (this.config.logging && !_.isFunction(this.config.logging)) { - this.config.logging = console.log; - } - - this.config = this.config[env]; + // The Sequelize library needs a function passed in to its logging option + if (api.config.logging && !_.isFunction(api.config.logging)) { + api.config.logging = console.log; } - // in case url is present - we overwrite the configuration - if (this.config.url) { - this.config = _.merge(this.config, this.parseDbUrl(this.config.url)); - } + api.config = api.config[env]; } - - return this.config; + return api.config; }, filteredUrl: function (url) { - var regExp = new RegExp(':?' + (this.config.password || '') + '@'); + var regExp = new RegExp(':?' + (api.config.password || '') + '@'); return url.replace(regExp, ':*****@'); }, @@ -129,7 +163,7 @@ module.exports = { }, options || {}); try { - config = this.readConfig(); + config = api.readConfig(); } catch (e) { if (options.ignoreConfig) { config = {}; @@ -165,7 +199,7 @@ module.exports = { }, parseDbUrl: function (urlString) { - var config = this.urlStringToConfigHash(urlString); + var config = api.urlStringToConfigHash(urlString); config = _.assign(config, { dialect: config.dialect.replace(/:$/, '') @@ -180,3 +214,5 @@ module.exports = { return config; } }; + +module.exports = api; diff --git a/lib/helpers/gulp-helper.js b/lib/helpers/gulp-helper.js index 825eeaf1..032810c9 100644 --- a/lib/helpers/gulp-helper.js +++ b/lib/helpers/gulp-helper.js @@ -4,6 +4,8 @@ var helpers = require(__dirname); var _ = require('lodash'); var clc = require('cli-color'); +var config = helpers.config.init(); + module.exports = { addTask: function (gulp, taskName, task) { gulp.task( @@ -11,7 +13,9 @@ module.exports = { task.descriptions.short, task.dependencies || [], function () { - task.task(); + return config.then(function () { + task.task(); + }); }, { aliases: task.aliases || [] } @@ -25,50 +29,57 @@ module.exports = { 'help:' + taskName, false, function () { - helpers.view.log(clc.bold('COMMANDS')); - - var commands = [ taskName ].concat(task.aliases || []); - var commandMargin = Math.max.apply(null, commands.map(function (c) { - return c.length; - })); - - commands.forEach(function (command) { - var s = [ - 'sequelize', command + (new Array(commandMargin - command.length + 1).join(' ')), - '--', task.descriptions.short - ].join(' '); - - helpers.view.log(' ' + s); - }); - helpers.view.log(); - - helpers.view.log(clc.bold('DESCRIPTION')); - - (task.descriptions.long || [task.descriptions.short]).forEach(function (line) { - helpers.view.log(' ' + line); + return config.then(function () { + Object.keys(task.descriptions).forEach(function (description) { + if (typeof task.descriptions[description] === 'function') { + task.descriptions[description] = task.descriptions[description](); + } + }); + helpers.view.log(clc.bold('COMMANDS')); + + var commands = [ taskName ].concat(task.aliases || []); + var commandMargin = Math.max.apply(null, commands.map(function (c) { + return c.length; + })); + + commands.forEach(function (command) { + var s = [ + 'sequelize', command + (new Array(commandMargin - command.length + 1).join(' ')), + '--', task.descriptions.short + ].join(' '); + + helpers.view.log(' ' + s); + }); + helpers.view.log(); + + helpers.view.log(clc.bold('DESCRIPTION')); + + (task.descriptions.long || [task.descriptions.short]).forEach(function (line) { + helpers.view.log(' ' + line); + }); + + (function (options) { + if (options) { + var margin = Math.max.apply(null, Object.keys(options).map(function (o) { + return o.length; + })); + + helpers.view.log(); + helpers.view.log(clc.bold('OPTIONS')); + + Object.keys(options).forEach(function (option) { + var args = [' ', option]; + + args.push(new Array(margin - option.length + 1).join(' ')); + args.push(options[option]); + + helpers.view.log.apply(helpers.view, args); + }); + } + })(_.assign(self.getGlobalOptions(), task.descriptions.options)); + + helpers.view.log(); }); - - (function (options) { - if (options) { - var margin = Math.max.apply(null, Object.keys(options).map(function (o) { - return o.length; - })); - - helpers.view.log(); - helpers.view.log(clc.bold('OPTIONS')); - - Object.keys(options).forEach(function (option) { - var args = [' ', option]; - - args.push(new Array(margin - option.length + 1).join(' ')); - args.push(options[option]); - - helpers.view.log.apply(helpers.view, args); - }); - } - })(_.assign(self.getGlobalOptions(), task.descriptions.options)); - - helpers.view.log(); } ); }, diff --git a/lib/tasks/model.js b/lib/tasks/model.js index 0219c673..3a2d06ab 100644 --- a/lib/tasks/model.js +++ b/lib/tasks/model.js @@ -12,7 +12,7 @@ module.exports = { descriptions: { 'short': 'Generates a model and its migration.', - 'long': (function () { + 'long': function () { var migrationFileName = helpers.path.getFileName( 'migration', helpers.migration.generateMigrationName({ name: 'User' }), @@ -61,7 +61,7 @@ module.exports = { ); return result; - })(), + }, options: { '--name': 'The name of the new model.', From c840030c0a900223a08204d12b4831e5c84a2e51 Mon Sep 17 00:00:00 2001 From: wwalser Date: Tue, 27 Sep 2016 16:05:07 -0400 Subject: [PATCH 2/5] Change logging optionality to get tests passing again. --- lib/helpers/config-helper.js | 20 ++++++-------------- lib/helpers/version-helper.js | 2 +- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/helpers/config-helper.js b/lib/helpers/config-helper.js index ddb6b313..803ce7fa 100644 --- a/lib/helpers/config-helper.js +++ b/lib/helpers/config-helper.js @@ -99,13 +99,9 @@ var api = { fs.writeFileSync(api.getConfigFile(), api.getDefaultConfig()); }, - readConfig: function (options) { + readConfig: function () { var env = helpers.generic.getEnvironment(); - options = _.assign({ - logging: true - }, options || {}); - if (api.config === undefined) { throw new Error( 'Error reading "' + @@ -121,12 +117,10 @@ var api = { ); } - if (options.logging) { - if (args.url) { - console.log('Parsed url ' + api.filteredUrl(args.url)); - } else { - console.log('Loaded configuration file "' + api.relativeConfigFile() + '".'); - } + if (args.url) { + console.log('Parsed url ' + api.filteredUrl(args.url)); + } else { + console.log('Loaded configuration file "' + api.relativeConfigFile() + '".'); } // in case url is present - we overwrite the configuration @@ -135,9 +129,7 @@ var api = { } if (api.config[env]) { - if (options.logging) { - console.log('Using environment "' + env + '".'); - } + console.log('Using environment "' + env + '".'); // The Sequelize library needs a function passed in to its logging option if (api.config.logging && !_.isFunction(api.config.logging)) { diff --git a/lib/helpers/version-helper.js b/lib/helpers/version-helper.js index e5249904..38a63399 100644 --- a/lib/helpers/version-helper.js +++ b/lib/helpers/version-helper.js @@ -16,7 +16,7 @@ module.exports = { getDialect: function () { try { - return helpers.config.readConfig({ logging: false }); + return helpers.config.readConfig(); } catch (e) { return null; } From 11bc0142cffeadb793d17f9c0d812c1f6d287741 Mon Sep 17 00:00:00 2001 From: wwalser Date: Tue, 27 Sep 2016 22:46:31 -0400 Subject: [PATCH 3/5] Ensure main body of readconfig is only run once. --- lib/helpers/config-helper.js | 68 +++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/lib/helpers/config-helper.js b/lib/helpers/config-helper.js index 803ce7fa..bebfce71 100644 --- a/lib/helpers/config-helper.js +++ b/lib/helpers/config-helper.js @@ -10,6 +10,7 @@ var _ = require('lodash'); var api = { config: undefined, + rawConfig: undefined, error: undefined, init: function () { return Bluebird.resolve() @@ -37,7 +38,7 @@ var api = { } }) .then(function (config) { - api.config = config; + api.rawConfig = config; }) .then(function () { // Always return the full config api @@ -100,43 +101,46 @@ var api = { }, readConfig: function () { - var env = helpers.generic.getEnvironment(); - - if (api.config === undefined) { - throw new Error( - 'Error reading "' + - api.relativeConfigFile() + - '". Error: ' + api.error - ); - } + if (!api.config) { + var env = helpers.generic.getEnvironment(); + + if (api.rawConfig === undefined) { + throw new Error( + 'Error reading "' + + api.relativeConfigFile() + + '". Error: ' + api.error + ); + } - if (typeof api.config !== 'object') { - throw new Error( - 'Config must be an object or a promise for and object: ' + - api.relativeConfigFile() - ); - } + if (typeof api.rawConfig !== 'object') { + throw new Error( + 'Config must be an object or a promise for and object: ' + + api.relativeConfigFile() + ); + } - if (args.url) { - console.log('Parsed url ' + api.filteredUrl(args.url)); - } else { - console.log('Loaded configuration file "' + api.relativeConfigFile() + '".'); - } + if (args.url) { + console.log('Parsed url ' + api.filteredUrl(args.url)); + } else { + console.log('Loaded configuration file "' + api.relativeConfigFile() + '".'); + } - // in case url is present - we overwrite the configuration - if (api.config.url) { - api.config = _.merge(api.config, api.parseDbUrl(api.config.url)); - } + // in case url is present - we overwrite the configuration + if (api.rawConfig.url) { + api.rawConfig = _.merge(api.rawConfig, api.parseDbUrl(api.rawConfig.url)); + } - if (api.config[env]) { - console.log('Using environment "' + env + '".'); + if (api.rawConfig[env]) { + console.log('Using environment "' + env + '".'); - // The Sequelize library needs a function passed in to its logging option - if (api.config.logging && !_.isFunction(api.config.logging)) { - api.config.logging = console.log; - } + // The Sequelize library needs a function passed in to its logging option + if (api.rawConfig.logging && !_.isFunction(api.rawConfig.logging)) { + api.rawConfig.logging = console.log; + } - api.config = api.config[env]; + api.rawConfig = api.rawConfig[env]; + } + api.config = api.rawConfig; } return api.config; }, From b49276ae5936e8f3d044add78f7a4c763fd57744 Mon Sep 17 00:00:00 2001 From: wwalser Date: Wed, 28 Sep 2016 18:42:46 -0400 Subject: [PATCH 4/5] Fix support for a url connection string. --- lib/helpers/config-helper.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/helpers/config-helper.js b/lib/helpers/config-helper.js index bebfce71..031354e5 100644 --- a/lib/helpers/config-helper.js +++ b/lib/helpers/config-helper.js @@ -114,22 +114,17 @@ var api = { if (typeof api.rawConfig !== 'object') { throw new Error( - 'Config must be an object or a promise for and object: ' + + 'Config must be an object or a promise for an object: ' + api.relativeConfigFile() ); } if (args.url) { - console.log('Parsed url ' + api.filteredUrl(args.url)); + console.log('Parsed url ' + api.filteredUrl(args.url, api.rawConfig)); } else { console.log('Loaded configuration file "' + api.relativeConfigFile() + '".'); } - // in case url is present - we overwrite the configuration - if (api.rawConfig.url) { - api.rawConfig = _.merge(api.rawConfig, api.parseDbUrl(api.rawConfig.url)); - } - if (api.rawConfig[env]) { console.log('Using environment "' + env + '".'); @@ -140,13 +135,19 @@ var api = { api.rawConfig = api.rawConfig[env]; } + + // in case url is present - we overwrite the configuration + if (api.rawConfig.url) { + api.rawConfig = _.merge(api.rawConfig, api.parseDbUrl(api.rawConfig.url)); + } + api.config = api.rawConfig; } return api.config; }, - filteredUrl: function (url) { - var regExp = new RegExp(':?' + (api.config.password || '') + '@'); + filteredUrl: function (url, config) { + var regExp = new RegExp(':?' + (config.password || '') + '@'); return url.replace(regExp, ':*****@'); }, From c2f244a24251514d2b4a2d8c9455c11cbc99d0ff Mon Sep 17 00:00:00 2001 From: wwalser Date: Tue, 22 Nov 2016 11:46:31 -0500 Subject: [PATCH 5/5] Add test for promises in config files. --- test/db/migrate.test.js | 8 +++++++- test/db/seed.test.js | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/test/db/migrate.test.js b/test/db/migrate.test.js index 772c226e..cdce20e2 100644 --- a/test/db/migrate.test.js +++ b/test/db/migrate.test.js @@ -15,7 +15,8 @@ var _ = require('lodash'); 'db:migrate --coffee', 'db:migrate --config ../../support/tmp/config/config.json', 'db:migrate --config ' + Support.resolveSupportPath('tmp', 'config', 'config.json'), - 'db:migrate --config ../../support/tmp/config/config.js' + 'db:migrate --config ../../support/tmp/config/config.js', + 'db:migrate --config ../../support/tmp/config/config-promise.js' ]).forEach(function (flag) { var prepare = function (callback, options) { options = _.assign({ config: {} }, options || {}); @@ -32,6 +33,11 @@ var _ = require('lodash'); if (flag.match(/config\.js$/)) { configPath = configPath + 'config.js'; configContent = 'module.exports = ' + configContent; + } else if (flag.match(/config-promise\.js/)) { + configPath = configPath + 'config-promise.js'; + configContent = '' + + 'var b = require("bluebird");' + + 'module.exports = b.resolve(' + configContent + ');'; } else { configPath = configPath + 'config.json'; } diff --git a/test/db/seed.test.js b/test/db/seed.test.js index d830a436..33c26d1e 100644 --- a/test/db/seed.test.js +++ b/test/db/seed.test.js @@ -16,7 +16,8 @@ var _ = require('lodash'); 'db:seed --config ../../support/tmp/config/config.json --seed seedPerson.js', 'db:seed --seed seedPerson.js --config ' + Support.resolveSupportPath('tmp', 'config', 'config.json'), - 'db:seed --seed seedPerson.js --config ../../support/tmp/config/config.js' + 'db:seed --seed seedPerson.js --config ../../support/tmp/config/config.js', + 'db:seed --seed seedPerson.js --config ../../support/tmp/config/config-promise.js' ]).forEach(function (flag) { var prepare = function (callback, options) { options = _.assign({ config: {} }, options || {}); @@ -32,6 +33,11 @@ var _ = require('lodash'); if (flag.match(/config\.js$/)) { configPath = configPath + 'config.js'; configContent = 'module.exports = ' + configContent; + } else if (flag.match(/config-promise\.js/)) { + configPath = configPath + 'config-promise.js'; + configContent = '' + + 'var b = require("bluebird");' + + 'module.exports = b.resolve(' + configContent + ');'; } else { configPath = configPath + 'config.json'; }