1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | 1× 1× 1× 1× 1× 1× 4× 4× 4× 4× 4× 4× 4× 4× 4× 4× 4× 4× 1× 4× 4× 4× 4× 4× 1× 1× 1× 4× 4× 1× 4× 4× 4× 16× 16× 16× 1× 1× 2× 8× 8× 8× 1× 4× 4× 4× 1× 1× 4× 4× 4× 1× 4× 4× 4× 1× 8× 4× 1× | 'use strict'; const Hoek = require('hoek'); const Joi = require('joi'); const async = require('neo-async'); const _ = require('lodash'); /** * Plugin Internals * @type {Object} */ const internals = {}; /** * Register the <tt>Configue</tt> plugin and process the various steps and hooks * @param server - Hapi server to configure * @param options - options of the Configue Plugin * @param next - plugin continuation */ exports.register = function register(server, options, next) { server.log(['plugin', 'info'], "Registering the hapi-configue plugin"); let settings = Hoek.cloneWithShallow(options, 'provider'); // load fresh instance of nconf delete require.cache[require.resolve('nconf')]; const nconf = require('nconf'); internals.nconf = nconf; // settings validation const results = Joi.validate(settings, internals.schema); Iif (results.error) return next(results.error); nconf.use('memory'); nconf.clear(); const decorate = internals.decorate(server, nconf, next); Iif (settings.customWorkflow) settings.customWorkflow(nconf, decorate); else internals.applyDefaultWorkflow(nconf, settings, decorate); }; /** * Decorate the server & the request with the nconf getter * @param server - hapi server * @param nconf - nconf object * @param next - end of plugin process callback * @returns {Function} */ internals.decorate = function decorate(server, nconf, next) { return (err) => { Iif (err) return next(err); server.decorate('server', 'configue', internals.getConfig(nconf)); server.decorate('request', 'configue', internals.getConfig(nconf)); return next(); }; }; exports.register.attributes = { pkg: require('../package.json') }; /** * Joi options schema */ internals.schema = [ Joi.object({customWorkflow: Joi.func()}), Joi.object({ disable: Joi.object({ argv: Joi.boolean(), env: Joi.boolean() }), files: [Joi.string(), Joi.array().items(Joi.object({ file: Joi.string().required(), format: Joi.object({stringify: Joi.func(), parse: Joi.func()}) })), Joi.array().items(Joi.string())], defaults: [Joi.object(), Joi.array().items(Joi.object())], postHooks: Joi.object({ overrides: Joi.func(), argv: Joi.func(), env: Joi.func(), files: Joi.func(), defaults: Joi.func() }) })]; /** * Apply the Default Configuration Workflow * @param nconf - the nconf object * @param settings - plugin config * @param next - callback */ internals.applyDefaultWorkflow = function applyDefaultWorkflow(nconf, settings, next) { const hooks = settings.postHooks; // Load eventual overrides values and then iterates over the different steps (in order: argv, env, files) return async.series([ this.processHook(hooks, 'overrides'), this.iterateSteps(this.steps, settings) ], next); }; /** * Iterate asynchronously over the various steps * @param steps - list of configuration steps * @param settings - project settings * @returns {Function} */ internals.iterateSteps = function iterateSteps(steps, settings) { const hooks = settings.postHooks; return (next) => { return async.eachSeries(steps, (stepName, key, done) => { this.stepActions[stepName](settings); Iif (hooks && hooks[stepName]) this.executePostHook(hooks[stepName], done); else done(); }, next) }; }; /** * Ordered list of configuration steps * @type {string[]} */ internals.steps = ['argv', 'env', 'files', 'defaults']; // Definition of associated actions below /** * Closure that takes the name of a nconf resource as a parameters and * loads it if it is not disabled in the plugin options * * @param resource - the ressource to be loaded. (argv, env, files...) * @returns {Function} */ internals.load = function load(resource) { return function onResource(options) { const disable = options.disable; Eif (!disable || !disable[resource]) return internals.nconf[resource](); } }; /** * Process the hook for a given step * @param hooks * @param stepName * @returns {Function} */ internals.processHook = function processHook(hooks, stepName) { return (done) => { Iif (hooks && hooks[stepName]) this.executePostHook(hooks[stepName], done); else done(); }; }; /** * Execute a hook * @param hook - a post step hook * @param done - callback */ internals.executePostHook = function executeHook(hook, done) { return hook(internals.nconf, done); }; /** * Load the files in options using <tt>nconf.file</tt> * @param options - plugin options */ internals.loadFiles = function loadFiles(options) { const files = options.files; Iif (Array.isArray(files) && files.length) { if (typeof files[0] === 'string') files.forEach((file) => internals.nconf.file(file, file)); else // file.file is used as namespace for nconf files.forEach((file) => internals.nconf.file(file.file, file)); } else Iif (typeof files === 'string' && files.length) internals.nconf.file(files); }; /** * Load the defaults in options using <tt>nconf.defaults</tt> * @param options - plugin options */ internals.loadDefaults = function loadDefaults(options) { const defaults = options.defaults; Iif (Array.isArray(defaults)) { internals.nconf.defaults(_.defaults.apply({}, defaults)); } else { internals.nconf.defaults(defaults); } }; /** * Closure that stores the nconf object as to expose a read only interface to the user * @param nconf - the nconf config * @returns {Function} - config getter function */ internals.getConfig = function getConfig(nconf) { return function onKey(key) { return nconf.get(key); } }; /** * Steps and their associated action function * @type {{argv: Function, env: Function, files: loadFiles}} */ internals.stepActions = { argv: internals.load('argv'), env: internals.load('env'), files: internals.loadFiles, defaults: internals.loadDefaults }; |