diff --git a/README.md b/README.md index 341d240..c1cef2d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ $ npm i @fastify/aws-lambda | callbackWaitsForEmptyEventLoop | See: [Official Documentation](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-context.html#nodejs-prog-model-context-properties) | `undefined` | | retainStage | Retain the stage part of the API Gateway URL | `false` | | pathParameterUsedAsPath | Use a defined pathParameter as path (i.e. `'proxy'`) | `false` | - +| parseCommaSeparatedQueryParams | Parse querystring with commas into an array of values. Affects the behavior of the querystring parser with commas while using [Payload Format Version 2.0](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html#http-api-develop-integrations-lambda.proxy-format) | `true` | ## 📖Example ### lambda.js diff --git a/index.js b/index.js index 2418fbc..2a2817d 100644 --- a/index.js +++ b/index.js @@ -17,6 +17,7 @@ module.exports = (app, options) => { options.decorateRequest = options.decorateRequest !== undefined ? options.decorateRequest : true options.retainStage = options.retainStage !== undefined ? options.retainStage : false options.pathParameterUsedAsPath = options.pathParameterUsedAsPath !== undefined ? options.pathParameterUsedAsPath : false + options.parseCommaSeparatedQueryParams = options.parseCommaSeparatedQueryParams !== undefined ? options.parseCommaSeparatedQueryParams : true let currentAwsArguments = {} if (options.decorateRequest) { options.decorationPropertyName = options.decorationPropertyName || 'awsLambda' @@ -48,6 +49,7 @@ module.exports = (app, options) => { url = url.substring(event.requestContext.stage.length + 1) } const query = {} + const parsedCommaSeparatedQuery = {} if (event.requestContext && event.requestContext.elb) { if (event.multiValueQueryStringParameters) { Object.keys(event.multiValueQueryStringParameters).forEach((q) => { @@ -56,20 +58,20 @@ module.exports = (app, options) => { } else if (event.queryStringParameters) { Object.keys(event.queryStringParameters).forEach((q) => { query[decodeURIComponent(q)] = decodeURIComponent(event.queryStringParameters[q]) - if (event.version === '2.0' && typeof query[decodeURIComponent(q)] === 'string' && query[decodeURIComponent(q)].indexOf(',') > 0) { - query[decodeURIComponent(q)] = query[decodeURIComponent(q)].split(',') + if (options.parseCommaSeparatedQueryParams && event.version === '2.0' && typeof query[decodeURIComponent(q)] === 'string' && query[decodeURIComponent(q)].indexOf(',') > 0) { + parsedCommaSeparatedQuery[decodeURIComponent(q)] = query[decodeURIComponent(q)].split(',') } }) } } else { - if (event.queryStringParameters && event.version === '2.0') { + if (event.queryStringParameters && options.parseCommaSeparatedQueryParams && event.version === '2.0') { Object.keys(event.queryStringParameters).forEach((k) => { if (typeof event.queryStringParameters[k] === 'string' && event.queryStringParameters[k].indexOf(',') > 0) { - event.queryStringParameters[k] = event.queryStringParameters[k].split(',') + parsedCommaSeparatedQuery[decodeURIComponent(k)] = event.queryStringParameters[k].split(',') } }) } - Object.assign(query, event.multiValueQueryStringParameters || event.queryStringParameters) + Object.assign(query, event.multiValueQueryStringParameters || event.queryStringParameters, parsedCommaSeparatedQuery) } const headers = Object.assign({}, event.headers) if (event.multiValueHeaders) { diff --git a/test/basic.test.js b/test/basic.test.js index 30cb7f5..ca6ed5b 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -207,6 +207,69 @@ test('GET with multi-value query params (queryStringParameters)', async (t) => { t.equal(ret.body, '{"foo":["qux","bar"]}') }) +test('GET with multi-value query params (queryStringParameters) with parseCommaSeparatedQueryParams disabled', async (t) => { + t.plan(2) + + const app = fastify() + app.get('/test', async (request, reply) => { + reply.send(request.query) + }) + const proxy = awsLambdaFastify(app, { parseCommaSeparatedQueryParams: false }) + + const ret = await proxy({ + version: '2.0', + httpMethod: 'GET', + path: '/test', + queryStringParameters: { + foo: 'qux,bar' + } + }) + t.equal(ret.statusCode, 200) + t.equal(ret.body, '{"foo":"qux,bar"}') +}) + +test('GET Retains queryStringParameters in the original awsLambda onRequest hook when parseCommaSeparatedQueryParams is enabled by default', async (t) => { + t.plan(2) + + const app = fastify() + app.get('/test', async (request, reply) => { + t.same(request.awsLambda.event.queryStringParameters, { foo: 'qux,bar' }) + reply.send(request.query) + }) + const proxy = awsLambdaFastify(app) + + const ret = await proxy({ + version: '2.0', + httpMethod: 'GET', + path: '/test', + queryStringParameters: { + foo: 'qux,bar' + } + }) + t.equal(ret.statusCode, 200) +}) + +test('GET Retains queryStringParameters in the original awsLambda onRequest hook when parseCommaSeparatedQueryParams is disabled', async (t) => { + t.plan(2) + + const app = fastify() + app.get('/test', async (request, reply) => { + t.same(request.awsLambda.event.queryStringParameters, { foo: 'qux,bar' }) + reply.send(request.query) + }) + const proxy = awsLambdaFastify(app, { parseCommaSeparatedQueryParams: false }) + + const ret = await proxy({ + version: '2.0', + httpMethod: 'GET', + path: '/test', + queryStringParameters: { + foo: 'qux,bar' + } + }) + t.equal(ret.statusCode, 200) +}) + test('GET with double encoded query value', async (t) => { t.plan(2) diff --git a/types/index.d.ts b/types/index.d.ts index 6604408..24e854d 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -16,6 +16,16 @@ declare namespace awsLambdaFastify { * usually set to 'proxy', if used */ pathParameterUsedAsPath?: string; + /** + * Parse querystring with commas into an array of values. + * Affects the behavior of the querystring parser with commas while using [Payload Format Version 2.0](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html#http-api-develop-integrations-lambda.proxy-format) + * + * e.g. when set to `true` (default) `?foo=qux,bar` => `{ foo: ['qux', 'bar'] }` + * + * e.g. when set to `false` `?foo=qux,bar` => `{ foo: 'qux,bar' }` + * @default true + */ + parseCommaSeparatedQueryParams?: boolean; } export interface LambdaResponse {