Skip to content

Commit 109b2a5

Browse files
hehooidanto
authored andcommitted
Expose keywords option during the initialization(in init method). (#47)
Expose keywords option during the initialization(in init method). Resolve #45
1 parent a56371c commit 109b2a5

12 files changed

Lines changed: 271 additions & 14 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ The function return Promise.
6464
Options currently supports:
6565
- `framework` - Defines in which framework the middleware is working ('koa' or 'express'). As default, set to 'express'.
6666
- `formats` - Array of formats that can be added to `ajv` configuration, each element in the array should include `name` and `pattern`.
67+
- `keywords` - Array of keywords that can be added to `ajv` configuration, each element in the array can be either an object or a function.
68+
If the element is an object, it must include `name` and `definition`. If the element is a function, it should accept `ajv` as its first argument and inside the function you need to call `ajv.addKeyword` to add your custom keyword
6769
- `beautifyErrors`- Boolean that indicates if to beautify the errors, in this case it will create a string from the Ajv error.
6870
- Examples:
6971
- `query/limit should be <= 100` - query param

package-lock.json

Lines changed: 9 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"swagger-parser": "^6.0.2"
5656
},
5757
"devDependencies": {
58+
"ajv-keywords": "^3.4.0",
5859
"body-parser": "^1.18.3",
5960
"chai": "^4.2.0",
6061
"chai-sinon": "^2.8.1",

src/middleware.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ function buildParametersValidation(parameters, contentTypes, middlewareOptions)
129129
const options = Object.assign({}, defaultAjvOptions, middlewareOptions.ajvConfigParams);
130130
let ajv = new Ajv(options);
131131

132-
ajvUtils.addCustomKeyword(ajv, middlewareOptions.formats);
132+
ajvUtils.addCustomKeyword(ajv, middlewareOptions.formats, middlewareOptions.keywords);
133133

134134
var ajvParametersSchema = {
135135
title: 'HTTP parameters',

src/swagger2/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function buildBodyValidation(schema, swaggerDefinitions, originalSwagger, curren
4141
const options = Object.assign({}, defaultAjvOptions, middlewareOptions.ajvConfigBody);
4242
let ajv = new Ajv(options);
4343

44-
ajvUtils.addCustomKeyword(ajv, middlewareOptions.formats);
44+
ajvUtils.addCustomKeyword(ajv, middlewareOptions.formats, middlewareOptions.keywords);
4545

4646
if (schema.discriminator) {
4747
return buildInheritance(schema.discriminator, swaggerDefinitions, originalSwagger, currentPath, currentMethod, parsedPath, ajv);

src/swagger3/open-api3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function buildBodyValidation(dereferenced, originalSwagger, currentPath, current
2020
const options = Object.assign({}, defaultAjvOptions, middlewareOptions.ajvConfigBody);
2121
let ajv = new Ajv(options);
2222

23-
ajvUtils.addCustomKeyword(ajv, middlewareOptions.formats);
23+
ajvUtils.addCustomKeyword(ajv, middlewareOptions.formats, middlewareOptions.keywords);
2424

2525
if (bodySchemaV3.discriminator) {
2626
return buildV3Inheritance(dereferenced, originalSwagger, currentPath, currentMethod, ajv);

src/utils/ajv-utils.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,33 @@
22
const filesKeyword = require('../customKeywords/files'),
33
contentKeyword = require('../customKeywords/contentTypeValidation');
44

5-
module.exports = {
6-
addCustomKeyword
7-
};
8-
9-
function addCustomKeyword(ajv, formats) {
5+
function addCustomKeyword(ajv, formats, keywords) {
106
if (formats) {
117
formats.forEach(function (format) {
128
ajv.addFormat(format.name, format.pattern);
139
});
1410
}
1511

12+
if (keywords) {
13+
keywords.forEach((keyword) => {
14+
if (typeof keyword === 'function') {
15+
return keyword(ajv);
16+
}
17+
18+
if (typeof keyword === 'object') {
19+
const name = keyword.name;
20+
const definition = keyword.definition;
21+
if (name && definition) {
22+
return ajv.addKeyword(name, definition);
23+
}
24+
}
25+
});
26+
}
27+
1628
ajv.addKeyword('files', filesKeyword);
1729
ajv.addKeyword('content', contentKeyword);
18-
}
30+
}
31+
32+
module.exports = {
33+
addCustomKeyword
34+
};

test/custom-keywords-swagger.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
swagger: "2.0"
2+
info:
3+
version: 1.0.0
4+
title: Swagger with custom ajv keywords
5+
schemes:
6+
- http
7+
consumes:
8+
- application/json
9+
produces:
10+
- application/json
11+
paths:
12+
/keywords:
13+
post:
14+
description: test custom keywords
15+
produces:
16+
- application/json
17+
parameters:
18+
- name: post data
19+
in: body
20+
required: true
21+
description: body data
22+
schema:
23+
type: object
24+
properties:
25+
age:
26+
type: number
27+
range: [15, 30]
28+
prohibited: ['ages']
29+
required:
30+
- age
31+
32+
responses:
33+
'200':
34+
description: Import result

test/express/middleware-test.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
var chai = require('chai'),
44
expect = chai.expect,
5-
sinon = require('sinon'),
65
chaiSinon = require('chai-sinon'),
76
request = require('supertest');
87
chai.use(chaiSinon);
@@ -2306,4 +2305,48 @@ describe('input-validation middleware tests - Express', function () {
23062305
});
23072306
});
23082307
});
2308+
describe('Keywords', function () {
2309+
var app;
2310+
before(function () {
2311+
return require('./test-server-keywords').then(function (testServer) {
2312+
app = testServer;
2313+
});
2314+
});
2315+
it('should pass the validation by the range keyword', function (done) {
2316+
request(app)
2317+
.post('/keywords')
2318+
.send({ age: 20 })
2319+
.expect(200, function (err, res) {
2320+
if (err) {
2321+
throw err;
2322+
}
2323+
expect(res.body.result).to.equal('OK');
2324+
done();
2325+
});
2326+
});
2327+
it('should be failed by the range keyword', function (done) {
2328+
request(app)
2329+
.post('/keywords')
2330+
.send({ age: 50 })
2331+
.expect(400, function (err, res) {
2332+
if (err) {
2333+
throw err;
2334+
}
2335+
expect(res.body.more_info).to.includes('body/age should be <= 30');
2336+
done();
2337+
});
2338+
});
2339+
it('should be failed by the prohibited keyword', function (done) {
2340+
request(app)
2341+
.post('/keywords')
2342+
.send({ ages: 20, age: 20 })
2343+
.expect(400, function (err, res) {
2344+
if (err) {
2345+
throw err;
2346+
}
2347+
expect(res.body.more_info).to.includes('body should NOT be valid');
2348+
done();
2349+
});
2350+
});
2351+
});
23092352
});
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
var express = require('express');
3+
var bodyParser = require('body-parser');
4+
var inputValidation = require('../../src/middleware');
5+
var range = require('ajv-keywords/keywords/range');
6+
7+
const definition = {
8+
type: 'object',
9+
macro: function (schema) {
10+
if (schema.length === 0) return true;
11+
if (schema.length === 1) return {not: {required: schema}};
12+
var schemas = schema.map(function (prop) {
13+
return {required: [prop]};
14+
});
15+
return {not: {anyOf: schemas}};
16+
},
17+
metaSchema: {
18+
type: 'array',
19+
items: {
20+
type: 'string'
21+
}
22+
}
23+
};
24+
25+
var inputValidationOptions = {
26+
keywords: [range, { name: 'prohibited', definition }],
27+
beautifyErrors: true,
28+
firstError: true,
29+
expectFormFieldsInBody: true
30+
};
31+
32+
module.exports = inputValidation.init('test/custom-keywords-swagger.yaml', inputValidationOptions)
33+
.then(function () {
34+
var app = express();
35+
app.use(bodyParser.json());
36+
app.post('/keywords', inputValidation.validate, function (req, res, next) {
37+
res.json({ result: 'OK' });
38+
});
39+
app.use(function (err, req, res, next) {
40+
if (err instanceof inputValidation.InputValidationError) {
41+
res.status(400).json({ more_info: err.errors });
42+
}
43+
});
44+
45+
module.exports = app;
46+
47+
return Promise.resolve(app);
48+
});

0 commit comments

Comments
 (0)