@nodeswork/sbase
Version:
Basic REST api foundation from Nodeswork.
268 lines (266 loc) • 8.17 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
const _ = require("underscore");
const declarations_1 = require("./declarations");
const overrides_1 = require("./overrides");
const params_1 = require("./params");
const utils_1 = require("./utils");
const koa_config_1 = require("./koa-config");
function Config(options) {
return (cls) => {
const meta = Reflect.getMetadata(declarations_1.METADATA_KEY, cls) || {
middlewares: [],
handlers: {},
routerOptions: {},
};
_.extend(meta.routerOptions, options);
Reflect.defineMetadata(declarations_1.METADATA_KEY, meta, cls);
};
}
exports.Config = Config;
function Handler(options = {}) {
return (target, propertyKey) => {
const cls = target.constructor;
const meta = Reflect.getMetadata(declarations_1.METADATA_KEY, cls) || {
middlewares: [],
handlers: {},
routerOptions: {},
};
if (meta.handlers[propertyKey] == null) {
meta.handlers[propertyKey] = {
method: declarations_1.Method.GET,
path: '/',
middleware: null,
};
}
const m = meta.handlers[propertyKey];
if (options.middleware) {
const middleware = _.isArray(options.middleware)
? utils_1.compose(options.middleware)
: options.middleware;
m.middleware =
m.middleware == null ? middleware : utils_1.compose([middleware, m.middleware]);
}
if (options.method) {
m.method = options.method;
}
if (options.path) {
m.path = options.path;
}
if (options.name) {
m.name = options.name;
}
if (m.path == null) {
m.path = '/';
}
Reflect.defineMetadata(declarations_1.METADATA_KEY, meta, cls);
};
}
exports.Handler = Handler;
function Get(path, options = {}) {
return Handler(_.extend({}, options, {
path,
method: declarations_1.Method.GET,
}));
}
exports.Get = Get;
function Post(path, options = {}) {
return Handler(_.extend({}, options, {
path,
method: declarations_1.Method.POST,
}));
}
exports.Post = Post;
function Put(path, options = {}) {
return Handler(_.extend({}, options, {
path,
method: declarations_1.Method.PUT,
}));
}
exports.Put = Put;
function Delete(path, options = {}) {
return Handler(_.extend({}, options, {
path,
method: declarations_1.Method.DELETE,
}));
}
exports.Delete = Delete;
function Middleware(middlewares) {
middlewares = _.chain([middlewares])
.flatten()
.map(koa_config_1.sbaseKoaConfig.middlewareMapper)
.value();
return (target, propertyKey, descriptor) => {
if (propertyKey == null) {
buildConstructorMiddleware(target, middlewares);
}
else {
buildPropertyMiddleware(target, propertyKey, middlewares, descriptor);
}
};
}
exports.Middleware = Middleware;
/**
* A IF middleware that helps a branch checking. Usage:
*
* @If(predictor, ifClause)
* - execute ifClause when predictor returns true.
* - execute next middleware when predictor returns false.
*
* @If(predictor, ifClause, elseClause)
* - execute ifClause when predictor returns true.
* - execute elseClause when predictor returns false.
*
* @param predictor - Predict which clause to execute.
* @param ifClause - Execute when predictor returns true.
* @param elseClause - Execute when predictor returns false. By default, execute
* else clause.
*/
function If(predictor, ifClause, elseClause) {
return Middleware(async (ctx, next) => {
const value = predictor(ctx);
const boolValue = utils_1.isPromise(value) ? await value : value;
if (boolValue) {
await ifClause(ctx, next);
}
else if (elseClause) {
await elseClause(ctx, next);
}
else {
await next();
}
});
}
exports.If = If;
/**
* A WHEN middleware that helps a branch checking, next middleware will always
* be executed regardless what predictor returns.
*
* @param predictor - Predict when the when clause will be executed.
* @param whenClause - Execute when predictor returns true.
*/
function When(predictor, whenClause) {
return Middleware(async (ctx, next) => {
const value = predictor(ctx);
const boolValue = utils_1.isPromise(value) ? await value : value;
if (boolValue && whenClause) {
await whenClause(ctx, () => Promise.resolve({}));
}
await next();
});
}
exports.When = When;
/**
* A CHECK middleware that helps determines if to execute next middleware.
* Usage:
*
* @Check((ctx: Router.IRouterContext) => ctx.isAuthenticated())
*
* @param predictor - Returns true to execute next middleware.
*/
function Check(predictor) {
return Middleware(async (ctx, next) => {
const value = predictor(ctx);
const boolValue = utils_1.isPromise(value) ? await value : value;
if (boolValue) {
await next();
}
});
}
exports.Check = Check;
/**
* A Tee middleware that helps execute a side function.
* Usage:
*
* @Tee((ctx: Router.IRouterContext) => console.log(ctx.path))
*
* @param fn - The side function to execute.
* @param opts.post - specifies run tee after returned.
*/
function Tee(fn, opts = {}) {
async function process(ctx) {
const value = fn(ctx, () => { });
if (utils_1.isPromise(value)) {
await value;
}
}
return Middleware(async (ctx, next) => {
if (!opts.post) {
await process(ctx);
}
await next();
if (opts.post) {
await process(ctx);
}
});
}
exports.Tee = Tee;
function buildConstructorMiddleware(cls, middlewares) {
const meta = _.clone(Reflect.getMetadata(declarations_1.METADATA_KEY, cls) || {
middlewares: [],
handlers: {},
routerOptions: {},
});
meta.middlewares = _.union(middlewares, meta.middlewares);
Reflect.defineMetadata(declarations_1.METADATA_KEY, meta, cls);
}
function buildPropertyMiddleware(target, propertyKey, middlewares, descriptor) {
const cls = target.constructor;
const meta = _.clone(Reflect.getMetadata(declarations_1.METADATA_KEY, cls) || {
middlewares: [],
handlers: {},
routerOptions: {},
});
if (meta.handlers[propertyKey] == null) {
meta.handlers[propertyKey] = {
method: declarations_1.Method.GET,
path: null,
middleware: null,
};
}
if (descriptor != null) {
descriptor.value = utils_1.compose(middlewares.concat(descriptor.value));
}
else {
const m = meta.handlers[propertyKey];
if (m.middleware == null) {
m.middleware = utils_1.compose(middlewares);
}
else {
m.middleware = utils_1.compose(middlewares.concat(m.middleware));
}
}
Reflect.defineMetadata(declarations_1.METADATA_KEY, meta, cls);
}
/**
* Generate an overrides middleware to help build mongoose queries.
*
* 1. Fetch query params to override query:
* @Overrides('request.query.status->query.status')
* @Overrides('request.body.status->query.status')
*
* 2. Set object to override query:
* @Overrides(['constValue', 'query.status'])
*
* 3. Extract object from ctx:
* @Overrides([(ctx) =>
* moment(ctx.request.query.date).startOf('month').toDate(),
* 'query.date',
* ])
*/
exports.Overrides = (...rules) => {
return Middleware(overrides_1.overrides(...rules));
};
/**
* Clear existing overrides.
*
* @ClearOverrides()
*/
exports.ClearOverrides = () => {
return Middleware(overrides_1.clearOverrides());
};
exports.Params = (options) => {
return Middleware(params_1.params(options));
};
//# sourceMappingURL=decorators.js.map