Skip to content

Commit 8d10b2d

Browse files
authored
Merge pull request #7 from fabrix-app/v1.1
V1.1
2 parents 4ff5a5a + 0af2b63 commit 8d10b2d

File tree

13 files changed

+268
-22
lines changed

13 files changed

+268
-22
lines changed

lib/RouterSpool.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ export class RouterSpool extends SystemSpool {
5555
Validator.validateRouter(this.app.config.get('router')),
5656
Promise.all(
5757
Object.values(this.app.config.get('routes') || {}).map(Validator.validateRoute)
58+
),
59+
Promise.all(
60+
Object.values(this.app.config.get('policies') || {}).map(Validator.validatePolicy)
5861
)
5962
])
6063
}
@@ -79,8 +82,8 @@ export class RouterSpool extends SystemSpool {
7982
}
8083

8184
sanity () {
82-
if (!isObject(this.app.routes)) {
83-
throw new Error('Sanity Failed: app.routes is not an array!')
85+
if (!(this.app.routes instanceof Map)) {
86+
throw new Error('Sanity Failed: app.routes is not a Map!')
8487
}
8588
}
8689
}

lib/config/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export { policies } from './policies'
12
export { router } from './router'
23
export { routes } from './routes'
34
export { spool } from './spool'
5+

lib/config/policies.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const policies = {
2+
}

lib/schemas/policy.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as joi from 'joi'
2+
3+
export const policySchema = joi.object().keys({
4+
'*': joi.alternatives().try(
5+
joi.func(),
6+
joi.string(),
7+
joi.object(),
8+
joi.array().items(joi.string())
9+
),
10+
GET: joi.alternatives().try(
11+
joi.func(),
12+
joi.string(),
13+
joi.object(),
14+
joi.array().items(joi.string())
15+
),
16+
HEAD: joi.alternatives().try(
17+
joi.func(),
18+
joi.string(),
19+
joi.object(),
20+
joi.array().items(joi.string())
21+
),
22+
POST: joi.alternatives().try(
23+
joi.func(),
24+
joi.string(),
25+
joi.object(),
26+
joi.array().items(joi.string())
27+
),
28+
PUT: joi.alternatives().try(
29+
joi.func(),
30+
joi.string(),
31+
joi.object(),
32+
joi.array().items(joi.string())
33+
),
34+
DELETE: joi.alternatives().try(
35+
joi.func(),
36+
joi.string(),
37+
joi.object(),
38+
joi.array().items(joi.string())
39+
),
40+
CONNECT: joi.alternatives().try(
41+
joi.func(),
42+
joi.string(),
43+
joi.object(),
44+
joi.array().items(joi.string())
45+
),
46+
OPTIONS: joi.alternatives().try(
47+
joi.func(),
48+
joi.string(),
49+
joi.object(),
50+
joi.array().items(joi.string())
51+
),
52+
TRACE: joi.alternatives().try(
53+
joi.func(),
54+
joi.string(),
55+
joi.object(),
56+
joi.array().items(joi.string())
57+
),
58+
PATCH: joi.alternatives().try(
59+
joi.func(),
60+
joi.string(),
61+
joi.object(),
62+
joi.array().items(joi.string())
63+
)
64+
}).unknown()

lib/utils.ts

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FabrixApp } from '@fabrix/fabrix'
2-
import { get, omit } from 'lodash'
2+
import { get, omit, isString } from 'lodash'
33
import { Router } from 'call'
44
import { IRoute } from './interfaces/IRoute'
55

@@ -16,6 +16,13 @@ export const Utils = {
1616
'PATCH'
1717
],
1818

19+
/**
20+
*
21+
*/
22+
stringToArray(strOrArray): string[] {
23+
return isString(strOrArray) ? [ strOrArray ] : strOrArray
24+
},
25+
1926
/**
2027
* Build a complete route, with bound handler and attached preconditions
2128
*/
@@ -37,13 +44,14 @@ export const Utils = {
3744
Utils.getHandlerFromString(app, orgRoute)
3845

3946
orgRoute.config.pre = orgRoute.config.pre
40-
.map(pre => Utils.getHandlerFromPrerequisite(app, pre))
47+
.map(pre => Utils.getPolicyFromPrerequisite(app, pre))
4148
.filter(handler => !!handler)
4249

43-
const orgRouteHandlers = Object.keys(orgRoute).filter(value => -1 !== Utils.methods.indexOf(value))
50+
const orgRouteHandlers = Object.keys(orgRoute)
51+
.filter(value => -1 !== Utils.methods.indexOf(value))
4452

4553
if (!orgRouteHandlers.some(v => Utils.methods.indexOf(v) >= 0 || !!orgRoute[v])) {
46-
app.log.error('spool-orgRouter: orgRoute ', path, ' handler [', orgRouteHandlers.join(', '), ']',
54+
app.log.error('spool-router: route ', path, ' handler [', orgRouteHandlers.join(', '), ']',
4755
'does not correspond to any defined Controller handler')
4856
return {}
4957
}
@@ -53,7 +61,8 @@ export const Utils = {
5361
orgRoute[method].config = orgRoute[method].config || orgRoute.config
5462
orgRoute[method].config.pre = orgRoute[method].config.pre || orgRoute.config.pre
5563
orgRoute[method].config.pre = orgRoute[method].config.pre
56-
.map(pre => Utils.getHandlerFromPrerequisite(app, pre))
64+
.map(pre => Utils.getPolicyFromPrerequisite(app, pre))
65+
// .map(pre => Utils.getPolicyFromPrerequisite(app, pre))
5766
.filter(handler => !!handler)
5867
}
5968
})
@@ -116,14 +125,41 @@ export const Utils = {
116125
return get(app.policies, handler)
117126
},
118127

