Skip to content

Commit f03686a

Browse files
authored
v0.12.0 - see CHANGELOG for details (#36)
1 parent aa5aff6 commit f03686a

File tree

7 files changed

+110
-38
lines changed

7 files changed

+110
-38
lines changed

.github/workflows/runtime-tests.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ on:
33
workflow_dispatch
44

55
jobs:
6-
76
test-bun:
87
name: Test on Bun
98
runs-on: ubuntu-latest
@@ -21,7 +20,7 @@ jobs:
2120
runs-on: ubuntu-latest
2221
strategy:
2322
matrix:
24-
node-version: ['18.x', '20.x']
23+
node-version: ["18.x", "20.x"]
2524
steps:
2625
- name: Use Node.js ${{ matrix.node-version }}
2726
uses: actions/setup-node@v4
@@ -33,13 +32,13 @@ jobs:
3332
npm install
3433
- name: Perform Tests
3534
run: npm test
36-
35+
3736
test-cloudflare-workers:
3837
name: Test on Cloudflare Workers dev env
3938
runs-on: ubuntu-latest
4039
strategy:
4140
matrix:
42-
node-version: ['18.x', '20.x']
41+
node-version: ["18.x", "20.x"]
4342
steps:
4443
- name: Use Node.js ${{ matrix.node-version }}
4544
uses: actions/setup-node@v4

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## [0.12.0] - 2024-11-05
2+
3+
### Changed
4+
5+
- lax parsing rule for GET requests with header 'content-type: application/json'
6+
- upgraded dependencies (`@std/path@^1.0.8`, `@std/testing@^1.0.4`,
7+
`@std/assert@^1.0.7`, `@oak/oak@^17.1.3`,
8+
`@asteasolutions/zod-to-openapi@^7.2.0`, `@std/io@^0.225.0`)
9+
- code format
10+
111
## [0.11.0] - 2024-09-11
212

313
### Changed

deps.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
export { join } from "jsr:@std/path@^1.0.4";
1+
export { join } from "jsr:@std/path@^1.0.8";
22

3-
export { Router, Status } from "jsr:@oak/oak@^17.0.0";
3+
export { Router, Status } from "jsr:@oak/oak@^17.1.3";
44

55
export type {
66
Application,
77
Context,
88
ErrorStatus,
99
Next,
1010
RouteContext,
11-
} from "jsr:@oak/oak@^17.0.0";
11+
} from "jsr:@oak/oak@^17.1.3";
1212

1313
import {
1414
extendZodWithOpenApi,
1515
type ResponseConfig,
1616
type RouteConfig,
17-
} from "npm:@asteasolutions/zod-to-openapi@^7.1.1";
17+
} from "npm:@asteasolutions/zod-to-openapi@^7.2.0";
1818

1919
export type OakOpenApiSpec =
2020
& Omit<RouteConfig, "method" | "path" | "responses">
@@ -30,9 +30,9 @@ export {
3030
OpenApiGeneratorV3,
3131
OpenAPIRegistry,
3232
type ZodRequestBody,
33-
} from "npm:@asteasolutions/zod-to-openapi@^7.1.1";
33+
} from "npm:@asteasolutions/zod-to-openapi@^7.2.0";
3434

35-
export { type OpenAPIObjectConfig } from "npm:@asteasolutions/zod-to-openapi@^7.1.1/dist/v3.0/openapi-generator";
35+
export { type OpenAPIObjectConfig } from "npm:@asteasolutions/zod-to-openapi@^7.2.0/dist/v3.0/openapi-generator";
3636

3737
// must import from `npm:` instead of from `deno.land` to be compatible with `@asteasolutions/zod-to-openapi`
3838
import { z as slowTypedZ } from "npm:zod@^3.23.8";

dev_deps.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ export {
55
assertObjectMatch,
66
assertStringIncludes,
77
assertThrows,
8-
} from "jsr:@std/assert@^1.0.4";
8+
} from "jsr:@std/assert@^1.0.7";
99

1010
export {
1111
type BodyType,
1212
type Middleware,
1313
Request,
1414
testing as oakTesting,
15-
} from "jsr:@oak/oak@^17.0.0";
15+
} from "jsr:@oak/oak@^17.1.3";
1616

17-
export { Body } from "jsr:@oak/oak@^17.0.0/body";
17+
export { Body } from "jsr:@oak/oak@^17.1.3/body";
1818

1919
export {
2020
assertSpyCall,
@@ -25,17 +25,17 @@ export {
2525
spy,
2626
type Stub,
2727
stub,
28-
} from "jsr:@std/testing@^1.0.2/mock";
28+
} from "jsr:@std/testing@^1.0.4/mock";
2929

