Last active
January 7, 2016 23:21
-
-
Save mctep/809069e182bbf0f5fbf1 to your computer and use it in GitHub Desktop.
Fortune Serializer Middleware Helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fortune from 'fortune'; | |
import fortuneJSONApi from 'fortune-json-api'; | |
import extend from './fortune-serializer-middleware'; | |
import Scope from './fortune-serializer-scope'; | |
import defineTypes from './define-types'; | |
const scope = new Scope(); | |
function api(Serializer) { | |
const ApiSerializer = fortuneJSONApi(Serializer); | |
return extend({ Serializer: ApiSerializer, scope }); | |
} | |
const store = fortune({ | |
serializers: [{ type: api }], | |
adapter: PostgresAdapter | |
}); | |
store.processRequest = scope.proccesRequest.bind(scope); | |
defineTypes(store); | |
export default store; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default function(store) { | |
store.defineType('user', {}); | |
store.processRequest('user', (context) => { | |
if (context.request.meta.role !== 'admin') { | |
throw new Error('Forbidden'); | |
} | |
return context; | |
}); | |
store.transformOutput('user', (context, record) => record); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Scope from './fortune-serializer-scope'; | |
export { Scope }; | |
export default function extend(options) { | |
const Serializer = options.Serializer; | |
const scope = options.scope; | |
validateScope(scope); | |
return class MiddlewaredSerializer extends Serializer { | |
processRequest(_context) { | |
const context = this.Promise.resolve( | |
super.processRequest(_context) | |
); | |
return context.then( | |
scope.handleMiddleware.bind(scope, this, 'processRequest') | |
); | |
} | |
}; | |
} | |
function validateScope(scope) { | |
if (scope instanceof Scope) { return; } | |
throw new TypeError('scope must be instanceof Scope'); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { get, set, contains, isEmpty } from 'lodash'; | |
export default class Scope { | |
constructor() { | |
this.middlewares = {}; | |
} | |
processRequest(_type, _handler) { | |
const { type, handler } = parseTypeAndHandlerParams(_type, _handler); | |
if (type) { | |
this.addTypeHandler('processRequest', type, handler); | |
} else { | |
this.addCommonHandler('processRequest', handler); | |
} | |
return this; | |
} | |
addCommonHandler(hook, handler) { | |
const key = `${hook}.common`; | |
return this.addMiddleware(key, handler); | |
} | |
addTypeHandler(hook, type, handler) { | |
const key = `${hook}.byType.${type}`; | |
return this.addMiddleware(key, handler); | |
} | |
addMiddleware(_key, handler) { | |
validateKey(_key); | |
const key = `middlewares.${_key}`; | |
let handlers = get(this, key); | |
if (!handlers) { | |
handlers = []; | |
set(this, key, handlers); | |
} | |
if (!contains(handlers, handler)) { | |
handlers.push(handler); | |
} | |
} | |
getMiddlewares(key) { | |
validateKey(key); | |
return get(this, `middlewares.${key}`); | |
} | |
handleMiddleware(serializer, hook, context) { | |
const commonHandlers = this.getMiddlewares(`${hook}.common`); | |
let result = serializer.Promise.resolve(context); | |
if (!isEmpty(commonHandlers)) { | |
commonHandlers.forEach((handler) => { | |
result = result.then(handler); | |
}); | |
} | |
return result.then( | |
this.handleTypeMiddlewares.bind(this, serializer, hook) | |
); | |
} | |
handleTypeMiddlewares(serializer, hook, context) { | |
const type = get(context, 'request.type'); | |
const typeHandlers = this.getMiddlewares(`${hook}.byType.${type}`); | |
let result = serializer.Promise.resolve(context); | |
if (!isEmpty(typeHandlers)) { | |
typeHandlers.forEach((handler) => { | |
result = result.then(handler); | |
}); | |
} | |
return result; | |
} | |
} | |
function parseTypeAndHandlerParams(_type, _handler) { | |
let type = _type; | |
let handler = _handler; | |
if (typeof type === 'function') { | |
handler = type; | |
type = null; | |
} | |
validateHandler(handler); | |
validateType(type); | |
return { type, handler }; | |
} | |
function validateHandler(handler) { | |
if (typeof handler !== 'function') { | |
throw new TypeError('handler must be a function'); | |
} | |
} | |
function validateType(type) { | |
if (type !== null && typeof type !== 'string') { | |
throw new TypeError(`Unexpected type value '${typeof type}'`); | |
} | |
} | |
function validateKey(key) { | |
if (typeof key !== 'string' || !key) { | |
throw new TypeError(`Unexpected key (${key}) middleware`); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fortune from 'fortune'; | |
import DefaultSerializer from 'fortune/lib/serializer/default'; | |
import extend from './fortune-serializer-middleware'; | |
import Scope from './fortune-serializer-scope'; | |
import defineTypes from './define-types'; | |
const scope = new Scope(); | |
const ScopedSerializer = extend({ Serializer: DefaultSerializer, scope }); | |
const id = ScopedSerializer.id = 'unit-api'; | |
const store = fortune({ | |
serializers: [{ | |
type: ScopedSerializer | |
}], | |
// use memory adapter for fast unit tests | |
}); | |
// redefine request method | |
// for using scoped serializer in tests | |
// without pass it id on each call | |
const { request } = store; | |
store.request = function(...args) { | |
args[0].serializerInput = args[0].serializerInput || id; | |
args[0].serializerOutput = args[0].serializerOutput || id; | |
return request.bind(store)(...args); | |
}; | |
store.processRequest = scope.proccesRequest.bind(scope); | |
defineTypes(store); | |
export default store; | |
// in tests | |
it('call request without admin role should throw error', () => { | |
return store.request({ | |
type: 'user', method: 'find', | |
payload: {}, meta: { role: 'user' } | |
}).should.rejected; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment