Comparing version 3.0.3 to 3.2.0
148
index.js
/*eslint no-process-env: 0, no-console: 0 */ | ||
const merge = require('lodash.merge') | ||
const EventEmitter = require('events').EventEmitter | ||
@@ -16,3 +16,2 @@ const lib = require('./lib') | ||
/** | ||
* @param pkg The application package.json | ||
* @param app.api The application api (api/ folder) | ||
@@ -23,4 +22,5 @@ * @param app.config The application configuration (config/ folder) | ||
* some necessary default configuration. | ||
* @param app config to create Trails instance | ||
*/ | ||
constructor (app) { | ||
constructor(app) { | ||
super() | ||
@@ -38,7 +38,7 @@ | ||
app.api.models || (app.api.models = { }) | ||
app.api.services || (app.api.services = { }) | ||
app.api.resolvers || (app.api.resolvers = { }) | ||
app.api.policies || (app.api.policies = { }) | ||
app.api.controllers || (app.api.controllers = { }) | ||
app.api.models || (app.api.models = {}) | ||
app.api.services || (app.api.services = {}) | ||
app.api.resolvers || (app.api.resolvers = {}) | ||
app.api.policies || (app.api.policies = {}) | ||
app.api.controllers || (app.api.controllers = {}) | ||
@@ -71,7 +71,2 @@ if (!process.env.NODE_ENV) { | ||
}, | ||
config: { | ||
value: new lib.Configuration(app.config, processEnv), | ||
configurable: true, | ||
writable: false | ||
}, | ||
api: { | ||
@@ -87,23 +82,37 @@ value: app.api, | ||
packs: { | ||
value: { } | ||
value: {} | ||
} | ||
}) | ||
this.setMaxListeners(this.config.get('main.maxListeners')) | ||
let trailpacksConfig = {} | ||
// instantiate trailpacks | ||
if (app.config.main && app.config.main.packs) { | ||
app.config.main.packs.forEach(Pack => { | ||
try { | ||
const pack = new Pack(this) | ||
this.packs[pack.name] = pack | ||
trailpacksConfig = merge(trailpacksConfig, pack.config) | ||
lib.Core.mergeApi(this, pack) | ||
lib.Core.bindTrailpackMethodListeners(this, pack) | ||
} | ||
catch (e) { | ||
console.log(e.stack) | ||
throw new TrailpackError(Pack, e, 'constructor') | ||
} | ||
}) | ||
} | ||
// instantiate trailpacks | ||
this.config.get('main.packs').forEach(Pack => { | ||
try { | ||
const pack = new Pack(this) | ||
this.packs[pack.name] = pack | ||
this.config.merge(pack.config) | ||
lib.Core.mergeApi(this, pack) | ||
lib.Core.bindTrailpackMethodListeners(this, pack) | ||
Object.defineProperties(this, { | ||
config: { | ||
value: new lib.Configuration(trailpacksConfig, processEnv), | ||
configurable: true, | ||
writable: false | ||
} | ||
catch (e) { | ||
console.log(e.stack) | ||
throw new TrailpackError(Pack, e, 'constructor') | ||
} | ||
}) | ||
this.config.merge(app.config) | ||
this.setMaxListeners(this.config.get('main.maxListeners')) | ||
// instantiate resource classes and bind resource methods | ||
@@ -116,2 +125,4 @@ this.controllers = lib.Core.bindMethods(this, 'controllers') | ||
this.emit('trails:configured') | ||
lib.Core.bindApplicationListeners(this) | ||
@@ -126,3 +137,3 @@ lib.Core.bindTrailpackPhaseListeners(this, Object.values(this.packs)) | ||
*/ | ||
async start () { | ||
async start() { | ||
this.emit('trails:start') | ||
@@ -137,13 +148,14 @@ await this.after('trails:ready') | ||
*/ | ||
async stop () { | ||
async stop() { | ||
this.emit('trails:stop') | ||
await Promise.all(Object.values(this.packs).map(pack => { | ||
this.log.debug('Unloading trailpack', pack.name, '...') | ||
return pack.unload() | ||
})) | ||
.then(() => { | ||
this.log.debug('All trailpacks unloaded. Done.') | ||
this.removeAllListeners() | ||
}) | ||
await Promise | ||
.all(Object.values(this.packs).map(pack => { | ||
this.log.debug('Unloading trailpack', pack.name, '...') | ||
return pack.unload() | ||
})) | ||
.then(() => { | ||
this.log.debug('All trailpacks unloaded. Done.') | ||
this.removeAllListeners() | ||
}) | ||
@@ -158,3 +170,3 @@ return this | ||
*/ | ||
async onceAny (events) { | ||
async onceAny(events) { | ||
if (!Array.isArray(events)) { | ||
@@ -166,16 +178,17 @@ events = [events] | ||
return Promise.race(events.map(eventName => { | ||
return new Promise(resolve => { | ||
resolveCallback = resolve | ||
this.once(eventName, resolveCallback) | ||
return Promise | ||
.race(events.map(eventName => { | ||
return new Promise(resolve => { | ||
resolveCallback = resolve | ||
this.once(eventName, resolveCallback) | ||
}) | ||
})) | ||
.then((...args) => { | ||
events.forEach(eventName => this.removeListener(eventName, resolveCallback)) | ||
return args | ||
}) | ||
})) | ||
.then((...args) => { | ||
events.forEach(eventName => this.removeListener(eventName, resolveCallback)) | ||
return args | ||
}) | ||
.catch(err => { | ||
this.log.error(err, 'handling onceAny events', events) | ||
throw err | ||
}) | ||
.catch(err => { | ||
this.log.error(err, 'handling onceAny events', events) | ||
throw err | ||
}) | ||
} | ||
@@ -189,21 +202,22 @@ | ||
*/ | ||
async after (events) { | ||
async after(events) { | ||
if (!Array.isArray(events)) { | ||
events = [ events ] | ||
events = [events] | ||
} | ||
return Promise.all(events.map(eventName => { | ||
return new Promise(resolve => { | ||
if (eventName instanceof Array){ | ||
resolve(this.onceAny(eventName)) | ||
} | ||
else { | ||
this.once(eventName, resolve) | ||
} | ||
return Promise | ||
.all(events.map(eventName => { | ||
return new Promise(resolve => { | ||
if (eventName instanceof Array) { | ||
resolve(this.onceAny(eventName)) | ||
} | ||
else { | ||
this.once(eventName, resolve) | ||
} | ||
}) | ||
})) | ||
.catch(err => { | ||
this.log.error(err, 'handling after events', events) | ||
throw err | ||
}) | ||
})) | ||
.catch(err => { | ||
this.log.error(err, 'handling after events', events) | ||
throw err | ||
}) | ||
} | ||
@@ -215,5 +229,5 @@ | ||
*/ | ||
get log () { | ||
get log() { | ||
return this.logger | ||
} | ||
} |
const merge = require('lodash.merge') | ||
const path = require('path') | ||
const joi = require('joi') | ||
const schemas = require('./schemas') | ||
const ConfigurationProxyHandler = { | ||
get (target, key) { | ||
get(target, key) { | ||
if (target.has && target.has(key)) { | ||
@@ -19,9 +17,7 @@ const value = target.immutable === true ? Object.freeze(target.get(key)) : target.get(key) | ||
module.exports = class Configuration extends Map { | ||
constructor (configTree = { }, processEnv = { }) { | ||
const config = Configuration.buildConfig(configTree) | ||
constructor(configTree = {}, processEnv = {}) { | ||
const config = Configuration.buildConfig(configTree, processEnv.NODE_ENV) | ||
const configEntries = Object.entries(Configuration.flattenTree(config)) | ||
super(configEntries) | ||
this.validateConfig(config) | ||
this.immutable = false | ||
@@ -38,3 +34,3 @@ this.env = processEnv | ||
set (key, value) { | ||
set(key, value) { | ||
if (this.immutable === true) { | ||
@@ -47,8 +43,16 @@ throw new IllegalAccessError('Cannot set properties directly on config. Use .set(key, value) (immutable)') | ||
/** | ||
* Merge tree into this configuration. Return overwritten keys | ||
* Merge tree into this configuration. Return overwritten keys | ||
*/ | ||
merge (configTree) { | ||
merge(configTree) { | ||
const configEntries = Object.entries(Configuration.flattenTree(configTree)) | ||
return configEntries.map(([ key, value ]) => { | ||
return configEntries.map(([key, value]) => { | ||
const hasKey = this.has(key) | ||
//if the key already exist and it's an object, we need to merge values before setting in on the map or we lose some data | ||
if (hasKey) { | ||
const currentValue = this.get(key) | ||
if (typeof currentValue === 'object' && !Array.isArray(currentValue)) { | ||
value = Object.assign({}, value, currentValue) | ||
} | ||
} | ||
this.set(key, value) | ||
@@ -63,3 +67,3 @@ | ||
*/ | ||
freeze () { | ||
freeze() { | ||
this.immutable = true | ||
@@ -71,3 +75,3 @@ } | ||
*/ | ||
unfreeze () { | ||
unfreeze() { | ||
this.immutable = false | ||
@@ -79,6 +83,6 @@ } | ||
*/ | ||
static flattenTree (tree = { }) { | ||
const toReturn = { } | ||
static flattenTree(tree = {}) { | ||
const toReturn = {} | ||
Object.entries(tree).forEach(([ k, v ]) => { | ||
Object.entries(tree).forEach(([k, v]) => { | ||
if (typeof v === 'object' && v !== null) { | ||
@@ -99,6 +103,6 @@ const flatObject = Configuration.flattenTree(v) | ||
*/ | ||
static buildConfig (initialConfig = { }, nodeEnv) { | ||
static buildConfig(initialConfig = {}, nodeEnv) { | ||
const root = path.resolve(path.dirname(require.main.filename)) | ||
const temp = path.resolve(root, '.tmp') | ||
const envConfig = initialConfig.env && initialConfig.env[nodeEnv] | ||
const envConfig = initialConfig.env && initialConfig.env[nodeEnv] || {} | ||
@@ -108,3 +112,3 @@ const configTemplate = { | ||
maxListeners: 128, | ||
packs: [ ], | ||
packs: [], | ||
paths: { | ||
@@ -116,30 +120,10 @@ root: root, | ||
}, | ||
timeouts: { | ||
start: 10000, | ||
stop: 10000 | ||
}, | ||
freezeConfig: true, | ||
createPaths: true | ||
}, | ||
log: { } | ||
} | ||
} | ||
const mergedConfig = merge(configTemplate, initialConfig, (envConfig || { })) | ||
mergedConfig.env = nodeEnv | ||
return mergedConfig | ||
return merge(configTemplate, initialConfig, envConfig, { env: nodeEnv }) | ||
} | ||
/** | ||
* Validate the structure and prerequisites of the configuration object. Throw | ||
* an Error if invalid; invalid configurations are unrecoverable and require | ||
* that the programmer fix them. | ||
*/ | ||
validateConfig (config) { | ||
const result = joi.validate(config, schemas.config) | ||
if (result.error) { | ||
throw new ValidationError('Project Configuration Error', result.error.details) | ||
} | ||
} | ||
} | ||
@@ -5,3 +5,2 @@ /*eslint no-console: 0 */ | ||
const mkdirp = require('mkdirp') | ||
const mapValues = require('lodash.mapvalues') | ||
const lib = require('./') | ||
@@ -60,11 +59,15 @@ | ||
bindMethods (app, resource) { | ||
return mapValues(app.api[resource], (Resource, resourceName) => { | ||
const obj = new Resource(app) | ||
return Object.entries(app.api[resource]) | ||
.map(([ resourceName, Resource ]) => { | ||
const obj = new Resource(app) | ||
obj.methods = Core.getClassMethods(obj) | ||
Object.entries(obj.methods).forEach(([ _, method ]) => { | ||
obj[method] = obj[method].bind(obj) | ||
obj.methods = Core.getClassMethods(obj) | ||
Object.entries(obj.methods).forEach(([ _, method ]) => { | ||
obj[method] = obj[method].bind(obj) | ||
}) | ||
return [ resourceName, obj ] | ||
}) | ||
return obj | ||
}) | ||
.reduce((result, [ resourceName, resource ]) => Object.assign(result, { | ||
[resourceName]: resource | ||
}), { }) | ||
}, | ||
@@ -71,0 +74,0 @@ |
exports.Errors = require('./errors') | ||
exports.Schemas = require('./schemas') | ||
@@ -4,0 +3,0 @@ exports.Configuration = require('./Configuration') |
{ | ||
"name": "trails", | ||
"version": "3.0.3", | ||
"version": "3.2.0", | ||
"description": "Modern Web Application Framework for Node.js", | ||
@@ -21,3 +21,3 @@ "keywords": [ | ||
"scripts": { | ||
"test": "eslint --ignore-path .gitignore . && nyc mocha", | ||
"test": "eslint --ignore-path .gitignore index.js lib/ test/ && nyc mocha", | ||
"test-performance": "eslint --ignore-path .gitignore . && mocha test-performance", | ||
@@ -72,6 +72,2 @@ "ci": "cd .. && ci" | ||
"dependencies": { | ||
"joi": "13.0.2", | ||
"lodash.defaultsdeep": "4.6.0", | ||
"lodash.isplainobject": "4.0.6", | ||
"lodash.mapvalues": "4.6.0", | ||
"lodash.merge": "4.6.0", | ||
@@ -81,11 +77,10 @@ "mkdirp": "0.5.1" | ||
"devDependencies": { | ||
"eslint": "^3", | ||
"eslint": "^4.15.0", | ||
"eslint-config-trails": "^3", | ||
"mocha": "^4", | ||
"nyc": "^11.0.2", | ||
"pre-commit": "^1.1.3", | ||
"proxyquire": "^1.7.11", | ||
"nyc": "^11.4.1", | ||
"pre-commit": "^1.2.2", | ||
"proxyquire": "^1.8.0", | ||
"smokesignals": "^3", | ||
"trailpack": "^3", | ||
"winston": "^2.1.1" | ||
"trailpack": "^3" | ||
}, | ||
@@ -92,0 +87,0 @@ "eslintConfig": { |
@@ -37,2 +37,10 @@ const path = require('path') | ||
}, | ||
testpack: { | ||
override: 'ok', | ||
defaultArray: ['ko'], | ||
defaultArrayWithDuplicates: ['ko', 'test'], | ||
defaultObject: { | ||
override: 'ok' | ||
} | ||
}, | ||
i18n: { | ||
@@ -39,0 +47,0 @@ lng: 'en', |
@@ -8,2 +8,14 @@ const Trailpack = require('trailpack') | ||
name: 'testpack' | ||
}, | ||
config: { | ||
testpack: { | ||
defaultValue: 'default', | ||
override: 'ko', | ||
defaultArray: ['ok'], | ||
defaultArrayWithDuplicates: ['test'], | ||
defaultObject: { | ||
test: 'ok', | ||
override: 'ko' | ||
} | ||
} | ||
} | ||
@@ -10,0 +22,0 @@ }) |
@@ -14,3 +14,3 @@ const fs = require('fs') | ||
it('should be able to start and stop many instances in a single node process', () => { | ||
const cycles = [ ] | ||
const cycles = [] | ||
for (let i = 0; i < 10; ++i) { | ||
@@ -63,2 +63,15 @@ cycles.push(new TrailsApp(testAppDefinition).start()) | ||
}) | ||
it('should set default config value if not configured explicitly by user', () => { | ||
assert.equal(global.app.config.get('testpack.defaultValue'), 'default') | ||
assert.equal(global.app.config.get('testpack.defaultObject.test'), 'ok') | ||
}) | ||
it('should override config if configured explicitly by user', () => { | ||
assert.equal(global.app.config.get('testpack.defaultValue'), 'default') | ||
assert.equal(global.app.config.get('testpack.override'), 'ok') | ||
assert.equal(global.app.config.get('testpack.defaultObject.override'), 'ok') | ||
assert.equal(global.app.config.get('testpack.defaultObject.test'), 'ok') | ||
assert.equal(global.app.config.testpack.defaultObject.test, 'ok') | ||
assert.deepEqual(global.app.config.get('testpack.defaultArray'), ['ko']) | ||
assert.deepEqual(global.app.config.get('testpack.defaultArrayWithDuplicates'), ['ko', 'test']) | ||
}) | ||
}) | ||
@@ -73,3 +86,3 @@ | ||
const def = { | ||
pkg: { }, | ||
pkg: {}, | ||
config: { | ||
@@ -99,4 +112,4 @@ main: { | ||
const def = { | ||
api: { }, | ||
pkg: { }, | ||
api: {}, | ||
pkg: {}, | ||
config: { | ||
@@ -106,3 +119,3 @@ main: { | ||
class Failpack extends Trailpack { | ||
constructor (app) { | ||
constructor(app) { | ||
super(app) | ||
@@ -122,7 +135,7 @@ } | ||
const def = { | ||
api: { }, | ||
api: {}, | ||
config: { | ||
main: { } | ||
main: {} | ||
}, | ||
pkg: { } | ||
pkg: {} | ||
} | ||
@@ -139,7 +152,7 @@ const app = new TrailsApp(def) | ||
const def = { | ||
pkg: { }, | ||
api: { }, | ||
pkg: {}, | ||
api: {}, | ||
config: { | ||
main: { | ||
packs: [ Testpack ] | ||
packs: [Testpack] | ||
}, | ||
@@ -161,7 +174,7 @@ foo: 'bar' | ||
const def = { | ||
pkg: { }, | ||
api: { }, | ||
pkg: {}, | ||
api: {}, | ||
config: { | ||
main: { | ||
packs: [ Testpack ] | ||
packs: [Testpack] | ||
}, | ||
@@ -173,3 +186,3 @@ foo: 'bar' | ||
assert.equal(app.config.get('foo'), 'bar') | ||
app.config = { } | ||
app.config = {} | ||
assert.equal(app.config.get('foo'), 'bar') | ||
@@ -187,3 +200,3 @@ }) | ||
it('should invoke listener when listening for a single event', () => { | ||
const eventPromise = app.after([ 'test1' ]) | ||
const eventPromise = app.after(['test1']) | ||
app.emit('test1') | ||
@@ -198,3 +211,3 @@ return eventPromise | ||
it('should invoke listener when listening for multiple events', () => { | ||
const eventPromise = app.after([ 'test3', 'test4', 'test5' ]) | ||
const eventPromise = app.after(['test3', 'test4', 'test5']) | ||
app.emit('test3') | ||
@@ -201,0 +214,0 @@ app.emit('test4') |
@@ -196,18 +196,2 @@ const assert = require('assert') | ||
}) | ||
describe('#validateConfig', () => { | ||
it('should throw ValidationError if main.packs contains an "undefined" trailpack', () => { | ||
const testConfig = { | ||
main: { | ||
packs: [ | ||
undefined | ||
] | ||
}, | ||
log: { | ||
logger: new smokesignals.Logger('silent') | ||
} | ||
} | ||
assert.throws(() => new lib.Configuration(testConfig), lib.Errors.ValidationError) | ||
}) | ||
}) | ||
describe('#get', () => { | ||
@@ -214,0 +198,0 @@ it('should return nested config value if it exists', () => { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2
8
105287
85
2746
- Removedjoi@13.0.2
- Removedlodash.defaultsdeep@4.6.0
- Removedlodash.isplainobject@4.0.6
- Removedlodash.mapvalues@4.6.0
- Removedhoek@5.0.46.1.3(transitive)
- Removedisemail@3.2.0(transitive)
- Removedjoi@13.0.2(transitive)
- Removedlodash.defaultsdeep@4.6.0(transitive)
- Removedlodash.isplainobject@4.0.6(transitive)
- Removedlodash.mapvalues@4.6.0(transitive)
- Removedpunycode@2.3.1(transitive)
- Removedtopo@3.0.3(transitive)