128+
/**
129+
* Get a Controller's method's policies
130+
*/
131+
getControllerPolicy(app: FabrixApp, handler, routeMethod, pre = [ ]) {
132+
const controller = Utils.getControllerFromHandler(handler)
133+
134+
if (app.config.get('policies.*.*')) {
135+
pre = [...new Set([...pre, ...Utils.stringToArray(app.config.get('policies.*.*'))])]
136+
}
137+
if (app.config.get(`policies.*.${routeMethod}`)) {
138+
pre = [...new Set([...pre, ...Utils.stringToArray(app.config.get(`policies.*.${routeMethod}`))])]
139+
}
140+
if (handler && controller && app.config.get(`policies.${controller}.*.*`)) {
141+
pre = [...new Set([...pre, ...Utils.stringToArray(app.config.get(`policies.${controller}.*.*`))])]
142+
}
143+
if (handler && controller && app.config.get(`policies.${controller}.*.${routeMethod}`)) {
144+
pre = [...new Set([...pre, ...Utils.stringToArray(app.config.get(`policies.${controller}.*.${routeMethod}`))])]
145+
}
146+
if (handler && app.config.get(`policies.${handler}.${routeMethod}`)) {
147+
pre = [...new Set([...pre, ...Utils.stringToArray(app.config.get(`policies.${handler}.${routeMethod}`))])]
148+
}
149+
return pre
150+
},
151+
119152
/**
120153
* Get handler method from a "hapi/hapi-like" prerequisite object/string
121154
*/
122-
getHandlerFromPrerequisite (app: FabrixApp, pre) {
155+
getPolicyFromPrerequisite (app: FabrixApp, pre) {
123156
let handler
124157
if (pre && typeof pre === 'string') {
125158
handler = Utils.getPolicyFromString(app, pre)
126159
}
160+
else if (pre && Array.isArray(pre)) {
161+
handler = pre.map(p => Utils.getPolicyFromString(app, p)).filter(p => p)
162+
}
127163
else if (pre && typeof pre.method === 'string') {
128164
handler = Utils.getPolicyFromString(app, pre.method)
129165
}
@@ -144,6 +180,10 @@ export const Utils = {
144180
return get(app.controllers, handler)
145181
},
146182

183+
getControllerFromHandler(handler) {
184+
return isString(handler) ? handler.split('.')[0] : handler
185+
},
186+
147187
/**
148188
* Get handler method from a controller.method string path
149189
*/
@@ -158,15 +198,32 @@ export const Utils = {
158198

159199
Utils.methods.forEach(method => {
160200
if (route[method]) {
201+
route.config = route.config || { }
202+
route.config.pre = Utils.getControllerPolicy(app, null, method, route.config.pre)
203+
161204
if (typeof route[method] === 'string') {
162-
return route[method] = { handler: Utils.getControllerFromString(app, route[method]) }
205+
route.config.pre = Utils.getControllerPolicy(app, route[method], method, route.config.pre)
206+
return route[method] = {
207+
handler: Utils.getControllerFromString(app, route[method]),
208+
config: route.config
209+
}
163210
}
164211
else if (route[method] instanceof Object && route[method].hasOwnProperty('handler')) {
212+
route[method].config = route[method].config || route.config
213+
route[method].config.pre = route[method].config.pre || route.config.pre
214+
165215
if (typeof route[method].handler === 'string') {
166-
return route[method].handler = Utils.getControllerFromString(app, route[method].handler)
216+
route.config.pre = Utils.getControllerPolicy(app, route[method].handler, method, route.config.pre)
217+
return route[method] = {
218+
...route[method],
219+
handler: Utils.getControllerFromString(app, route[method].handler)
220+
}
167221
}
168222
else {
169-
return route[method].handler
223+
return route[method] = {
224+
...route[method],
225+
handler: route[method].handler
226+
}
170227
}
171228
}
172229
else {

lib/validator.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import * as joi from 'joi'
22
import { Utils } from './utils'
3+
4+
import { policySchema } from './schemas/policy'
35
import { routeSchema } from './schemas/route'
46
import { routerSchema } from './schemas/router'
57

8+
69
export const Validator = {
710

811
/**
@@ -19,6 +22,20 @@ export const Validator = {
1922
})
2023
})
2124
},
25+
/**
26+
* Validate the structure of an individual route
27+
*/
28+
validatePolicy (policy) {
29+
return new Promise((resolve, reject) => {
30+
joi.validate(policy, policySchema, (err, value) => {
31+
if (err) {
32+
return reject(err)
33+
}
34+
35+
return resolve(value)
36+
})
37+
})
38+
},
2239

2340
/**
2441
* Validate the structure of router

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@fabrix/spool-router",
3-
"version": "1.1.2",
3+
"version": "1.1.3",
44
"description": "Spool - Router for Fabrix",
55
"scripts": {
66
"build": "tsc -p ./lib/tsconfig.release.json",
@@ -49,7 +49,7 @@
4949
"lodash": "^4.17.10"
5050
},
5151
"devDependencies": {
52-
"@fabrix/fabrix": "^1.1.1",
52+
"@fabrix/fabrix": "^1.1.2",
5353
"@fabrix/lint": "^1.0.0-alpha.3",
5454
"@types/lodash": "^4.14.109",
5555
"@types/node": "~10.3.4",
@@ -64,7 +64,7 @@
6464
"typescript": "~2.8.1"
6565
},
6666
"peerDependencies": {
67-
"@fabrix/fabrix": "^1.1.1"
67+
"@fabrix/fabrix": "^1.1.2"
6868
},
6969
"license": "MIT",
7070
"bugs": {

test/fixtures/app.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ module.exports = {
2020
policies: {
2121
FooPolicy: class FooPolicy extends Policy {
2222
bar () { }
23+
},
24+
GlobalPolicy: class GlobalPolicy extends Policy {
25+
foo () { }
26+
},
27+
GetPolicy: class GetPolicy extends Policy {
28+
foo () { }
29+
},
30+
FooGetPolicy: class FooGetPolicy extends Policy {
31+
foo () { }
32+
},
33+
FooWildCardPolicy: class FooWildCardPolicy extends Policy {
34+
foo () { }
2335
}
2436
}
2537
},
@@ -37,6 +49,20 @@ module.exports = {
3749
router: {
3850
debug: true
3951
},
52+
policies: {
53+
'*': {
54+
'*': ['GlobalPolicy.foo'],
55+
'GET': ['GetPolicy.foo']
56+
},
57+
'TestController': {
58+
'*': {
59+
'*': ['FooWildCardPolicy.foo']
60+
},
61+
'foo': {
62+
'GET': ['FooGetPolicy.foo']
63+
}
64+
}
65+
},
4066
routes: {
4167
'/test/foo': {
4268
'GET': 'TestController.foo'

test/integration/lib/util.test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,44 @@ describe('lib.Util', () => {
7474
assert.equal(route.GET.config.pre[0], global.app.policies.FooPolicy.bar)
7575
})
7676
})
77+
describe('#policies', () => {
78+
it('should inherit the Global Policy on Every Method', () => {
79+
const { path, route} = lib.Utils.buildRoute(global.app, '/foo/bar', {
80+
'*': 'FooController.bar',
81+
config: {
82+
pre: [
83+
{
84+
method: 'FooPolicy.bar'
85+
}
86+
]
87+
}
88+
})
89+
assert.equal(route.GET.config.pre[0], global.app.policies.FooPolicy.bar)
90+
assert.equal(route.GET.config.pre[1], global.app.policies.GlobalPolicy.foo)
91+
assert.equal(route.HEAD.config.pre[0], global.app.policies.FooPolicy.bar)
92+
assert.equal(route.HEAD.config.pre[1], global.app.policies.GlobalPolicy.foo)
93+
assert.equal(route.POST.config.pre[0], global.app.policies.FooPolicy.bar)
94+
assert.equal(route.POST.config.pre[1], global.app.policies.GlobalPolicy.foo)
95+
assert.equal(route.PUT.config.pre[0], global.app.policies.FooPolicy.bar)
96+
assert.equal(route.PUT.config.pre[1], global.app.policies.GlobalPolicy.foo)
97+
})
7798

99+
it('should inherit the global policy and the global GET policy', () => {
100+
const { path, route} = lib.Utils.buildRoute(global.app, '/foo/bar', {
101+
'GET': 'FooController.bar'
102+
})
103+
assert.equal(route.GET.config.pre[0], global.app.policies.GlobalPolicy.foo)
104+
assert.equal(route.GET.config.pre[1], global.app.policies.GetPolicy.foo)
105+
})
106+
107+
it('should inherit the global policy and the global GET policy and the Controller Specific Get Policy', () => {
108+
const { path, route} = lib.Utils.buildRoute(global.app, '/foo/bar', {
109+
'GET': 'TestController.foo'
110+
})
111+
assert.equal(route.GET.config.pre[0], global.app.policies.GlobalPolicy.foo)
112+
assert.equal(route.GET.config.pre[1], global.app.policies.GetPolicy.foo)
113+
assert.equal(route.GET.config.pre[2], global.app.policies.FooWildCardPolicy.foo)
114+
assert.equal(route.GET.config.pre[3], global.app.policies.FooGetPolicy.foo)
115+
})
116+
})
78117
})

0 commit comments

Comments
 (0)