ravel
Version:
Ravel Rapid Application Development Framework
125 lines (113 loc) • 6.06 kB
JavaScript
;function _asyncToGenerator(fn) {return function () {var gen = fn.apply(this, arguments);return new Promise(function (resolve, reject) {function step(key, arg) {try {var info = gen[key](arg);var value = info.value;} catch (error) {reject(error);return;}if (info.done) {resolve(value);} else {return Promise.resolve(value).then(function (value) {step("next", value);}, function (err) {step("throw", err);});}}return step("next");});};}
const url = require('url');
const httpCodes = require('./http_codes');
/*!
* Simplifies HTTP response codes and headers. Exposed as $Rest.respond below.
*
* @param {Object} ravelInstance - A `ravel instance`.
* @param {Object} request - Koa.js request.
* @param {Object} response - Koa.js response.
* @param {Object} options - Any extra data, including:
* - {(number | undefined)} okCode Desired 'success' response code (20x) to send if
* there are no API errors. 200 OK by default for
* GET/PUT/DELETE and 201 CREATED by default for POST
* - {(number | undefined)} start
* - {(number | undefined)} end
* - {(number | undefined)} count
* @private
*/
const buildRestResponse = function (ravelInstance, request, response, options) {
options = options || {};
if (options.okCode === undefined) {
if (response.body && request.method.toUpperCase() === 'POST') {
options.okCode = httpCodes.CREATED;
} else if (response.body) {
options.okCode = httpCodes.OK;
} else if (response.status >= 200 && response.status < 300) {
// set no-content on this route if there's no body and no status is already set
options.okCode = httpCodes.NO_CONTENT;
} else {
options.okCode = response.status;
}
} else if (options.okCode === httpCodes.PARTIAL_CONTENT && response.body !== undefined &&
options !== undefined && options.start !== undefined &&
options.end !== undefined && options.count !== undefined) {
response.set('Content-Range', 'items ' + options.start + '-' + options.end + '/' + options.count);
}
if (options.okCode === httpCodes.CREATED &&
response.body && response.body.id && request.method.toUpperCase() === 'POST') {
// try to set location header if we're creating something
response.set('Location', url.resolve(
String(request.headers.origin), String(request.url)) + '/' + String(response.body.id));
}
response.status = options.okCode;
};
const sRavelInstance = Symbol.for('_ravelInstance');
/**
* Useful things related to REST, including the
* automatic translation of Ravel.ApplicationError
* errors into appropriate response codes
* @private
*/let
Rest = class Rest {
/**
* @param {Ravel} ravelInstance - A reference to a Ravel app instance.
* @private
*/
constructor(ravelInstance) {
this[sRavelInstance] = ravelInstance;
Object.assign(this, require('./http_codes'));
}
/**
* Syntactically simpler version of buildRestResponse
* which is exposed to clients for callback-building.
*
* @returns {AsyncFunction} Koa middleware which will yield to user logic, catch thrown errors,
* and respond with appropriate codes depending on the current verb or
* error. Status can be configured using ctx.respondOptions.okCode.
* @private
*/
respond() {var _this = this;
return (() => {var _ref = _asyncToGenerator(function* (ctx, next) {
// overwrite ctx.status to be an alias for respondOptions.okCode
Object.defineProperty(ctx, 'status', {
get: function () {return ctx.respondOptions ? ctx.respondOptions.okCode : undefined;},
set: function (newStatus) {
if (ctx.respondOptions === undefined) {
ctx.respondOptions = {};
}
ctx.respondOptions.okCode = newStatus;
} });
yield next();
buildRestResponse(_this[sRavelInstance], ctx.request, ctx.response, ctx.respondOptions);
});return function (_x, _x2) {return _ref.apply(this, arguments);};})();
}
/**
* Generic error-handling middleware. Catches exceptions and converts them
* into appropriate HTTP status codes.
* Automatically applied before all other middleware in Ravel.
*
* @private
*/
errorHandler() {var _this2 = this;
return (() => {var _ref2 = _asyncToGenerator(function* (ctx, next) {
try {
yield next();
} catch (err) {
// always overwrite body with error message
ctx.response.type = 'text/plain; charset=utf-8';
if (err instanceof _this2[sRavelInstance].ApplicationError.General) {
ctx.response.status = err.code;
ctx.response.body = err.message;
} else {
_this2[sRavelInstance].log.trace(err.stack);
ctx.response.status = httpCodes.INTERNAL_SERVER_ERROR;
ctx.response.body = err.stack;
}
}
});return function (_x3, _x4) {return _ref2.apply(this, arguments);};})();
}};
/*!
* Export `Rest`
*/
module.exports = Rest;