@themost/web
Version:
MOST Web Framework 2.0 - Web Server Module
412 lines (399 loc) • 16 kB
JavaScript
/**
* @license
* MOST Web Framework 2.0 Codename Blueshift
* Copyright (c) 2017, THEMOST LP All rights reserved
*
* Use of this source code is governed by an BSD-3-Clause license that can be
* found in the LICENSE file at https://themost.io/license
*/
///
var _ = require('lodash');
var Q = require('q');
var TraceUtils = require('@themost/common/utils').TraceUtils;
var LangUtils = require('@themost/common/utils').LangUtils;
var HttpBadRequestError = require('@themost/common/errors').HttpBadRequestError;
var HttpUnauthorizedError = require('@themost/common/errors').HttpUnauthorizedError;
var HttpConsumer = require('./consumers').HttpConsumer;
var DataTypeValidator = require('@themost/data/data-validator').DataTypeValidator;
var MinLengthValidator = require('@themost/data/data-validator').MinLengthValidator;
var MaxLengthValidator = require('@themost/data/data-validator').MaxLengthValidator;
var MinValueValidator = require('@themost/data/data-validator').MinValueValidator;
var MaxValueValidator = require('@themost/data/data-validator').MaxValueValidator;
var RequiredValidator = require('@themost/data/data-validator').RequiredValidator;
var PatternValidator = require('@themost/data/data-validator').PatternValidator;
/**
* @module @themost/web/decorators
*/
/**
* @class
* @constructor
* @extends Error
* @augments Error
*/
function DecoratorError() {
DecoratorError.super_.call(this, 'Decorator is not valid on this declaration type.');
}
LangUtils.inherits(DecoratorError, Error);
/**
* @returns {Function}
*/
function httpController() {
return function (target, key, descriptor) {
if (typeof target === 'function') {
target.httpController = true;
}
return descriptor;
}
}
function httpGet() {
return function (target, key, descriptor) {
if (typeof descriptor.value === 'function') {
descriptor.value.httpGet = true;
}
return descriptor;
}
}
/**
* @returns {Function}
*/
function httpAny() {
return function (target, key, descriptor) {
if (typeof descriptor.value === 'function') {
descriptor.value.httpGet = true;
descriptor.value.httpPost = true;
descriptor.value.httpPut = true;
descriptor.value.httpDelete = true;
descriptor.value.httpOptions = true;
descriptor.value.httpHead = true;
}
return descriptor;
}
}
/**
* @returns {Function}
*/
function httpPost() {
return function (target, key, descriptor) {
if (typeof descriptor.value === 'function') {
descriptor.value.httpPost = true;
}
return descriptor;
}
}
/**
* @returns {Function}
*/
function httpPatch() {
return function (target, key, descriptor) {
if (typeof descriptor.value === 'function') {
descriptor.value.httpPatch = true;
}
return descriptor;
}
}
/**
* @returns {Function}
*/
function httpPut() {
return function (target, key, descriptor) {
if (typeof descriptor.value === 'function') {
descriptor.value.httpPut = true;
}
return descriptor;
}
}
/**
* @returns {Function}
*/
function httpDelete() {
return function (target, key, descriptor) {
if (typeof descriptor.value === 'function') {
descriptor.value.httpDelete = true;
}
return descriptor;
}
}
/**
* @returns {Function}
*/
function httpOptions() {
return function (target, key, descriptor) {
if (typeof descriptor.value === 'function') {
descriptor.value.httpOptions = true;
}
return descriptor;
}
}
/**
* @returns {Function}
*/
function httpHead() {
return function (target, key, descriptor) {
if (typeof descriptor.value === 'function') {
descriptor.value.httpHead = true;
}
return descriptor;
}
}
/**
* @returns {Function}
*/
function httpAction(name) {
if (typeof name !== 'string') {
throw new TypeError('Action name must be a string');
}
return function (target, key, descriptor) {
if (typeof descriptor.value !== 'function') {
throw new Error('Decorator is not valid on this declaration type.');
}
descriptor.value.httpAction = name;
return descriptor;
}
}
/**
*
* @param {string} name
* @param {string} alias
* @returns {Function}
*/
function httpParamAlias(name, alias) {
if (typeof name !== 'string') {
throw new TypeError('Parameter name must be a string');
}
if (typeof alias !== 'string') {
throw new TypeError('Parameter alias must be a string');
}
return function (target, key, descriptor) {
if (typeof descriptor.value !== 'function') {
throw new Error('Decorator is not valid on this declaration type.');
}
descriptor.value.httpParamAlias = descriptor.value.httpParamAlias || { };
descriptor.value.httpParamAlias[name] = alias;
return descriptor;
}
}
/**
* @class
* @abstract
* @property {string} name
* @property {string} type
* @property {RegExp|string} pattern
* @property {date|number|*} minValue
* @property {date|number|*} maxValue
* @property {number} minLength
* @property {number} maxLength
* @property {boolean} required
* @property {string} message
* @constructor
*/
// eslint-disable-next-line no-unused-vars
function HttpParamAttributeOptions() {
}
/**
* @param {HttpParamAttributeOptions|*=} options
* @returns {Function}
*/
function httpParam(options) {
if (typeof options !== 'object') { throw new TypeError('Parameter options must be an object'); }
if (typeof options.name !== 'string') { throw new TypeError('Parameter name must be a string'); }
return function (target, key, descriptor) {
if (typeof descriptor.value !== 'function') {
throw new Error('Decorator is not valid on this declaration type.');
}
descriptor.value.httpParams = descriptor.value.httpParams || { };
descriptor.value.httpParams[options.name] = _.extend({"type":"Text"}, options);
if (typeof descriptor.value.httpParam === 'undefined') {
descriptor.value.httpParam = new HttpConsumer(function (context) {
var httpParamValidationFailedCallback = function httpParamValidationFailedCallback(context, httpParam, validationResult) {
TraceUtils.log(_.assign(validationResult, {
"param":httpParam,
"request": {
"url":context.request.url,
"method":context.request.method
}
}));
return Q.reject(new HttpBadRequestError('Bad request parameter', httpParam.message || validationResult.message));
};
var methodParams = LangUtils.getFunctionParams(descriptor.value);
var httpParams = descriptor.value.httpParams;
if (methodParams.length>0) {
var k = 0, httpParam, validator, validationResult, functionParam, contextParam;
while (k < methodParams.length) {
functionParam = methodParams[k];
if (typeof context.getParam === 'function') {
contextParam = context.getParam(functionParam);
}
else {
contextParam = context.params[functionParam];
}
if (_.isObject(httpParams)) {
httpParam = httpParams[functionParam];
if (_.isObject(httpParam)) {
if (typeof httpParam.type === 'string') {
//--validate type
validator = new DataTypeValidator(httpParam.type);
validator.setContext(context);
validationResult = validator.validateSync(contextParam);
if (validationResult) {
return httpParamValidationFailedCallback(context, httpParam, validationResult);
}
}
if (httpParam.pattern instanceof RegExp) {
//--validate pattern
validator = new PatternValidator(httpParam.pattern);
validator.setContext(context);
validationResult = validator.validateSync(contextParam);
if (validationResult) {
return httpParamValidationFailedCallback(context, httpParam, validationResult);
}
}
if (typeof httpParam.minLength === 'number') {
//--validate min length
validator = new MinLengthValidator(httpParam.minLength);
validator.setContext(context);
validationResult = validator.validateSync(contextParam);
if (validationResult) {
return httpParamValidationFailedCallback(context, httpParam, validationResult);
}
}
if (typeof httpParam.maxLength === 'number') {
//--validate max length
validator = new MaxLengthValidator(httpParam.maxLength);
validator.setContext(context);
validationResult = validator.validateSync(contextParam);
if (validationResult) {
return httpParamValidationFailedCallback(context, httpParam, validationResult);
}
}
if (typeof httpParam.minValue !== 'undefined') {
//--validate min value
validator = new MinValueValidator(httpParam.minValue);
validator.setContext(context);
validationResult = validator.validateSync(contextParam);
if (validationResult) {
return httpParamValidationFailedCallback(context, httpParam, validationResult);
}
}
if (typeof httpParam.maxValue !== 'undefined') {
//--validate max value
validator = new MaxValueValidator(httpParam.required);
validator.setContext(context);
validationResult = validator.validateSync(contextParam);
if (validationResult) {
return httpParamValidationFailedCallback(context, httpParam, validationResult);
}
}
if ((typeof httpParam.required !== 'undefined') && (httpParam.required === true)) {
//--validate required value
validator = new RequiredValidator();
validator.setContext(context);
validationResult = validator.validateSync(contextParam);
if (validationResult) {
return httpParamValidationFailedCallback(context, httpParam, validationResult);
}
}
}
}
k += 1;
}
}
return Q();
});
}
return descriptor;
}
}
/**
* @param {boolean=} value
* @returns {Function}
*/
function httpAuthorize(value) {
return function (target, key, descriptor) {
if (typeof descriptor.value !== 'function') {
throw new Error('Decorator is not valid on this declaration type.');
}
var authorize = true;
if (typeof value === 'boolean') {
authorize = value;
}
if (authorize) {
descriptor.value.authorize = new HttpConsumer(function (context) {
if (context.user && context.user.name !== 'anonymous') {
return Q();
}
return Q.reject(new HttpUnauthorizedError());
});
}
return descriptor;
};
}
/**
*
* @param {Object|Function} proto - The constructor function of a class or the prototype of a class
* @param {string} key - The name of the property or method where the decorator will be included
* @param {Function} decorator - The decorator to be included
*/
function defineDecorator(proto, key, decorator) {
if ((typeof proto !== 'object') && (typeof proto !== 'function')) {
throw new DecoratorError('Invalid prototype. Expected object or function.');
}
if (typeof key !== 'string') {
throw new DecoratorError('Invalid property name. Expected string.');
}
if (typeof decorator !== 'function') {
throw new DecoratorError('Invalid decorator. Expected function.');
}
decorator(proto, key, Object.getOwnPropertyDescriptor(proto, key));
}
/**
* @param {string} name
* @param {Function|HttpConsumer} consumer
* @returns {Function}
*/
function httpActionConsumer(name, consumer) {
return function (target, key, descriptor) {
if (typeof descriptor.value !== 'function') {
throw new Error('Decorator is not valid on this declaration type.');
}
if (consumer instanceof HttpConsumer) {
//set consumer
descriptor.value[name] = consumer;
//and exit
return descriptor;
}
//validate consumer function
if (typeof consumer !== 'function') {
throw new Error('Consumer may be a function.');
}
descriptor.value[name] = new HttpConsumer(consumer);
return descriptor;
};
}
//extend object
if (typeof Object.defineDecorator === 'undefined') {
/**
* @function defineDecorator
* @param {Object|Function} proto - The constructor function of a class or the prototype of a class
* @param {string} key - The name of the property or method where the decorator will be included
* @param {Function} decorator - The decorator to be included
* @memberOf Object
* @static
*/
Object.defineDecorator = defineDecorator;
}
module.exports.DecoratorError = DecoratorError;
module.exports.httpGet = httpGet;
module.exports.httpAny = httpAny;
module.exports.httpPost = httpPost;
module.exports.httpPut = httpPut;
module.exports.httpPatch = httpPatch;
module.exports.httpDelete = httpDelete;
module.exports.httpOptions = httpOptions;
module.exports.httpHead = httpHead;
module.exports.httpAction = httpAction;
module.exports.httpController = httpController;
module.exports.httpParamAlias = httpParamAlias;
module.exports.httpParam = httpParam;
module.exports.httpAuthorize = httpAuthorize;
module.exports.defineDecorator = defineDecorator;
module.exports.httpActionConsumer = httpActionConsumer;