Skip to content

Commit 6d1896e

Browse files
committed
path params validation
1 parent cfe314c commit 6d1896e

8 files changed

Lines changed: 98 additions & 11 deletions

File tree

examples/validation-example/app.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const { orka } = require('../../build');
22

33
const w = orka({
4-
diamorphosis: { configFolder: './examples/simple-example' },
4+
diamorphosis: { configFolder: './examples/validation-example' },
55
routesPath: './examples/validation-example/routes.js',
6-
logoPath: './examples/simple-example/logo.txt',
6+
logoPath: './examples/validation-example/logo.txt',
77
beforeStart: () => {
88
const config = require('../simple-example/config');
99
console.log(`Going to start env: ${config.nodeEnv}`);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
nodeEnv: 'demo',
3+
port: 3210,
4+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
|
2+
3+
O .
4+
O ' '
5+
o ' .
6+
o .'
7+
__________.-' '...___
8+
.-' ### '''...__
9+
/ a### ## ''--.._ ______
10+
'. # ######## ' .-'
11+
'-._ ..**********#### ___...---'''\ '
12+
'-._ __________...---''' \ l
13+
\ | '._|
14+
\__;
15+

examples/validation-example/routes.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const {
2-
middlewares: { validateQueryString, validateBody }
2+
middlewares: { validateQueryString, validateBody, validateParams }
33
} = require('../../build');
44
const Joi = require('joi');
55

@@ -10,9 +10,15 @@ const schema = Joi.object().keys({
1010
keyStringArray: Joi.array().items(Joi.string())
1111
});
1212

13+
const paramsSchema = Joi.object().keys({
14+
id: Joi.number().required(),
15+
name: Joi.string()
16+
});
17+
1318
module.exports = {
1419
get: {
15-
'/testGet': [validateQueryString(schema), async (ctx, next) => (ctx.body = ctx.request.body)]
20+
'/testGet': [validateQueryString(schema), async (ctx, next) => (ctx.body = ctx.request.body)],
21+
'/testParams/:id/:name': [validateParams(paramsSchema), async (ctx, next) => (ctx.body = ctx.params)]
1622
},
1723
post: {
1824
'/testPost': [validateBody(schema), async (ctx, next) => (ctx.body = ctx.request.body)]

src/middlewares/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import health from './health';
22
import metrics from './metrics';
33
import datadogMatchRoutes from './datadog-match-routes';
4-
import { validateBody, validateQueryString } from './validate-params';
4+
import { validateBody, validateQueryString, validateParams } from './validate-params';
55
import growthbook from './growthbook';
66

7-
export { health, validateBody, validateQueryString, metrics, datadogMatchRoutes, growthbook };
7+
export { health, validateBody, validateQueryString, validateParams, metrics, datadogMatchRoutes, growthbook };

src/middlewares/validate-params.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,11 @@ export function validateQueryString(schema: Joi.ObjectSchema) {
3232
await next();
3333
};
3434
}
35+
36+
export function validateParams(schema: Joi.ObjectSchema) {
37+
return async (ctx: Koa.Context, next: any) => {
38+
const result = validate(ctx.params, schema);
39+
ctx.params = result.value;
40+
await next();
41+
};
42+
}

test/examples/validation-examples.test.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@ describe('Validation examples', () => {
1515
});
1616

1717
it('/testGet returns 200', async () => {
18-
const { text } = await supertest('localhost:3000')
18+
const { text } = await supertest('localhost:3210')
1919
.get('/testGet?keyNumber=2')
2020
.expect(200);
2121

2222
text.should.eql(JSON.stringify({}));
2323
});
2424

2525
it('/testGet returns 400', async () => {
26-
const { text } = await supertest('localhost:3000')
26+
const { text } = await supertest('localhost:3210')
2727
.get('/testGet?keyNumber=somestring')
2828
.expect(400);
2929

3030
text.should.equal(JSON.stringify({ keyNumber: '"keyNumber" must be a number' }));
3131
});
3232

3333
it('/testPost returns 200', async () => {
34-
const { text } = await supertest('localhost:3000')
34+
const { text } = await supertest('localhost:3210')
3535
.post('/testPost')
3636
.send({ keyNumber: 2 })
3737
.expect(200);
@@ -40,11 +40,35 @@ describe('Validation examples', () => {
4040
});
4141

4242
it('/testPost returns 400', async () => {
43-
const { text } = await supertest('localhost:3000')
43+
const { text } = await supertest('localhost:3210')
4444
.post('/testPost')
4545
.send({ keyNumber: 'somestring' })
4646
.expect(400);
4747

4848
text.should.equal(JSON.stringify({ keyNumber: '"keyNumber" must be a number' }));
4949
});
50+
51+
it('/testParams/:id/:name returns 200 with coerced params', async () => {
52+
const { body } = await supertest('localhost:3210')
53+
.get('/testParams/123/john')
54+
.expect(200);
55+
56+
body.should.eql({ id: 123, name: 'john' });
57+
});
58+
59+
it('/testParams/:id returns 200 with optional param missing', async () => {
60+
const { body } = await supertest('localhost:3210')
61+
.get('/testParams/456')
62+
.expect(200);
63+
64+
body.should.eql({ id: 456 });
65+
});
66+
67+
it('/testParams/:id returns 400 when id is not a number', async () => {
68+
const { text } = await supertest('localhost:3210')
69+
.get('/testParams/notanumber')
70+
.expect(400);
71+
72+
text.should.equal(JSON.stringify({ id: '"id" must be a number' }));
73+
});
5074
});

test/initializers/koa/validate-params.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'should';
22
import * as sinon from 'sinon';
33
import * as Joi from 'joi';
4-
import { validateBody, validateQueryString } from '../../../src/middlewares/validate-params';
4+
import { validateBody, validateQueryString, validateParams } from '../../../src/middlewares/validate-params';
55

66
const sandbox = sinon.createSandbox();
77

@@ -77,4 +77,34 @@ describe('validate-params', function() {
7777
);
7878
next.called.should.be.equal(false);
7979
});
80+
81+
it('tests validate path params', async function() {
82+
const next = sandbox.stub();
83+
const ctx = {
84+
params: {
85+
keyString: 'somestring',
86+
keyNumber: '2',
87+
keyBoolean: 'true',
88+
keyStringArray: ['a', 'b']
89+
}
90+
} as any;
91+
92+
await validateParams(schema)(ctx, next);
93+
next.calledOnce.should.be.equal(true);
94+
ctx.params.should.eql({
95+
keyString: 'somestring',
96+
keyNumber: 2,
97+
keyBoolean: true,
98+
keyStringArray: ['a', 'b']
99+
});
100+
});
101+
102+
it('tests validate path params with error', async function() {
103+
const next = sandbox.stub();
104+
const ctx = { params: { keyNumber: 'somestring' } } as any;
105+
await validateParams(schema)(ctx, next).should.be.rejectedWith(
106+
JSON.stringify({ keyNumber: '"keyNumber" must be a number' })
107+
);
108+
next.called.should.be.equal(false);
109+
});
80110
});

0 commit comments

Comments
 (0)