diff --git a/example/endpoints/accept-raw.ts b/example/endpoints/accept-raw.ts index a6eb3faa7..45bf972e1 100644 --- a/example/endpoints/accept-raw.ts +++ b/example/endpoints/accept-raw.ts @@ -3,8 +3,8 @@ import { ez } from "../../src"; import { taggedEndpointsFactory } from "../factories"; export const rawAcceptingEndpoint = taggedEndpointsFactory.build({ - method: "post", - tag: "files", + methods: "post", + tags: "files", input: ez .raw() // requires to enable rawParser option in server config .extend({}), // additional inputs, route params for example, if needed diff --git a/example/endpoints/create-user.ts b/example/endpoints/create-user.ts index d42a2d5b1..484855420 100644 --- a/example/endpoints/create-user.ts +++ b/example/endpoints/create-user.ts @@ -5,8 +5,8 @@ import { statusDependingFactory } from "../factories"; /** @desc depending on the thrown error, the custom result handler of the factory responds slightly differently */ export const createUserEndpoint = statusDependingFactory.build({ - method: "post", - tag: "users", + methods: "post", + tags: "users", input: z.object({ name: z.string().min(1), }), diff --git a/example/endpoints/list-users.ts b/example/endpoints/list-users.ts index 25a34b7d3..f6af8ddc6 100644 --- a/example/endpoints/list-users.ts +++ b/example/endpoints/list-users.ts @@ -7,8 +7,8 @@ import { arrayRespondingFactory } from "../factories"; * Avoid doing this in new projects. This feature is only for easier migration of legacy APIs. * */ export const listUsersEndpoint = arrayRespondingFactory.build({ - method: "get", - tag: "users", + methods: "get", + tags: "users", input: z.object({}), output: withMeta( z.object({ diff --git a/example/endpoints/retrieve-user.ts b/example/endpoints/retrieve-user.ts index 9357c7906..d8c2ad624 100644 --- a/example/endpoints/retrieve-user.ts +++ b/example/endpoints/retrieve-user.ts @@ -18,8 +18,8 @@ const feature: z.ZodType = baseFeature.extend({ export const retrieveUserEndpoint = taggedEndpointsFactory .addMiddleware(methodProviderMiddleware) .build({ - method: "get", - tag: "users", + methods: "get", + tags: "users", shortDescription: "Retrieves the user.", description: "Example user retrieval endpoint.", input: z.object({ diff --git a/example/endpoints/send-avatar.ts b/example/endpoints/send-avatar.ts index 05c306bd7..324e93c6d 100644 --- a/example/endpoints/send-avatar.ts +++ b/example/endpoints/send-avatar.ts @@ -3,7 +3,7 @@ import { fileSendingEndpointsFactory } from "../factories"; import { readFile } from "node:fs/promises"; export const sendAvatarEndpoint = fileSendingEndpointsFactory.build({ - method: "get", + methods: "get", shortDescription: "Sends a file content.", tags: ["files", "users"], input: z.object({ diff --git a/example/endpoints/stream-avatar.ts b/example/endpoints/stream-avatar.ts index 218a05852..b16740876 100644 --- a/example/endpoints/stream-avatar.ts +++ b/example/endpoints/stream-avatar.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { fileStreamingEndpointsFactory } from "../factories"; export const streamAvatarEndpoint = fileStreamingEndpointsFactory.build({ - method: "get", + methods: "get", shortDescription: "Streams a file content.", tags: ["users", "files"], input: z.object({ diff --git a/example/endpoints/update-user.ts b/example/endpoints/update-user.ts index 190715881..b93dae671 100644 --- a/example/endpoints/update-user.ts +++ b/example/endpoints/update-user.ts @@ -6,8 +6,8 @@ import { keyAndTokenAuthenticatedEndpointsFactory } from "../factories"; export const updateUserEndpoint = keyAndTokenAuthenticatedEndpointsFactory.build({ - method: "patch", - tag: "users", + methods: "patch", + tags: "users", description: "Changes the user record. Example user update endpoint.", input: withMeta( z.object({ diff --git a/example/endpoints/upload-avatar.ts b/example/endpoints/upload-avatar.ts index e9cd2598c..af81f9c35 100644 --- a/example/endpoints/upload-avatar.ts +++ b/example/endpoints/upload-avatar.ts @@ -4,8 +4,8 @@ import { createHash } from "node:crypto"; import { taggedEndpointsFactory } from "../factories"; export const uploadAvatarEndpoint = taggedEndpointsFactory.build({ - method: "post", - tag: "files", + methods: "post", + tags: "files", description: "Handles a file upload.", input: z .object({ diff --git a/src/endpoints-factory.ts b/src/endpoints-factory.ts index 1e5f43875..a3aa3e701 100644 --- a/src/endpoints-factory.ts +++ b/src/endpoints-factory.ts @@ -36,9 +36,10 @@ type BuildProps< description?: string; shortDescription?: string; operationId?: string | ((method: Method) => string); -} & ({ method: Method } | { methods: Method[] }) & - ({ scopes?: SCO[] } | { scope?: SCO }) & - ({ tags?: TAG[] } | { tag?: TAG }); + methods: Method | Method[]; + scopes?: SCO | SCO[]; + tags?: TAG | TAG[]; +}; export class EndpointsFactory< IN extends IOSchema<"strip"> | null = null, @@ -145,7 +146,9 @@ export class EndpointsFactory< description, shortDescription, operationId, - ...rest + methods, + scopes, + tags, }: BuildProps): Endpoint< ProbableIntersection, BOUT, @@ -154,25 +157,16 @@ export class EndpointsFactory< TAG > { const { middlewares, resultHandler } = this; - const methods = "methods" in rest ? rest.methods : [rest.method]; const getOperationId = typeof operationId === "function" ? operationId : () => operationId; - const scopes = - "scopes" in rest - ? rest.scopes - : "scope" in rest && rest.scope - ? [rest.scope] - : []; - const tags = - "tags" in rest ? rest.tags : "tag" in rest && rest.tag ? [rest.tag] : []; return new Endpoint({ handler, middlewares, outputSchema, resultHandler, - scopes, - tags, - methods, + scopes: scopes ? (Array.isArray(scopes) ? scopes : [scopes]) : [], + tags: tags ? (Array.isArray(tags) ? tags : [tags]) : [], + methods: Array.isArray(methods) ? methods : [methods], getOperationId, description, shortDescription, diff --git a/tests/system/system.spec.ts b/tests/system/system.spec.ts index 118f8afb7..bf34c99c8 100644 --- a/tests/system/system.spec.ts +++ b/tests/system/system.spec.ts @@ -28,7 +28,7 @@ describe("App", async () => { }, ) .build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({ corsDone: z.boolean() }), handler: async ({ options }) => ({ @@ -59,7 +59,7 @@ describe("App", async () => { }), ) .build({ - method: "get", + methods: "get", input: z.object({ epError: z .any() diff --git a/tests/unit/depends-on-method.spec.ts b/tests/unit/depends-on-method.spec.ts index 0a747ac7c..741729ea2 100644 --- a/tests/unit/depends-on-method.spec.ts +++ b/tests/unit/depends-on-method.spec.ts @@ -16,7 +16,7 @@ describe("DependsOnMethod", () => { test("should accept an endpoint with a corresponding method", () => { const instance = new DependsOnMethod({ post: new EndpointsFactory(defaultResultHandler).build({ - method: "post", + methods: "post", input: z.object({}), output: z.object({}), handler: async () => ({}), diff --git a/tests/unit/documentation.spec.ts b/tests/unit/documentation.spec.ts index fa2c62f44..44e3ddc5b 100644 --- a/tests/unit/documentation.spec.ts +++ b/tests/unit/documentation.spec.ts @@ -271,7 +271,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ bigint: z.bigint(), boolean: z.boolean().readonly(), @@ -301,7 +301,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({}), output: z.object({ simple: z.record(z.number().int()), @@ -331,7 +331,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({ any: z.any(), }), @@ -355,7 +355,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ double: z.number(), doublePositive: z.number().positive(), @@ -387,7 +387,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ regular: z.string(), min: z.string().min(1), @@ -429,7 +429,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ ofOne: z.tuple([z.boolean()]), ofStrings: z.tuple([z.string(), z.string().nullable()]), @@ -459,7 +459,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ regularEnum: z.enum(["ABC", "DEF"]), }), @@ -491,7 +491,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({ string, number }), output: z.object({ boolean }), handler: async () => ({ boolean: [] }), @@ -525,7 +525,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: baseCategorySchema, output: z.object({ zodExample: categorySchema, @@ -569,7 +569,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ property: zodType, }), @@ -631,7 +631,7 @@ describe("Documentation", () => { v1: { getSomething: defaultEndpointsFactory.addMiddleware(mw1).build({ scopes: ["this should be omitted"], - method: "get", + methods: "get", input: z.object({ str: z.string(), }), @@ -641,15 +641,15 @@ describe("Documentation", () => { handler: async () => ({ num: 123 }), }), setSomething: defaultEndpointsFactory.addMiddleware(mw2).build({ - scope: "write", - method: "post", + scopes: "write", + methods: "post", input: z.object({}), output: z.object({}), handler: async () => ({}), }), updateSomething: defaultEndpointsFactory.addMiddleware(mw3).build({ scopes: ["this should be omitted"], - method: "put", + methods: "put", input: z.object({}), output: z.object({}), handler: async () => ({}), @@ -671,14 +671,14 @@ describe("Documentation", () => { getSome: { thing: defaultEndpointsFactory.build({ description: "thing is the path segment", - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: async () => ({}), }), ":thing": defaultEndpointsFactory.build({ description: "thing is the path parameter", - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: async () => ({}), @@ -702,7 +702,7 @@ describe("Documentation", () => { getSome: { thing: defaultEndpointsFactory.build({ description: "thing is the path segment", - method: "get", + methods: "get", operationId, input: z.object({}), output: z.object({}), @@ -762,7 +762,7 @@ describe("Documentation", () => { getSome: { thing: defaultEndpointsFactory.build({ description: "thing is the path segment", - method: "get", + methods: "get", operationId, input: z.object({}), output: z.object({}), @@ -772,7 +772,7 @@ describe("Documentation", () => { getSomeTwo: { thing: defaultEndpointsFactory.build({ description: "thing is the path segment", - method: "get", + methods: "get", operationId, input: z.object({}), output: z.object({}), @@ -808,7 +808,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: factory.build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: async () => ({}), @@ -868,7 +868,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: unionSchema, output: unionSchema, handler: async ({ input }) => { @@ -901,7 +901,7 @@ describe("Documentation", () => { routing: { v1: { ":name": defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ name: z.literal("John").or(z.literal("Jane")), other: z.boolean(), @@ -936,7 +936,7 @@ describe("Documentation", () => { routing: { v1: { ":name": defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ name: z.literal("John").or(z.literal("Jane")), other: z.boolean(), @@ -960,7 +960,7 @@ describe("Documentation", () => { routing: { v1: { ":name": defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({ name: z.literal("John").or(z.literal("Jane")), other: z.boolean(), @@ -990,7 +990,7 @@ describe("Documentation", () => { routing: { v1: { test: defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({ id: z.string(), "x-request-id": z.string(), @@ -1038,7 +1038,7 @@ describe("Documentation", () => { routing: { v1: { mtpl: factory.build({ - method: "post", + methods: "post", input: z.object({ test: z.number() }), output: z.object({ payload: z.string() }), handler: async () => ({ payload: "test" }), @@ -1057,7 +1057,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({ str: z.string().describe("here is the test"), }), @@ -1086,7 +1086,7 @@ describe("Documentation", () => { routing: { hris: { employees: defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({ cursor: z .string() @@ -1114,7 +1114,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({ strNum: withMeta( z.string().transform((v) => parseInt(v, 10)), @@ -1142,7 +1142,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: withMeta( z.object({ strNum: z.string().transform((v) => parseInt(v, 10)), @@ -1174,7 +1174,7 @@ describe("Documentation", () => { routing: { v1: { getSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: withMeta( z.object({ strNum: z.string().transform((v) => parseInt(v, 10)), @@ -1219,7 +1219,7 @@ describe("Documentation", () => { }), ) .build({ - method: "post", + methods: "post", input: withMeta( z.object({ str: z.string(), @@ -1252,7 +1252,7 @@ describe("Documentation", () => { routing: { v1: { addSomething: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: withMeta(zodSchema).example({ a: "first" }), output: withMeta(zodSchema.extend({ b: z.string() })) .example({ a: "first", b: "prefix_first" }) diff --git a/tests/unit/endpoint.spec.ts b/tests/unit/endpoint.spec.ts index 803f9eed1..5b1c9d83f 100644 --- a/tests/unit/endpoint.spec.ts +++ b/tests/unit/endpoint.spec.ts @@ -127,7 +127,7 @@ describe("Endpoint", () => { test("should close the stream on OPTIONS request", async () => { const handlerMock = vi.fn(); const endpoint = defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: handlerMock, @@ -172,7 +172,7 @@ describe("Endpoint", () => { describe("#parseOutput", () => { test("Should throw on output validation failure", async () => { const endpoint = defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({}), output: z.object({ email: z.string().email() }), handler: async () => ({ email: "not email" }), @@ -190,7 +190,7 @@ describe("Endpoint", () => { test("Should throw on output parsing non-Zod error", async () => { const factory = new EndpointsFactory(defaultResultHandler); const endpoint = factory.build({ - method: "post", + methods: "post", input: z.object({}), output: z.object({ test: z.number().transform(() => assert.fail("Something unexpected")), @@ -230,7 +230,7 @@ describe("Endpoint", () => { ); const handlerMock = vi.fn(); const endpoint = factory.build({ - method: "post", + methods: "post", input: z.object({}), output: z.object({}), handler: handlerMock, @@ -267,7 +267,7 @@ describe("Endpoint", () => { }), ); const endpoint = factory.build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({ test: z.string(), @@ -301,7 +301,7 @@ describe("Endpoint", () => { something: z.number(), }); const endpoint = factory.build({ - method: "get", + methods: "get", input, output, handler: vi.fn(), @@ -317,7 +317,7 @@ describe("Endpoint", () => { (variant) => { const factory = new EndpointsFactory(defaultResultHandler); const endpoint = factory.build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({ something: z.number() }), handler: vi.fn(), @@ -335,7 +335,7 @@ describe("Endpoint", () => { (variant) => { const factory = new EndpointsFactory(defaultResultHandler); const endpoint = factory.build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({ something: z.number() }), handler: vi.fn(), @@ -439,7 +439,7 @@ describe("Endpoint", () => { test("thrown in #parseOutput()", async () => { const factory = new EndpointsFactory(defaultResultHandler); const endpoint = factory.build({ - method: "post", + methods: "post", input: z.object({}), output: z.object({ test: z.number().transform(() => assert.fail("Something unexpected")), @@ -468,7 +468,7 @@ describe("Endpoint", () => { }), ); const endpoint = factory.build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({ test: z.string(), @@ -520,7 +520,7 @@ describe("Endpoint", () => { describe("Issue #654: Top level refinements", () => { const endpoint = defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z .object({ type: z.union([z.literal("type1"), z.literal("type2")]), @@ -616,7 +616,7 @@ describe("Endpoint", () => { describe("Feature #600: Top level refinements", () => { const endpoint = defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z .object({ email: z.string().email().optional(), @@ -761,7 +761,7 @@ describe("Endpoint", () => { const endpoint = defaultEndpointsFactory .addMiddleware(dateInputMiddleware) .build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: async ({ input: { middleware_date_input }, logger }) => { diff --git a/tests/unit/endpoints-factory.spec.ts b/tests/unit/endpoints-factory.spec.ts index 46efab274..34a253ded 100644 --- a/tests/unit/endpoints-factory.spec.ts +++ b/tests/unit/endpoints-factory.spec.ts @@ -284,7 +284,7 @@ describe("EndpointsFactory", () => { ); const handlerMock = vi.fn(); const endpoint = factory.build({ - method: "get", + methods: "get", input: z.object({ s: z.string(), }), @@ -330,7 +330,7 @@ describe("EndpointsFactory", () => { middleware, ); const endpoint = factory.build({ - method: "get", + methods: "get", input: z.object({ i: z.string(), }), diff --git a/tests/unit/integration.spec.ts b/tests/unit/integration.spec.ts index a48cc8518..4139cb56e 100644 --- a/tests/unit/integration.spec.ts +++ b/tests/unit/integration.spec.ts @@ -22,7 +22,7 @@ describe("Integration", () => { routing: { v1: { "test-with-dashes": defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ opt: z.string().optional(), }), @@ -45,7 +45,7 @@ describe("Integration", () => { routing: { v1: { "test-with-dashes": defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ opt: z.string().optional(), }), @@ -67,7 +67,7 @@ describe("Integration", () => { routing: { v1: { test: defaultEndpointsFactory.build({ - method: "post", + methods: "post", input: z.object({ opt: z.string().optional(), }), @@ -108,7 +108,7 @@ describe("Integration", () => { routing: { v1: { mtpl: factory.build({ - method: "post", + methods: "post", input: z.object({ test: z.number() }), output: z.object({ payload: z.string() }), handler: async () => ({ payload: "test" }), diff --git a/tests/unit/routing.spec.ts b/tests/unit/routing.spec.ts index bccc406bd..8e3b8df9b 100644 --- a/tests/unit/routing.spec.ts +++ b/tests/unit/routing.spec.ts @@ -200,13 +200,13 @@ describe("Routing", () => { const input = z.object({}); const output = z.object({}); const getEndpoint = factory.build({ - method: "get", + methods: "get", input, output, handler, }); const postEndpoint = factory.build({ - method: "post", + methods: "post", input, output, handler, @@ -410,7 +410,7 @@ describe("Routing", () => { }; const handlerMock = vi.fn(); const endpoint = defaultEndpointsFactory.build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: handlerMock, diff --git a/tests/unit/server.spec.ts b/tests/unit/server.spec.ts index e1ab83879..2cf822068 100644 --- a/tests/unit/server.spec.ts +++ b/tests/unit/server.spec.ts @@ -146,7 +146,7 @@ describe("Server", () => { const routingMock = { v1: { test: new EndpointsFactory(defaultResultHandler).build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: vi.fn(), @@ -180,7 +180,7 @@ describe("Server", () => { const routingMock = { v1: { test: new EndpointsFactory(defaultResultHandler).build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: vi.fn(), @@ -206,7 +206,7 @@ describe("Server", () => { const routingMock = { v1: { test: new EndpointsFactory(defaultResultHandler).build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: vi.fn(), @@ -236,7 +236,7 @@ describe("Server", () => { const routingMock = { v1: { test: new EndpointsFactory(defaultResultHandler).build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: vi.fn(), diff --git a/tests/unit/testing.spec.ts b/tests/unit/testing.spec.ts index 23589a48d..519b0a80e 100644 --- a/tests/unit/testing.spec.ts +++ b/tests/unit/testing.spec.ts @@ -30,7 +30,7 @@ describe("Testing", () => { }), ) .build({ - method: "get", + methods: "get", input: z.object({}), output: z.object({}), handler: async () => ({}),