Skip to content

Commit 4302ce0

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/main/arktype-2.1.26
2 parents 86595fc + 26d7bcf commit 4302ce0

File tree

12 files changed

+325
-184
lines changed

12 files changed

+325
-184
lines changed

.github/workflows/dependency-review.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ jobs:
1919
- name: 'Checkout Repository'
2020
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
2121
- name: 'Dependency Review'
22-
uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1
22+
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2

docs/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# version 9.5.35
2-
FROM squidfunk/mkdocs-material@sha256:58dee36ad85b0ae4836522ee6d3f0150d828bca9a1f7d3bfbf430bca771c1441
2+
FROM squidfunk/mkdocs-material@sha256:980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628
33

44
# Install Node.js
55
RUN apk add --no-cache nodejs=22.13.1-r0 npm

docs/requirements.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mike==1.1.2
2-
mkdocs-material==9.6.23
2+
mkdocs-material==9.7.0
33
mkdocs-git-revision-date-plugin==0.3.2
44
mkdocs-exclude==1.0.2
55
mkdocs-typedoc==1.0.4

docs/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ mkdocs-llmstxt==0.4.0 \
276276
--hash=sha256:7244bf0ac917c9964030c93e9c3e26c02d2d14a0f66fc113416007125b6da0fc \
277277
--hash=sha256:a7e4d20496bc8c55b6773b55c8d69cf552448a9ad38915b6e8c657ae3a46c8b8
278278
# via -r requirements.in
279-
mkdocs-material==9.6.23 \
280-
--hash=sha256:3bf3f1d82d269f3a14ed6897bfc3a844cc05e1dc38045386691b91d7e6945332 \
281-
--hash=sha256:62ebc9cdbe90e1ae4f4e9b16a6aa5c69b93474c7b9e79ebc0b11b87f9f055e00
279+
mkdocs-material==9.7.0 \
280+
--hash=sha256:602b359844e906ee402b7ed9640340cf8a474420d02d8891451733b6b02314ec \
281+
--hash=sha256:da2866ea53601125ff5baa8aa06404c6e07af3c5ce3d5de95e3b52b80b442887
282282
# via -r requirements.in
283283
mkdocs-material-extensions==1.3.1 \
284284
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \

package-lock.json

