Skip to content

Commit

Permalink
Merge pull request #7 from jfrconley/joco/fix-undefined-bodies
Browse files Browse the repository at this point in the history
Fix undefined bodies
  • Loading branch information
jfrconley authored Oct 31, 2023
2 parents bf138b6 + 517fb37 commit 689cd42
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 91 deletions.
5 changes: 5 additions & 0 deletions .changeset/friendly-turkeys-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nornir/rest": minor
---

added AnyMimeType to replace MimeType.None
5 changes: 5 additions & 0 deletions .changeset/shy-deers-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nornir/rest": patch
---

fix undefined bodies
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"syncpack": "^9.8.4",
"ts-patch": "^3.0.2",
"turbo": "^1.9.2",
"typescript": "^5.1.6"
"typescript": "^5.2.2"
},
"engines": {
"node": ">=18.0.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
"author": "John Conley",
"devDependencies": {
"@jest/globals": "^29.5.0",
"@nrfcloud/ts-json-schema-transformer": "^1.2.3",
"@nrfcloud/ts-json-schema-transformer": "^1.2.4",
"@types/jest": "^29.4.0",
"@types/node": "^18.15.11",
"esbuild": "^0.17.18",
"eslint": "^8.45.0",
"jest": "^29.5.0",
"ts-patch": "^3.0.2",
"typescript": "^5.1.6"
"typescript": "^5.2.2"
},
"engines": {
"node": ">=18.0.0",
Expand Down
37 changes: 31 additions & 6 deletions packages/rest/__tests__/src/routing.spec.mts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
AnyMimeType,
Controller,
GetChain,
HttpEvent,
HttpRequest,
HttpRequestEmpty,
HttpStatusCode,
MimeType,
normalizeEventHeaders,
Expand All @@ -15,10 +15,7 @@ import {nornir, Nornir} from "@nornir/core";
import {describe} from "@jest/globals";
import {NornirRouteNotFoundError} from "../../dist/runtime/router.mjs";

interface RouteGetInput extends HttpRequestEmpty {
headers: {
"content-type": MimeType.None;
};
interface RouteGetInput extends HttpRequest {
}


Expand Down Expand Up @@ -85,7 +82,7 @@ class TestController {
@GetChain("/route")
public getRoute(chain: Nornir<RouteGetInput>) {
return chain
.use(input => input.headers["content-type"])
.use(console.log)
.use(() => ({
statusCode: HttpStatusCode.Ok,
body: `cool`,
Expand All @@ -96,6 +93,18 @@ class TestController {
}));
}

@GetChain("/route2")
public getEmptyRoute(chain: Nornir<RouteGetInput>) {
return chain
.use(() => ({
statusCode: HttpStatusCode.Ok,
body: undefined,
headers: {
"content-type": AnyMimeType
},
}));
}

@PostChain("/route")
public postRoute(chain: Nornir<RoutePostInput>) {
return chain
Expand Down Expand Up @@ -152,6 +161,22 @@ describe("REST tests", () => {
}
})
})

it("Should process a GET request with an empty body", async () => {
const response = await handler({
method: "GET",
path: "/basepath/route2",
headers: {},
query: {}
});
expect(response).toEqual({
statusCode: HttpStatusCode.Ok,
body: undefined,
headers: {
"content-type": AnyMimeType
}
})
})
})

describe("Invalid requests", () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/rest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"version": "1.2.1",
"dependencies": {
"@nornir/core": "workspace:^",
"@nrfcloud/ts-json-schema-transformer": "^1.2.3",
"@nrfcloud/ts-json-schema-transformer": "^1.2.4",
"@types/aws-lambda": "^8.10.115",
"ajv": "^8.12.0",
"openapi-types": "^12.1.0",
"trouter": "^3.2.1",
"ts-json-schema-generator": "^1.3.0-next.7",
"ts-json-schema-generator": "^1.4.0",
"ts-morph": "^19.0.0",
"tsutils": "^3.21.0"
},
Expand All @@ -20,7 +20,7 @@
"eslint": "^8.45.0",
"jest": "^29.5.0",
"ts-patch": "^3.0.2",
"typescript": "^5.1.6"
"typescript": "^5.2.2"
},
"engines": {
"node": ">=18.0.0",
Expand Down
19 changes: 13 additions & 6 deletions packages/rest/src/runtime/http-event.mts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {Nominal} from "./utils.mjs";

export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";

export type HttpEvent = Omit<HttpRequest, "pathParams" | "headers"> & {
Expand All @@ -16,9 +18,10 @@ export type UnparsedHttpEvent = Omit<HttpEvent, "body" | "query"> & {
// } & Record<string, string | number>

export type HttpHeadersWithContentType = {
readonly "content-type": MimeType;
readonly "content-type": MimeType | AnyMimeType
} & HttpHeaders;


export type HttpHeaders = Record<string, number | string>;

export interface HttpRequest {
Expand All @@ -33,9 +36,9 @@ export interface HttpRequest {

export interface HttpRequestEmpty extends HttpRequest {
headers: {
"content-type": MimeType.None;
"content-type": AnyMimeType;
}
body: never;
// body?: undefined;
}

export interface HttpRequestJSON extends HttpRequest {
Expand All @@ -56,7 +59,10 @@ export interface SerializedHttpResponse extends Omit<HttpResponse, "body"> {
}

export interface HttpResponseEmpty extends HttpResponse {
body?: never;
headers: {
"content-type": AnyMimeType;
},
body?: undefined;
}

export enum HttpStatusCode {
Expand Down Expand Up @@ -121,6 +127,9 @@ export enum HttpStatusCode {
NotExtended = "510",
}

export type AnyMimeType = Nominal<string | undefined, "AnyMimeType">
export const AnyMimeType = "*/*" as AnyMimeType;

export enum MimeType {
None = "",
ApplicationJson = "application/json",
Expand Down Expand Up @@ -153,6 +162,4 @@ export enum MimeType {
VideoXMsVideo = "video/x-msvideo",
VideoXFlv = "video/x-flv",
VideoWebm = "video/webm",


}
2 changes: 1 addition & 1 deletion packages/rest/src/runtime/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export {
export {
HttpResponse, HttpRequest, HttpEvent, HttpMethod, HttpRequestEmpty, HttpResponseEmpty,
HttpStatusCode, HttpRequestJSON, HttpHeaders, MimeType,
UnparsedHttpEvent, SerializedHttpResponse
UnparsedHttpEvent, SerializedHttpResponse, AnyMimeType
} from './http-event.mjs';
export {RouteHolder, NornirRestRequestValidationError} from './route-holder.mjs'
export {NornirRestRequestError, NornirRestError, httpErrorHandler, mapError, mapErrorClass} from './error.mjs'
Expand Down
14 changes: 11 additions & 3 deletions packages/rest/src/runtime/router.mts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import Trouter from 'trouter';
import {RouteBuilder, RouteHolder} from './route-holder.mjs';
import {HttpEvent, HttpMethod, HttpRequest, HttpResponse, HttpStatusCode, MimeType} from './http-event.mjs';
import {
HttpEvent,
HttpHeadersWithContentType,
HttpMethod,
HttpRequest,
HttpResponse,
HttpStatusCode,
MimeType
} from './http-event.mjs';
import {AttachmentRegistry, Nornir, Result} from '@nornir/core';
import {NornirRestRequestError} from "./error.mjs";

Expand Down Expand Up @@ -60,9 +68,9 @@ export class Router {
const request: HttpRequest = {
...event,
headers: {
"content-type": MimeType.None,
"content-type": "" as never,
...event.headers,
},
} as HttpHeadersWithContentType,
pathParams: params,
}
if (handler == undefined) {
Expand Down
4 changes: 2 additions & 2 deletions packages/rest/src/runtime/serialize.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ export type HttpBodySerializer<T> = (body: T | undefined) => Buffer
export type HttpBodySerializerMap = Partial<Record<MimeType | "default", HttpBodySerializer<never>>>

const DEFAULT_BODY_SERIALIZERS: HttpBodySerializerMap & {default: HttpBodySerializer<never>} = {
"application/json": (body?: object) => Buffer.from(JSON.stringify(body), "utf8"),
"application/json": (body?: object) => Buffer.from(JSON.stringify(body) || "", "utf8"),
"text/plain": (body?: string) => Buffer.from(body?.toString() || "", "utf8"),
"default": (body?: never) => Buffer.from(JSON.stringify(body), "utf8")
"default": (body?: never) => Buffer.from(JSON.stringify(body) || "", "utf8")
}

export function httpResponseSerializer(bodySerializerMap?: HttpBodySerializerMap) {
Expand Down
12 changes: 12 additions & 0 deletions packages/rest/src/runtime/utils.mts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,15 @@ export function normalizeHeaders(headers: HttpHeaders): HttpHeaders {
...lowercaseHeaders
}
}

/**
* @internal
*/
export declare class Tagged<N extends string> {
protected _nominal_: N;
}

/**
* @internal
*/
export type Nominal<T, N extends string, E extends T & Tagged<string> = T & Tagged<N>> = (T & Tagged<N>) | E;
4 changes: 2 additions & 2 deletions packages/test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
},
"devDependencies": {
"@jest/globals": "^29.5.0",
"@nrfcloud/ts-json-schema-transformer": "^1.2.3",
"@nrfcloud/ts-json-schema-transformer": "^1.2.4",
"@types/aws-lambda": "^8.10.115",
"@types/jest": "^29.4.0",
"@types/node": "^18.15.11",
"esbuild": "^0.17.18",
"eslint": "^8.45.0",
"jest": "^29.5.0",
"ts-patch": "^3.0.2",
"typescript": "^5.1.6"
"typescript": "^5.2.2"
},
"engines": {
"node": ">=18.0.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/test/src/controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Nornir } from "@nornir/core";
import {
AnyMimeType,
Controller,
GetChain,
type HttpRequest,
Expand All @@ -13,7 +14,7 @@ import { assertValid } from "@nrfcloud/ts-json-schema-transformer";
interface RouteGetInput extends HttpRequestEmpty {
headers: {
// eslint-disable-next-line sonarjs/no-duplicate-string
"content-type": MimeType.None;
"content-type": AnyMimeType;
};
}

Expand Down
5 changes: 3 additions & 2 deletions packages/test/src/controller2.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Nornir } from "@nornir/core";
import {
AnyMimeType,
Controller,
GetChain,
HttpRequest,
Expand All @@ -16,7 +17,7 @@ interface RouteGetInput extends HttpRequestEmpty {
headers: GetHeaders;
}
interface GetHeaders {
"content-type": MimeType.None;
"content-type": AnyMimeType;
[key: string]: string;
}

Expand Down Expand Up @@ -72,7 +73,7 @@ export class TestController {
.use(() => ({
statusCode: HttpStatusCode.Created,
headers: {
"content-type": MimeType.TextPlain,
"content-type": AnyMimeType,
},
}));
}
Expand Down
5 changes: 3 additions & 2 deletions packages/test/src/rest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import nornir, { AttachmentRegistry } from "@nornir/core";
import nornir from "@nornir/core";
import {
AnyMimeType,
ApiGatewayProxyV2,
httpErrorHandler,
httpEventParser,
Expand Down Expand Up @@ -39,7 +40,7 @@ const frameworkChain = nornir<UnparsedHttpEvent>()
mapErrorClass(TestError, err => ({
statusCode: HttpStatusCode.InternalServerError,
headers: {
"content-type": MimeType.None,
"content-type": AnyMimeType,
},
})),
]))
Expand Down
Loading

0 comments on commit 689cd42

Please sign in to comment.