30-
export { assertSnapshot } from "jsr:@std/testing@^1.0.2/snapshot";
30+
export { assertSnapshot } from "jsr:@std/testing@^1.0.4/snapshot";
3131

32-
export { Buffer } from "jsr:@std/io@^0.224.7";
32+
export { Buffer } from "jsr:@std/io@^0.225.0";
3333

3434
export {
3535
afterEach,
3636
beforeEach,
3737
describe,
3838
it,
39-
} from "jsr:@std/testing@^1.0.2/bdd";
39+
} from "jsr:@std/testing@^1.0.4/bdd";
4040

4141
export { ZodObject } from "npm:zod@^3.23.8";

jsr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@dklab/oak-routing-ctrl",
3-
"version": "0.11.0",
3+
"version": "0.12.0",
44
"exports": {
55
".": "./mod.ts",
66
"./mod": "./mod.ts"

src/ControllerMethodArgs.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { debug } from "./utils/logger.ts";
2-
import { Context, RouteContext } from "../deps.ts";
2+
import { type Context, type RouteContext } from "../deps.ts";
33
import { ERR_UNSUPPORTED_CLASS_METHOD_DECORATOR_RUNTIME_BEHAVIOR } from "./Constants.ts";
44

55
/**
@@ -128,13 +128,23 @@ function getEnhancedHandler(
128128
try {
129129
parsedReqBody = await _internal.parseOakReqBody(ctx);
130130
} catch (e) {
131-
return ctx.throw(
132-
400,
133-
`Unable to parse request body: ${(e as Error).message}`,
134-
{
135-
stack: (e as Error).stack,
136-
},
137-
);
131+
if (
132+
ctx.request.method === "GET" &&
133+
ctx.request.headers.get("Content-Type") === "application/json" &&
134+
(e as Error).message?.includes("Unexpected end of JSON input")
135+
) {
136+
// we ignore this parsing error because the client was sending
137+
// a weird combination of method & content-type header
138+
} else {
139+
// for other case, we trigger the error back to userland
140+
return ctx.throw(
141+
400,
142+
`Unable to parse request body: ${(e as Error).message}`,
143+
{
144+
stack: (e as Error).stack,
145+
},
146+
);
147+
}
138148
}
139149

140150
const parsedReqSearchParams: Record<string, string> = {};

src/ControllerMethodArgs_test.ts

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,33 @@ Deno.test("getEnhancedHandler with a faulty ctx.request.body", async () => {
636636
spyParseOakRequestBody.restore();
637637
});
638638

639+
Deno.test("getEnhancedHandler with a faulty request method and content-type combination", async () => {
640+
const spyParseOakRequestBody = spy(_internal, "parseOakReqBody");
641+
function testHandler() {
642+
return "weird.method.content-type.combination.handled";
643+
}
644+
// deno-lint-ignore ban-types
645+
const enhancedHandler: Function = _internal.getEnhancedHandler(testHandler);
646+
const ctx = createMockContext({
647+
method: "GET",
648+
headers: [["Content-Type", "application/json"]],
649+
});
650+
Object.defineProperty(ctx.request, "body", {
651+
get: () => createMockRequestBody("json", "Unexpected end of JSON input"),
652+
});
653+
const spyCtxThrow = spy();
654+
Object.defineProperty(ctx, "throw", {
655+
value: (errorStatus: unknown, message?: string, props?: unknown) => {
656+
spyCtxThrow(errorStatus, message, props);
657+
},
658+
});
659+
const retVal = await enhancedHandler(ctx);
660+
assertSpyCalls(spyCtxThrow, 0);
661+
assertSpyCalls(spyParseOakRequestBody, 1);
662+
assertEquals(retVal, "weird.method.content-type.combination.handled");
663+
spyParseOakRequestBody.restore();
664+
});
665+
639666
Deno.test("getEnhancedHandler with a faulty ctx.request.url.searchParams", async () => {
640667
const spyParseOakRequestBody = spy(_internal, "parseOakReqBody");
641668
function testHandler() {
@@ -669,7 +696,8 @@ Deno.test("getEnhancedHandler with a faulty ctx.request.url.searchParams", async
669696
});
670697

671698
Deno.test("getEnhancedHandler - declaring 4 desirable params in order A", async () => {
672-
const testHandler = spy((..._rest) => 42);
699+
// deno-lint-ignore no-explicit-any
700+
const testHandler = spy((..._rest: any[]) => 42);
673701
// deno-lint-ignore ban-types
674702
const enhancedHandler: Function = _internal.getEnhancedHandler(
675703
testHandler,
@@ -699,7 +727,8 @@ Deno.test("getEnhancedHandler - declaring 4 desirable params in order A", async
699727
});
700728

701729
Deno.test("getEnhancedHandler - declaring 4 desirable params in order B", async () => {
702-
const testHandler = spy((..._rest) => 43);
730+
// deno-lint-ignore no-explicit-any
731+
const testHandler = spy((..._rest: any[]) => 43);
703732
// deno-lint-ignore ban-types
704733
const enhancedHandler: Function = _internal.getEnhancedHandler(
705734
testHandler,
@@ -730,7 +759,8 @@ Deno.test("getEnhancedHandler - declaring 4 desirable params in order B", async
730759

731760
Deno.test("getEnhancedHandler - declaring 5 desirable params", async () => {
732761
const spyParseOakRequestBody = spy(_internal, "parseOakReqBody");
733-
const testHandler = spy((..._rest) => 44);
762+
// deno-lint-ignore no-explicit-any
763+
const testHandler = spy((..._rest: any[]) => 44);
734764
// deno-lint-ignore ban-types
735765
const enhancedHandler: Function = _internal.getEnhancedHandler(
736766
testHandler,
@@ -765,7 +795,8 @@ Deno.test("getEnhancedHandler - declaring 5 desirable params", async () => {
765795
});
766796

767797
Deno.test("getEnhancedHandler - declaring 3 desirable params in order A", async () => {
768-
const testHandler = spy((..._rest) => 45);
798+
// deno-lint-ignore no-explicit-any
799+
const testHandler = spy((..._rest: any[]) => 45);
769800
// deno-lint-ignore ban-types
770801
const enhancedHandler: Function = _internal.getEnhancedHandler(
771802
testHandler,
@@ -794,7 +825,8 @@ Deno.test("getEnhancedHandler - declaring 3 desirable params in order A", async
794825
});
795826

796827
Deno.test("getEnhancedHandler - declaring 3 desirable params in order B", async () => {
797-
const testHandler = spy((..._rest) => 46);
828+
// deno-lint-ignore no-explicit-any
829+
const testHandler = spy((..._rest: any[]) => 46);
798830
// deno-lint-ignore ban-types
799831
const enhancedHandler: Function = _internal.getEnhancedHandler(
800832
testHandler,
@@ -847,7 +879,10 @@ Deno.test("getEnhancedHandler - not declaring any param", async () => {
847879
/**
848880
* @NOTE if/when `oak` supports such a method, better import from there instead
849881
*/
850-
function createMockRequestBody(type: BodyType): Body {
882+
function createMockRequestBody(
883+
type: BodyType,
884+
thrownMsgWhenParsed?: string,
885+
): Body {
851886
const buf = new Buffer();
852887
const rs = new ReadableStream();
853888
const retVal = new Body({
@@ -865,22 +900,40 @@ function createMockRequestBody(type: BodyType): Body {
865900
value: () => type,
866901
},
867902
json: {
868-
value: () => Promise.resolve({ mock: "mock" }),
903+
value: () =>
904+
!thrownMsgWhenParsed
905+
? Promise.resolve({ mock: "mock" })
906+
: Promise.reject(new Error(thrownMsgWhenParsed)),
869907
},
870908
text: {
871-
value: () => Promise.resolve("mock"),
909+
value: () =>
910+
!thrownMsgWhenParsed
911+
? Promise.resolve("mock")
912+
: Promise.reject(new Error(thrownMsgWhenParsed)),
872913
},
873914
blob: {
874-
value: () => Promise.resolve(buf),
915+
value: () =>
916+
!thrownMsgWhenParsed
917+
? Promise.resolve(buf)
918+
: Promise.reject(new Error(thrownMsgWhenParsed)),
875919
},
876920
form: {
877-
value: () => Promise.resolve(new URLSearchParams({ mock: "mock" })),
921+
value: () =>
922+
!thrownMsgWhenParsed
923+
? Promise.resolve(new URLSearchParams({ mock: "mock" }))
924+
: Promise.reject(new Error(thrownMsgWhenParsed)),
878925
},
879926
formData: {
880-
value: () => Promise.resolve(new FormData()),
927+
value: () =>
928+
!thrownMsgWhenParsed
929+
? Promise.resolve(new FormData())
930+
: Promise.reject(new Error(thrownMsgWhenParsed)),
881931
},
882932
arrayBuffer: {
883-
value: () => Promise.resolve(buf),
933+
value: () =>
934+
!thrownMsgWhenParsed
935+
? Promise.resolve(buf)
936+
: Promise.reject(new Error(thrownMsgWhenParsed)),
884937
},
885938
});
886939
return retVal;

0 commit comments

Comments
 (0)