Lines changed: 36 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
},
5151
"homepage": "https://github.com/aws-powertools/powertools-lambda-typescript#readme",
5252
"devDependencies": {
53-
"@biomejs/biome": "^2.3.4",
53+
"@biomejs/biome": "^2.3.5",
5454
"@types/aws-lambda": "^8.10.157",
5555
"@types/node": "^24.10.0",
5656
"@vitest/coverage-v8": "^3.2.4",

packages/event-handler/src/rest/Router.ts

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { Readable } from 'node:stream';
22
import { pipeline } from 'node:stream/promises';
33
import type streamWeb from 'node:stream/web';
4-
import type { GenericLogger } from '@aws-lambda-powertools/commons/types';
4+
import type {
5+
GenericLogger,
6+
JSONValue,
7+
} from '@aws-lambda-powertools/commons/types';
8+
import { isRecord } from '@aws-lambda-powertools/commons/typeutils';
59
import {
610
getStringFromEnv,
711
isDevMode,
@@ -457,19 +461,7 @@ class Router {
457461
) {
458462
return body;
459463
}
460-
if (!body.statusCode) {
461-
if (error instanceof NotFoundError) {
462-
body.statusCode = HttpStatusCodes.NOT_FOUND;
463-
} else if (error instanceof MethodNotAllowedError) {
464-
body.statusCode = HttpStatusCodes.METHOD_NOT_ALLOWED;
465-
}
466-
}
467-
return new Response(JSON.stringify(body), {
468-
status:
469-
(body.statusCode as number) ??
470-
HttpStatusCodes.INTERNAL_SERVER_ERROR,
471-
headers: { 'Content-Type': 'application/json' },
472-
});
464+
return this.#errorBodyToWebResponse(body, error);
473465
} catch (handlerError) {
474466
if (handlerError instanceof HttpError) {
475467
return await this.handleError(handlerError, options);
@@ -488,6 +480,48 @@ class Router {
488480
return this.#defaultErrorHandler(error);
489481
}
490482

483+
/**
484+
* Converts an error handler's response body to an HTTP Response object.
485+
*
486+
* If the body is a record object without a status code, sets the status code for
487+
* NotFoundError (404) or MethodNotAllowedError (405). Uses the status code from
488+
* the body if present, otherwise defaults to 500 Internal Server Error.
489+
*
490+
* @param body - The response body returned by the error handler, of type JSONValue
491+
* @param error - The Error object associated with the response
492+
*/
493+
#errorBodyToWebResponse(body: JSONValue, error: Error): Response {
494+
let status: number = HttpStatusCodes.INTERNAL_SERVER_ERROR;
495+
496+
if (isRecord(body)) {
497+
body.statusCode = body.statusCode ?? this.#getStatusCodeFromError(error);
498+
status = (body.statusCode as number) ?? status;
499+
}
500+
501+
return new Response(JSON.stringify(body), {
502+
status,
503+
headers: { 'Content-Type': 'application/json' },
504+
});
505+
}
506+
507+
/**
508+
* Extracts the HTTP status code from an error instance.
509+
*
510+
* Maps specific error types to their corresponding HTTP status codes:
511+
* - `NotFoundError` maps to 404 (NOT_FOUND)
512+
* - `MethodNotAllowedError` maps to 405 (METHOD_NOT_ALLOWED)
513+
*
514+
* @param error - The error instance to extract the status code from
515+
*/
516+
#getStatusCodeFromError(error: Error): number | undefined {
517+
if (error instanceof NotFoundError) {
518+
return HttpStatusCodes.NOT_FOUND;
519+
}
520+
if (error instanceof MethodNotAllowedError) {
521+
return HttpStatusCodes.METHOD_NOT_ALLOWED;
522+
}
523+
}
524+
491525
/**
492526
* Default error handler that returns a 500 Internal Server Error response.
493527
* In development mode, includes stack trace and error details.

packages/event-handler/src/types/rest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Readable } from 'node:stream';
22
import type {
33
GenericLogger,
4-
JSONObject,
4+
JSONValue,
55
} from '@aws-lambda-powertools/commons/types';
66
import type {
77
APIGatewayProxyEvent,
@@ -81,7 +81,7 @@ type ExtendedAPIGatewayProxyResult = Omit<APIGatewayProxyResult, 'body'> & {
8181

8282
type HandlerResponse =
8383
| Response
84-
| JSONObject
84+
| JSONValue
8585
| ExtendedAPIGatewayProxyResult
8686
| BinaryResult;
8787

packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,68 @@ describe.each([
1414
{ version: 'V1', createEvent: createTestEvent },
1515
{ version: 'V2', createEvent: createTestEventV2 },
1616
])('Class: Router - Basic Routing ($version)', ({ createEvent }) => {
17-
it.each([
17+
const httpMethods = [
1818
['GET', 'get'],
1919
['POST', 'post'],
2020
['PUT', 'put'],
2121
['PATCH', 'patch'],
2222
['DELETE', 'delete'],
2323
['HEAD', 'head'],
2424
['OPTIONS', 'options'],
25-
])('routes %s requests', async (method, verb) => {
26-
// Prepare
27-
const app = new Router();
28-
(
29-
app[verb as Lowercase<HttpMethod>] as (
30-
path: string,
31-
handler: RouteHandler
32-
) => void
33-
)('/test', async () => ({ result: `${verb}-test` }));
34-
// Act
35-
const actual = await app.resolve(createEvent('/test', method), context);
36-
// Assess
37-
expect(actual.statusCode).toBe(200);
38-
expect(actual.body).toBe(JSON.stringify({ result: `${verb}-test` }));
39-
expect(actual.headers?.['content-type']).toBe('application/json');
40-
expect(actual.isBase64Encoded).toBe(false);
41-
});
25+
];
26+
it.each(httpMethods)(
27+
'routes %s requests with object response',
28+
async (method, verb) => {
29+
// Prepare
30+
const app = new Router();
31+
(
32+
app[verb as Lowercase<HttpMethod>] as (
33+
path: string,
34+
handler: RouteHandler
35+
) => void
36+
)('/test', async () => ({ result: `${verb}-test` }));
37+
38+
// Act
39+
const actual = await app.resolve(createEvent('/test', method), context);
40+
41+
// Assess
42+
expect(actual.statusCode).toBe(200);
43+
expect(actual.body).toBe(JSON.stringify({ result: `${verb}-test` }));
44+
expect(actual.headers?.['content-type']).toBe('application/json');
45+
expect(actual.isBase64Encoded).toBe(false);
46+
}
47+
);
48+
49+
it.each(httpMethods)(
50+
'routes %s requests with array response',
51+
async (method, verb) => {
52+
// Prepare
53+
const app = new Router();
54+
(
55+
app[verb as Lowercase<HttpMethod>] as (
56+
path: string,
57+
handler: RouteHandler
58+
) => void
59+
)('/test', async () => [
60+
{ id: 1, result: `${verb}-test-1` },
61+
{ id: 2, result: `${verb}-test-2` },
62+
]);
63+
64+
// Act
65+
const actual = await app.resolve(createEvent('/test', method), context);
66+
67+
// Assess
68+
expect(actual.statusCode).toBe(200);
69+
expect(actual.body).toBe(
70+
JSON.stringify([
71+
{ id: 1, result: `${verb}-test-1` },
72+
{ id: 2, result: `${verb}-test-2` },
73+
])
74+
);
75+
expect(actual.headers?.['content-type']).toBe('application/json');
76+
expect(actual.isBase64Encoded).toBe(false);
77+
}
78+
);
4279

4380
it.each([['CONNECT'], ['TRACE']])(
4481
'throws MethodNotAllowedError for %s requests',

0 commit comments

Comments
 (0)