Skip to content

Commit

Permalink
Exposing the expected request content type (#1739)
Browse files Browse the repository at this point in the history
This should enable connecting uploader/raw parsers only to the endpoints
that really need it (initRouting).
Then can revert #1733
  • Loading branch information
RobinTail committed May 7, 2024
1 parent b0e5e89 commit 111f27e
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 31 deletions.
6 changes: 3 additions & 3 deletions src/common-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { InputValidationError, OutputValidationError } from "./errors";
import { AbstractLogger } from "./logger";
import { getMeta } from "./metadata";
import { AuxMethod, Method } from "./method";
import { mimeMultipart } from "./mime";
import { contentTypes } from "./content-type";

export type FlatObject = Record<string, unknown>;

const areFilesAvailable = (request: Request): boolean => {
const contentType = request.header("content-type") || "";
const isMultipart = contentType.toLowerCase().startsWith(mimeMultipart);
return "files" in request && isMultipart;
const isUpload = contentType.toLowerCase().startsWith(contentTypes.upload);
return "files" in request && isUpload;
};

export const defaultInputSources: InputSources = {
Expand Down
7 changes: 7 additions & 0 deletions src/content-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const contentTypes = {
json: "application/json",
upload: "multipart/form-data",
raw: "application/octet-stream",
};

export type ContentType = keyof typeof contentTypes;
26 changes: 18 additions & 8 deletions src/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { AbstractLogger } from "./logger";
import { LogicalContainer, combineContainers } from "./logical-container";
import { AuxMethod, Method } from "./method";
import { AnyMiddlewareDef } from "./middleware";
import { mimeJson, mimeMultipart, mimeRaw } from "./mime";
import { ContentType, contentTypes } from "./content-type";
import { AnyResultHandlerDefinition } from "./result-handler";
import { Security } from "./security";

Expand Down Expand Up @@ -61,6 +61,7 @@ export abstract class AbstractEndpoint {
public abstract getScopes(): string[];
public abstract getTags(): string[];
public abstract getOperationId(method: Method): string | undefined;
public abstract getRequestType(): ContentType;
}

export class Endpoint<
Expand All @@ -81,6 +82,7 @@ export class Endpoint<
readonly #scopes: SCO[];
readonly #tags: TAG[];
readonly #getOperationId: (method: Method) => string | undefined;
readonly #requestType: ContentType;

constructor({
methods,
Expand Down Expand Up @@ -128,10 +130,13 @@ export class Endpoint<
this.#responses = {
positive: normalizeApiResponse(
resultHandler.getPositiveResponse(outputSchema),
{ mimeTypes: [mimeJson], statusCodes: [defaultStatusCodes.positive] },
{
mimeTypes: [contentTypes.json],
statusCodes: [defaultStatusCodes.positive],
},
),
negative: normalizeApiResponse(resultHandler.getNegativeResponse(), {
mimeTypes: [mimeJson],
mimeTypes: [contentTypes.json],
statusCodes: [defaultStatusCodes.negative],
}),
};
Expand All @@ -143,12 +148,13 @@ export class Endpoint<
),
);
}
this.#requestType = hasUpload(inputSchema)
? "upload"
: hasRaw(inputSchema)
? "raw"
: "json";
this.#mimeTypes = {
input: hasUpload(inputSchema)
? [mimeMultipart]
: hasRaw(inputSchema)
? [mimeRaw]
: [mimeJson],
input: [contentTypes[this.#requestType]],
positive: this.#responses.positive.flatMap(({ mimeTypes }) => mimeTypes),
negative: this.#responses.negative.flatMap(({ mimeTypes }) => mimeTypes),
};
Expand Down Expand Up @@ -178,6 +184,10 @@ export class Endpoint<
return this.#mimeTypes[variant];
}

public override getRequestType() {
return this.#requestType;
}

