UNPKG

@openapi-mock/swagger-node-runner

Version:
188 lines (153 loc) 5.73 kB
'use strict'; module.exports = init; var debug = require('debug')('swagger'); var debugContent = require('debug')('swagger:content'); var _ = require('lodash'); var util = require('util'); var EventEmitter = require('events').EventEmitter; function init(runner) { return new Middleware(runner); } function Middleware(runner) { this.runner = runner; this.middleware = function middleware() { return function middleware(req, res, next) { // flow back to connect pipe var operation = runner.getOperation(req); if (!operation) { var path = runner.getPath(req); if (!path) { return next(); } if (!path['x-swagger-pipe'] && req.method !== 'OPTIONS') { var msg = util.format('Path [%s] defined in Swagger, but %s operation is not.', path.path, req.method); var err = new Error(msg); err.statusCode = 405; err.status = err.statusCode; // for Sails, see: https://github.com/theganyo/swagger-node-runner/pull/31 var allowedMethods = _.map(path.operationObjects, function(operation) { return operation.method.toUpperCase(); }); err.allowedMethods = allowedMethods; res.setHeader('Allow', allowedMethods.sort().join(', ')); return next(err); } } runner.applyMetadata(req, operation, function(err) { if (err) { /* istanbul ignore next */ return next(err); } var pipe = runner.getPipe(req); if (!pipe) { var err = new Error('No implementation found for this path.'); err.statusCode = 405; return next(err); } var context = { // system values _errorHandler: runner.defaultErrorHandler(), request: req, response: res, // user-modifiable values input: undefined, statusCode: undefined, headers: {}, output: undefined }; context._finish = function finishConnect(ignore1, ignore2) { // must have arity of 2 debugContent("exec", context.error); if (context.error) { return next(context.error); } try { var response = context.response; if (context.statusCode) { debug('setting response statusCode: %d', context.statusCode); response.statusCode = context.statusCode; } if (context.headers) { debugContent('setting response headers: %j', context.headers); _.each(context.headers, function(value, name) { response.setHeader(name, value); }); } if (undefined === context.output) { return next(); } var contentType = response.getHeader('content-type'); if (!contentType) { contentType = request.headers.accept; if ("*/*" == contentType) contentType = operation.produces[0]; if (contentType) response.setHeader("content-type", contentType); } var body = translate(context.output, contentType); debugContent('sending response body: %s', body); response.end(body); } catch (err) { /* istanbul ignore next */ next(err); } }; /* istanbul ignore next */ var listenerCount = (runner.listenerCount) ? runner.listenerCount('responseValidationError') : // Node >= 4.0 EventEmitter.listenerCount(runner, 'responseValidationError'); // Node < 4.0 if (listenerCount) { hookResponseForValidation(context, runner); } runner.bagpipes.play(pipe, context); }); }; }; this.register = function register(app) { app.use(this.middleware()); }; } function translate(output, mimeType) { if (typeof output !== 'object') { return output; } switch(true) { case /json/.test(mimeType): return JSON.stringify(output); default: return util.inspect(output) } } function hookResponseForValidation(context, eventEmitter) { debug('add response validation hook'); var res = context.response; var end = res.end; var write = res.write; var written; res.write = function hookWrite(chunk, encoding, callback) { if (written) { written = ''; res.write = write; res.end = end; debug('multiple writes, will not validate response'); } else { written = chunk; } write.apply(res, arguments); }; res.end = function hookEnd(data, encoding, callback) { res.write = write; res.end = end; if (written && data) { debug('multiple writes, will not validate response'); } else if (!context.request.swagger.operation) { debug('not a swagger operation, will not validate response'); } else { debug('validating response'); try { var headers = res._headers || res.headers || {}; var body = data || written; debugContent('response body type: %s value: %s', typeof body, body); var validateResult = context.request.swagger.operation.validateResponse({ statusCode: res.statusCode, headers: headers, body: body }); debug('validation result:', validateResult); if (validateResult.errors.length || validateResult.warnings.length) { debug('emitting responseValidationError'); eventEmitter.emit('responseValidationError', validateResult, context.request, res); } } catch (err) { /* istanbul ignore next */ console.error(err.stack); } } end.apply(res, arguments); }; }