Skip to content

Commit

Permalink
[feat] default resources
Browse files Browse the repository at this point in the history
  • Loading branch information
scott-wyatt committed Jun 26, 2018
1 parent bb790bc commit 3281865
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 22 deletions.
40 changes: 34 additions & 6 deletions lib/Configuration.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { merge, defaultsDeep } from 'lodash'
import { merge, defaultsDeep, isArray } from 'lodash'
import { resolve, dirname } from 'path'
import { IllegalAccessError } from './errors'
import { IllegalAccessError, ConfigValueError } from './errors'
import { requireMainFilename } from './utils'

// Proxy Handler for get requests to the configuration
const ConfigurationProxyHandler: ProxyHandler<Configuration> = {
get (target: any, key: string) {
if (target.has && target.has(key)) {
Expand All @@ -15,6 +16,9 @@ const ConfigurationProxyHandler: ProxyHandler<Configuration> = {
}
}

/**
* Extend map class for getter/setter tuple config
*/
export class Configuration extends Map<any, any> {
public immutable: boolean
public env: {}
Expand All @@ -37,6 +41,18 @@ export class Configuration extends Map<any, any> {
return toReturn
}

static initialResources (tree) {
if (tree.hasOwnProperty('main') && tree.main.hasOwnProperty('resources')) {
if (!isArray(tree.main['resources'])) {
throw new ConfigValueError('if set, main.resources must be an array')
}
return tree.main['resources']
}
else {
return ['controllers', 'policies', 'services', 'models', 'resolvers']
}
}

/**
* Copy and merge the provided configuration into a new object, decorated with
* necessary default and environment-specific values.
Expand All @@ -48,6 +64,7 @@ export class Configuration extends Map<any, any> {

const configTemplate = {
main: {
resources: Configuration.initialResources(initialConfig),
maxListeners: 128,
spools: [ ],
paths: {
Expand All @@ -71,21 +88,30 @@ export class Configuration extends Map<any, any> {
NODE_ENV?: string
} = { }
) {
// Constants for configuration
const config = Configuration.buildConfig(configTree, processEnv['NODE_ENV'])
const configEntries = Object.entries(Configuration.flattenTree(config))
// Add to the map constructor
super(configEntries)

// Initial values
this.immutable = false
this.env = processEnv

// Bind methods
this.get = this.get.bind(this)
this.set = this.set.bind(this)
this.entries = this.entries.bind(this)
this.has = this.has.bind(this)

// Return Proxy
return new Proxy(this, ConfigurationProxyHandler)
}

/**
* Throws IllegalAccessError if the configuration has already been set to immutable
* and an attempt to set value occurs.
*/
set (key: string, value: any) {
if (this.immutable === true) {
throw new IllegalAccessError('Cannot set properties directly on config. Use .set(key, value) (immutable)')
Expand All @@ -96,18 +122,20 @@ export class Configuration extends Map<any, any> {
/**
* Merge tree into this configuration. Return overwritten keys
*/
merge (configTree: {[key: string]: any}, configAction = 'hold') {
merge (configTree: {[key: string]: any}, configAction = 'hold'): { hasKey: boolean, key: any }[] {
const configEntries = Object.entries(Configuration.flattenTree(configTree))
return configEntries.map(([ key, value ]) => {
const hasKey = this.has(key)
// If the key has never been set, it is added to the config
// If configAction is set to hold, then it will replace the initial config
if (!hasKey || configAction === 'hold') {
this.set(key, value)
}
// If configAction is set to merge, it will merge values over the initial config
else if (hasKey && configAction === 'merge') {
const firstValue = this.get(key)
value = defaultsDeep(firstValue, value)
this.set(key, value)
this.set(key, defaultsDeep(this.get(key), value))
}
// If configAction is replaceable, and the key already exists, it's ignored completely
else if (hasKey && configAction === 'replaceable') {
// Do Nothing
}
Expand Down
10 changes: 8 additions & 2 deletions lib/Core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const Core = {
'app',
'api',
'log',
'__',
'__', // this reserved method comes from i18n
'constructor',
'undefined',
'methods',
Expand Down Expand Up @@ -133,17 +133,23 @@ export const Core = {
return props
},

/**
* Merge the app api resources with the ones provided by the spools
*/
mergeApi (
app: FabrixApp,
spool: Spool,
defaults = ['controllers', 'services', 'policies', 'models', 'resolvers']
defaults = [ ]
) {
defaults.forEach(resource => Object.assign(
app.api[resource] || {},
spool.api[resource] || {})
)
},

/**
* Merge extensions provided by spools into the app
*/
mergeExtensions (
app: FabrixApp,
spool: Spool
Expand Down
36 changes: 24 additions & 12 deletions lib/Fabrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@ export class FabrixApp extends EventEmitter {
private _api: IApi
private _fabrix: any
private _spools: {[key: string]: Spool | ServerSpool | ExtensionSpool | DatastoreSpool | SystemSpool | ToolSpool | MiscSpool }
private _resources: string[] = [ ]

public resources: string[] = ['controllers', 'policies', 'services', 'models', 'resolvers']
public controllers: {[key: string]: FabrixController }
public controllers: {[key: string]: any } // FabrixController }
public services: {[key: string]: any } // FabrixService }
public policies: {[key: string]: FabrixPolicy }
public models: {[key: string]: FabrixModel }
public resolvers: {[key: string]: FabrixResolver }
public routes: any[] = []
public policies: {[key: string]: any } // FabrixPolicy }
public models: {[key: string]: any } // FabrixModel }
public resolvers: {[key: string]: any } // FabrixResolver }

public routes: any[] = [ ]


/**
Expand Down Expand Up @@ -73,11 +74,6 @@ export class FabrixApp extends EventEmitter {
throw new Errors.ApiNotDefinedError()
}

// Set each api resource to make sure it's provided as an object in app
this.resources.forEach(resource => {
app.api[resource] = app.api[resource] || (app.api[resource] = { })
})

// set the process node env if not established by environment
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'development'
Expand All @@ -94,9 +90,17 @@ export class FabrixApp extends EventEmitter {
this._api = app.api
this._fabrix = pkg

// set the max listeners from the config
// Set the max listeners from the config
this.setMaxListeners(this.config.get('main.maxListeners'))

// Set the resources from the configuration
this.resources = this.config.get('main.resources')

// Set each api resource to make sure it's provided as an object in app
this.resources.forEach(resource => {
app.api[resource] = app.api[resource] || (app.api[resource] = { })
})

// instantiate spools TOTO type of Spool
this.config.get('main.spools').forEach((NewSpool: any) => {
try {
Expand Down Expand Up @@ -187,6 +191,14 @@ export class FabrixApp extends EventEmitter {
return this.logger
}

set resources (values) {
this._resources = Object.assign(this._resources, values)
}

get resources() {
return this._resources
}

/**
* Start the App. Load all Spools.
*/
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fabrix/fabrix",
"version": "1.0.0-alpha.7",
"version": "1.0.0-alpha.8",
"description": "Strongly Typed Modern Web Application Framework for Node.js",
"keywords": [
"framework",
Expand Down
58 changes: 58 additions & 0 deletions test/integration/trailsapp.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ describe('Fabrix', () => {
it('should set paths.sockets if not configured explicitly by user', () => {
assert(global.app.config.get('main.paths.sockets'))
})
it('should set resources if not configured explicitly by user', () => {
assert(global.app.config.get('main.resources'))
})
})

describe('errors', () => {
Expand Down Expand Up @@ -203,6 +206,61 @@ describe('Fabrix', () => {
assert.equal(app.config.get('test.val'), 1)
assert.equal(app.config.get('test.otherval'), 1)
})

it('should have default resources', () => {
const def = {
pkg: { },
api: { },
config: {
main: {
}
}
}
const app = new FabrixApp(def)
assert.deepEqual(app.config.get('main.resources'), [
'controllers',
'policies',
'services',
'models',
'resolvers'
])

assert(app['controllers'])
assert(app['services'])
assert(app['models'])
assert(app['resolvers'])
assert(app['policies'])
})

it('should override default resources', () => {
const def = {
pkg: { },
api: { },
config: {
main: {
resources: ['controllers','events']
}
}
}
const app = new FabrixApp(def)
assert.deepEqual(app.config.get('main.resources'), ['controllers', 'events'])
assert(app['controllers'])
assert(app['events'])
assert(!app['models'])
})

it('should throw error on incorrectly configured main.resources', () => {
const def = {
pkg: { },
api: { },
config: {
main: {
resources: {}
}
}
}
assert.throws(() => new FabrixApp(def), Error)
})
})
})

Expand Down

0 comments on commit 3281865

Please sign in to comment.