public override getResponses(variant: ResponseVariant) {
return this.#responses[variant];
}
Expand Down
8 changes: 5 additions & 3 deletions src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from "./integration-helpers";
import { defaultSerializer, makeCleanId } from "./common-helpers";
import { Method, methods } from "./method";
import { mimeJson } from "./mime";
import { contentTypes } from "./content-type";
import { loadPeer } from "./peer-helpers";
import { Routing } from "./routing";
import { walkRouting } from "./routing-walker";
Expand Down Expand Up @@ -219,7 +219,9 @@ export class Integration {
positive: positiveResponseId,
negative: negativeResponseId,
response: genericResponseId,
isJson: endpoint.getMimeTypes("positive").includes(mimeJson),
isJson: endpoint
.getMimeTypes("positive")
.includes(contentTypes.json),
tags: endpoint.getTags(),
},
);
Expand Down Expand Up @@ -482,7 +484,7 @@ export class Integration {
f.createObjectLiteralExpression([
f.createPropertyAssignment(
f.createStringLiteral("Content-Type"),
f.createStringLiteral(mimeJson),
f.createStringLiteral(contentTypes.json),
),
]),
undefined,
Expand Down
3 changes: 0 additions & 3 deletions src/mime.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import http from "node:http";
import { CommonConfig } from "./config-type";
import { AbstractEndpoint } from "./endpoint";
import { AbstractLogger } from "./logger";
import { mimeJson } from "./mime";
import { contentTypes } from "./content-type";
import { loadAlternativePeer } from "./peer-helpers";

/**
Expand All @@ -25,7 +25,7 @@ export const makeRequestMock = <REQ extends Record<string, any>>({
}) =>
({
method: "GET",
header: fnMethod(() => mimeJson),
header: fnMethod(() => contentTypes.json),
...requestProps,
}) as { method: string } & Record<"header", MockOverrides> & REQ;

Expand Down
12 changes: 12 additions & 0 deletions tests/unit/content-type.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { contentTypes } from "../../src/content-type";
import { describe, expect, test } from "vitest";

describe("contentTypes", () => {
test("should has predefined properties", () => {
expect(contentTypes).toEqual({
json: "application/json",
upload: "multipart/form-data",
raw: "application/octet-stream",
});
});
});
4 changes: 2 additions & 2 deletions tests/unit/documentation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
ez,
} from "../../src";
import { expectType } from "tsd";
import { mimeJson } from "../../src/mime";
import { contentTypes } from "../../src/content-type";
import { z } from "zod";
import { givePort } from "../helpers";
import { describe, expect, test, vi } from "vitest";
Expand Down Expand Up @@ -791,7 +791,7 @@ describe("Documentation", () => {
const resultHandler = createResultHandler({
getPositiveResponse: (output) => ({
schema: z.object({ status: z.literal("OK"), result: output }),
mimeTypes: [mimeJson, "text/vnd.yaml"],
mimeTypes: [contentTypes.json, "text/vnd.yaml"],
statusCode: 201,
}),
getNegativeResponse: () => ({
Expand Down
20 changes: 20 additions & 0 deletions tests/unit/endpoint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,26 @@ describe("Endpoint", () => {
);
});

describe("getRequestType()", () => {
test.each([
{ input: z.object({}), expected: "json" },
{ input: ez.raw(), expected: "raw" },
{ input: z.object({ file: ez.upload() }), expected: "upload" },
])(
"should return the assigned one upon constructing",
({ input, expected }) => {
const factory = new EndpointsFactory(defaultResultHandler);
const endpoint = factory.build({
method: "get",
input,
output: z.object({}),
handler: vi.fn(),
});
expect(endpoint.getRequestType()).toEqual(expected);
},
);
});

describe(".getOperationId()", () => {
test("should return undefined if its not defined upon creaton", () => {
expect(
Expand Down
10 changes: 0 additions & 10 deletions tests/unit/mime.spec.ts

This file was deleted.

0 comments on commit 111f27e

Please sign in to comment.