Skip to content

Instantly share code, notes, and snippets.

@amcdnl
Last active January 28, 2016 18:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amcdnl/318f7d91a2007656c7da to your computer and use it in GitHub Desktop.
Save amcdnl/318f7d91a2007656c7da to your computer and use it in GitHub Desktop.
import { Controller, httpGet, httpPost, httpPut, httpDelete } from '../common/routing';
import { authenticate } from '../common/auth';
import { Application } from '../models/Application';
@Controller('/app' , authenticate) // <= authentication middleware for passport
export default class ApplicationController {
appRepository: any;
@socket('message') <== web socket event
messaged(socket){
console.log('message recieved!')
}
@httpGet()
async getAll(ctx) {
ctx.body = await this.appRepository.findAll();
}
@httpPost(validate({ body: Application })) <!== validation middleware
async post(ctx) {
let app = new Application(ctx.request.body);
ctx.body = await this.appRepository.create(app);
}
}
/*
* Route decorators for KOA Router
* Stolen from: https://github.com/buunguyen/route-decorators
*
* Changes:
* - Added http prefix which fixed delete issue
* - Made it work with TypeScript
* - Added Socket Events
*/
const PREFIX = '$$route_';
const SOCKET_PREFIX = '$$sock_';
function destruct(args) {
const hasPath = typeof args[0] === 'string';
const path = hasPath ? args[0] : '';
const middleware = hasPath ? args.slice(1) : args;
if (middleware.some(m => typeof m !== 'function')) {
throw new Error('Middleware must be function')
}
return [path, middleware];
}
/**
* Route Decorator
* Signature: `@route(method, path: optional, ...middleware: optional)`
*
* @param {Function} method
* @param {Object} args
* @return {Function}
*/
export function route(method, path?, m1?, m2?, m3?, m4?, m5?) {
if (typeof method !== 'string') {
throw new Error('The first argument must be an HTTP method');
}
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
const destructed = destruct(args);
var path = destructed[0];
var middleware = destructed[1];
return function (target, name) {
target['' + PREFIX + name] = { method: method, path: path, middleware: middleware };
};
}
// @[method](...args) === @route(method, ...args)
const methods = ['head', 'options', 'get', 'post', 'put', 'patch', 'del', 'delete', 'all'];
methods.forEach(function (method) {
const methName = method.charAt(0).toUpperCase() + method.slice(1);
exports['http' + methName] = route.bind(null, method);
});
// hack to get ^^ to work w/ TS
export var httpGet;
export var httpPost;
export var httpPut;
export var httpPatch;
export var httpDelete;
/**
* Socket Decorator
* Signature: `@socket(event)`
*
* @param {String} event
* @return {Function}
*/
export function socket(event) {
if (typeof event !== 'string') {
throw new Error('The first argument must be an event name');
}
return function (target, name) {
target['' + SOCKET_PREFIX + name] = { event: event };
};
}
/**
* Controller Decorator
* Signature: `@controller(path: optional, ...middleware: optional)`
*
* @param {...[Object]} args
*/
export function Controller(path?, m1?, m2?, m3?, m4?, m5?) {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
const destructed = destruct(args);
const ctrlPath = destructed[0];
const ctrlMiddleware = destructed[1];
return function (target) {
target.$path = ctrlPath;
const proto = target.prototype;
proto.$routes = Object.getOwnPropertyNames(proto)
.filter(prop => prop.indexOf(PREFIX) === 0)
.map(prop => {
const method = proto[prop].method;
const path = proto[prop].path;
const actionMiddleware = proto[prop].middleware;
const url = '' + ctrlPath + path;
const middleware = ctrlMiddleware.concat(actionMiddleware);
const fnName = prop.substring(PREFIX.length);
return { method: method, url: url, middleware: middleware, fnName: fnName };
});
proto.$sockets = Object.getOwnPropertyNames(proto)
.filter(prop => prop.indexOf(SOCKET_PREFIX) === 0)
.map(prop => {
const event = proto[prop].event;
const fnName = prop.substring(PREFIX.length);
return { event, fnName};
});
};
}
import * as glob from 'glob';
import * as path from 'path';
/**
* The filenames of all controllers.
* @type {Array}
*/
export const files =
glob.sync(`dist/api/**/*[Controller].js`);
/**
* Static instances of the controllers.
* @type {Array}
*/
export const controllers =
files.map(f => {
return require(path.resolve(f));
});
/**
* Binds the router to the controller routes
* @param {Object} routerRoutes
* @return {Object} router routes
*/
export function bindRoutes(routerRoutes){
for(let ctrl of controllers){
let routes = ctrl.default.prototype.$routes;
for(const route of routes) {
let url = route.url,
method = route.method,
middleware = route.middleware,
fnName = route.fnName;
routerRoutes[method].apply(routerRoutes, [url].concat(Array.from(middleware), [function () {
// TS lacks object spread/rest
// https://github.com/Microsoft/TypeScript/issues/2103
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
let inst = new ctrl.default();
return inst[fnName](this, args);
}]));
}
}
return routerRoutes;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment