From 44ce77f814aab331395439f4bd7a3db9ce0ff13d Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Wed, 6 Jul 2022 01:17:59 +0200 Subject: [PATCH 1/4] fix: test return booking get --- pages/api/bookings/[id].ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/api/bookings/[id].ts b/pages/api/bookings/[id].ts index f4f11b143f..6897cb0464 100644 --- a/pages/api/bookings/[id].ts +++ b/pages/api/bookings/[id].ts @@ -52,7 +52,7 @@ export async function bookingById( * description: Booking was not found */ case "GET": - await prisma.booking + return await prisma.booking .findUnique({ where: { id: safeQuery.data.id } }) .then((data) => schemaBookingReadPublic.parse(data)) .then((booking) => res.status(200).json({ booking })) @@ -170,4 +170,4 @@ export async function bookingById( } } -export default withMiddleware("HTTP_GET_DELETE_PATCH")(withValidQueryIdTransformParseInt(bookingById)); +export default withMiddleware("HTTP_GET_DELETE_PATCH")(bookingById); From cef2768725d21b3362637f31791953d67ebfb5b6 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Fri, 8 Jul 2022 02:05:38 +0200 Subject: [PATCH 2/4] fix: add next-axiom to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index a02580eb97..7ab3bb2db3 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "modify-response-middleware": "^1.1.0", "next": "^12.2.0", "next-api-middleware": "^1.0.1", + "next-axiom": "^0.10.0", "next-swagger-doc": "^0.3.4", "next-transpile-modules": "^9.0.0", "next-validations": "^0.2.0", From 6b09fb24ab5d09efba0953f6458f6bc72631c2f7 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Fri, 8 Jul 2022 17:23:06 +0200 Subject: [PATCH 3/4] fix: adds returns, no breaks --- pages/api/bookings/[id].ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pages/api/bookings/[id].ts b/pages/api/bookings/[id].ts index 6897cb0464..f24a90bad0 100644 --- a/pages/api/bookings/[id].ts +++ b/pages/api/bookings/[id].ts @@ -62,7 +62,6 @@ export async function bookingById( error, }) ); - break; /** * @swagger * /bookings/{id}: @@ -110,12 +109,12 @@ export async function bookingById( res.status(400).json({ message: "Bad request", error: safeBody.error }); return; } - await prisma.booking + return await prisma.booking .update({ where: { id: safeQuery.data.id }, data: safeBody.data, }) - // .then((data) => schemaBookingReadPublic.parse(data)) + .then((data) => schemaBookingReadPublic.parse(data)) .then((booking) => res.status(200).json({ booking })) .catch((error: Error) => res.status(404).json({ @@ -123,7 +122,6 @@ export async function bookingById( error, }) ); - break; /** * @swagger * /bookings/{id}: @@ -148,7 +146,7 @@ export async function bookingById( * description: Authorization information is missing or invalid. */ case "DELETE": - await prisma.booking + return await prisma.booking .delete({ where: { id: safeQuery.data.id } }) .then(() => res.status(200).json({ @@ -161,13 +159,11 @@ export async function bookingById( error, }) ); - break; default: - res.status(405).json({ message: "Method not allowed" }); - break; + return res.status(405).json({ message: "Method not allowed" }); } } } -export default withMiddleware("HTTP_GET_DELETE_PATCH")(bookingById); +export default withMiddleware("HTTP_GET_DELETE_PATCH")(withValidQueryIdTransformParseInt(bookingById)); From 308c192912a2a774d572240ed4865bafacda9ee5 Mon Sep 17 00:00:00 2001 From: zomars Date: Fri, 8 Jul 2022 11:21:40 -0600 Subject: [PATCH 4/4] Splits booking/[id] endpoint # Conflicts: # pages/api/bookings/[id].ts --- pages/api/bookings/[id].ts | 169 -------------------- pages/api/bookings/[id]/_auth-middleware.ts | 25 +++ pages/api/bookings/[id]/_delete.ts | 37 +++++ pages/api/bookings/[id]/_get.ts | 38 +++++ pages/api/bookings/[id]/_patch.ts | 56 +++++++ pages/api/bookings/[id]/index.ts | 16 ++ 6 files changed, 172 insertions(+), 169 deletions(-) delete mode 100644 pages/api/bookings/[id].ts create mode 100644 pages/api/bookings/[id]/_auth-middleware.ts create mode 100644 pages/api/bookings/[id]/_delete.ts create mode 100644 pages/api/bookings/[id]/_get.ts create mode 100644 pages/api/bookings/[id]/_patch.ts create mode 100644 pages/api/bookings/[id]/index.ts diff --git a/pages/api/bookings/[id].ts b/pages/api/bookings/[id].ts deleted file mode 100644 index f24a90bad0..0000000000 --- a/pages/api/bookings/[id].ts +++ /dev/null @@ -1,169 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import { withMiddleware } from "@lib/helpers/withMiddleware"; -import type { BookingResponse } from "@lib/types"; -import { schemaBookingEditBodyParams, schemaBookingReadPublic } from "@lib/validations/booking"; -import { - schemaQueryIdParseInt, - withValidQueryIdTransformParseInt, -} from "@lib/validations/shared/queryIdTransformParseInt"; - -export async function bookingById( - { method, query, body, userId, prisma, isAdmin }: NextApiRequest, - res: NextApiResponse -) { - const safeQuery = schemaQueryIdParseInt.safeParse(query); - if (!safeQuery.success) { - res.status(400).json({ message: "Your query was invalid" }); - return; - } - const userWithBookings = await prisma.user.findUnique({ - where: { id: userId }, - include: { bookings: true }, - }); - if (!userWithBookings) throw new Error("User not found"); - const userBookingIds = userWithBookings.bookings.map((booking: { id: number }) => booking.id).flat(); - - if (!isAdmin) { - if (!userBookingIds.includes(safeQuery.data.id)) res.status(401).json({ message: "Unauthorized" }); - } else { - switch (method) { - /** - * @swagger - * /bookings/{id}: - * get: - * summary: Find a booking - * operationId: getBookingById - * parameters: - * - in: path - * name: id - * schema: - * type: integer - * required: true - * description: ID of the booking to get - * tags: - * - bookings - * responses: - * 200: - * description: OK - * 401: - * description: Authorization information is missing or invalid. - * 404: - * description: Booking was not found - */ - case "GET": - return await prisma.booking - .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaBookingReadPublic.parse(data)) - .then((booking) => res.status(200).json({ booking })) - .catch((error: Error) => - res.status(404).json({ - message: `Booking with id: ${safeQuery.data.id} not found`, - error, - }) - ); - /** - * @swagger - * /bookings/{id}: - * patch: - * summary: Edit an existing booking - * operationId: editBookingById - * requestBody: - * description: Edit an existing booking related to one of your event-types - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * title: - * type: string - * example: 15min - * startTime: - * type: string - * example: 1970-01-01T17:00:00.000Z - * endTime: - * type: string - * example: 1970-01-01T17:00:00.000Z - * parameters: - * - in: path - * name: id - * schema: - * type: integer - * required: true - * description: ID of the booking to edit - * tags: - * - bookings - * responses: - * 201: - * description: OK, booking edited successfuly - * 400: - * description: Bad request. Booking body is invalid. - * 401: - * description: Authorization information is missing or invalid. - */ - case "PATCH": - const safeBody = schemaBookingEditBodyParams.safeParse(body); - if (!safeBody.success) { - console.log(safeBody.error); - res.status(400).json({ message: "Bad request", error: safeBody.error }); - return; - } - return await prisma.booking - .update({ - where: { id: safeQuery.data.id }, - data: safeBody.data, - }) - .then((data) => schemaBookingReadPublic.parse(data)) - .then((booking) => res.status(200).json({ booking })) - .catch((error: Error) => - res.status(404).json({ - message: `Booking with id: ${safeQuery.data.id} not found`, - error, - }) - ); - /** - * @swagger - * /bookings/{id}: - * delete: - * summary: Remove an existing booking - * operationId: removeBookingById - * parameters: - * - in: path - * name: id - * schema: - * type: integer - * required: true - * description: ID of the booking to delete - * tags: - * - bookings - * responses: - * 201: - * description: OK, booking removed successfuly - * 400: - * description: Bad request. Booking id is invalid. - * 401: - * description: Authorization information is missing or invalid. - */ - case "DELETE": - return await prisma.booking - .delete({ where: { id: safeQuery.data.id } }) - .then(() => - res.status(200).json({ - message: `Booking with id: ${safeQuery.data.id} deleted successfully`, - }) - ) - .catch((error: Error) => - res.status(404).json({ - message: `Booking with id: ${safeQuery.data.id} not found`, - error, - }) - ); - - default: - return res.status(405).json({ message: "Method not allowed" }); - } - } -} - -export default withMiddleware("HTTP_GET_DELETE_PATCH")(withValidQueryIdTransformParseInt(bookingById)); diff --git a/pages/api/bookings/[id]/_auth-middleware.ts b/pages/api/bookings/[id]/_auth-middleware.ts new file mode 100644 index 0000000000..ead390f413 --- /dev/null +++ b/pages/api/bookings/[id]/_auth-middleware.ts @@ -0,0 +1,25 @@ +import type { NextApiRequest } from "next"; + +import { HttpError } from "@calcom/lib/http-error"; +import { defaultResponder } from "@calcom/lib/server"; + +import { schemaQueryIdParseInt } from "@lib/validations/shared/queryIdTransformParseInt"; + +export async function authMiddleware(req: NextApiRequest) { + const { userId, prisma, isAdmin, query } = req; + const { id } = schemaQueryIdParseInt.parse(query); + const userWithBookings = await prisma.user.findUnique({ + where: { id: userId }, + include: { bookings: true }, + }); + + if (!userWithBookings) throw new HttpError({ statusCode: 404, message: "User not found" }); + + const userBookingIds = userWithBookings.bookings.map((booking) => booking.id); + + if (!isAdmin && !userBookingIds.includes(id)) { + throw new HttpError({ statusCode: 401, message: "You are not authorized" }); + } +} + +export default defaultResponder(authMiddleware); diff --git a/pages/api/bookings/[id]/_delete.ts b/pages/api/bookings/[id]/_delete.ts new file mode 100644 index 0000000000..46014f5785 --- /dev/null +++ b/pages/api/bookings/[id]/_delete.ts @@ -0,0 +1,37 @@ +import type { NextApiRequest } from "next"; + +import { defaultResponder } from "@calcom/lib/server"; + +import { schemaQueryIdParseInt } from "@lib/validations/shared/queryIdTransformParseInt"; + +/** + * @swagger + * /bookings/{id}: + * delete: + * summary: Remove an existing booking + * operationId: removeBookingById + * parameters: + * - in: path + * name: id + * schema: + * type: integer + * required: true + * description: ID of the booking to delete + * tags: + * - bookings + * responses: + * 201: + * description: OK, booking removed successfuly + * 400: + * description: Bad request. Booking id is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ +export async function deleteHandler(req: NextApiRequest) { + const { prisma, query } = req; + const { id } = schemaQueryIdParseInt.parse(query); + await prisma.booking.delete({ where: { id } }); + return { message: `Booking with id: ${id} deleted successfully` }; +} + +export default defaultResponder(deleteHandler); diff --git a/pages/api/bookings/[id]/_get.ts b/pages/api/bookings/[id]/_get.ts new file mode 100644 index 0000000000..567312261a --- /dev/null +++ b/pages/api/bookings/[id]/_get.ts @@ -0,0 +1,38 @@ +import type { NextApiRequest } from "next"; + +import { defaultResponder } from "@calcom/lib/server"; + +import { schemaBookingReadPublic } from "@lib/validations/booking"; +import { schemaQueryIdParseInt } from "@lib/validations/shared/queryIdTransformParseInt"; + +/** + * @swagger + * /bookings/{id}: + * get: + * summary: Find a booking + * operationId: getBookingById + * parameters: + * - in: path + * name: id + * schema: + * type: integer + * required: true + * description: ID of the booking to get + * tags: + * - bookings + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: Booking was not found + */ +export async function getHandler(req: NextApiRequest) { + const { prisma, query } = req; + const { id } = schemaQueryIdParseInt.parse(query); + const booking = await prisma.booking.findUnique({ where: { id } }); + return { booking: schemaBookingReadPublic.parse(booking) }; +} + +export default defaultResponder(getHandler); diff --git a/pages/api/bookings/[id]/_patch.ts b/pages/api/bookings/[id]/_patch.ts new file mode 100644 index 0000000000..d2700ae229 --- /dev/null +++ b/pages/api/bookings/[id]/_patch.ts @@ -0,0 +1,56 @@ +import type { NextApiRequest } from "next"; + +import { defaultResponder } from "@calcom/lib/server"; + +import { schemaBookingEditBodyParams, schemaBookingReadPublic } from "@lib/validations/booking"; +import { schemaQueryIdParseInt } from "@lib/validations/shared/queryIdTransformParseInt"; + +/** + * @swagger + * /bookings/{id}: + * patch: + * summary: Edit an existing booking + * operationId: editBookingById + * requestBody: + * description: Edit an existing booking related to one of your event-types + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * title: + * type: string + * example: 15min + * startTime: + * type: string + * example: 1970-01-01T17:00:00.000Z + * endTime: + * type: string + * example: 1970-01-01T17:00:00.000Z + * parameters: + * - in: path + * name: id + * schema: + * type: integer + * required: true + * description: ID of the booking to edit + * tags: + * - bookings + * responses: + * 201: + * description: OK, booking edited successfuly + * 400: + * description: Bad request. Booking body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ +export async function patchHandler(req: NextApiRequest) { + const { prisma, query, body } = req; + const { id } = schemaQueryIdParseInt.parse(query); + const data = schemaBookingEditBodyParams.parse(body); + const booking = await prisma.booking.update({ where: { id }, data }); + return { booking: schemaBookingReadPublic.parse(booking) }; +} + +export default defaultResponder(patchHandler); diff --git a/pages/api/bookings/[id]/index.ts b/pages/api/bookings/[id]/index.ts new file mode 100644 index 0000000000..4b740bcbb5 --- /dev/null +++ b/pages/api/bookings/[id]/index.ts @@ -0,0 +1,16 @@ +import { NextApiRequest, NextApiResponse } from "next"; + +import { defaultHandler } from "@calcom/lib/server"; + +import { withMiddleware } from "@lib/helpers/withMiddleware"; + +import authMiddleware from "./_auth-middleware"; + +export default withMiddleware("HTTP_GET_DELETE_PATCH")(async (req: NextApiRequest, res: NextApiResponse) => { + await authMiddleware(req, res); + return defaultHandler({ + GET: import("./_get"), + PATCH: import("./_patch"), + DELETE: import("./_delete"), + })(req, res); +});