UNPKG

ravel

Version:

Ravel Rapid Application Development Framework

125 lines (113 loc) 6.06 kB
'use strict';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;