Skip to content

Commit

Permalink
v0.12.0 - see CHANGELOG for details (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thesephi authored Nov 5, 2024
1 parent aa5aff6 commit f03686a
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 38 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/runtime-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ on:
workflow_dispatch

jobs:

test-bun:
name: Test on Bun
runs-on: ubuntu-latest
Expand All @@ -21,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['18.x', '20.x']
node-version: ["18.x", "20.x"]
steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
Expand All @@ -33,13 +32,13 @@ jobs:
npm install
- name: Perform Tests
run: npm test

test-cloudflare-workers:
name: Test on Cloudflare Workers dev env
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['18.x', '20.x']
node-version: ["18.x", "20.x"]
steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## [0.12.0] - 2024-11-05

### Changed

- lax parsing rule for GET requests with header 'content-type: application/json'
- upgraded dependencies (`@std/path@^1.0.8`, `@std/testing@^1.0.4`,
`@std/assert@^1.0.7`, `@oak/oak@^17.1.3`,
`@asteasolutions/zod-to-openapi@^7.2.0`, `@std/io@^0.225.0`)
- code format

## [0.11.0] - 2024-09-11

### Changed
Expand Down
12 changes: 6 additions & 6 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
export { join } from "jsr:@std/path@^1.0.4";
export { join } from "jsr:@std/path@^1.0.8";

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

export type {
Application,
Context,
ErrorStatus,
Next,
RouteContext,
} from "jsr:@oak/oak@^17.0.0";
} from "jsr:@oak/oak@^17.1.3";

import {
extendZodWithOpenApi,
type ResponseConfig,
type RouteConfig,
} from "npm:@asteasolutions/zod-to-openapi@^7.1.1";
} from "npm:@asteasolutions/zod-to-openapi@^7.2.0";

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

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

// must import from `npm:` instead of from `deno.land` to be compatible with `@asteasolutions/zod-to-openapi`
import { z as slowTypedZ } from "npm:zod@^3.23.8";
Expand Down
14 changes: 7 additions & 7 deletions dev_deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ export {
assertObjectMatch,
assertStringIncludes,
assertThrows,
} from "jsr:@std/assert@^1.0.4";
} from "jsr:@std/assert@^1.0.7";

export {
type BodyType,
type Middleware,
Request,
testing as oakTesting,
} from "jsr:@oak/oak@^17.0.0";
} from "jsr:@oak/oak@^17.1.3";

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

export {
assertSpyCall,
Expand All @@ -25,17 +25,17 @@ export {
spy,
type Stub,
stub,
} from "jsr:@std/testing@^1.0.2/mock";
} from "jsr:@std/testing@^1.0.4/mock";

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

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

export {
afterEach,
beforeEach,
describe,
it,
} from "jsr:@std/testing@^1.0.2/bdd";
} from "jsr:@std/testing@^1.0.4/bdd";

