diff --git a/apps/api/pages/api/slots/_get.ts b/apps/api/pages/api/slots/_get.ts new file mode 100644 index 0000000000..a74d3ae83f --- /dev/null +++ b/apps/api/pages/api/slots/_get.ts @@ -0,0 +1,27 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import { HttpError } from "@calcom/lib/http-error"; +import { defaultResponder } from "@calcom/lib/server"; +import { createContext } from "@calcom/trpc/server/createContext"; +import { viewerRouter } from "@calcom/trpc/server/routers/viewer/_router"; + +import { TRPCError } from "@trpc/server"; +import { getHTTPStatusCodeFromError } from "@trpc/server/http"; + +async function handler(req: NextApiRequest, res: NextApiResponse) { + /** @see https://trpc.io/docs/server-side-calls */ + const ctx = await createContext({ req, res }); + const caller = viewerRouter.createCaller(ctx); + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return await caller.slots.getSchedule(req.query as any /* Let tRPC handle this */); + } catch (cause) { + if (cause instanceof TRPCError) { + const statusCode = getHTTPStatusCodeFromError(cause); + throw new HttpError({ statusCode, message: cause.message }); + } + throw cause; + } +} + +export default defaultResponder(handler); diff --git a/apps/api/pages/api/slots/index.ts b/apps/api/pages/api/slots/index.ts new file mode 100644 index 0000000000..c53e4b8ef3 --- /dev/null +++ b/apps/api/pages/api/slots/index.ts @@ -0,0 +1,9 @@ +import { defaultHandler } from "@calcom/lib/server"; + +import { withMiddleware } from "~/lib/helpers/withMiddleware"; + +export default withMiddleware()( + defaultHandler({ + GET: import("./_get"), + }) +); diff --git a/packages/trpc/server/routers/viewer/slots/types.ts b/packages/trpc/server/routers/viewer/slots/types.ts index b488cee2e3..402f845271 100644 --- a/packages/trpc/server/routers/viewer/slots/types.ts +++ b/packages/trpc/server/routers/viewer/slots/types.ts @@ -7,13 +7,13 @@ export const getScheduleSchema = z // endTime ISOString endTime: z.string(), // Event type ID - eventTypeId: z.number().int().optional(), + eventTypeId: z.coerce.number().int().optional(), // Event type slug - eventTypeSlug: z.string(), + eventTypeSlug: z.string().optional(), // invitee timezone timeZone: z.string().optional(), // or list of users (for dynamic events) - usernameList: z.array(z.string()).optional(), + usernameList: z.union([z.string(), z.array(z.string())]).optional(), debug: z.boolean().optional(), // to handle event types with multiple duration options duration: z @@ -21,9 +21,16 @@ export const getScheduleSchema = z .optional() .transform((val) => val && parseInt(val)), }) + .transform((val) => { + // Need this so we can pass a single username in the query string form public API + if (val.usernameList) { + val.usernameList = Array.isArray(val.usernameList) ? val.usernameList : [val.usernameList]; + } + return val; + }) .refine( - (data) => !!data.eventTypeId || !!data.usernameList, - "Either usernameList or eventTypeId should be filled in." + (data) => !!data.eventTypeId || (!!data.usernameList && !!data.eventTypeSlug), + "You need to either pass an eventTypeId OR an usernameList/eventTypeSlug combination" ); export const reserveSlotSchema = z diff --git a/packages/trpc/server/routers/viewer/slots/util.ts b/packages/trpc/server/routers/viewer/slots/util.ts index d699f4f84c..34c9afd7ac 100644 --- a/packages/trpc/server/routers/viewer/slots/util.ts +++ b/packages/trpc/server/routers/viewer/slots/util.ts @@ -11,8 +11,7 @@ import isTimeOutOfBounds from "@calcom/lib/isOutOfBounds"; import logger from "@calcom/lib/logger"; import { performance } from "@calcom/lib/server/perfObserver"; import getTimeSlots from "@calcom/lib/slots"; -import { availabilityUserSelect } from "@calcom/prisma"; -import prisma from "@calcom/prisma"; +import prisma, { availabilityUserSelect } from "@calcom/prisma"; import { SchedulingType } from "@calcom/prisma/enums"; import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { EventBusyDate } from "@calcom/types/Calendar"; @@ -140,6 +139,12 @@ export async function getEventType(input: TGetScheduleInputSchema) { export async function getDynamicEventType(input: TGetScheduleInputSchema) { // For dynamic booking, we need to get and update user credentials, schedule and availability in the eventTypeObject as they're required in the new availability logic + if (!input.eventTypeSlug) { + throw new TRPCError({ + message: "eventTypeSlug is required for dynamic booking", + code: "BAD_REQUEST", + }); + } const dynamicEventType = getDefaultEvent(input.eventTypeSlug); const users = await prisma.user.findMany({ where: {