Skip to content

Instantly share code, notes, and snippets.

@mctep
Last active January 7, 2016 23:21
Show Gist options
  • Save mctep/809069e182bbf0f5fbf1 to your computer and use it in GitHub Desktop.
Save mctep/809069e182bbf0f5fbf1 to your computer and use it in GitHub Desktop.
Fortune Serializer Middleware Helper
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;
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);
}
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');
}
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`);
}
}
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