export { ZodObject } from "npm:zod@^3.23.8";
2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dklab/oak-routing-ctrl",
"version": "0.11.0",
"version": "0.12.0",
"exports": {
".": "./mod.ts",
"./mod": "./mod.ts"
Expand Down
26 changes: 18 additions & 8 deletions src/ControllerMethodArgs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { debug } from "./utils/logger.ts";
import { Context, RouteContext } from "../deps.ts";
import { type Context, type RouteContext } from "../deps.ts";
import { ERR_UNSUPPORTED_CLASS_METHOD_DECORATOR_RUNTIME_BEHAVIOR } from "./Constants.ts";

/**
Expand Down Expand Up @@ -128,13 +128,23 @@ function getEnhancedHandler(
try {
parsedReqBody = await _internal.parseOakReqBody(ctx);
} catch (e) {
return ctx.throw(
400,
`Unable to parse request body: ${(e as Error).message}`,
{
stack: (e as Error).stack,
},
);
if (
ctx.request.method === "GET" &&
ctx.request.headers.get("Content-Type") === "application/json" &&
(e as Error).message?.includes("Unexpected end of JSON input")
) {
// we ignore this parsing error because the client was sending
// a weird combination of method & content-type header
} else {
// for other case, we trigger the error back to userland
return ctx.throw(
400,
`Unable to parse request body: ${(e as Error).message}`,
{
stack: (e as Error).stack,
},
);
}
}

const parsedReqSearchParams: Record<string, string> = {};
Expand Down
77 changes: 65 additions & 12 deletions src/ControllerMethodArgs_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,33 @@ Deno.test("getEnhancedHandler with a faulty ctx.request.body", async () => {
spyParseOakRequestBody.restore();
});

Deno.test("getEnhancedHandler with a faulty request method and content-type combination", async () => {
const spyParseOakRequestBody = spy(_internal, "parseOakReqBody");
function testHandler() {
return "weird.method.content-type.combination.handled";
}
// deno-lint-ignore ban-types
const enhancedHandler: Function = _internal.getEnhancedHandler(testHandler);
const ctx = createMockContext({
method: "GET",
headers: [["Content-Type", "application/json"]],
});
Object.defineProperty(ctx.request, "body", {
get: () => createMockRequestBody("json", "Unexpected end of JSON input"),
});
const spyCtxThrow = spy();
Object.defineProperty(ctx, "throw", {
value: (errorStatus: unknown, message?: string, props?: unknown) => {
spyCtxThrow(errorStatus, message, props);
},
});
const retVal = await enhancedHandler(ctx);
assertSpyCalls(spyCtxThrow, 0);
assertSpyCalls(spyParseOakRequestBody, 1);
assertEquals(retVal, "weird.method.content-type.combination.handled");
spyParseOakRequestBody.restore();
});

Deno.test("getEnhancedHandler with a faulty ctx.request.url.searchParams", async () => {
const spyParseOakRequestBody = spy(_internal, "parseOakReqBody");
function testHandler() {
Expand Down Expand Up @@ -669,7 +696,8 @@ Deno.test("getEnhancedHandler with a faulty ctx.request.url.searchParams", async
});

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

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

Deno.test("getEnhancedHandler - declaring 5 desirable params", async () => {
const spyParseOakRequestBody = spy(_internal, "parseOakReqBody");
const testHandler = spy((..._rest) => 44);
// deno-lint-ignore no-explicit-any
const testHandler = spy((..._rest: any[]) => 44);
// deno-lint-ignore ban-types
const enhancedHandler: Function = _internal.getEnhancedHandler(
testHandler,
Expand Down Expand Up @@ -765,7 +795,8 @@ Deno.test("getEnhancedHandler - declaring 5 desirable params", async () => {
});

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

Deno.test("getEnhancedHandler - declaring 3 desirable params in order B", async () => {
const testHandler = spy((..._rest) => 46);
// deno-lint-ignore no-explicit-any
const testHandler = spy((..._rest: any[]) => 46);
// deno-lint-ignore ban-types
const enhancedHandler: Function = _internal.getEnhancedHandler(
testHandler,
Expand Down Expand Up @@ -847,7 +879,10 @@ Deno.test("getEnhancedHandler - not declaring any param", async () => {
/**
* @NOTE if/when `oak` supports such a method, better import from there instead
*/
function createMockRequestBody(type: BodyType): Body {
function createMockRequestBody(
type: BodyType,
thrownMsgWhenParsed?: string,
): Body {
const buf = new Buffer();
const rs = new ReadableStream();
const retVal = new Body({
Expand All @@ -865,22 +900,40 @@ function createMockRequestBody(type: BodyType): Body {
value: () => type,
},
json: {
value: () => Promise.resolve({ mock: "mock" }),
value: () =>
!thrownMsgWhenParsed
? Promise.resolve({ mock: "mock" })
: Promise.reject(new Error(thrownMsgWhenParsed)),
},
text: {
value: () => Promise.resolve("mock"),
value: () =>
!thrownMsgWhenParsed
? Promise.resolve("mock")
: Promise.reject(new Error(thrownMsgWhenParsed)),
},
blob: {
value: () => Promise.resolve(buf),
value: () =>
!thrownMsgWhenParsed
? Promise.resolve(buf)
: Promise.reject(new Error(thrownMsgWhenParsed)),
},
form: {
value: () => Promise.resolve(new URLSearchParams({ mock: "mock" })),
value: () =>
!thrownMsgWhenParsed
? Promise.resolve(new URLSearchParams({ mock: "mock" }))
: Promise.reject(new Error(thrownMsgWhenParsed)),
},
formData: {
value: () => Promise.resolve(new FormData()),
value: () =>
!thrownMsgWhenParsed
? Promise.resolve(new FormData())
: Promise.reject(new Error(thrownMsgWhenParsed)),
},
arrayBuffer: {
value: () => Promise.resolve(buf),
value: () =>
!thrownMsgWhenParsed
? Promise.resolve(buf)
: Promise.reject(new Error(thrownMsgWhenParsed)),
},
});
return retVal;
Expand Down

0 comments on commit f03686a

Please sign in to comment.