Skip to content
This repository has been archived by the owner on Aug 4, 2023. It is now read-only.

fixes stream validation #464

Open
wants to merge 6 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions middleware/swagger-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ var debug = require('debug')('swagger-tools:middleware:validator');
var mHelpers = require('./helpers');
var validators = require('../lib/validators');

var checkIsFile = function (schema, version) {
var isFile = false;
if (version === '1.2') {
var type = (schema.items ? schema.items.type || schema.items.$ref : schema.type);
isFile = type === 'file';
}
else {
if (!_.isUndefined(schema.schema)) {
isFile = schema.schema.type === 'file';
}
}
return isFile;
};

var sendData = function (swaggerVersion, res, data, encoding, skipped) {
// 'res.end' requires a Buffer or String so if it's not one, create a String
if (!(data instanceof Buffer) && !_.isString(data)) {
Expand Down Expand Up @@ -203,6 +217,11 @@ var wrapEnd = function (req, res, next) {
val = data;
}
}
else if(writtenData.length === 0) {
// Some code expects val to be undefined and not an empty string
// when no data is passed to 'end' or written with 'write'.
val = undefined;
}
else if(writtenData.length !== 0) {
val = Buffer.concat(writtenData);
}
Expand All @@ -216,9 +235,6 @@ var wrapEnd = function (req, res, next) {
debug(' Response validation:');

// If the data is a buffer, convert it to a string so we can parse it prior to validation
if (val instanceof Buffer) {
val = val.toString(encoding);
}

// Express removes the Content-Type header from 204/304 responses which makes response validation impossible
if (_.isUndefined(res.getHeader('content-type')) && [204, 304].indexOf(res.statusCode) > -1) {
Expand Down Expand Up @@ -281,6 +297,11 @@ var wrapEnd = function (req, res, next) {
if (_.isUndefined(schema)) {
sendData(swaggerVersion, res, val, encoding, true);
} else {
// If the data is a buffer, convert it to a string so we can parse it prior to validation
var isFile = checkIsFile(schema, req.swagger.apiDeclaration ? '1.2' : '2.0');
if (val instanceof Buffer && !isFile) {
val = val.toString(encoding);
}
validateValue(req, schema, vPath, val, 'body', function (err) {
if (err) {
throw err;
Expand Down
89 changes: 89 additions & 0 deletions test/2.0/test-middleware-swagger-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ var async = require('async');
var helpers = require('../helpers');
var request = require('supertest');
var stream = require('stream');
var fs = require('fs');
var crypto = require('crypto');

var petStoreJson = _.cloneDeep(require('../../samples/2.0/petstore.json'));

Expand Down Expand Up @@ -1135,7 +1137,45 @@ describe('Swagger Validator Middleware v2.0', function () {
});
});

it('should not return an error with a null schema on an empty response', function (done) {
var cPetStoreJson = _.cloneDeep(petStoreJson);

cPetStoreJson.paths['/redirect'] = {
'get': {
'x-swagger-router-controller': 'Test',
'operationId': 'redirectTest',
'responses': {
'302': {
'description': 'Redirect'
}
}
}
};

helpers.createServer([cPetStoreJson], {
swaggerRouterOptions: {
controllers: {
'Test_redirectTest': function (req, res) {
res.statusCode = 302;
res.end();
}
}
},
swaggerValidatorOptions: {
validateResponse: true
}
}, function (app) {
request(app)
.get('/api/redirect')
.expect(302)
.end(function (err, res) {
if (err) {
throw err + '\n' + res.error.text;
}
done();
});
});
});

it('should validate a valid piped response', function (done) {
var cPetStoreJson = _.cloneDeep(petStoreJson);
Expand Down Expand Up @@ -1575,6 +1615,55 @@ describe('Swagger Validator Middleware v2.0', function () {
});
});

it('should validate file and return it (issue #463)', function (done) {
var cPetStore = _.cloneDeep(petStoreJson);

cPetStore.paths['/image'] = {
get: {
summary: 'Get image',
description: 'Retrieves image.',
operationId: 'getImage',
produces: ['image/png'],
responses: {
'200': {
description: 'OK',
schema: {
type: 'file'
}
}
}
}
};

helpers.createServer([cPetStore], {
swaggerRouterOptions: {
controllers: {
getImage: function (req, res) {
res.setHeader('Content-type', 'image/png');
return fs.createReadStream('test/image.png').pipe(res);
}
}
},
swaggerValidatorOptions: {
validateResponse: true
}
}, function (app) {
try {
request(app)
.get('/api/image')
.end(function(err, res) {
assert.strictEqual(
crypto.createHash('sha256').update(fs.readFileSync('test/image.png')).digest('hex'), //expected
crypto.createHash('sha256').update(res.body).digest('hex') //actual
);
done(err);
});
} catch (err) {
done();
}
});
});

it('should not throw an error for empty responses that validate void (new issue)', function (done) {
var cPetStoreJson = _.cloneDeep(petStoreJson);

Expand Down
Binary file added test/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.