Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mockModeNoCallback option and controller cache #51

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
92 changes: 65 additions & 27 deletions fittings/swagger_router.js
Expand Up @@ -5,6 +5,7 @@ var path = require('path');
var assert = require('assert');
var SWAGGER_ROUTER_CONTROLLER = 'x-swagger-router-controller';
var util = require('util');
var translateResponse = require('../lib/connect_middleware').translateResponse;

module.exports = function create(fittingDef, bagpipes) {

Expand All @@ -18,16 +19,18 @@ module.exports = function create(fittingDef, bagpipes) {

var mockMode = !!fittingDef.mockMode || !!swaggerNodeRunner.config.swagger.mockMode;

var ignoreMissingHandlers = !!fittingDef.ignoreMissingHandlers;

var controllersDirs = mockMode ? fittingDef.mockControllersDirs : fittingDef.controllersDirs;

controllersDirs = controllersDirs.map(function(dir) {
controllersDirs = controllersDirs.map(function (dir) {
return path.resolve(appRoot, dir);
});

var controllerFunctionsCache = {};

return function swagger_router(context, cb) {
debug('exec');
debug('exec %s', context.request.swagger.operation.ptr || context.request.swagger.operation.pathObject.ptr);

var operation = context.request.swagger.operation;
var controllerName = operation[SWAGGER_ROUTER_CONTROLLER] || operation.pathObject[SWAGGER_ROUTER_CONTROLLER];
Expand All @@ -47,56 +50,91 @@ module.exports = function create(fittingDef, bagpipes) {
try {
controller = require(controllerPath);
controllerFunctionsCache[controllerName] = controller;
debug('controller found', controllerPath);
debug('controller found', path.relative(appRoot, controllerPath));
break;
} catch (err) {
if (!mockMode && i === controllersDirs.length - 1) {
debug('controller not in', path.relative(appRoot, controllerPath));
if (!mockMode && !ignoreMissingHandlers && i === controllersDirs.length - 1) {
return cb(err);
}
debug('controller not in', controllerPath);
}
}
}

if (!controller && (mockMode || ignoreMissingHandlers)) {
controller = controllerFunctionsCache[controllerName] = {};
debug('created mock controller %s', controllerName);
}

if (controller) {

var operationId = operation.definition['operationId'] || context.request.method.toLowerCase();
var controllerFunction = controller[operationId];

if (!controllerFunction && mockMode) {
controllerFunction = controller[operationId] = createMockControllerFunction(fittingDef, bagpipes);
debug('created mock controller function %s.%s', controllerName, operationId);
} else if (!controllerFunction && ignoreMissingHandlers) {
controllerFunction = controller[operationId] = function (req, res, cb) {
debug('%s.%s is missing but ignoreMissingHandlers is true', controllerName, operationId);
cb();
};
}

if (controllerFunction && typeof controllerFunction === 'function') {
debug('running controller');
debug('running controller %s.%s', controllerName, operationId);
return controllerFunction(context.request, context.response, cb);
}

var msg = util.format('Controller %s doesn\'t export handler function %s', controllerName, operationId);
if (mockMode) {
debug(msg);
} else {
return cb(new Error(msg));
}
debug(util.format('Controller %s doesn\'t export handler function %s', controllerName, operationId));
return cb(new Error(msg));
}

if (mockMode) {
// for completeness, we should never actually get here
cb(new Error(util.format('No controller found for %s in %j', controllerName, controllersDirs)));
}
};

function createMockControllerFunction(fittingDef, bagpipes) {
var swaggerNodeRunner = bagpipes.config.swaggerNodeRunner;
var appRoot = swaggerNodeRunner.config.swagger.appRoot;

var statusCode = parseInt(context.request.get('_mockreturnstatus')) || 200;
var mockModeNoCallback = !!fittingDef.mockModeNoCallback || !!swaggerNodeRunner.config.swagger.mockModeNoCallback;

var mimetype = context.request.get('accept') || 'application/json';
var mock = operation.getResponseExample(statusCode, mimetype);
return function default_mock_controller(request, response, cb) {
debug('exec default_mock_controller')

if (mock) {
debug('returning mock example value', mock);
var operation = request.swagger.operation;
var controllerName = operation[SWAGGER_ROUTER_CONTROLLER] || operation.pathObject[SWAGGER_ROUTER_CONTROLLER];

var statusCode = parseInt(request.get('_mockreturnstatus')) || 200;

var mimetype = request.get('accept');
if (!mimetype || mimetype === '*/*') {
if (!operation.produces || !operation.produces.length || operation.produces.indexOf('application/json') >= 0) {
mimetype = 'application/json';
} else {
mock = operation.getResponseSample(statusCode);
debug('returning mock sample value', mock);
mimetype = operation.produces[0];
}
}

context.headers['Content-Type'] = mimetype;
context.statusCode = statusCode;
var mock = operation.getResponseExample(statusCode, mimetype);

return cb(null, mock);
if (mock) {
debug('returning mock example value', mock);
} else {
mock = operation.getResponseSample(statusCode);
debug('returning mock sample value', mock);
}

// for completeness, we should never actually get here
cb(new Error(util.format('No controller found for %s in %j', controllerName, controllersDirs)));
}
};
response.setHeader('Content-Type', mimetype);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can someone please confirm if it's ok to use response.setHeader instead of context.headers[]?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The best confirmation you'll get is a test that runs this code


response.statusCode = statusCode;

if (mockModeNoCallback) {
response.end(translateResponse(mock, mimetype));
} else {
cb(null, mock);
}
};
}
1 change: 1 addition & 0 deletions lib/connect_middleware.js
Expand Up @@ -130,6 +130,7 @@ function translate(output, mimeType) {
return util.inspect(output)
}
}
module.exports.translateResponse = translate;

function hookResponseForValidation(context, eventEmitter) {

Expand Down