From 2c97c4c7ad084c228026575eda3a14abbf2876eb Mon Sep 17 00:00:00 2001 From: Kalven Schraut <30308012+kalvenschraut@users.noreply.github.com> Date: Fri, 23 Sep 2022 02:58:37 -0500 Subject: [PATCH] feat: parse request body for http SEARCH requests (#4298) --- docs/Reference/Routes.md | 4 +- lib/handleRequest.js | 4 +- test/search.test.js | 199 +++++++++++++++++++++++++++++++++------ 3 files changed, 173 insertions(+), 34 deletions(-) diff --git a/docs/Reference/Routes.md b/docs/Reference/Routes.md index d3b39f3245..90f1703483 100644 --- a/docs/Reference/Routes.md +++ b/docs/Reference/Routes.md @@ -41,8 +41,8 @@ fastify.route(options) need to be in [JSON Schema](https://json-schema.org/) format, check [here](./Validation-and-Serialization.md) for more info. - * `body`: validates the body of the request if it is a POST, PUT, or PATCH - method. + * `body`: validates the body of the request if it is a POST, PUT, PATCH, + TRACE, or SEARCH method. * `querystring` or `query`: validates the querystring. This can be a complete JSON Schema object, with the property `type` of `object` and `properties` object of parameters, or simply the values of what would be contained in the diff --git a/lib/handleRequest.js b/lib/handleRequest.js index a8edccc935..ec9d3decff 100644 --- a/lib/handleRequest.js +++ b/lib/handleRequest.js @@ -18,14 +18,14 @@ function handleRequest (err, request, reply) { const method = request.raw.method const headers = request.headers - if (method === 'GET' || method === 'HEAD' || method === 'SEARCH') { + if (method === 'GET' || method === 'HEAD') { handler(request, reply) return } const contentType = headers['content-type'] - if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE') { + if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE' || method === 'SEARCH') { if (contentType === undefined) { if ( headers['transfer-encoding'] === undefined && diff --git a/test/search.test.js b/test/search.test.js index 68a8414df4..c0f1ba2bdf 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -1,18 +1,17 @@ 'use strict' const t = require('tap') +const sget = require('simple-get').concat const test = t.test const fastify = require('..')() const schema = { - schema: { - response: { - '2xx': { - type: 'object', - properties: { - hello: { - type: 'string' - } + response: { + '2xx': { + type: 'object', + properties: { + hello: { + type: 'string' } } } @@ -20,35 +19,45 @@ const schema = { } const querySchema = { - schema: { - querystring: { - type: 'object', - properties: { - hello: { - type: 'integer' - } + querystring: { + type: 'object', + properties: { + hello: { + type: 'integer' } } } } const paramsSchema = { - schema: { - params: { - type: 'object', - properties: { - foo: { - type: 'string' - }, - test: { - type: 'integer' - } + params: { + type: 'object', + properties: { + foo: { + type: 'string' + }, + test: { + type: 'integer' } } } } -test('shorthand - search', t => { +const bodySchema = { + body: { + type: 'object', + properties: { + foo: { + type: 'string' + }, + test: { + type: 'integer' + } + } + } +} + +test('search', t => { t.plan(1) try { fastify.route({ @@ -65,13 +74,13 @@ test('shorthand - search', t => { } }) -test('shorthand - search params', t => { +test('search, params schema', t => { t.plan(1) try { fastify.route({ method: 'SEARCH', url: '/params/:foo/:test', - paramsSchema, + schema: paramsSchema, handler: function (request, reply) { reply.code(200).send(request.params) } @@ -82,13 +91,13 @@ test('shorthand - search params', t => { } }) -test('shorthand - get, querystring schema', t => { +test('search, querystring schema', t => { t.plan(1) try { fastify.route({ method: 'SEARCH', url: '/query', - querySchema, + schema: querySchema, handler: function (request, reply) { reply.code(200).send(request.query) } @@ -98,3 +107,133 @@ test('shorthand - get, querystring schema', t => { t.fail() } }) + +test('search, body schema', t => { + t.plan(1) + try { + fastify.route({ + method: 'SEARCH', + url: '/body', + schema: bodySchema, + handler: function (request, reply) { + reply.code(200).send(request.body) + } + }) + t.pass() + } catch (e) { + t.fail() + } +}) + +fastify.listen({ port: 0 }, err => { + t.error(err) + t.teardown(() => { fastify.close() }) + + const url = `http://localhost:${fastify.server.address().port}` + + test('request - search', t => { + t.plan(4) + sget({ + method: 'SEARCH', + url + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(response.headers['content-length'], '' + body.length) + t.same(JSON.parse(body), { hello: 'world' }) + }) + }) + + test('request search params schema', t => { + t.plan(4) + sget({ + method: 'SEARCH', + url: `${url}/params/world/123` + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(response.headers['content-length'], '' + body.length) + t.same(JSON.parse(body), { foo: 'world', test: 123 }) + }) + }) + + test('request search params schema error', t => { + t.plan(3) + sget({ + method: 'SEARCH', + url: `${url}/params/world/string` + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 400) + t.same(JSON.parse(body), { + error: 'Bad Request', + message: 'params/test must be integer', + statusCode: 400 + }) + }) + }) + + test('request search querystring schema', t => { + t.plan(4) + sget({ + method: 'SEARCH', + url: `${url}/query?hello=123` + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(response.headers['content-length'], '' + body.length) + t.same(JSON.parse(body), { hello: 123 }) + }) + }) + + test('request search querystring schema error', t => { + t.plan(3) + sget({ + method: 'SEARCH', + url: `${url}/query?hello=world` + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 400) + t.same(JSON.parse(body), { + error: 'Bad Request', + message: 'querystring/hello must be integer', + statusCode: 400 + }) + }) + }) + + test('request search body schema', t => { + t.plan(4) + const replyBody = { foo: 'bar', test: 5 } + sget({ + method: 'SEARCH', + url: `${url}/body`, + body: JSON.stringify(replyBody), + headers: { 'content-type': 'application/json' } + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + t.equal(response.headers['content-length'], '' + body.length) + t.same(JSON.parse(body), replyBody) + }) + }) + + test('request search body schema error', t => { + t.plan(4) + sget({ + method: 'SEARCH', + url: `${url}/body`, + body: JSON.stringify({ foo: 'bar', test: 'test' }), + headers: { 'content-type': 'application/json' } + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 400) + t.equal(response.headers['content-length'], '' + body.length) + t.same(JSON.parse(body), { + error: 'Bad Request', + message: 'body/test must be integer', + statusCode: 400 + }) + }) + }) +})