From bae26835c40aed71b65fdb62a0963fd6e18ebecf Mon Sep 17 00:00:00 2001 From: dk <2597375+Thesephi@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:52:04 +0200 Subject: [PATCH 1/3] added support for 'headers' in 'ControllerMethodArg' --- CHANGELOG.md | 7 +++ jsr.json | 2 +- src/ControllerMethodArgs.ts | 10 +++- src/ControllerMethodArgs_test.ts | 100 +++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a6abd..9936f3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [Unreleased] + +### Added + +- support for `headers` in `ControllerMethodArg` as a shortcut to parse & + retrieve request headers + ## [0.9.0] - 2024-07-16 ### Changed diff --git a/jsr.json b/jsr.json index 030f042..4cf3d6d 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@dklab/oak-routing-ctrl", - "version": "0.9.0", + "version": "0.10.0-alpha.1", "exports": { ".": "./mod.ts", "./mod": "./mod.ts" diff --git a/src/ControllerMethodArgs.ts b/src/ControllerMethodArgs.ts index 160899c..8dcc476 100644 --- a/src/ControllerMethodArgs.ts +++ b/src/ControllerMethodArgs.ts @@ -17,7 +17,8 @@ import { ERR_UNSUPPORTED_CLASS_METHOD_DECORATOR_RUNTIME_BEHAVIOR } from "./Const export type ControllerMethodArg = | "param" | "body" - | "query"; + | "query" + | "headers"; // an enhanced version of a (decorated) method which // is declared in the (decorated) Controller Class @@ -164,6 +165,13 @@ function getEnhancedHandler( // search query a.k.a URLSearchParams decoratedArgs.push(parsedReqSearchParams); break; + case p === "headers": { + // request headers + const headers: Record = {}; + ctx.request.headers.forEach((v, k) => headers[k] = v); + decoratedArgs.push(headers); + break; + } case ["ctx", "context"].includes(p): // `ctx` or `context` is supported by default (as the last argument) // but can also be declared explicitly diff --git a/src/ControllerMethodArgs_test.ts b/src/ControllerMethodArgs_test.ts index 681da37..85a2fe9 100644 --- a/src/ControllerMethodArgs_test.ts +++ b/src/ControllerMethodArgs_test.ts @@ -126,6 +126,53 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring body, assertEquals(enhancedHandlerRetVal, { "i.am": "deep" }); }); +Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring body, param, query, headers", async () => { + let enhancedHandler; + let enhancedHandlerRetVal; + try { + enhancedHandler = ControllerMethodArgs("body", "param", "query", "headers")( + function testHandler( + _body: unknown, + _param: unknown, + _query: unknown, + headers: unknown, + ) { + // @TODO consider mocking `body`, `param`, and `query` and assert for + // their parsed values + return { + "i.am.also": "deep", + "my.headers.include": headers, + }; + }, + { + name: "testHandler", + } as ClassMethodDecoratorContext, + ); + } catch (e) { + throw new Error(`test case should not have thrown ${e.message}`); + } + assertEquals(typeof enhancedHandler, "function"); + + try { + const ctx = createMockContext(); + Object.defineProperty(ctx.request, "body", { + get: () => createMockRequestBody("json"), + }); + Object.defineProperty(ctx.request, "headers", { + value: new Map([["x-foo", "bar"]]), + }); + + enhancedHandlerRetVal = await enhancedHandler(ctx); + } catch (e) { + throw new Error(`test case should not have thrown ${e.message}`); + } + + assertEquals(enhancedHandlerRetVal, { + "i.am.also": "deep", + "my.headers.include": { "x-foo": "bar" }, + }); +}); + Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring param, body", async () => { let enhancedHandler; let enhancedHandlerRetVal; @@ -308,6 +355,59 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin assertEquals(enhancedHandlerRetVal, { "i.am": "deep.too" }); }); +Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declaring body, param, query, headers", async () => { + const methodDescriptor = { + value: function testHandler( + _body: unknown, + _param: unknown, + _query: unknown, + headers: unknown, + ) { + return { + "i.am.also": "deep.ya", + "my.headers.include": headers + }; + }, + writable: true, + enumerable: false, + configurable: true, + }; + let decoratorRetVal; + try { + decoratorRetVal = ControllerMethodArgs("body", "param", "query", "headers")( + {}, + "testHandler", + methodDescriptor, + ); + } catch (e) { + throw new Error(`test case should not have thrown ${e.message}`); + } + assertEquals(decoratorRetVal, undefined); + + // by this point, `methodDescriptor.value` has been written with a new value + // which is the enhanced `testHandler` + let enhancedHandlerRetVal; + try { + // deno-lint-ignore ban-types + const enhancedHandler: Function = methodDescriptor.value; + const ctx = createMockContext(); + Object.defineProperty(ctx.request, "body", { + get: () => createMockRequestBody("json"), + }); + Object.defineProperty(ctx.request, "headers", { + value: new Map([["x-foo", "bar"]]) + }) + enhancedHandlerRetVal = await enhancedHandler(ctx); + } catch (e) { + throw new Error(`test case should not have thrown ${e.message}`); + } + + assertEquals(enhancedHandlerRetVal, { + "i.am.also": "deep.ya", + "my.headers.include": { "x-foo": "bar" }, + }); +}); + Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declaring param, body", async () => { const methodDescriptor = { value: function testHandler() { From c709e0abb62c5c40c84a12a7436aa971690ae302 Mon Sep 17 00:00:00 2001 From: dk <2597375+Thesephi@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:53:49 +0200 Subject: [PATCH 2/3] updated format --- src/ControllerMethodArgs_test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ControllerMethodArgs_test.ts b/src/ControllerMethodArgs_test.ts index 85a2fe9..ffe4eb3 100644 --- a/src/ControllerMethodArgs_test.ts +++ b/src/ControllerMethodArgs_test.ts @@ -365,7 +365,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin ) { return { "i.am.also": "deep.ya", - "my.headers.include": headers + "my.headers.include": headers, }; }, writable: true, @@ -395,8 +395,8 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin get: () => createMockRequestBody("json"), }); Object.defineProperty(ctx.request, "headers", { - value: new Map([["x-foo", "bar"]]) - }) + value: new Map([["x-foo", "bar"]]), + }); enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { throw new Error(`test case should not have thrown ${e.message}`); From f40b1697263c1af15755514f133617da46c2e605 Mon Sep 17 00:00:00 2001 From: dk <2597375+Thesephi@users.noreply.github.com> Date: Sun, 8 Sep 2024 21:54:53 +0200 Subject: [PATCH 3/3] v0.10.0 - see CHANGELOG for details --- CHANGELOG.md | 9 +++++- deps.ts | 6 ++-- dev_deps.ts | 14 +++++----- jsr.json | 2 +- src/ControllerMethodArgs.ts | 18 +++++++----- src/ControllerMethodArgs_test.ts | 48 ++++++++++++++++---------------- src/useOas.ts | 5 +++- 7 files changed, 58 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9936f3f..4f838e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,17 @@ -## [Unreleased] +## [0.10.0] - 2024-09-08 ### Added - support for `headers` in `ControllerMethodArg` as a shortcut to parse & retrieve request headers +### Changed + +- upgraded dependencies (`@oak/oak@17.0.0`, `@std/path@1.0.4` + `@std/assert@1.0.4`, `@std/testing@1.0.2`, `@std/io@0.224.7`) + +- minor TypeScript syntax updates to better support Deno 2 + ## [0.9.0] - 2024-07-16 ### Changed diff --git a/deps.ts b/deps.ts index 17dd3df..43b7b8d 100644 --- a/deps.ts +++ b/deps.ts @@ -1,13 +1,13 @@ -export { join } from "jsr:@std/path@^0.225.2"; +export { join } from "jsr:@std/path@^1.0.4"; -export { Router } from "jsr:@oak/oak@^16.1.0"; +export { Router } from "jsr:@oak/oak@^17.0.0"; export type { Application, Context, Next, RouteContext, -} from "jsr:@oak/oak@^16.1.0"; +} from "jsr:@oak/oak@^17.0.0"; import { extendZodWithOpenApi, diff --git a/dev_deps.ts b/dev_deps.ts index 13a3404..b9a4025 100644 --- a/dev_deps.ts +++ b/dev_deps.ts @@ -5,16 +5,16 @@ export { assertObjectMatch, assertStringIncludes, assertThrows, -} from "jsr:@std/assert@^1.0.0"; +} from "jsr:@std/assert@^1.0.4"; export { type BodyType, type Middleware, Request, testing as oakTesting, -} from "jsr:@oak/oak@^16.1.0"; +} from "jsr:@oak/oak@^17.0.0"; -export { Body } from "jsr:@oak/oak@^16.1.0/body"; +export { Body } from "jsr:@oak/oak@^17.0.0/body"; export { assertSpyCall, @@ -24,17 +24,17 @@ export { spy, type Stub, stub, -} from "jsr:@std/testing@^0.225.3/mock"; +} from "jsr:@std/testing@^1.0.2/mock"; -export { assertSnapshot } from "jsr:@std/testing@^0.225.3/snapshot"; +export { assertSnapshot } from "jsr:@std/testing@^1.0.2/snapshot"; -export { Buffer } from "jsr:@std/io@^0.224.3"; +export { Buffer } from "jsr:@std/io@^0.224.7"; export { afterEach, beforeEach, describe, it, -} from "jsr:@std/testing@^0.225.3/bdd"; +} from "jsr:@std/testing@^1.0.2/bdd"; export { ZodObject } from "npm:zod@^3.23.8"; diff --git a/jsr.json b/jsr.json index 4cf3d6d..55531ba 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@dklab/oak-routing-ctrl", - "version": "0.10.0-alpha.1", + "version": "0.10.0", "exports": { ".": "./mod.ts", "./mod": "./mod.ts" diff --git a/src/ControllerMethodArgs.ts b/src/ControllerMethodArgs.ts index 8dcc476..15bcd05 100644 --- a/src/ControllerMethodArgs.ts +++ b/src/ControllerMethodArgs.ts @@ -128,22 +128,26 @@ function getEnhancedHandler( try { parsedReqBody = await _internal.parseOakReqBody(ctx); } catch (e) { - return ctx.throw(400, `Unable to parse request body: ${e.message}`, { - stack: e.stack, - }); + return ctx.throw( + 400, + `Unable to parse request body: ${(e as Error).message}`, + { + stack: (e as Error).stack, + }, + ); } const parsedReqSearchParams: Record = {}; try { - ctx.request.url.searchParams.forEach((value, key) => + ctx.request.url.searchParams.forEach((value: string, key: string) => parsedReqSearchParams[key] = value ); } catch (e) { return ctx.throw( 400, - `Unable to parse request search params: ${e.message}`, + `Unable to parse request search params: ${(e as Error).message}`, { - stack: e.stack, + stack: (e as Error).stack, }, ); } @@ -168,7 +172,7 @@ function getEnhancedHandler( case p === "headers": { // request headers const headers: Record = {}; - ctx.request.headers.forEach((v, k) => headers[k] = v); + ctx.request.headers.forEach((v: string, k: string) => headers[k] = v); decoratedArgs.push(headers); break; } diff --git a/src/ControllerMethodArgs_test.ts b/src/ControllerMethodArgs_test.ts index ffe4eb3..d290f23 100644 --- a/src/ControllerMethodArgs_test.ts +++ b/src/ControllerMethodArgs_test.ts @@ -108,7 +108,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring body, } as ClassMethodDecoratorContext, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(typeof enhancedHandler, "function"); @@ -120,7 +120,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring body, enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "i.am": "deep" }); @@ -149,7 +149,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring body, } as ClassMethodDecoratorContext, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(typeof enhancedHandler, "function"); @@ -164,7 +164,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring body, enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { @@ -188,7 +188,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring param, } as ClassMethodDecoratorContext, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(typeof enhancedHandler, "function"); @@ -200,7 +200,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring param, enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "declaring": "param, body" }); @@ -221,7 +221,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring param, } as ClassMethodDecoratorContext, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(typeof enhancedHandler, "function"); @@ -233,7 +233,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring param, enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "declaring": "param, query" }); @@ -254,7 +254,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring body", } as ClassMethodDecoratorContext, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(typeof enhancedHandler, "function"); @@ -266,7 +266,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring body", enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "declaring": "body" }); @@ -285,7 +285,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring nothin } as ClassMethodDecoratorContext, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(typeof enhancedHandler, "function"); @@ -297,7 +297,7 @@ Deno.test("ControllerMethodArgs Decorator - Standard strategy - declaring nothin enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "declaring": "nothing" }); @@ -333,7 +333,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin methodDescriptor, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(decoratorRetVal, undefined); @@ -349,7 +349,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin }); enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "i.am": "deep.too" }); @@ -380,7 +380,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin methodDescriptor, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(decoratorRetVal, undefined); @@ -399,7 +399,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin }); enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { @@ -425,7 +425,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin methodDescriptor, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(decoratorRetVal, undefined); @@ -441,7 +441,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin }); enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "declaring": "param, body" }); @@ -464,7 +464,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin methodDescriptor, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(decoratorRetVal, undefined); @@ -480,7 +480,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin }); enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "declaring": "param, query" }); @@ -503,7 +503,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin methodDescriptor, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(decoratorRetVal, undefined); @@ -519,7 +519,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin }); enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "declaring": "body" }); @@ -542,7 +542,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin methodDescriptor, ); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(decoratorRetVal, undefined); @@ -558,7 +558,7 @@ Deno.test("ControllerMethodArgs Decorator - CloudflareWorker strategy - declarin }); enhancedHandlerRetVal = await enhancedHandler(ctx); } catch (e) { - throw new Error(`test case should not have thrown ${e.message}`); + throw new Error(`test case should not have thrown ${(e as Error).message}`); } assertEquals(enhancedHandlerRetVal, { "declaring": "nothing" }); diff --git a/src/useOas.ts b/src/useOas.ts index cbc39c1..60dc67d 100644 --- a/src/useOas.ts +++ b/src/useOas.ts @@ -96,7 +96,10 @@ export const useOas = ( try { _useOas(app, cfg); } catch (e) { - debug("unable to complete OpenApiSpec initialization:", e.message); + debug( + "unable to complete OpenApiSpec initialization:", + (e as Error).message, + ); } };