Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 38 additions & 12 deletions packages/event-handler/src/rest/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,15 @@ const createBody = (body: string | null, isBase64Encoded: boolean) => {
};

/**
* Converts an API Gateway proxy event to a Web API Request object.
* Populates headers from single and multi-value header entries.
*
* @param headers - The Headers object to populate
* @param event - The API Gateway proxy event
* @returns A Web API Request object
*/
const proxyEventV1ToWebRequest = (event: APIGatewayProxyEvent): Request => {
const { httpMethod, path } = event;
const { domainName } = event.requestContext;

const headers = new Headers();
const populateV1Headers = (
headers: Headers,
event: APIGatewayProxyEvent
): void => {
for (const [name, value] of Object.entries(event.headers ?? {})) {
if (value !== undefined) headers.set(name, value);
}
Expand All @@ -66,15 +65,21 @@ const proxyEventV1ToWebRequest = (event: APIGatewayProxyEvent): Request => {
}
}
}
const hostname = headers.get('Host') ?? domainName;
const protocol = headers.get('X-Forwarded-Proto') ?? 'https';

const url = new URL(path, `${protocol}://${hostname}/`);
};

/**
* Populates URL search parameters from single and multi-value query string parameters.
*
* @param url - The URL object to populate
* @param event - The API Gateway proxy event
*/
const populateV1QueryParams = (url: URL, event: APIGatewayProxyEvent): void => {
for (const [name, value] of Object.entries(
event.queryStringParameters ?? {}
)) {
if (value != null) url.searchParams.append(name, value);
if (value != null && !event.multiValueQueryStringParameters?.[name]) {
url.searchParams.append(name, value);
}
}

for (const [name, values] of Object.entries(
Expand All @@ -84,6 +89,27 @@ const proxyEventV1ToWebRequest = (event: APIGatewayProxyEvent): Request => {
url.searchParams.append(name, value);
}
}
};

/**
* Converts an API Gateway proxy event to a Web API Request object.
*
* @param event - The API Gateway proxy event
* @returns A Web API Request object
*/
const proxyEventV1ToWebRequest = (event: APIGatewayProxyEvent): Request => {
const { httpMethod, path } = event;
const { domainName } = event.requestContext;

const headers = new Headers();
populateV1Headers(headers, event);

const hostname = headers.get('Host') ?? domainName;
const protocol = headers.get('X-Forwarded-Proto') ?? 'https';

const url = new URL(path, `${protocol}://${hostname}/`);
populateV1QueryParams(url, event);

return new Request(url.toString(), {
method: httpMethod,
headers,
Expand Down
47 changes: 47 additions & 0 deletions packages/event-handler/tests/unit/rest/converters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,53 @@ describe('Converters', () => {
expect(url.searchParams.getAll('multi')).toEqual(['value1', 'value2']);
});

it('handles same parameter in both queryStringParameters and multiValueQueryStringParameters without duplication', () => {
// Prepare
const event = {
...baseEvent,
queryStringParameters: {
filter: 'published', // Last value (API Gateway behavior)
},
multiValueQueryStringParameters: {
filter: ['active', 'published'], // All values (API Gateway behavior)
},
};

// Act
const request = proxyEventToWebRequest(event);

// Assess
expect(request).toBeInstanceOf(Request);
const url = new URL(request.url);
expect(url.searchParams.getAll('filter')).toEqual([
'active',
'published',
]);
});

it('handles mixed single and multi-value query parameters correctly', () => {
// Prepare
const event = {
...baseEvent,
queryStringParameters: {
single: 'value1', // Only in single-value
multi: 'last', // Also in multi-value (should be ignored)
},
multiValueQueryStringParameters: {
multi: ['first', 'last'], // Should take precedence
},
};

// Act
const request = proxyEventToWebRequest(event);

// Assess
expect(request).toBeInstanceOf(Request);
const url = new URL(request.url);
expect(url.searchParams.get('single')).toBe('value1');
expect(url.searchParams.getAll('multi')).toEqual(['first', 'last']);
});

it('skips undefined queryStringParameter values', () => {
// Prepare
const event = {
Expand Down
Loading