diff --git a/packages/open-next/src/adapters/event-mapper.ts b/packages/open-next/src/adapters/event-mapper.ts index b0a343c7..15c632d7 100644 --- a/packages/open-next/src/adapters/event-mapper.ts +++ b/packages/open-next/src/adapters/event-mapper.ts @@ -9,6 +9,7 @@ import type { } from "aws-lambda"; import { debug } from "./logger.js"; +import { convertToQuery } from "./routing/util.js"; import { parseCookies } from "./util.js"; export type InternalEvent = { @@ -123,7 +124,7 @@ function convertFromAPIGatewayProxyEventV2( body: normalizeAPIGatewayProxyEventV2Body(event), headers: normalizeAPIGatewayProxyEventV2Headers(event), remoteAddress: requestContext.http.sourceIp, - query: removeUndefinedFromQuery(event.queryStringParameters ?? {}), + query: removeUndefinedFromQuery(convertToQuery(rawQueryString)), cookies: event.cookies?.reduce((acc, cur) => { const [key, value] = cur.split("="); @@ -148,13 +149,7 @@ function convertFromCloudFrontRequestEvent( ), headers: normalizeCloudFrontRequestEventHeaders(headers), remoteAddress: clientIp, - query: querystring.split("&").reduce( - (acc, cur) => ({ - ...acc, - [cur.split("=")[0]]: cur.split("=")[1], - }), - {}, - ), + query: convertToQuery(querystring), cookies: headers.cookie?.reduce((acc, cur) => { const { key, value } = cur; diff --git a/packages/open-next/src/adapters/routing/util.ts b/packages/open-next/src/adapters/routing/util.ts index 7bb72ea3..ce3ff879 100644 --- a/packages/open-next/src/adapters/routing/util.ts +++ b/packages/open-next/src/adapters/routing/util.ts @@ -73,6 +73,21 @@ export function convertToQueryString(query: Record) { return queryString ? `?${queryString}` : ""; } +/** + * Given a raw query string, returns a record with key value-array pairs + * similar to how multiValueQueryStringParameters are structured + */ +export function convertToQuery(querystring: string) { + const query = new URLSearchParams(querystring); + const queryObject: Record = {}; + + for (const key of query.keys()) { + queryObject[key] = query.getAll(key); + } + + return queryObject; +} + export function getMiddlewareMatch(middlewareManifest: MiddlewareManifest) { const rootMiddleware = middlewareManifest.middleware["/"]; if (!rootMiddleware?.matchers) return []; diff --git a/packages/tests-e2e/tests/appRouter/query.test.ts b/packages/tests-e2e/tests/appRouter/query.test.ts index 7f0ab214..f931426a 100644 --- a/packages/tests-e2e/tests/appRouter/query.test.ts +++ b/packages/tests-e2e/tests/appRouter/query.test.ts @@ -10,7 +10,7 @@ test("SearchQuery", async ({ page }) => { let mwEl = page.getByText(`Search Params via Middleware: mw/e2etest`); let multiEl = page.getByText(`Multi-value Params (key: multi): 2`); let multiOne = page.getByText(`one`); - let multiTwo = page.getByText(`Two`); + let multiTwo = page.getByText(`two`); await expect(propsEl).toBeVisible(); await expect(mwEl).toBeVisible(); await expect(multiEl).toBeVisible(); diff --git a/packages/tests-unit/tests/adapter.utils.test.ts b/packages/tests-unit/tests/adapter.utils.test.ts index b96a26ab..654c4e1b 100644 --- a/packages/tests-unit/tests/adapter.utils.test.ts +++ b/packages/tests-unit/tests/adapter.utils.test.ts @@ -1,4 +1,7 @@ -import { convertToQueryString } from "../../open-next/src/adapters/routing/util"; +import { + convertToQuery, + convertToQueryString, +} from "../../open-next/src/adapters/routing/util"; import { parseCookies } from "../../open-next/src/adapters/util"; describe("adapter utils", () => { @@ -76,4 +79,39 @@ describe("adapter utils", () => { ); }); }); + + describe("convertToQuery", () => { + it("returns an empty object for empty string", () => { + const querystring = ""; + expect(convertToQuery(querystring)).toEqual({}); + }); + + it("converts a single querystring parameter to one query entry", () => { + const querystring = "key=value"; + expect(convertToQuery(querystring)).toEqual({ key: ["value"] }); + }); + + it("converts multiple distinct entries to an entry in the query", () => { + const querystring = "key=value&another=value2"; + expect(convertToQuery(querystring)).toEqual({ + key: ["value"], + another: ["value2"], + }); + }); + + it("converts multi-value parameters to an array in the query", () => { + const querystring = "key=value1&key=value2"; + expect(convertToQuery(querystring)).toEqual({ + key: ["value1", "value2"], + }); + }); + + it("converts mixed multi-value and single value parameters", () => { + const querystring = "key=value1&key=value2&another=value3"; + expect(convertToQuery(querystring)).toEqual({ + key: ["value1", "value2"], + another: ["value3"], + }); + }); + }); });