Skip to content

Commit

Permalink
feat: Exported validation function for request params validation
Browse files Browse the repository at this point in the history
  • Loading branch information
vpatidar-systango committed Mar 5, 2020
1 parent ac50dd1 commit 0cf9c51
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 5 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ NPM module to generate swagger documentation for Express APIs with minimum addit
>[![Downloads](https://badgen.net/npm/dt/swagger-generator-express)](https://www.npmjs.com/package/swagger-generator-express) [![npm dependents](https://badgen.net/npm/dependents/swagger-generator-express)](https://www.npmjs.com/package/swagger-generator-express?activeTab=dependents)
## Description
This NPM module let's you generate swagger (OpenAPI) documentation for your Express APIs without putting in much extra efforts. You just need to follow the convention for your request and response objects, and the module will take care of the rest. This module will cover your controllers, API specs along with request and response object structures.
This NPM module let's you validate and generate swagger (OpenAPI) documentation for your Express APIs without putting in much extra efforts. You just need to follow the convention for your request and response objects, and the module will take care of the rest. This module will cover your controllers, API specs along with request and response object structures.


## Usage ##
Expand Down Expand Up @@ -62,7 +62,7 @@ swagger.serveSwagger(app, "/swagger", options, {routePath : './src/routes/', req
'use strict';
var express = require('express');
var router = express.Router();
var validation = require('../middleware/validate');
var { validation } = require('swagger-generator-express');
var userController = require('../controller/user');
var requestModel = require('../requestModel/users');

Expand Down Expand Up @@ -254,6 +254,29 @@ module.exports = {

Open `http://`<app_host>`:`<app_port>`/swagger` in your browser to view the documentation.

## Request Parameter Validation

- Use `validation` function exported from this module to validate request params.

```javascript
'use strict';
var express = require('express');
var router = express.Router();
var { validation } = require('swagger-generator-express');
var userController = require('../controller/user');
var requestModel = require('../requestModel/users');

/**
* Function: validation
* Use validation function to validate request parameter.
* @param schema Joi schema for request.
*/
router.post('/', validation(requestModel[0]), userController.createUser);

module.exports = router;
```


## Pre-requisite

- Node v10 or above
Expand Down
2 changes: 1 addition & 1 deletion example/express-swagger-example/src/routes/users.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
var express = require('express');
var router = express.Router();
var validation = require('../middleware/validate');
var { validation } = require('swagger-generator-express');
var userController = require('../controller/user');
var requestModel = require('../requestModel/users');

Expand Down
2 changes: 2 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import responsesEnum from './responsesEnum';
import { isEmpty, map, get, has } from 'lodash';
import fs from 'fs';
import { resolve, join } from 'path';
import expressValidation from './validation/validate';

/**
* This module will support all the functionality of swagger-spec-express with additonal
Expand All @@ -18,6 +19,7 @@ export = {
swaggerize,
createModel,
serveSwagger,
validation: expressValidation
};

/**
Expand Down
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "swagger-generator-express",
"title": "swagger-generator-express",
"version": "2.0.0",
"version": "2.1.0",
"description": "Generate swagger documentation automatically using joi validation.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
111 changes: 111 additions & 0 deletions validation/validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use strict';
import Joi from '@hapi/joi'
import ValidationError from './validation-error'
import { assignIn, find, defaults } from 'lodash'

const defaultOptions = {
contextRequest: false,
allowUnknownHeaders: true,
allowUnknownBody: true,
allowUnknownQuery: true,
allowUnknownParams: true,
allowUnknownCookies: true,
status: 400,
statusText: 'Bad Request'
};
let globalOptions = {};

// maps the corresponding request object to an `express-validation` option
const unknownMap: any = {
headers: 'allowUnknownHeaders',
body: 'allowUnknownBody',
query: 'allowUnknownQuery',
params: 'allowUnknownParams',
cookies: 'allowUnknownCookies'
};

export default function expressValidation(schema: any) {
if (!schema) throw new Error('Please provide a validation schema');

return async (req: any, res: any, next: any) => {
const errors: any = [];

// Set default options
const options = defaults({}, schema.options || {}, globalOptions, defaultOptions);

// NOTE: mutates `errors`
const requestInputType = ['headers', 'body', 'query', 'params', 'cookies'];

// tslint:disable-next-line:prefer-for-of
for (let index = 0; index < requestInputType.length; index++) {
const key = requestInputType[index];
const allowUnknown = options[unknownMap[key]];
const entireContext = options.contextRequest ? req : null;
if (schema[key]) {
await validate(errors, req[key], schema[key], key, allowUnknown, entireContext);
}
}
if (errors && errors.length === 0) return next();

// tslint:disable-next-line:new-parens
return next(new (ValidationError as any)(errors, options));
};
};

exports.ValidationError = ValidationError;

exports.options = (opts: any) => {
if (!opts) {
globalOptions = {};
return;
}

globalOptions = defaults({}, globalOptions, opts);
};

/**
* validate checks the current `Request` for validations
* NOTE: mutates `request` in case the object is valid.
*/
async function validate(errObj: any, request: any, schema: any, location: any, allowUnknown: any, context: any) {
if (!request || !schema) return;

const joiOptions = {
context: context || request,
allowUnknown,
abortEarly: false
};

const {
error,
value
} = await Joi.object(schema).validate(request, joiOptions);

const errors = error;
if (!errors || errors.details.length === 0) {
assignIn(request, value); // joi responses are parsed into JSON
return;
}
// tslint:disable-next-line:no-shadowed-variable
errors.details.forEach((error) => {
const errorExists = find(errObj, (item)=> {
if (item && item.field === error.path && item.location === location) {
item.messages.push(error.message);
item.types.push(error.type);
return item;
}
return;
});

if (!errorExists) {
errObj.push({
field: error.path,
location,
messages: [error.message],
types: [error.type]
});
}

});
return errObj;
};
27 changes: 27 additions & 0 deletions validation/validation-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';
import { map, flatten } from 'lodash'

function ValidationError (this: any, errors : any, options: any) {
this.name = 'ValidationError';
this.message = 'validation error';
this.errors = errors;
this.flatten = options.flatten;
this.status = options.status;
this.statusText = options.statusText;
};
ValidationError.prototype = Object.create(Error.prototype);

ValidationError.prototype.toString = function () {
return JSON.stringify(this.toJSON());
};

ValidationError.prototype.toJSON = function () {
if (this.flatten) return flatten(map(this.errors, 'messages'));
return {
status: this.status,
statusText: this.statusText,
errors: this.errors
};
};

export default ValidationError;

0 comments on commit 0cf9c51

Please sign in to comment.