Skip to content

Commit

Permalink
Merge pull request #240 from cdimascio/improve_binary_support
Browse files Browse the repository at this point in the history
Improve binary support
  • Loading branch information
cdimascio committed Feb 22, 2020
2 parents b1fb6d6 + a2025a9 commit 37a57f0
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 13 deletions.
4 changes: 1 addition & 3 deletions examples/3-eov-operations-babel/src/routes/ping.js
@@ -1,3 +1 @@
export default {
ping: (req, res) => res.status(200).send('pong'),
};
export const ping = (req, res) => res.status(200).send('pong');
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "express-openapi-validator",
"version": "3.7.1",
"version": "3.7.2",
"description": "Automatically validate API requests and responses with OpenAPI 3 and Express.",
"main": "dist/index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Expand Up @@ -240,7 +240,7 @@ export class OpenApiValidator {
const modulePath = path.join(this.options.operationHandlers, baseName);
if (!tmpModules[modulePath]) {
tmpModules[modulePath] = require(modulePath);
if (!tmpModules[modulePath].oId) {
if (!tmpModules[modulePath][oId]) {
// if oId is not found only module, try the module's default export
tmpModules[modulePath] = tmpModules[modulePath].default;
}
Expand Down
38 changes: 32 additions & 6 deletions src/middlewares/openapi.multipart.ts
@@ -1,24 +1,27 @@
import { OpenApiContext } from '../framework/openapi.context';
import { createRequestAjv } from '../framework/ajv';
import { validationError } from './util';
import { Request } from 'express';
import {
OpenAPIV3,
OpenApiRequest,
OpenApiRequestHandler,
ValidationError,
} from '../framework/types';
import { MulterError } from 'multer';
import ajv = require('ajv');

const multer = require('multer');

export function multipart(
OpenApiContext: OpenApiContext,
context: OpenApiContext,
multerOpts: {},
): OpenApiRequestHandler {
const mult = multer(multerOpts);
const Ajv = createRequestAjv(context.apiDoc, {});
return (req, res, next) => {
// TODO check that format: binary (for upload) else do not use multer.any()
// use multer.none() if no binary parameters exist
if (isMultipart(req) && isValidContentType(req)) {
if (shouldHandle(Ajv, req)) {
mult.any()(req, res, err => {
if (err) {
next(error(req, err));
Expand Down Expand Up @@ -66,9 +69,32 @@ export function multipart(
};
}

function isValidContentType(req: Request): boolean {
const contentType = req.headers['content-type'];
return !contentType || contentType.includes('multipart/form-data');
function shouldHandle(Ajv, req: OpenApiRequest): boolean {
const reqContentType = req.headers['content-type'];
if (isMultipart(req) && reqContentType?.includes('multipart/form-data')) {
return true;
}

const bodyRef = (<any>req?.openapi)?.schema?.$ref;
const requestBody = bodyRef
? Ajv.getSchema(bodyRef)
: (<any>req?.openapi)?.schema?.requestBody;
const bodyContent = requestBody?.content;
if (!bodyContent) return false;

const content = <{ [media: string]: OpenAPIV3.MediaTypeObject }>bodyContent;
const contentTypes = Object.entries(content);
for (const [contentType, mediaType] of contentTypes) {
if (!contentType.includes(reqContentType)) continue;
const mediaTypeSchema = <any>mediaType?.schema;
const schema = mediaTypeSchema?.$ref
? Ajv.getSchema(mediaTypeSchema.$ref)
: mediaTypeSchema;
const format = schema?.format;
if (format === 'binary') {
return true;
}
}
}

function isMultipart(req: OpenApiRequest): boolean {
Expand Down
Binary file added test/assets/image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion test/multipart.spec.ts
@@ -1,5 +1,6 @@
import * as express from 'express';
import * as path from 'path';
import * as fs from 'fs';
import { expect } from 'chai';
import * as request from 'supertest';
import { createApp } from './common/app';
Expand Down Expand Up @@ -33,7 +34,8 @@ describe(packageJson.name, () => {
metadata: req.body.metadata,
});
})
.post(`/sample_1`, (req, res) => res.json(req.body)),
.post(`/sample_1`, (req, res) => res.json(req.body))
.post(`/sample_3`, (req, res) => res.json(req.body)),
),
);
});
Expand Down Expand Up @@ -67,6 +69,17 @@ describe(packageJson.name, () => {
.attach('file', 'package.json')
.expect(400));

it('should validate application/octet-stream file and metadata', done => {
const testImage = `${__dirname}/assets/image.png`;
const req = request(app)
.post(`${app.basePath}/sample_3`)
.set('content-type', 'application/octet-stream');

const imgStream = fs.createReadStream(testImage);
imgStream.on('end', () => req.end(done));
imgStream.pipe(<any>req, { end: false });
});

it('should validate multipart file and metadata', async () => {
await request(app)
.post(`${app.basePath}/sample_2`)
Expand Down
13 changes: 13 additions & 0 deletions test/resources/multipart.yaml
Expand Up @@ -74,6 +74,19 @@ paths:
responses:
"200":
description: form data
/sample_3:
post:
description: upload a photo of the pet
operationId: octetStream
requestBody:
content:
application/octet-stream:
schema:
type: string
format: binary
responses:
"200":
description: binary
/range:
get:
parameters:
Expand Down

0 comments on commit 37a57f0

Please sign in to comment.