From 3759eccfca19433b9215d2f360c4287ee58135ea Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Tue, 26 Apr 2022 22:12:28 +0200 Subject: [PATCH 1/5] fix: attendees move to use pick, separate patch/post --- lib/validations/attendee.ts | 27 +++++++++++++++++++-------- lib/validations/user.ts | 7 ++----- pages/api/attendees/[id].ts | 4 ++-- pages/api/attendees/index.ts | 4 ++-- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/validations/attendee.ts b/lib/validations/attendee.ts index 290ea16adf..5b7fee4e7f 100644 --- a/lib/validations/attendee.ts +++ b/lib/validations/attendee.ts @@ -3,21 +3,32 @@ import { z } from "zod"; import { _AttendeeModel as Attendee } from "@calcom/prisma/zod"; -export const schemaAttendeeBaseBodyParams = Attendee.omit({ id: true }).partial(); +export const schemaAttendeeBaseBodyParams = Attendee.pick({ + bookingId: true, + email: true, + name: true, + timeZone: true, +}).partial(); export const schemaAttendeePublic = Attendee.omit({}); -const schemaAttendeeRequiredParams = z.object({ +const schemaAttendeeCreateRequiredParams = z.object({ bookingId: z.any(), email: z.string().email(), name: z.string(), timeZone: z.string(), }); -export const schemaAttendeeBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeRequiredParams); - -export const withValidAttendee = withValidation({ - schema: schemaAttendeeBodyParams, - type: "Zod", - mode: "body", +const schemaAttendeeEditeRequiredParams = z.object({ + // bookingId: z.any(), + // @note: disallowing email changes in attendee via API for now. + // email: z.string().email(), + name: z.string(), + timeZone: z.string(), }); +export const schemaAttendeeEditBodyParams = schemaAttendeeBaseBodyParams.merge( + schemaAttendeeEditeRequiredParams +); +export const schemaAttendeeCreateBodyParams = schemaAttendeeBaseBodyParams.merge( + schemaAttendeeCreateRequiredParams +); diff --git a/lib/validations/user.ts b/lib/validations/user.ts index 7452c6ad44..c3e26e9cb0 100644 --- a/lib/validations/user.ts +++ b/lib/validations/user.ts @@ -74,7 +74,7 @@ export const schemaUserBaseBodyParams = User.pick({ // Here we can both require or not (adding optional or nullish) and also rewrite validations for any value // for example making weekStart only accept weekdays as input -const schemaUserRequiredParams = z.object({ +const schemaUserEditParams = z.object({ weekStart: z.nativeEnum(weekdays).optional(), brandColor: z.string().min(4).max(9).regex(/^#/).optional(), timeZone: z @@ -96,7 +96,7 @@ const schemaUserRequiredParams = z.object({ // @note: These are the values that are editable via PATCH method on the user Model, // merging both BaseBodyParams with RequiredParams, and omiting whatever we want at the end. -export const schemaUserEditBodyParams = schemaUserBaseBodyParams.merge(schemaUserRequiredParams).omit({}); +export const schemaUserEditBodyParams = schemaUserBaseBodyParams.merge(schemaUserEditParams).omit({}); // @note: These are the values that are always returned when reading a user export const schemaUserReadPublic = User.pick({ @@ -123,6 +123,3 @@ export const schemaUserReadPublic = User.pick({ verified: true, invitedTo: true, }); - -// @note: This is the validation for the PATCH method on the user Model. Not used for now. -export const withValidUser = withValidation({ schema: schemaUserEditBodyParams, type: "Zod", mode: "body" }); diff --git a/pages/api/attendees/[id].ts b/pages/api/attendees/[id].ts index e6780c6929..23712e58b6 100644 --- a/pages/api/attendees/[id].ts +++ b/pages/api/attendees/[id].ts @@ -4,7 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { AttendeeResponse } from "@lib/types"; -import { schemaAttendeeBodyParams, schemaAttendeePublic } from "@lib/validations/attendee"; +import { schemaAttendeeEditBodyParams, schemaAttendeePublic } from "@lib/validations/attendee"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, @@ -88,7 +88,6 @@ import { export async function attendeeById(req: NextApiRequest, res: NextApiResponse) { const { method, query, body, userId } = req; const safeQuery = schemaQueryIdParseInt.safeParse(query); - const safeBody = schemaAttendeeBodyParams.safeParse(body); if (!safeQuery.success) { res.status(400).json({ error: safeQuery.error }); throw new Error("Invalid request query", safeQuery.error); @@ -117,6 +116,7 @@ export async function attendeeById(req: NextApiRequest, res: NextApiResponse Date: Tue, 26 Apr 2022 22:23:33 +0200 Subject: [PATCH 2/5] fix: moves to pick and separates availabilities --- lib/validations/attendee.ts | 21 +++++++++++--------- lib/validations/availability.ts | 33 ++++++++++++++++++++----------- pages/api/attendees/[id].ts | 6 +++--- pages/api/attendees/index.ts | 4 ++-- pages/api/availabilities/[id].ts | 9 ++++----- pages/api/availabilities/index.ts | 9 ++++----- pages/api/users/[id].ts | 2 +- 7 files changed, 48 insertions(+), 36 deletions(-) diff --git a/lib/validations/attendee.ts b/lib/validations/attendee.ts index 5b7fee4e7f..e0b91fb309 100644 --- a/lib/validations/attendee.ts +++ b/lib/validations/attendee.ts @@ -10,25 +10,28 @@ export const schemaAttendeeBaseBodyParams = Attendee.pick({ timeZone: true, }).partial(); -export const schemaAttendeePublic = Attendee.omit({}); - -const schemaAttendeeCreateRequiredParams = z.object({ +const schemaAttendeeCreateParams = z.object({ bookingId: z.any(), email: z.string().email(), name: z.string(), timeZone: z.string(), }); -const schemaAttendeeEditeRequiredParams = z.object({ - // bookingId: z.any(), - // @note: disallowing email changes in attendee via API for now. - // email: z.string().email(), +const schemaAttendeeEditParams = z.object({ + // @note: disallowing email/bookingId changes in attendee via API for now as it would introduce side effects name: z.string(), timeZone: z.string(), }); export const schemaAttendeeEditBodyParams = schemaAttendeeBaseBodyParams.merge( - schemaAttendeeEditeRequiredParams + schemaAttendeeEditParams ); export const schemaAttendeeCreateBodyParams = schemaAttendeeBaseBodyParams.merge( - schemaAttendeeCreateRequiredParams + schemaAttendeeCreateParams ); + +export const schemaAttendeeReadPublic = Attendee.pick({ + bookingId: true, + name: true, + email: true, + timeZone: true, +}); diff --git a/lib/validations/availability.ts b/lib/validations/availability.ts index 20e3c61c94..be4691cf0e 100644 --- a/lib/validations/availability.ts +++ b/lib/validations/availability.ts @@ -11,21 +11,32 @@ export const schemaAvailabilityBaseBodyParams = Availability.pick({ days: true, }).partial(); -export const schemaAvailabilityPublic = Availability.omit({}); +export const schemaAvailabilityPublic = Availability.pick({ + startTime: true, + endTime: true, + date: true, + scheduleId: true, + days: true, + userId: true, + eventTypeId: true, +}); -const schemaAvailabilityRequiredParams = z.object({ +const schemaAvailabilityCreateParams = z.object({ + startTime: z.date().or(z.string()), + endTime: z.date().or(z.string()), + days: z.array(z.number()).optional(), + eventTypeId: z.number().optional(), +}); + +const schemaAvailabilityEditParams = z.object({ startTime: z.date().or(z.string()).optional(), endTime: z.date().or(z.string()).optional(), days: z.array(z.number()).optional(), eventTypeId: z.number().optional(), }); - -export const schemaAvailabilityBodyParams = schemaAvailabilityBaseBodyParams.merge( - schemaAvailabilityRequiredParams +export const schemaAvailabilityEditBodyParams = schemaAvailabilityBaseBodyParams.merge( + schemaAvailabilityEditParams +); +export const schemaAvailabilityCreateBodyParams = schemaAvailabilityBaseBodyParams.merge( + schemaAvailabilityCreateParams ); - -export const withValidAvailability = withValidation({ - schema: schemaAvailabilityBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/pages/api/attendees/[id].ts b/pages/api/attendees/[id].ts index 23712e58b6..4d6aecd7f1 100644 --- a/pages/api/attendees/[id].ts +++ b/pages/api/attendees/[id].ts @@ -4,7 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { AttendeeResponse } from "@lib/types"; -import { schemaAttendeeEditBodyParams, schemaAttendeePublic } from "@lib/validations/attendee"; +import { schemaAttendeeEditBodyParams, schemaAttendeeReadPublic } from "@lib/validations/attendee"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, @@ -105,7 +105,7 @@ export async function attendeeById(req: NextApiRequest, res: NextApiResponse schemaAttendeePublic.parse(data)) + .then((data) => schemaAttendeeReadPublic.parse(data)) .then((attendee) => res.status(200).json({ attendee })) .catch((error: Error) => res.status(404).json({ @@ -123,7 +123,7 @@ export async function attendeeById(req: NextApiRequest, res: NextApiResponse schemaAttendeePublic.parse(data)) + .then((data) => schemaAttendeeReadPublic.parse(data)) .then((attendee) => res.status(200).json({ attendee })) .catch((error: Error) => res.status(404).json({ diff --git a/pages/api/attendees/index.ts b/pages/api/attendees/index.ts index ffab82e919..80afde914a 100644 --- a/pages/api/attendees/index.ts +++ b/pages/api/attendees/index.ts @@ -4,7 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { AttendeeResponse, AttendeesResponse } from "@lib/types"; -import { schemaAttendeeCreateBodyParams, schemaAttendeePublic } from "@lib/validations/attendee"; +import { schemaAttendeeCreateBodyParams, schemaAttendeeReadPublic } from "@lib/validations/attendee"; /** * @swagger @@ -87,7 +87,7 @@ async function createOrlistAllAttendees( booking: { connect: { id: parseInt(bookingId) } }, }, }); - const attendee = schemaAttendeePublic.parse(data); + const attendee = schemaAttendeeReadPublic.parse(data); if (attendee) { res.status(201).json({ diff --git a/pages/api/availabilities/[id].ts b/pages/api/availabilities/[id].ts index cd277b058e..c77574692a 100644 --- a/pages/api/availabilities/[id].ts +++ b/pages/api/availabilities/[id].ts @@ -4,7 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { AvailabilityResponse } from "@lib/types"; -import { schemaAvailabilityBodyParams, schemaAvailabilityPublic } from "@lib/validations/availability"; +import { schemaAvailabilityEditBodyParams, schemaAvailabilityReadPublic } from "@lib/validations/availability"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, @@ -93,7 +93,6 @@ import { */ export async function availabilityById(req: NextApiRequest, res: NextApiResponse) { const { method, query, body } = req; - const safeBody = schemaAvailabilityBodyParams.safeParse(body); const safeQuery = schemaQueryIdParseInt.safeParse(query); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); const userId = req.userId; @@ -105,7 +104,7 @@ export async function availabilityById(req: NextApiRequest, res: NextApiResponse case "GET": await prisma.availability .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaAvailabilityPublic.parse(data)) + .then((data) => schemaAvailabilityReadPublic.parse(data)) .then((availability) => res.status(200).json({ availability })) .catch((error: Error) => res.status(404).json({ message: `Availability with id: ${safeQuery.data.id} not found`, error }) @@ -113,7 +112,7 @@ export async function availabilityById(req: NextApiRequest, res: NextApiResponse break; case "PATCH": - + const safeBody = schemaAvailabilityEditBodyParams.safeParse(body); if (!safeBody.success) throw new Error("Invalid request body"); const userEventTypes = await prisma.eventType.findMany({ where: { userId } }); const userEventTypesIds = userEventTypes.map((event) => event.id); @@ -127,7 +126,7 @@ export async function availabilityById(req: NextApiRequest, res: NextApiResponse where: { id: safeQuery.data.id }, data: safeBody.data, }) - .then((data) => schemaAvailabilityPublic.parse(data)) + .then((data) => schemaAvailabilityReadPublic.parse(data)) .then((availability) => res.status(200).json({ availability })) .catch((error: Error) => { console.log(error); diff --git a/pages/api/availabilities/index.ts b/pages/api/availabilities/index.ts index ab7aa3b388..999639dc37 100644 --- a/pages/api/availabilities/index.ts +++ b/pages/api/availabilities/index.ts @@ -4,7 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { AvailabilityResponse, AvailabilitiesResponse } from "@lib/types"; -import { schemaAvailabilityBodyParams, schemaAvailabilityPublic } from "@lib/validations/availability"; +import { schemaAvailabilityCreateBodyParams, schemaAvailabilityReadPublic } from "@lib/validations/availability"; /** * @swagger @@ -50,7 +50,7 @@ async function createOrlistAllAvailabilities( if (method === "GET") { const data = await prisma.availability.findMany({ where: { userId } }); - const availabilities = data.map((availability) => schemaAvailabilityPublic.parse(availability)); + const availabilities = data.map((availability) => schemaAvailabilityReadPublic.parse(availability)); if (availabilities) res.status(200).json({ availabilities }); else (error: Error) => @@ -59,13 +59,12 @@ async function createOrlistAllAvailabilities( error, }); } else if (method === "POST") { - console.log(req.body); - const safe = schemaAvailabilityBodyParams.safeParse(req.body); + const safe = schemaAvailabilityCreateBodyParams.safeParse(req.body); if (!safe.success) throw new Error("Invalid request body"); const data = await prisma.availability.create({ data: { ...safe.data, userId } }); console.log(data); - const availability = schemaAvailabilityPublic.parse(data); + const availability = schemaAvailabilityReadPublic.parse(data); if (availability) res.status(201).json({ availability, message: "Availability created successfully" }); else diff --git a/pages/api/users/[id].ts b/pages/api/users/[id].ts index 24bf1075ed..ee9279da0a 100644 --- a/pages/api/users/[id].ts +++ b/pages/api/users/[id].ts @@ -8,7 +8,7 @@ import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, } from "@lib/validations/shared/queryIdTransformParseInt"; -import { schemaUserEditBodyParams, schemaUserReadPublic, withValidUser } from "@lib/validations/user"; +import { schemaUserEditBodyParams, schemaUserReadPublic } from "@lib/validations/user"; /** * @swagger From 14e2964a6dc17e715d85a684560a485af928745d Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Tue, 26 Apr 2022 22:48:15 +0200 Subject: [PATCH 3/5] fix: booking reference moved to pick-first create/edit separate validations --- README.md | 42 ++++++++++++----------- lib/validations/attendee.ts | 8 ++--- lib/validations/availability.ts | 2 +- lib/validations/booking-reference.ts | 48 ++++++++++++++++++++------- pages/api/availabilities/[id].ts | 5 ++- pages/api/availabilities/index.ts | 5 ++- pages/api/booking-references/[id].ts | 23 +++++++------ pages/api/booking-references/index.ts | 8 ++--- 8 files changed, 85 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 19baacdf58..17082202cd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Cal.com Public API (Enterprise Only) -This is the public REST api for cal.com. -It exposes CRUD Endpoints of all our most important resources. +This is the public REST api for cal.com. +It exposes CRUD Endpoints of all our most important resources. And it makes it easy for anyone to integrate with Cal.com at the application programming level. ## Stack @@ -63,9 +63,10 @@ API Keys optionally may have expiry dates, if they are expired they won't work. In the future we might add support for header Bearer Auth if we need to or if our customers require it. ## Middlewares + We don't use the new NextJS 12 Beta Middlewares, mainly because they run on the edge, and are not able to call prisma from api endpoints. We use instead a very nifty library called next-api-middleware that let's us use a similar approach building our own middlewares and applying them as we see fit. -* withMiddleware() requires some default middlewares (verifyApiKey, etc...) +- withMiddleware() requires some default middlewares (verifyApiKey, etc...) ## Next.config.js @@ -121,21 +122,21 @@ We aim to provide a fully tested API for our peace of mind, this is accomplished ## Endpoints matrix -| resource | get [id] | get all | create | edit | delete | -|--------------------------|----------|----------|---------|-------|--------| -| attendees | ✅ | ✅ | ✅ | ✅ | ✅ | -| availabilities | ✅ | ✅ | ✅ | ✅ | ✅ | -| booking-references | ✅ | ✅ | ✅ | ✅ | ✅ | -| daily-event-references | ✅ | ✅ | ✅ | ✅ | ✅ | -| destination-calendars | ✅ | ✅ | ✅ | ✅ | ✅ | -| event-type-custom-inputs | ✅ | ✅ | ✅ | ✅ | ✅ | -| event-types | ✅ | ✅ | ✅ | ✅ | ✅ | -| memberships | ✅ | ✅ | ✅ | ✅ | ✅ | -| payments | ✅ | ✅ | ❌ | ❌ | ❌ | -| schedules | ✅ | ✅ | ✅ | ✅ | ✅ | -| selected-calendars | ✅ | ✅ | ✅ | ✅ | ✅ | -| teams | ✅ | ✅ | ✅ | ✅ | ✅ | -| users | ✅ | 👤[1] | ✅ | ✅ | ✅ | +| resource | get [id] | get all | create | edit | delete | +| ------------------------ | -------- | ------- | ------ | ---- | ------ | +| attendees | ✅ | ✅ | ✅ | ✅ | ✅ | +| availabilities | ✅ | ✅ | ✅ | ✅ | ✅ | +| booking-references | ✅ | ✅ | ✅ | ✅ | ✅ | +| daily-event-references | ✅ | ✅ | ✅ | ✅ | ✅ | +| destination-calendars | ✅ | ✅ | ✅ | ✅ | ✅ | +| event-type-custom-inputs | ✅ | ✅ | ✅ | ✅ | ✅ | +| event-types | ✅ | ✅ | ✅ | ✅ | ✅ | +| memberships | ✅ | ✅ | ✅ | ✅ | ✅ | +| payments | ✅ | ✅ | ❌ | ❌ | ❌ | +| schedules | ✅ | ✅ | ✅ | ✅ | ✅ | +| selected-calendars | ✅ | ✅ | ✅ | ✅ | ✅ | +| teams | ✅ | ✅ | ✅ | ✅ | ✅ | +| users | ✅ | 👤[1] | ✅ | ✅ | ✅ | ## Models from database that are not exposed @@ -170,5 +171,6 @@ DATABASE_URL=DATABASE_URL="postgresql://postgres:@localhost:5450/calendso" ## Optional -API_KEY_PREFIX=cal_# This can be changed per envirorment so cal_test_ for staging for example. -> If you're self-hosting under our commercial license, you can use any prefix you want for api keys. either leave the default cal_ (not providing any envirorment variable) or modify it +API*KEY_PREFIX=cal*# This can be changed per envirorment so cal*test* for staging for example. + +> If you're self-hosting under our commercial license, you can use any prefix you want for api keys. either leave the default cal\_ (not providing any envirorment variable) or modify it diff --git a/lib/validations/attendee.ts b/lib/validations/attendee.ts index e0b91fb309..eb956f5206 100644 --- a/lib/validations/attendee.ts +++ b/lib/validations/attendee.ts @@ -22,12 +22,8 @@ const schemaAttendeeEditParams = z.object({ name: z.string(), timeZone: z.string(), }); -export const schemaAttendeeEditBodyParams = schemaAttendeeBaseBodyParams.merge( - schemaAttendeeEditParams -); -export const schemaAttendeeCreateBodyParams = schemaAttendeeBaseBodyParams.merge( - schemaAttendeeCreateParams -); +export const schemaAttendeeEditBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeEditParams); +export const schemaAttendeeCreateBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeCreateParams); export const schemaAttendeeReadPublic = Attendee.pick({ bookingId: true, diff --git a/lib/validations/availability.ts b/lib/validations/availability.ts index be4691cf0e..85392c7b1a 100644 --- a/lib/validations/availability.ts +++ b/lib/validations/availability.ts @@ -11,7 +11,7 @@ export const schemaAvailabilityBaseBodyParams = Availability.pick({ days: true, }).partial(); -export const schemaAvailabilityPublic = Availability.pick({ +export const schemaAvailabilityReadPublic = Availability.pick({ startTime: true, endTime: true, date: true, diff --git a/lib/validations/booking-reference.ts b/lib/validations/booking-reference.ts index 75a7d95f0c..75aedfe6d4 100644 --- a/lib/validations/booking-reference.ts +++ b/lib/validations/booking-reference.ts @@ -3,21 +3,45 @@ import { z } from "zod"; import { _BookingReferenceModel as BookingReference } from "@calcom/prisma/zod"; -export const schemaBookingReferenceBaseBodyParams = BookingReference.omit({ id: true }).partial(); +export const schemaBookingReferenceBaseBodyParams = BookingReference.pick({ + type: true, + bookingId: true, + uid: true, + meetingId: true, + meetingPassword: true, + meetingUrl: true, + deleted: true, +}).partial(); -export const schemaBookingReferencePublic = BookingReference.omit({}); +export const schemaBookingReferenceReadPublic = BookingReference.pick({ + type: true, + bookingId: true, + uid: true, + meetingId: true, + meetingPassword: true, + meetingUrl: true, + deleted: true, +}); -const schemaBookingReferenceRequiredParams = z.object({ +const schemaBookingReferenceEditParams = z.object({ type: z.string(), uid: z.string(), + meetingId: z.string(), + meetingPassword: z.string(), + meetingUrl: z.string(), + deleted: z.boolean(), }); - -export const schemaBookingReferenceBodyParams = schemaBookingReferenceBaseBodyParams.merge( - schemaBookingReferenceRequiredParams +const schemaBookingReferenceCreateParams = z.object({ + type: z.string(), + uid: z.string(), + meetingId: z.string(), + meetingPassword: z.string(), + meetingUrl: z.string(), + deleted: z.boolean(), +}); +export const schemaBookingCreateBodyParams = schemaBookingReferenceBaseBodyParams.merge( + schemaBookingReferenceCreateParams +); +export const schemaBookingEditBodyParams = schemaBookingReferenceBaseBodyParams.merge( + schemaBookingReferenceEditParams ); - -export const withValidBookingReference = withValidation({ - schema: schemaBookingReferenceBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/pages/api/availabilities/[id].ts b/pages/api/availabilities/[id].ts index c77574692a..0bef2ab1a9 100644 --- a/pages/api/availabilities/[id].ts +++ b/pages/api/availabilities/[id].ts @@ -4,7 +4,10 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { AvailabilityResponse } from "@lib/types"; -import { schemaAvailabilityEditBodyParams, schemaAvailabilityReadPublic } from "@lib/validations/availability"; +import { + schemaAvailabilityEditBodyParams, + schemaAvailabilityReadPublic, +} from "@lib/validations/availability"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, diff --git a/pages/api/availabilities/index.ts b/pages/api/availabilities/index.ts index 999639dc37..869530566e 100644 --- a/pages/api/availabilities/index.ts +++ b/pages/api/availabilities/index.ts @@ -4,7 +4,10 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { AvailabilityResponse, AvailabilitiesResponse } from "@lib/types"; -import { schemaAvailabilityCreateBodyParams, schemaAvailabilityReadPublic } from "@lib/validations/availability"; +import { + schemaAvailabilityCreateBodyParams, + schemaAvailabilityReadPublic, +} from "@lib/validations/availability"; /** * @swagger diff --git a/pages/api/booking-references/[id].ts b/pages/api/booking-references/[id].ts index 5f08ab4c0d..1789c940b8 100644 --- a/pages/api/booking-references/[id].ts +++ b/pages/api/booking-references/[id].ts @@ -6,8 +6,8 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { BookingReferenceResponse } from "@lib/types"; import { - schemaBookingReferenceBodyParams, - schemaBookingReferencePublic, + schemaBookingEditBodyParams, + schemaBookingReferenceReadPublic, } from "@lib/validations/booking-reference"; import { schemaQueryIdParseInt, @@ -18,14 +18,14 @@ import { * @swagger * /booking-references/{id}: * get: - * summary: Get a daily event reference by ID + * summary: Get a booking reference by ID * parameters: * - in: path * name: id * schema: * type: integer * required: true - * description: Numeric ID of the daily event reference to get + * description: Numeric ID of the booking reference to get * security: * - ApiKeyAuth: [] * tags: @@ -38,7 +38,7 @@ import { * 404: * description: BookingReference was not found * patch: - * summary: Edit an existing daily event reference + * summary: Edit an existing booking reference * consumes: * - application/json * parameters: @@ -54,7 +54,7 @@ import { * schema: * type: integer * required: true - * description: Numeric ID of the daily event reference to edit + * description: Numeric ID of the booking reference to edit * security: * - ApiKeyAuth: [] * tags: @@ -68,14 +68,14 @@ import { * 401: * description: Authorization information is missing or invalid. * delete: - * summary: Remove an existing daily event reference + * summary: Remove an existing booking reference * parameters: * - in: path * name: id * schema: * type: integer * required: true - * description: Numeric ID of the daily event reference to delete + * description: Numeric ID of the booking reference to delete * security: * - ApiKeyAuth: [] * tags: @@ -95,7 +95,6 @@ export async function bookingReferenceById( ) { const { method, query, body } = req; const safeQuery = schemaQueryIdParseInt.safeParse(query); - const safeBody = schemaBookingReferenceBodyParams.safeParse(body); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); const userId = req.userId; const userWithBookings = await prisma.user.findUnique({ @@ -111,7 +110,7 @@ export async function bookingReferenceById( case "GET": await prisma.bookingReference .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaBookingReferencePublic.parse(data)) + .then((data) => schemaBookingReferenceReadPublic.parse(data)) .then((booking_reference) => res.status(200).json({ booking_reference })) .catch((error: Error) => res.status(404).json({ @@ -122,12 +121,14 @@ export async function bookingReferenceById( break; case "PATCH": + const safeBody = schemaBookingEditBodyParams.safeParse(body); + if (!safeBody.success) { throw new Error("Invalid request body"); } await prisma.bookingReference .update({ where: { id: safeQuery.data.id }, data: safeBody.data }) - .then((data) => schemaBookingReferencePublic.parse(data)) + .then((data) => schemaBookingReferenceReadPublic.parse(data)) .then((booking_reference) => res.status(200).json({ booking_reference })) .catch((error: Error) => res.status(404).json({ diff --git a/pages/api/booking-references/index.ts b/pages/api/booking-references/index.ts index f77b0c586d..b715317c20 100644 --- a/pages/api/booking-references/index.ts +++ b/pages/api/booking-references/index.ts @@ -5,8 +5,8 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { BookingReferenceResponse, BookingReferencesResponse } from "@lib/types"; import { - schemaBookingReferenceBodyParams, - schemaBookingReferencePublic, + schemaBookingCreateBodyParams, + schemaBookingReferenceReadPublic, } from "@lib/validations/booking-reference"; /** @@ -55,7 +55,7 @@ async function createOrlistAllBookingReferences( if (method === "GET") { const data = await prisma.bookingReference.findMany({ where: { id: { in: userBookingIds } } }); const booking_references = data.map((bookingReference) => - schemaBookingReferencePublic.parse(bookingReference) + schemaBookingReferenceReadPublic.parse(bookingReference) ); if (booking_references) res.status(200).json({ booking_references }); else @@ -65,7 +65,7 @@ async function createOrlistAllBookingReferences( error, }); } else if (method === "POST") { - const safe = schemaBookingReferenceBodyParams.safeParse(req.body); + const safe = schemaBookingCreateBodyParams.safeParse(req.body); if (!safe.success) { throw new Error("Invalid request body"); } From e52af0bbef6a3c106d8155b55c55e68d0922a092 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Wed, 27 Apr 2022 19:25:36 +0200 Subject: [PATCH 4/5] fix: post / patch attendees docs w examples --- lib/validations/attendee.ts | 31 ++++++++----- lib/validations/availability.ts | 2 +- lib/validations/booking-reference.ts | 2 +- lib/validations/booking.ts | 40 ++++++++++++----- lib/validations/credential.ts | 25 ----------- lib/validations/daily-event-reference.ts | 37 +++++++++++----- lib/validations/destination-calendar.ts | 42 +++++++++++++----- lib/validations/event-type-custom-input.ts | 9 ---- lib/validations/event-type.ts | 7 --- lib/validations/membership.ts | 7 --- lib/validations/payment.ts | 8 ---- lib/validations/reminder-mail.ts | 7 --- lib/validations/schedule.ts | 7 --- lib/validations/selected-calendar.ts | 7 --- lib/validations/shared/timeZone.ts | 5 +++ lib/validations/team.ts | 7 --- lib/validations/user.ts | 10 ++--- lib/validations/webhook.ts | 8 ---- pages/api/attendees/[id].ts | 33 ++++++++++---- pages/api/attendees/index.ts | 49 ++++++++++++++++----- pages/api/availabilities/[id].ts | 2 +- pages/api/availabilities/index.ts | 2 +- pages/api/booking-references/[id].ts | 2 +- pages/api/booking-references/index.ts | 2 +- pages/api/bookings/[id].ts | 11 ++--- pages/api/bookings/index.ts | 10 ++--- pages/api/daily-event-references/[id].ts | 12 ++--- pages/api/daily-event-references/index.ts | 12 ++--- pages/api/destination-calendars/[id].ts | 12 ++--- pages/api/destination-calendars/index.ts | 12 ++--- pages/api/docs.ts | 1 + pages/api/event-type-custom-inputs/[id].ts | 2 +- pages/api/event-type-custom-inputs/index.ts | 2 +- pages/api/event-types/[id].ts | 2 +- pages/api/event-types/index.ts | 2 +- pages/api/index.ts | 5 +++ pages/api/memberships/[id].ts | 2 +- pages/api/memberships/index.ts | 2 +- pages/api/payments/[id].ts | 2 +- pages/api/payments/index.ts | 2 +- pages/api/schedules/[id].ts | 2 +- pages/api/schedules/index.ts | 4 +- pages/api/selected-calendars/[id].ts | 2 +- pages/api/selected-calendars/index.ts | 2 +- pages/api/teams/[id].ts | 2 +- pages/api/teams/index.ts | 4 +- pages/api/users/[id].ts | 2 +- pages/api/users/index.ts | 2 +- templates/endpoints/[id]/delete.ts | 2 +- templates/endpoints/[id]/edit.ts | 2 +- templates/endpoints/[id]/index.ts | 2 +- templates/endpoints/get_all.ts | 2 +- templates/endpoints/get_all_and_post.ts | 2 +- templates/endpoints/post.ts | 2 +- templates/zod-validation.ts | 7 --- 55 files changed, 247 insertions(+), 234 deletions(-) delete mode 100644 lib/validations/credential.ts create mode 100644 lib/validations/shared/timeZone.ts diff --git a/lib/validations/attendee.ts b/lib/validations/attendee.ts index eb956f5206..d603eaaf63 100644 --- a/lib/validations/attendee.ts +++ b/lib/validations/attendee.ts @@ -1,8 +1,9 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _AttendeeModel as Attendee } from "@calcom/prisma/zod"; +import { timeZone } from "@lib/validations/shared/timeZone"; + export const schemaAttendeeBaseBodyParams = Attendee.pick({ bookingId: true, email: true, @@ -10,22 +11,28 @@ export const schemaAttendeeBaseBodyParams = Attendee.pick({ timeZone: true, }).partial(); -const schemaAttendeeCreateParams = z.object({ - bookingId: z.any(), - email: z.string().email(), - name: z.string(), - timeZone: z.string(), -}); +const schemaAttendeeCreateParams = z + .object({ + bookingId: z.number().int(), + email: z.string().email(), + name: z.string(), + timeZone: timeZone, + }) + .strict(); -const schemaAttendeeEditParams = z.object({ - // @note: disallowing email/bookingId changes in attendee via API for now as it would introduce side effects - name: z.string(), - timeZone: z.string(), -}); +const schemaAttendeeEditParams = z + .object({ + // @note: disallowing bookingId changes in attendee via API for now as it would introduce side effects + name: z.string().optional(), + email: z.string().email().optional(), + timeZone: timeZone.optional(), + }) + .strict(); export const schemaAttendeeEditBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeEditParams); export const schemaAttendeeCreateBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeCreateParams); export const schemaAttendeeReadPublic = Attendee.pick({ + id: true, bookingId: true, name: true, email: true, diff --git a/lib/validations/availability.ts b/lib/validations/availability.ts index 85392c7b1a..6039dfb80f 100644 --- a/lib/validations/availability.ts +++ b/lib/validations/availability.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _AvailabilityModel as Availability } from "@calcom/prisma/zod"; @@ -12,6 +11,7 @@ export const schemaAvailabilityBaseBodyParams = Availability.pick({ }).partial(); export const schemaAvailabilityReadPublic = Availability.pick({ + id: true, startTime: true, endTime: true, date: true, diff --git a/lib/validations/booking-reference.ts b/lib/validations/booking-reference.ts index 75aedfe6d4..f207b09208 100644 --- a/lib/validations/booking-reference.ts +++ b/lib/validations/booking-reference.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _BookingReferenceModel as BookingReference } from "@calcom/prisma/zod"; @@ -14,6 +13,7 @@ export const schemaBookingReferenceBaseBodyParams = BookingReference.pick({ }).partial(); export const schemaBookingReferenceReadPublic = BookingReference.pick({ + id: true, type: true, bookingId: true, uid: true, diff --git a/lib/validations/booking.ts b/lib/validations/booking.ts index 77eca1d156..5bf187f523 100644 --- a/lib/validations/booking.ts +++ b/lib/validations/booking.ts @@ -1,23 +1,43 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _BookingModel as Booking } from "@calcom/prisma/zod"; -const schemaBookingBaseBodyParams = Booking.omit({ id: true }).partial(); +const schemaBookingBaseBodyParams = Booking.pick({ + uid: true, + userId: true, + eventTypeId: true, + title: true, + startTime: true, + endTime: true, +}).partial(); -const schemaBookingRequiredParams = z.object({ +const schemaBookingCreateParams = z.object({ + uid: z.string(), + userId: z.number(), + eventTypeId: z.number(), + title: z.string(), + startTime: z.date(), + endTime: z.date(), +}); + +export const schemaBookingCreateBodyParams = schemaBookingBaseBodyParams.merge(schemaBookingCreateParams); + +// @note: disallowing userId/eventTypeId changes in booking endpoint via PATCH for now as it would introduce side effects +const schemaBookingEditParams = z.object({ uid: z.string(), title: z.string(), startTime: z.date(), endTime: z.date(), }); -export const schemaBookingBodyParams = schemaBookingBaseBodyParams.merge(schemaBookingRequiredParams); +export const schemaBookingEditBodyParams = schemaBookingBaseBodyParams.merge(schemaBookingEditParams); -export const schemaBookingPublic = Booking.omit({}); - -export const withValidBooking = withValidation({ - schema: schemaBookingBodyParams, - type: "Zod", - mode: "body", +export const schemaBookingReadPublic = Booking.pick({ + id: true, + userId: true, + eventTypeId: true, + uid: true, + title: true, + startTime: true, + endTime: true, }); diff --git a/lib/validations/credential.ts b/lib/validations/credential.ts deleted file mode 100644 index 9397ef2577..0000000000 --- a/lib/validations/credential.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { withValidation } from "next-validations"; -import { z } from "zod"; - -import { _CredentialModel as Credential } from "@calcom/prisma/zod"; - -import { jsonSchema } from "./shared/jsonSchema"; - -const schemaCredentialBaseBodyParams = Credential.omit({ id: true, userId: true }).partial(); - -const schemaCredentialRequiredParams = z.object({ - type: z.string(), - key: jsonSchema, -}); - -export const schemaCredentialBodyParams = schemaCredentialBaseBodyParams.merge( - schemaCredentialRequiredParams -); - -export const schemaCredentialPublic = Credential.omit({}); - -export const withValidCredential = withValidation({ - schema: schemaCredentialBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/daily-event-reference.ts b/lib/validations/daily-event-reference.ts index f477f6bec6..ca74d5a95f 100644 --- a/lib/validations/daily-event-reference.ts +++ b/lib/validations/daily-event-reference.ts @@ -1,22 +1,35 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _DailyEventReferenceModel as DailyEventReference } from "@calcom/prisma/zod"; -export const schemaDailyEventReferenceBaseBodyParams = DailyEventReference.omit({ id: true }); +export const schemaDailyEventReferenceBaseBodyParams = DailyEventReference.pick({ + dailytoken: true, + dailyurl: true, + bookingId: true, +}).partial(); -const schemaDailyEventReferenceRequiredParams = z.object({ - email: z.string().email(), +const schemaDailyEventReferenceCreateParams = z.object({ + dailytoken: z.string(), + dailyurl: z.string(), + bookingId: z.number(), }); -export const schemaDailyEventReferenceBodyParams = schemaDailyEventReferenceBaseBodyParams.merge( - schemaDailyEventReferenceRequiredParams +export const schemaDailyEventReferenceCreateBodyParams = schemaDailyEventReferenceBaseBodyParams.merge( + schemaDailyEventReferenceCreateParams ); -export const schemaDailyEventReferencePublic = DailyEventReference.omit({}); - -export const withValidDailyEventReference = withValidation({ - schema: schemaDailyEventReferenceBodyParams, - type: "Zod", - mode: "body", +const schemaDailyEventReferenceEditParams = z.object({ + dailytoken: z.string(), + dailyurl: z.string(), + // @note: disallowing bookingId changes in daily-event-reference via API endpoint for now as it would introduce side effects +}); + +export const schemaDailyEventReferenceEditBodyParams = schemaDailyEventReferenceBaseBodyParams.merge( + schemaDailyEventReferenceEditParams +); +export const schemaDailyEventReferenceReadPublic = DailyEventReference.pick({ + id: true, + dailytoken: true, + dailyurl: true, + bookingId: true, }); diff --git a/lib/validations/destination-calendar.ts b/lib/validations/destination-calendar.ts index 87e367ee19..541260d4dc 100644 --- a/lib/validations/destination-calendar.ts +++ b/lib/validations/destination-calendar.ts @@ -1,23 +1,43 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _DestinationCalendarModel as DestinationCalendar } from "@calcom/prisma/zod"; -export const schemaDestinationCalendarBaseBodyParams = DestinationCalendar.omit({ id: true }).partial(); +export const schemaDestinationCalendarBaseBodyParams = DestinationCalendar.pick({ + integration: true, + externalId: true, + eventTypeId: true, + bookingId: true, + userId: true, +}).partial(); -const schemaDestinationCalendarRequiredParams = z.object({ +const schemaDestinationCalendarEditParams = z.object({ integration: z.string(), externalId: z.string(), + eventTypeId: z.number(), + bookingId: z.number(), + userId: z.number(), }); -export const schemaDestinationCalendarBodyParams = schemaDestinationCalendarBaseBodyParams.merge( - schemaDestinationCalendarRequiredParams +export const schemaDestinationCalendarEditBodyParams = schemaDestinationCalendarBaseBodyParams.merge( + schemaDestinationCalendarEditParams +); +const schemaDestinationCalendarCreateParams = z.object({ + integration: z.string(), + externalId: z.string(), + eventTypeId: z.number(), + bookingId: z.number(), + userId: z.number(), +}); + +export const schemaDestinationCalendarCreateBodyParams = schemaDestinationCalendarBaseBodyParams.merge( + schemaDestinationCalendarCreateParams ); -export const schemaDestinationCalendarPublic = DestinationCalendar.omit({}); - -export const withValidDestinationCalendar = withValidation({ - schema: schemaDestinationCalendarBodyParams, - type: "Zod", - mode: "body", +export const schemaDestinationCalendarReadPublic = DestinationCalendar.pick({ + id: true, + integration: true, + externalId: true, + eventTypeId: true, + bookingId: true, + userId: true, }); diff --git a/lib/validations/event-type-custom-input.ts b/lib/validations/event-type-custom-input.ts index 73555285e4..560d3d7684 100644 --- a/lib/validations/event-type-custom-input.ts +++ b/lib/validations/event-type-custom-input.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _EventTypeCustomInputModel as EventTypeCustomInput } from "@calcom/prisma/zod"; @@ -17,8 +16,6 @@ const schemaEventTypeCustomInputRequiredParams = z.object({ eventType: z.object({ connect: z.object({ id: z.number().optional(), - // username: z.string().optional(), - // email: z.string().optional(), }), // FIXME: Provide valid EventTypeModel schema here, but not sure how yet. create: z.any(), @@ -28,9 +25,3 @@ const schemaEventTypeCustomInputRequiredParams = z.object({ export const schemaEventTypeCustomInputBodyParams = schemaEventTypeCustomInputBaseBodyParams.merge( schemaEventTypeCustomInputRequiredParams ); - -export const withValidEventTypeCustomInput = withValidation({ - schema: schemaEventTypeCustomInputBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/event-type.ts b/lib/validations/event-type.ts index 119edcb248..e8665a9e3b 100644 --- a/lib/validations/event-type.ts +++ b/lib/validations/event-type.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _EventTypeModel as EventType } from "@calcom/prisma/zod"; @@ -14,9 +13,3 @@ const schemaEventTypeRequiredParams = z.object({ export const schemaEventTypeBodyParams = schemaEventTypeBaseBodyParams.merge(schemaEventTypeRequiredParams); // @NOTE: Removing locations and metadata properties before validation, add them later if required export const schemaEventTypePublic = EventType.omit({ locations: true, metadata: true }); - -export const withValidEventType = withValidation({ - schema: schemaEventTypeBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/membership.ts b/lib/validations/membership.ts index bb5d7cb7fc..d58eea2c33 100644 --- a/lib/validations/membership.ts +++ b/lib/validations/membership.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _MembershipModel as Membership } from "@calcom/prisma/zod"; @@ -13,9 +12,3 @@ export const schemaMembershipBodyParams = schemaMembershipBaseBodyParams.merge( ); export const schemaMembershipPublic = Membership.omit({}); - -export const withValidMembership = withValidation({ - schema: schemaMembershipBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/payment.ts b/lib/validations/payment.ts index 5789d4a092..32c5d8d641 100644 --- a/lib/validations/payment.ts +++ b/lib/validations/payment.ts @@ -1,13 +1,5 @@ -import { withValidation } from "next-validations"; - import { _PaymentModel as Payment } from "@calcom/prisma/zod"; // FIXME: Payment seems a delicate endpoint, do we need to remove anything here? export const schemaPaymentBodyParams = Payment.omit({ id: true }); export const schemaPaymentPublic = Payment.omit({ externalId: true }); - -export const withValidPayment = withValidation({ - schema: schemaPaymentBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/reminder-mail.ts b/lib/validations/reminder-mail.ts index d97c65c2b1..635dc84dac 100644 --- a/lib/validations/reminder-mail.ts +++ b/lib/validations/reminder-mail.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _ReminderMailModel as ReminderMail } from "@calcom/prisma/zod"; @@ -16,9 +15,3 @@ const schemaReminderMailRequiredParams = z.object({ export const schemaReminderMailBodyParams = schemaReminderMailBaseBodyParams.merge( schemaReminderMailRequiredParams ); - -export const withValidReminderMail = withValidation({ - schema: schemaReminderMailBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/schedule.ts b/lib/validations/schedule.ts index e5782a08d7..176d972da1 100644 --- a/lib/validations/schedule.ts +++ b/lib/validations/schedule.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _ScheduleModel as Schedule } from "@calcom/prisma/zod"; @@ -13,9 +12,3 @@ const schemaScheduleRequiredParams = z.object({ export const schemaScheduleBodyParams = schemaScheduleBaseBodyParams.merge(schemaScheduleRequiredParams); export const schemaSchedulePublic = Schedule.omit({}); - -export const withValidSchedule = withValidation({ - schema: schemaScheduleBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/selected-calendar.ts b/lib/validations/selected-calendar.ts index 4eab89d937..ffdc54b96a 100644 --- a/lib/validations/selected-calendar.ts +++ b/lib/validations/selected-calendar.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _SelectedCalendarModel as SelectedCalendar } from "@calcom/prisma/zod"; @@ -24,9 +23,3 @@ const schemaSelectedCalendarRequiredParams = z.object({ export const schemaSelectedCalendarBodyParams = schemaSelectedCalendarBaseBodyParams.merge( schemaSelectedCalendarRequiredParams ); - -export const withValidSelectedCalendar = withValidation({ - schema: schemaSelectedCalendarBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/shared/timeZone.ts b/lib/validations/shared/timeZone.ts new file mode 100644 index 0000000000..9dfa98003d --- /dev/null +++ b/lib/validations/shared/timeZone.ts @@ -0,0 +1,5 @@ +import * as tzdb from "tzdata"; +import * as z from "zod"; + +// @note: This is a custom validation that checks if the timezone is valid and exists in the tzdb library +export const timeZone = z.string().refine((tz: string) => Object.keys(tzdb.zones).includes(tz)); diff --git a/lib/validations/team.ts b/lib/validations/team.ts index ec6cbf35ec..d980607f87 100644 --- a/lib/validations/team.ts +++ b/lib/validations/team.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _TeamModel as Team } from "@calcom/prisma/zod"; @@ -10,9 +9,3 @@ const schemaTeamRequiredParams = z.object({}); export const schemaTeamBodyParams = schemaTeamBaseBodyParams.merge(schemaTeamRequiredParams); export const schemaTeamPublic = Team.omit({}); - -export const withValidTeam = withValidation({ - schema: schemaTeamBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/lib/validations/user.ts b/lib/validations/user.ts index c3e26e9cb0..9eb7b05094 100644 --- a/lib/validations/user.ts +++ b/lib/validations/user.ts @@ -1,9 +1,9 @@ -import { withValidation } from "next-validations"; -import * as tzdb from "tzdata"; import { z } from "zod"; import { _UserModel as User } from "@calcom/prisma/zod"; +import { timeZone } from "@lib/validations/shared/timeZone"; + // @note: These are the ONLY values allowed as weekStart. So user don't introduce bad data. enum weekdays { MONDAY = "Monday", @@ -77,11 +77,7 @@ export const schemaUserBaseBodyParams = User.pick({ const schemaUserEditParams = z.object({ weekStart: z.nativeEnum(weekdays).optional(), brandColor: z.string().min(4).max(9).regex(/^#/).optional(), - timeZone: z - .string() - // @note: This is a custom validation that checks if the timezone is valid and exists in the tzdb library - .refine((tz: string) => Object.keys(tzdb.zones).includes(tz)) - .optional(), + timeZone: timeZone.optional(), bufferTime: z.number().min(0).max(86400).optional(), startTime: z.number().min(0).max(86400).optional(), endTime: z.number().min(0).max(86400).optional(), diff --git a/lib/validations/webhook.ts b/lib/validations/webhook.ts index 746443213d..40b0731507 100644 --- a/lib/validations/webhook.ts +++ b/lib/validations/webhook.ts @@ -1,13 +1,5 @@ -import { withValidation } from "next-validations"; - import { _WebhookModel as Webhook } from "@calcom/prisma/zod"; export const schemaWebhookBodyParams = Webhook.omit({ id: true }).partial(); export const schemaWebhookPublic = Webhook.omit({}); - -export const withValidWebhook = withValidation({ - schema: schemaWebhookBodyParams, - type: "Zod", - mode: "body", -}); diff --git a/pages/api/attendees/[id].ts b/pages/api/attendees/[id].ts index 4d6aecd7f1..ce41228cd0 100644 --- a/pages/api/attendees/[id].ts +++ b/pages/api/attendees/[id].ts @@ -14,7 +14,7 @@ import { * @swagger * /attendees/{id}: * get: - * summary: Get an attendee by ID + * summary: Find an attendee by ID * parameters: * - in: path * name: id @@ -22,6 +22,7 @@ import { * type: integer * required: true * description: Numeric ID of the attendee to get + * example: 3 * security: * - ApiKeyAuth: [] * tags: @@ -37,18 +38,34 @@ import { * summary: Edit an existing attendee * consumes: * - application/json + * requestBody: + * description: Edit an existing attendee related to one of your bookings + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - bookingId + * - name + * - email + * - timeZone + * properties: + * email: + * type: string + * example: email@example.com + * name: + * type: string + * example: John Doe + * timeZone: + * type: string + * example: Europe/London * parameters: - * - in: body - * name: attendee - * description: The attendee to edit - * schema: - * type: object - * $ref: '#/components/schemas/Attendee' - * required: true * - in: path * name: id * schema: * type: integer + * example: 3 * required: true * description: Numeric ID of the attendee to edit * security: diff --git a/pages/api/attendees/index.ts b/pages/api/attendees/index.ts index 80afde914a..3adbc8a78c 100644 --- a/pages/api/attendees/index.ts +++ b/pages/api/attendees/index.ts @@ -10,7 +10,7 @@ import { schemaAttendeeCreateBodyParams, schemaAttendeeReadPublic } from "@lib/v * @swagger * /attendees: * get: - * summary: Get all attendees + * summary: Find all attendees * security: * - ApiKeyAuth: [] * tags: @@ -26,6 +26,33 @@ import { schemaAttendeeCreateBodyParams, schemaAttendeeReadPublic } from "@lib/v * summary: Creates a new attendee * security: * - ApiKeyAuth: [] + * consumes: + * - application/json + * requestBody: + * description: Create a new attendee related to one of your bookings + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - bookingId + * - name + * - email + * - timeZone + * properties: + * bookingId: + * type: number + * example: 1 + * email: + * type: string + * example: email@example.com + * name: + * type: string + * example: John Doe + * timeZone: + * type: string + * example: Europe/London * tags: * - attendees * responses: @@ -61,11 +88,12 @@ async function createOrlistAllAttendees( error, }); } else if (method === "POST") { - const safe = schemaAttendeeCreateBodyParams.safeParse(req.body); - if (!safe.success) { - throw new Error("Invalid request body", safe.error); + const safePost = schemaAttendeeCreateBodyParams.safeParse(req.body); + if (!safePost.success) { + console.log(safePost.error); + res.status(400).json({ error: safePost.error }); + throw new Error("Invalid request body", safePost.error); } - const bookingId = safe.data.bookingId; const userId = req.userId; const userWithBookings = await prisma.user.findUnique({ where: { id: userId }, @@ -76,15 +104,14 @@ async function createOrlistAllAttendees( } const userBookingIds = userWithBookings.bookings.map((booking: any) => booking.id).flat(); // Here we make sure to only return attendee's of the user's own bookings. - if (!userBookingIds.includes(parseInt(safe.data.bookingId))) - res.status(401).json({ message: "Unauthorized" }); + if (!userBookingIds.includes(safePost.data.bookingId)) res.status(401).json({ message: "Unauthorized" }); else { - delete safe.data.bookingId; - const noBookingId = safe.data; const data = await prisma.attendee.create({ data: { - ...noBookingId, - booking: { connect: { id: parseInt(bookingId) } }, + email: safePost.data.email, + name: safePost.data.name, + timeZone: safePost.data.timeZone, + booking: { connect: { id: safePost.data.bookingId } }, }, }); const attendee = schemaAttendeeReadPublic.parse(data); diff --git a/pages/api/availabilities/[id].ts b/pages/api/availabilities/[id].ts index 0bef2ab1a9..65f750dba7 100644 --- a/pages/api/availabilities/[id].ts +++ b/pages/api/availabilities/[id].ts @@ -17,7 +17,7 @@ import { * @swagger * /availabilities/{id}: * get: - * summary: Get an availability by ID + * summary: Find an availability by ID * parameters: * - in: path * name: id diff --git a/pages/api/availabilities/index.ts b/pages/api/availabilities/index.ts index 869530566e..3e519bc6ad 100644 --- a/pages/api/availabilities/index.ts +++ b/pages/api/availabilities/index.ts @@ -13,7 +13,7 @@ import { * @swagger * /availabilities: * get: - * summary: Get all availabilities + * summary: Find all availabilities * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/booking-references/[id].ts b/pages/api/booking-references/[id].ts index 1789c940b8..21b314f728 100644 --- a/pages/api/booking-references/[id].ts +++ b/pages/api/booking-references/[id].ts @@ -18,7 +18,7 @@ import { * @swagger * /booking-references/{id}: * get: - * summary: Get a booking reference by ID + * summary: Find a booking reference by ID * parameters: * - in: path * name: id diff --git a/pages/api/booking-references/index.ts b/pages/api/booking-references/index.ts index b715317c20..bc82f23701 100644 --- a/pages/api/booking-references/index.ts +++ b/pages/api/booking-references/index.ts @@ -13,7 +13,7 @@ import { * @swagger * /booking-references: * get: - * summary: Get all booking references + * summary: Find all booking references * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/bookings/[id].ts b/pages/api/bookings/[id].ts index c0b74d09f8..21be432cd1 100644 --- a/pages/api/bookings/[id].ts +++ b/pages/api/bookings/[id].ts @@ -4,7 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { BookingResponse } from "@lib/types"; -import { schemaBookingBodyParams, schemaBookingPublic } from "@lib/validations/booking"; +import { schemaBookingEditBodyParams, schemaBookingReadPublic } from "@lib/validations/booking"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, @@ -14,7 +14,7 @@ import { * @swagger * /bookings/{id}: * get: - * summary: Get a booking by ID + * summary: Find a booking by ID * parameters: * - in: path * name: id @@ -88,7 +88,6 @@ import { export async function bookingById(req: NextApiRequest, res: NextApiResponse) { const { method, query, body } = req; const safeQuery = schemaQueryIdParseInt.safeParse(query); - const safeBody = schemaBookingBodyParams.safeParse(body); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); const userId = req.userId; const userWithBookings = await prisma.user.findUnique({ @@ -103,7 +102,7 @@ export async function bookingById(req: NextApiRequest, res: NextApiResponse schemaBookingPublic.parse(data)) + .then((data) => schemaBookingReadPublic.parse(data)) .then((booking) => res.status(200).json({ booking })) .catch((error: Error) => res.status(404).json({ @@ -114,6 +113,8 @@ export async function bookingById(req: NextApiRequest, res: NextApiResponse schemaBookingPublic.parse(data)) + .then((data) => schemaBookingReadPublic.parse(data)) .then((booking) => res.status(200).json({ booking })) .catch((error: Error) => res.status(404).json({ diff --git a/pages/api/bookings/index.ts b/pages/api/bookings/index.ts index cb0f2089ed..8bb9cae30d 100644 --- a/pages/api/bookings/index.ts +++ b/pages/api/bookings/index.ts @@ -4,13 +4,13 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { BookingResponse, BookingsResponse } from "@lib/types"; -import { schemaBookingBodyParams, schemaBookingPublic, withValidBooking } from "@lib/validations/booking"; +import { schemaBookingCreateBodyParams, schemaBookingReadPublic } from "@lib/validations/booking"; /** * @swagger * /bookings: * get: - * summary: Get all bookings + * summary: Find all bookings * security: * - ApiKeyAuth: [] * tags: @@ -46,7 +46,7 @@ async function createOrlistAllBookings( if (method === "GET") { const data = await prisma.booking.findMany({ where: { userId } }); - const bookings = data.map((booking) => schemaBookingPublic.parse(booking)); + const bookings = data.map((booking) => schemaBookingReadPublic.parse(booking)); if (bookings) res.status(200).json({ bookings }); else (error: Error) => @@ -55,11 +55,11 @@ async function createOrlistAllBookings( error, }); } else if (method === "POST") { - const safe = schemaBookingBodyParams.safeParse(req.body); + const safe = schemaBookingCreateBodyParams.safeParse(req.body); if (!safe.success) throw new Error("Invalid request body"); const data = await prisma.booking.create({ data: { ...safe.data, userId } }); - const booking = schemaBookingPublic.parse(data); + const booking = schemaBookingReadPublic.parse(data); if (booking) res.status(201).json({ booking, message: "Booking created successfully" }); else diff --git a/pages/api/daily-event-references/[id].ts b/pages/api/daily-event-references/[id].ts index 280ad0c623..6ef4ac01e2 100644 --- a/pages/api/daily-event-references/[id].ts +++ b/pages/api/daily-event-references/[id].ts @@ -5,8 +5,8 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { DailyEventReferenceResponse } from "@lib/types"; import { - schemaDailyEventReferenceBodyParams, - schemaDailyEventReferencePublic, + schemaDailyEventReferenceEditBodyParams, + schemaDailyEventReferenceReadPublic, } from "@lib/validations/daily-event-reference"; import { schemaQueryIdParseInt, @@ -17,7 +17,7 @@ import { * @swagger * /daily-event-references/{id}: * get: - * summary: Get a daily event reference by ID + * summary: Find a daily event reference by ID * parameters: * - in: path * name: id @@ -94,7 +94,7 @@ export async function dailyEventReferenceById( ) { const { method, query, body } = req; const safeQuery = schemaQueryIdParseInt.safeParse(query); - const safeBody = schemaDailyEventReferenceBodyParams.safeParse(body); + const safeBody = schemaDailyEventReferenceEditBodyParams.safeParse(body); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); const userId = req.userId; const userBookings = await prisma.booking.findMany({ where: { userId } }); @@ -112,7 +112,7 @@ export async function dailyEventReferenceById( case "GET": await prisma.dailyEventReference .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaDailyEventReferencePublic.parse(data)) + .then((data) => schemaDailyEventReferenceReadPublic.parse(data)) .then((daily_event_reference) => res.status(200).json({ daily_event_reference })) .catch((error: Error) => res.status(404).json({ @@ -128,7 +128,7 @@ export async function dailyEventReferenceById( } await prisma.dailyEventReference .update({ where: { id: safeQuery.data.id }, data: safeBody.data }) - .then((data) => schemaDailyEventReferencePublic.parse(data)) + .then((data) => schemaDailyEventReferenceReadPublic.parse(data)) .then((daily_event_reference) => res.status(200).json({ daily_event_reference })) .catch((error: Error) => res.status(404).json({ diff --git a/pages/api/daily-event-references/index.ts b/pages/api/daily-event-references/index.ts index 670b18421c..dfafe246ce 100644 --- a/pages/api/daily-event-references/index.ts +++ b/pages/api/daily-event-references/index.ts @@ -5,15 +5,15 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { DailyEventReferenceResponse, DailyEventReferencesResponse } from "@lib/types"; import { - schemaDailyEventReferenceBodyParams, - schemaDailyEventReferencePublic, + schemaDailyEventReferenceCreateBodyParams, + schemaDailyEventReferenceReadPublic, } from "@lib/validations/daily-event-reference"; /** * @swagger * /daily-event-references: * get: - * summary: Get all daily event reference + * summary: Find all daily event reference * security: * - ApiKeyAuth: [] * tags: @@ -54,7 +54,7 @@ async function createOrlistAllDailyEventReferences( where: { bookingId: { in: userBookingIds } }, }); const daily_event_references = data.map((dailyEventReference) => - schemaDailyEventReferencePublic.parse(dailyEventReference) + schemaDailyEventReferenceReadPublic.parse(dailyEventReference) ); if (daily_event_references) res.status(200).json({ daily_event_references }); else @@ -64,11 +64,11 @@ async function createOrlistAllDailyEventReferences( error, }); } else if (method === "POST") { - const safe = schemaDailyEventReferenceBodyParams.safeParse(req.body); + const safe = schemaDailyEventReferenceCreateBodyParams.safeParse(req.body); if (!safe.success) throw new Error("Invalid request body"); const data = await prisma.dailyEventReference.create({ data: safe.data }); - const daily_event_reference = schemaDailyEventReferencePublic.parse(data); + const daily_event_reference = schemaDailyEventReferenceReadPublic.parse(data); if (daily_event_reference) res.status(201).json({ daily_event_reference, message: "DailyEventReference created successfully" }); diff --git a/pages/api/destination-calendars/[id].ts b/pages/api/destination-calendars/[id].ts index f5ba4d90ee..60982a7058 100644 --- a/pages/api/destination-calendars/[id].ts +++ b/pages/api/destination-calendars/[id].ts @@ -5,8 +5,8 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { DestinationCalendarResponse } from "@lib/types"; import { - schemaDestinationCalendarBodyParams, - schemaDestinationCalendarPublic, + schemaDestinationCalendarEditBodyParams, + schemaDestinationCalendarReadPublic, } from "@lib/validations/destination-calendar"; import { schemaQueryIdParseInt, @@ -17,7 +17,7 @@ import { * @swagger * /destination-calendars/{id}: * get: - * summary: Get a destination calendar by ID + * summary: Find a destination calendar by ID * parameters: * - in: path * name: id @@ -94,7 +94,7 @@ export async function destionationCalendarById( ) { const { method, query, body } = req; const safeQuery = schemaQueryIdParseInt.safeParse(query); - const safeBody = schemaDestinationCalendarBodyParams.safeParse(body); + const safeBody = schemaDestinationCalendarEditBodyParams.safeParse(body); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); const userId = req.userId; const data = await prisma.destinationCalendar.findMany({ where: { userId } }); @@ -107,7 +107,7 @@ export async function destionationCalendarById( case "GET": await prisma.destinationCalendar .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaDestinationCalendarPublic.parse(data)) + .then((data) => schemaDestinationCalendarReadPublic.parse(data)) .then((destination_calendar) => res.status(200).json({ destination_calendar })) .catch((error: Error) => res.status(404).json({ @@ -123,7 +123,7 @@ export async function destionationCalendarById( } await prisma.destinationCalendar .update({ where: { id: safeQuery.data.id }, data: safeBody.data }) - .then((data) => schemaDestinationCalendarPublic.parse(data)) + .then((data) => schemaDestinationCalendarReadPublic.parse(data)) .then((destination_calendar) => res.status(200).json({ destination_calendar })) .catch((error: Error) => res.status(404).json({ diff --git a/pages/api/destination-calendars/index.ts b/pages/api/destination-calendars/index.ts index 782612518c..d7921491c9 100644 --- a/pages/api/destination-calendars/index.ts +++ b/pages/api/destination-calendars/index.ts @@ -5,15 +5,15 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { DestinationCalendarResponse, DestinationCalendarsResponse } from "@lib/types"; import { - schemaDestinationCalendarBodyParams, - schemaDestinationCalendarPublic, + schemaDestinationCalendarCreateBodyParams, + schemaDestinationCalendarReadPublic, } from "@lib/validations/destination-calendar"; /** * @swagger * /destination-calendars: * get: - * summary: Get all destination calendars + * summary: Find all destination calendars * security: * - ApiKeyAuth: [] * tags: @@ -50,7 +50,7 @@ async function createOrlistAllDestinationCalendars( if (method === "GET") { const data = await prisma.destinationCalendar.findMany({ where: { userId } }); const destination_calendars = data.map((destinationCalendar) => - schemaDestinationCalendarPublic.parse(destinationCalendar) + schemaDestinationCalendarReadPublic.parse(destinationCalendar) ); if (data) res.status(200).json({ destination_calendars }); else @@ -60,11 +60,11 @@ async function createOrlistAllDestinationCalendars( error, }); } else if (method === "POST") { - const safe = schemaDestinationCalendarBodyParams.safeParse(req.body); + const safe = schemaDestinationCalendarCreateBodyParams.safeParse(req.body); if (!safe.success) throw new Error("Invalid request body"); const data = await prisma.destinationCalendar.create({ data: { ...safe.data, userId } }); - const destination_calendar = schemaDestinationCalendarPublic.parse(data); + const destination_calendar = schemaDestinationCalendarReadPublic.parse(data); if (destination_calendar) res.status(201).json({ destination_calendar, message: "DestinationCalendar created successfully" }); diff --git a/pages/api/docs.ts b/pages/api/docs.ts index 25d9c7ce1b..7dd7afa65d 100644 --- a/pages/api/docs.ts +++ b/pages/api/docs.ts @@ -20,6 +20,7 @@ const swaggerHandler = withSwagger({ }, components: { securitySchemes: { ApiKeyAuth: { type: "apiKey", in: "query", name: "apiKey" } }, + security: { ApiKeyAuth: [] }, schemas: { ...jsonSchema.definitions }, }, }, diff --git a/pages/api/event-type-custom-inputs/[id].ts b/pages/api/event-type-custom-inputs/[id].ts index 23e4c73a9a..7d3ffa7a5d 100644 --- a/pages/api/event-type-custom-inputs/[id].ts +++ b/pages/api/event-type-custom-inputs/[id].ts @@ -17,7 +17,7 @@ import { * @swagger * /event-type-custom-inputs/{id}: * get: - * summary: Get a eventTypeCustomInput by ID + * summary: Find a eventTypeCustomInput by ID * parameters: * - in: path * name: id diff --git a/pages/api/event-type-custom-inputs/index.ts b/pages/api/event-type-custom-inputs/index.ts index f1931b8b12..01f5af921f 100644 --- a/pages/api/event-type-custom-inputs/index.ts +++ b/pages/api/event-type-custom-inputs/index.ts @@ -13,7 +13,7 @@ import { * @swagger * /event-type-custom-inputs: * get: - * summary: Get all eventTypeCustomInputs + * summary: Find all eventTypeCustomInputs * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/event-types/[id].ts b/pages/api/event-types/[id].ts index 465e85b428..37a5b38be7 100644 --- a/pages/api/event-types/[id].ts +++ b/pages/api/event-types/[id].ts @@ -14,7 +14,7 @@ import { * @swagger * /event-types/{id}: * get: - * summary: Get a eventType by ID + * summary: Find a eventType by ID * parameters: * - in: path * name: id diff --git a/pages/api/event-types/index.ts b/pages/api/event-types/index.ts index 9d73be9458..9e1e83c038 100644 --- a/pages/api/event-types/index.ts +++ b/pages/api/event-types/index.ts @@ -10,7 +10,7 @@ import { schemaEventTypeBodyParams, schemaEventTypePublic } from "@lib/validatio * @swagger * /event-types: * get: - * summary: Get all event types + * summary: Find all event types * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/index.ts b/pages/api/index.ts index 94461cedce..3f72b7eaef 100644 --- a/pages/api/index.ts +++ b/pages/api/index.ts @@ -1,5 +1,10 @@ import type { NextApiRequest, NextApiResponse } from "next"; +/** + * @swagger + * security: + * - ApiKeyAuth: [] + */ export default async function CalcomApi(_: NextApiRequest, res: NextApiResponse) { res.status(201).json({ message: "Welcome to Cal.com API - docs are at https://docs.cal.com/api" }); } diff --git a/pages/api/memberships/[id].ts b/pages/api/memberships/[id].ts index c4a1d362f7..0e7498d16c 100644 --- a/pages/api/memberships/[id].ts +++ b/pages/api/memberships/[id].ts @@ -11,7 +11,7 @@ import { schemaQueryIdAsString, withValidQueryIdString } from "@lib/validations/ * @swagger * /memberships/{userId}_{teamId}: * get: - * summary: Get a membership by userID and teamID + * summary: Find a membership by userID and teamID * parameters: * - in: path * name: userId diff --git a/pages/api/memberships/index.ts b/pages/api/memberships/index.ts index 702effdfd6..3df3b56d13 100644 --- a/pages/api/memberships/index.ts +++ b/pages/api/memberships/index.ts @@ -10,7 +10,7 @@ import { schemaMembershipBodyParams, schemaMembershipPublic } from "@lib/validat * @swagger * /memberships: * get: - * summary: Get all memberships + * summary: Find all memberships * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/payments/[id].ts b/pages/api/payments/[id].ts index 6ccf227641..8ab4d5eab9 100644 --- a/pages/api/payments/[id].ts +++ b/pages/api/payments/[id].ts @@ -14,7 +14,7 @@ import { * @swagger * /payments/{id}: * get: - * summary: Get one of your own payments by ID + * summary: Find one of your own payments by ID * parameters: * - in: path * name: id diff --git a/pages/api/payments/index.ts b/pages/api/payments/index.ts index 2b00d85ebb..db0cd349ca 100644 --- a/pages/api/payments/index.ts +++ b/pages/api/payments/index.ts @@ -10,7 +10,7 @@ import { schemaPaymentPublic } from "@lib/validations/payment"; * @swagger * /payments: * get: - * summary: Get all payments + * summary: Find all payments * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/schedules/[id].ts b/pages/api/schedules/[id].ts index 154dfa41f1..8a08128f4f 100644 --- a/pages/api/schedules/[id].ts +++ b/pages/api/schedules/[id].ts @@ -14,7 +14,7 @@ import { * @swagger * /schedules/{id}: * get: - * summary: Get a schedule by ID + * summary: Find a schedule by ID * parameters: * - in: path * name: id diff --git a/pages/api/schedules/index.ts b/pages/api/schedules/index.ts index d04d35cccb..228599eef3 100644 --- a/pages/api/schedules/index.ts +++ b/pages/api/schedules/index.ts @@ -4,13 +4,13 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { ScheduleResponse, SchedulesResponse } from "@lib/types"; -import { schemaScheduleBodyParams, schemaSchedulePublic, withValidSchedule } from "@lib/validations/schedule"; +import { schemaScheduleBodyParams, schemaSchedulePublic } from "@lib/validations/schedule"; /** * @swagger * /schedules: * get: - * summary: Get all schedules + * summary: Find all schedules * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/selected-calendars/[id].ts b/pages/api/selected-calendars/[id].ts index 1b3e5e9cd5..6efbca86ee 100644 --- a/pages/api/selected-calendars/[id].ts +++ b/pages/api/selected-calendars/[id].ts @@ -14,7 +14,7 @@ import { schemaQueryIdAsString, withValidQueryIdString } from "@lib/validations/ * @swagger * /selected-calendars/{userId}_{integration}_{externalId}: * get: - * summary: Get a selected-calendar by userID and teamID + * summary: Find a selected-calendar by userID and teamID * parameters: * - in: path * name: userId diff --git a/pages/api/selected-calendars/index.ts b/pages/api/selected-calendars/index.ts index d9b80a8541..83537208ba 100644 --- a/pages/api/selected-calendars/index.ts +++ b/pages/api/selected-calendars/index.ts @@ -13,7 +13,7 @@ import { * @swagger * /selected-calendars: * get: - * summary: Get all selected calendars + * summary: Find all selected calendars * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/teams/[id].ts b/pages/api/teams/[id].ts index 2990d3642b..0fead8e066 100644 --- a/pages/api/teams/[id].ts +++ b/pages/api/teams/[id].ts @@ -14,7 +14,7 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team"; * @swagger * /teams/{id}: * get: - * summary: Get a team by ID + * summary: Find a team by ID * parameters: * - in: path * name: id diff --git a/pages/api/teams/index.ts b/pages/api/teams/index.ts index d6392e0c1b..ce335fff9d 100644 --- a/pages/api/teams/index.ts +++ b/pages/api/teams/index.ts @@ -5,13 +5,13 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { TeamResponse, TeamsResponse } from "@lib/types"; import { schemaMembershipPublic } from "@lib/validations/membership"; -import { schemaTeamBodyParams, schemaTeamPublic, withValidTeam } from "@lib/validations/team"; +import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team"; /** * @swagger * /teams: * get: - * summary: Get all teams + * summary: Find all teams * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/users/[id].ts b/pages/api/users/[id].ts index ee9279da0a..06e5612dd0 100644 --- a/pages/api/users/[id].ts +++ b/pages/api/users/[id].ts @@ -14,7 +14,7 @@ import { schemaUserEditBodyParams, schemaUserReadPublic } from "@lib/validations * @swagger * /users/{id}: * get: - * summary: Get a user by ID, returns your user if regular user. + * summary: Find a user by ID, returns your user if regular user. * parameters: * - in: path * name: id diff --git a/pages/api/users/index.ts b/pages/api/users/index.ts index aa35dca042..fa28b36f5e 100644 --- a/pages/api/users/index.ts +++ b/pages/api/users/index.ts @@ -10,7 +10,7 @@ import { schemaUserReadPublic } from "@lib/validations/user"; * @swagger * /users: * get: - * summary: Get all users (admin only), returns your user if regular user. + * summary: Find all users (admin only), returns your user if regular user. * security: * - ApiKeyAuth: [] * tags: diff --git a/templates/endpoints/[id]/delete.ts b/templates/endpoints/[id]/delete.ts index 5cf646aee0..9c5c6f9f7c 100644 --- a/templates/endpoints/[id]/delete.ts +++ b/templates/endpoints/[id]/delete.ts @@ -22,7 +22,7 @@ import { * required: true * description: Numeric ID of the resource to delete * security: - * - ApiKeyAuth: [] + * - ApiKeyAuth: [] * tags: * - resources * responses: diff --git a/templates/endpoints/[id]/edit.ts b/templates/endpoints/[id]/edit.ts index 54b8fec540..8ddad4601d 100644 --- a/templates/endpoints/[id]/edit.ts +++ b/templates/endpoints/[id]/edit.ts @@ -23,7 +23,7 @@ import { * required: true * description: Numeric ID of the resource to edit * security: - * - ApiKeyAuth: [] + * - ApiKeyAuth: [] * tags: * - resources * responses: diff --git a/templates/endpoints/[id]/index.ts b/templates/endpoints/[id]/index.ts index 66e7ab105c..9f4c64f11d 100644 --- a/templates/endpoints/[id]/index.ts +++ b/templates/endpoints/[id]/index.ts @@ -14,7 +14,7 @@ import { * @swagger * /v1/resources/{id}: * get: - * summary: Get a resource by ID + * summary: Find a resource by ID * parameters: * - in: path * name: id diff --git a/templates/endpoints/get_all.ts b/templates/endpoints/get_all.ts index b3b9257e9a..ee66e7c773 100644 --- a/templates/endpoints/get_all.ts +++ b/templates/endpoints/get_all.ts @@ -10,7 +10,7 @@ import { schemaResourcePublic } from "@lib/validations/resource"; * @swagger * /v1/resources: * get: - * summary: Get all resources + * summary: Find all resources * security: * - ApiKeyAuth: [] * tags: diff --git a/templates/endpoints/get_all_and_post.ts b/templates/endpoints/get_all_and_post.ts index bf6fee016f..2ce4b8dfe9 100644 --- a/templates/endpoints/get_all_and_post.ts +++ b/templates/endpoints/get_all_and_post.ts @@ -10,7 +10,7 @@ import { schemaPaymentBodyParams, schemaPaymentPublic } from "@lib/validations/p * @swagger * /v1/payments: * get: - * summary: Get all payments + * summary: Find all payments * security: * - ApiKeyAuth: [] * tags: diff --git a/templates/endpoints/post.ts b/templates/endpoints/post.ts index e2b9e17683..999bce39c1 100644 --- a/templates/endpoints/post.ts +++ b/templates/endpoints/post.ts @@ -19,7 +19,7 @@ import { schemaResourceBodyParams, schemaResourcePublic, withValidResource } fro * schema: * $ref: '#/components/schemas/Resource' * security: - * - ApiKeyAuth: [] + * - ApiKeyAuth: [] * tags: * - resources * responses: diff --git a/templates/zod-validation.ts b/templates/zod-validation.ts index 02b0597529..82a95a97d1 100644 --- a/templates/zod-validation.ts +++ b/templates/zod-validation.ts @@ -1,4 +1,3 @@ -import { withValidation } from "next-validations"; import { z } from "zod"; import { _ModelModel as Model } from "@calcom/prisma/zod"; @@ -15,9 +14,3 @@ export const schemaModelPublic = Model.omit({ id: true, userId: true, }); - -export const withValidModel = withValidation({ - schema: schemaModelBodyParams, - type: "Zod", - mode: "body", -}); From 238b03791e819992bfe7979b66b4d994b6420388 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Wed, 27 Apr 2022 19:45:59 +0200 Subject: [PATCH 5/5] Fix: add strict and optional to have errors on extraneus params --- lib/validations/attendee.ts | 3 +- lib/validations/availability.ts | 29 ++++++++++------- lib/validations/booking-reference.ts | 38 +++++++++++++--------- lib/validations/booking.ts | 33 ++++++++++--------- lib/validations/daily-event-reference.ts | 23 +++++++------ lib/validations/destination-calendar.ts | 41 +++++++++++++----------- 6 files changed, 94 insertions(+), 73 deletions(-) diff --git a/lib/validations/attendee.ts b/lib/validations/attendee.ts index d603eaaf63..70444437aa 100644 --- a/lib/validations/attendee.ts +++ b/lib/validations/attendee.ts @@ -15,14 +15,13 @@ const schemaAttendeeCreateParams = z .object({ bookingId: z.number().int(), email: z.string().email(), - name: z.string(), + name: z.string().optional(), timeZone: timeZone, }) .strict(); const schemaAttendeeEditParams = z .object({ - // @note: disallowing bookingId changes in attendee via API for now as it would introduce side effects name: z.string().optional(), email: z.string().email().optional(), timeZone: timeZone.optional(), diff --git a/lib/validations/availability.ts b/lib/validations/availability.ts index 6039dfb80f..a6ddf4b5c6 100644 --- a/lib/validations/availability.ts +++ b/lib/validations/availability.ts @@ -21,19 +21,24 @@ export const schemaAvailabilityReadPublic = Availability.pick({ eventTypeId: true, }); -const schemaAvailabilityCreateParams = z.object({ - startTime: z.date().or(z.string()), - endTime: z.date().or(z.string()), - days: z.array(z.number()).optional(), - eventTypeId: z.number().optional(), -}); +const schemaAvailabilityCreateParams = z + .object({ + startTime: z.date().or(z.string()), + endTime: z.date().or(z.string()), + days: z.array(z.number()).optional(), + eventTypeId: z.number().optional(), + }) + .strict(); + +const schemaAvailabilityEditParams = z + .object({ + startTime: z.date().or(z.string()).optional(), + endTime: z.date().or(z.string()).optional(), + days: z.array(z.number()).optional(), + eventTypeId: z.number().optional(), + }) + .strict(); -const schemaAvailabilityEditParams = z.object({ - startTime: z.date().or(z.string()).optional(), - endTime: z.date().or(z.string()).optional(), - days: z.array(z.number()).optional(), - eventTypeId: z.number().optional(), -}); export const schemaAvailabilityEditBodyParams = schemaAvailabilityBaseBodyParams.merge( schemaAvailabilityEditParams ); diff --git a/lib/validations/booking-reference.ts b/lib/validations/booking-reference.ts index f207b09208..66aabc3d88 100644 --- a/lib/validations/booking-reference.ts +++ b/lib/validations/booking-reference.ts @@ -23,22 +23,28 @@ export const schemaBookingReferenceReadPublic = BookingReference.pick({ deleted: true, }); -const schemaBookingReferenceEditParams = z.object({ - type: z.string(), - uid: z.string(), - meetingId: z.string(), - meetingPassword: z.string(), - meetingUrl: z.string(), - deleted: z.boolean(), -}); -const schemaBookingReferenceCreateParams = z.object({ - type: z.string(), - uid: z.string(), - meetingId: z.string(), - meetingPassword: z.string(), - meetingUrl: z.string(), - deleted: z.boolean(), -}); +const schemaBookingReferenceCreateParams = z + .object({ + type: z.string(), + uid: z.string(), + meetingId: z.string(), + meetingPassword: z.string(), + meetingUrl: z.string(), + deleted: z.boolean(), + }) + .strict(); + +const schemaBookingReferenceEditParams = z + .object({ + type: z.string().optional(), + uid: z.string().optional(), + meetingId: z.string().optional(), + meetingPassword: z.string().optional(), + meetingUrl: z.string().optional(), + deleted: z.boolean().optional(), + }) + .strict(); + export const schemaBookingCreateBodyParams = schemaBookingReferenceBaseBodyParams.merge( schemaBookingReferenceCreateParams ); diff --git a/lib/validations/booking.ts b/lib/validations/booking.ts index 5bf187f523..60c231efa5 100644 --- a/lib/validations/booking.ts +++ b/lib/validations/booking.ts @@ -11,24 +11,27 @@ const schemaBookingBaseBodyParams = Booking.pick({ endTime: true, }).partial(); -const schemaBookingCreateParams = z.object({ - uid: z.string(), - userId: z.number(), - eventTypeId: z.number(), - title: z.string(), - startTime: z.date(), - endTime: z.date(), -}); +const schemaBookingCreateParams = z + .object({ + uid: z.string(), + userId: z.number(), + eventTypeId: z.number(), + title: z.string(), + startTime: z.date(), + endTime: z.date(), + }) + .strict(); export const schemaBookingCreateBodyParams = schemaBookingBaseBodyParams.merge(schemaBookingCreateParams); -// @note: disallowing userId/eventTypeId changes in booking endpoint via PATCH for now as it would introduce side effects -const schemaBookingEditParams = z.object({ - uid: z.string(), - title: z.string(), - startTime: z.date(), - endTime: z.date(), -}); +const schemaBookingEditParams = z + .object({ + uid: z.string().optional(), + title: z.string().optional(), + startTime: z.date().optional(), + endTime: z.date().optional(), + }) + .strict(); export const schemaBookingEditBodyParams = schemaBookingBaseBodyParams.merge(schemaBookingEditParams); diff --git a/lib/validations/daily-event-reference.ts b/lib/validations/daily-event-reference.ts index ca74d5a95f..0ce79e6324 100644 --- a/lib/validations/daily-event-reference.ts +++ b/lib/validations/daily-event-reference.ts @@ -8,21 +8,24 @@ export const schemaDailyEventReferenceBaseBodyParams = DailyEventReference.pick( bookingId: true, }).partial(); -const schemaDailyEventReferenceCreateParams = z.object({ - dailytoken: z.string(), - dailyurl: z.string(), - bookingId: z.number(), -}); +const schemaDailyEventReferenceCreateParams = z + .object({ + dailytoken: z.string(), + dailyurl: z.string(), + bookingId: z.number(), + }) + .strict(); export const schemaDailyEventReferenceCreateBodyParams = schemaDailyEventReferenceBaseBodyParams.merge( schemaDailyEventReferenceCreateParams ); -const schemaDailyEventReferenceEditParams = z.object({ - dailytoken: z.string(), - dailyurl: z.string(), - // @note: disallowing bookingId changes in daily-event-reference via API endpoint for now as it would introduce side effects -}); +const schemaDailyEventReferenceEditParams = z + .object({ + dailytoken: z.string().optional(), + dailyurl: z.string().optional(), + }) + .strict(); export const schemaDailyEventReferenceEditBodyParams = schemaDailyEventReferenceBaseBodyParams.merge( schemaDailyEventReferenceEditParams diff --git a/lib/validations/destination-calendar.ts b/lib/validations/destination-calendar.ts index 541260d4dc..7f90cd0002 100644 --- a/lib/validations/destination-calendar.ts +++ b/lib/validations/destination-calendar.ts @@ -10,29 +10,34 @@ export const schemaDestinationCalendarBaseBodyParams = DestinationCalendar.pick( userId: true, }).partial(); -const schemaDestinationCalendarEditParams = z.object({ - integration: z.string(), - externalId: z.string(), - eventTypeId: z.number(), - bookingId: z.number(), - userId: z.number(), -}); - -export const schemaDestinationCalendarEditBodyParams = schemaDestinationCalendarBaseBodyParams.merge( - schemaDestinationCalendarEditParams -); -const schemaDestinationCalendarCreateParams = z.object({ - integration: z.string(), - externalId: z.string(), - eventTypeId: z.number(), - bookingId: z.number(), - userId: z.number(), -}); +const schemaDestinationCalendarCreateParams = z + .object({ + integration: z.string(), + externalId: z.string(), + eventTypeId: z.number(), + bookingId: z.number(), + userId: z.number(), + }) + .strict(); export const schemaDestinationCalendarCreateBodyParams = schemaDestinationCalendarBaseBodyParams.merge( schemaDestinationCalendarCreateParams ); +const schemaDestinationCalendarEditParams = z + .object({ + integration: z.string().optional(), + externalId: z.string().optional(), + eventTypeId: z.number().optional(), + bookingId: z.number().optional(), + userId: z.number().optional(), + }) + .strict(); + +export const schemaDestinationCalendarEditBodyParams = schemaDestinationCalendarBaseBodyParams.merge( + schemaDestinationCalendarEditParams +); + export const schemaDestinationCalendarReadPublic = DestinationCalendar.pick({ id: true, integration: true,