diff --git a/lib/helpers/captureErrors.ts b/lib/helpers/captureErrors.ts index 9229ad92d8..109e4e2254 100644 --- a/lib/helpers/captureErrors.ts +++ b/lib/helpers/captureErrors.ts @@ -8,7 +8,8 @@ export const captureErrors: NextMiddleware = async (_req, res, next) => { await next(); } catch (err) { Sentry.captureException(err); - res.status(500); - res.json({ error: err }); + console.log(err); + res.status(400).json({ message: "Something went wrong", error: err }); + // res.json({ error: err }); } }; diff --git a/lib/helpers/verifyApiKey.ts b/lib/helpers/verifyApiKey.ts index 0da9a2090b..45f8acd9ce 100644 --- a/lib/helpers/verifyApiKey.ts +++ b/lib/helpers/verifyApiKey.ts @@ -11,13 +11,14 @@ const dateInPast = function (firstDate: Date, secondDate: Date) { const today = new Date(); export const verifyApiKey: NextMiddleware = async (req, res, next) => { + if (!req.query.apiKey) res.status(401).json({ message: "No API key provided" }); const apiKey = await prisma.apiKey.findUnique({ where: { id: req.query.apiKey as string } }); if (!apiKey) { - res.status(400).json({ error: "Your api key is not valid" }); + res.status(401).json({ error: "Your api key is not valid" }); throw new Error("No api key found"); } if (apiKey.expiresAt && apiKey.userId && dateInPast(apiKey.expiresAt, today)) { res.setHeader("Calcom-User-ID", apiKey.userId); await next(); - } else res.status(400).json({ error: "Your api key is not valid" }); + } else res.status(401).json({ error: "Your api key is not valid" }); }; diff --git a/lib/types.ts b/lib/types.ts index fde9ba7e87..bef7b32e4a 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,4 +1,4 @@ -import { User, ApiKey } from "@calcom/prisma/client"; +import { User, ApiKey, Team, SelectedCalendar } from "@calcom/prisma/client"; // Base response, used for all responses export type BaseResponse = { @@ -20,3 +20,19 @@ export type ApiKeyResponse = BaseResponse & { export type ApiKeysResponse = BaseResponse & { data?: Partial[]; }; + +// API Key +export type TeamResponse = BaseResponse & { + data?: Partial; +}; +export type TeamsResponse = BaseResponse & { + data?: Partial[]; +}; + +// API Key +export type SelectedCalendarResponse = BaseResponse & { + data?: Partial; +}; +export type SelectedCalendarsResponse = BaseResponse & { + data?: Partial[]; +}; diff --git a/lib/validations/selected-calendar.ts b/lib/validations/selected-calendar.ts index 29ffdb9c11..2c8d871d52 100644 --- a/lib/validations/selected-calendar.ts +++ b/lib/validations/selected-calendar.ts @@ -1,11 +1,13 @@ import { withValidation } from "next-validations"; -import { z } from "zod"; -const schemaSelectedCalendar = z.object({}).strict(); -const withValidSelectedCalendar = withValidation({ - schema: schemaSelectedCalendar, +import { _SelectedCalendarModel as SelectedCalendar } from "@calcom/prisma/zod"; + +export const schemaSelectedCalendarBodyParams = SelectedCalendar.omit({}); + +export const schemaSelectedCalendarPublic = SelectedCalendar.omit({ userId: true }); + +export const withValidSelectedCalendar = withValidation({ + schema: schemaSelectedCalendarBodyParams, type: "Zod", mode: "body", }); - -export { schemaSelectedCalendar, withValidSelectedCalendar }; diff --git a/lib/validations/shared/baseApiParams.ts b/lib/validations/shared/baseApiParams.ts index 03b13838ee..a7791a5b9d 100644 --- a/lib/validations/shared/baseApiParams.ts +++ b/lib/validations/shared/baseApiParams.ts @@ -6,7 +6,7 @@ export const baseApiParams = z .object({ // since we added apiKey as query param this is required by next-validations helper // for query params to work properly and not fail. - apiKey: z.string().cuid(), + apiKey: z.string().cuid().optional(), // version required for supporting /v1/ redirect to query in api as *?version=1 version: z.string().optional(), }) diff --git a/lib/validations/team.ts b/lib/validations/team.ts index 44cb1329a8..20b79ac1c4 100644 --- a/lib/validations/team.ts +++ b/lib/validations/team.ts @@ -1,19 +1,13 @@ import { withValidation } from "next-validations"; -import { z } from "zod"; -const schemaTeam = z - .object({ - slug: z.string().min(3), - name: z.string().min(3), - hideBranding: z.boolean().default(false), - bio: z.string().min(3).optional(), - logo: z.string().optional(), - }) - .strict(); -const withValidTeam = withValidation({ - schema: schemaTeam, +import { _TeamModel as Team } from "@calcom/prisma/zod"; + +export const schemaTeamBodyParams = Team.omit({ id: true }); + +export const schemaTeamPublic = Team.omit({}); + +export const withValidTeam = withValidation({ + schema: schemaTeamBodyParams, type: "Zod", mode: "body", }); - -export { schemaTeam, withValidTeam }; diff --git a/package.json b/package.json index 79ed5ea648..8f496b55e7 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,14 @@ "babel-jest": "^27.5.1", "husky": "^7.0.4", "jest": "^27.5.1", - "node-mocks-http": "^1.11.0" + "node-mocks-http": "^1.11.0", + "prettier": "^2.6.1" }, "dependencies": { "@sentry/nextjs": "^6.19.2", "next": "^12.1.0", "next-api-middleware": "^1.0.1", + "next-swagger-doc": "^0.2.1", "next-transpile-modules": "^9.0.0", "next-validations": "^0.1.11", "typescript": "^4.6.3", diff --git a/pages/_middleware.ts b/pages/api/_middleware.ts similarity index 56% rename from pages/_middleware.ts rename to pages/api/_middleware.ts index 59f0b1501f..6a3038451a 100644 --- a/pages/_middleware.ts +++ b/pages/api/_middleware.ts @@ -1,17 +1,22 @@ +// import { NextApiResponse } from "next"; import { NextRequest, NextResponse } from "next/server"; // Not much useful yet as prisma.client can't be used in the middlewares (client is not available) // For now we just throw early if no apiKey is passed, // but we could also check if the apiKey is valid if we had prisma here. -export default async function requireApiKeyAsQueryParams({ nextUrl }: NextRequest) { +export default async function requireApiKeyAsQueryParams({ nextUrl }: NextRequest, res: NextResponse) { const response = NextResponse.next(); const apiKey = nextUrl.searchParams.get("apiKey"); if (apiKey) return response; - // if no apiKey is passed, we throw early + // if no apiKey is passed, we throw early a 401 unauthorized else - throw new Error( - "You need to pass an apiKey as query param: https://api.cal.com/resource?apiKey=" + new NextResponse( + JSON.stringify({ + message: + "You need to pass an apiKey as query param: https://api.cal.com/resource?apiKey=", + }), + { status: 401, statusText: "Unauthorized" } ); } diff --git a/pages/api/docs.ts b/pages/api/docs.ts new file mode 100644 index 0000000000..e855eacfd8 --- /dev/null +++ b/pages/api/docs.ts @@ -0,0 +1,13 @@ +import { withSwagger } from "next-swagger-doc"; + +const swaggerHandler = withSwagger({ + definition: { + openapi: "3.0.0", + info: { + title: "Cal.com Public API", + version: "1.0.0", + }, + }, + apiFolder: "pages/api", +}); +export default swaggerHandler(); diff --git a/pages/api/selected-calendars/[id]/delete.ts b/pages/api/selected-calendars/[id]/delete.ts index a3de0e715b..249912dce9 100644 --- a/pages/api/selected-calendars/[id]/delete.ts +++ b/pages/api/selected-calendars/[id]/delete.ts @@ -2,28 +2,41 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; +import { withMiddleware } from "@lib/helpers/withMiddleware"; +import type { BaseResponse } from "@lib/types"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, } from "@lib/validations/shared/queryIdTransformParseInt"; -type ResponseData = { - message?: string; - error?: unknown; -}; +/** + * @swagger + * /api/selectedCalendars/:id/delete: + * delete: + * description: Remove an existing selectedCalendar + * responses: + * 201: + * description: OK, selectedCalendar removed successfuly + * model: SelectedCalendar + * 400: + * description: Bad request. SelectedCalendar id is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ +export async function deleteSelectedCalendar(req: NextApiRequest, res: NextApiResponse) { + const safe = await schemaQueryIdParseInt.safeParse(req.query); + if (!safe.success) throw new Error("Invalid request query", safe.error); -export async function deleteSelectedCalendar(req: NextApiRequest, res: NextApiResponse) { - const { query, method } = req; - const safe = await schemaQueryIdParseInt.safeParse(query); - if (method === "DELETE" && safe.success && safe.data) { - const selectedCalendar = await prisma.selectedCalendar.delete({ where: { id: safe.data.id } }); - // We only remove the selectedCalendar type from the database if there's an existing resource. - if (selectedCalendar) - res.status(200).json({ message: `selectedCalendar with id: ${safe.data.id} deleted successfully` }); - // This catches the error thrown by prisma.selectedCalendar.delete() if the resource is not found. - else res.status(400).json({ message: `Resource with id:${safe.data.id} was not found` }); - // Reject any other HTTP method than POST - } else res.status(405).json({ message: "Only DELETE Method allowed" }); + const data = await prisma.selectedCalendar.delete({ where: { id: safe.data.id } }); + + if (data) + res.status(200).json({ message: `SelectedCalendar with id: ${safe.data.id} deleted successfully` }); + else + (error: Error) => + res.status(400).json({ + message: `SelectedCalendar with id: ${safe.data.id} was not able to be processed`, + error, + }); } -export default withValidQueryIdTransformParseInt(deleteSelectedCalendar); +export default withMiddleware("HTTP_DELETE")(withValidQueryIdTransformParseInt(deleteSelectedCalendar)); diff --git a/pages/api/selected-calendars/[id]/edit.ts b/pages/api/selected-calendars/[id]/edit.ts index 0854ffd44f..96c9f08675 100644 --- a/pages/api/selected-calendars/[id]/edit.ts +++ b/pages/api/selected-calendars/[id]/edit.ts @@ -1,38 +1,56 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; -import { SelectedCalendar } from "@calcom/prisma/client"; -import { schemaSelectedCalendar, withValidSelectedCalendar } from "@lib/validations/selected-calendar"; +import { withMiddleware } from "@lib/helpers/withMiddleware"; +import type { SelectedCalendarResponse } from "@lib/types"; +import { + schemaSelectedCalendarBodyParams, + schemaSelectedCalendarPublic, + withValidSelectedCalendar, +} from "@lib/validations/selected-calendar"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, } from "@lib/validations/shared/queryIdTransformParseInt"; -type ResponseData = { - data?: SelectedCalendar; - message?: string; - error?: unknown; -}; +/** + * @swagger + * /api/selectedCalendars/:id/edit: + * patch: + * description: Edits an existing selectedCalendar + * responses: + * 201: + * description: OK, selectedCalendar edited successfuly + * model: SelectedCalendar + * 400: + * description: Bad request. SelectedCalendar body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ +export async function editSelectedCalendar( + req: NextApiRequest, + res: NextApiResponse +) { + const safeQuery = await schemaQueryIdParseInt.safeParse(req.query); + const safeBody = await schemaSelectedCalendarBodyParams.safeParse(req.body); -export async function editSelectedCalendar(req: NextApiRequest, res: NextApiResponse) { - const { query, body, method } = req; - const safeQuery = await schemaQueryIdParseInt.safeParse(query); - const safeBody = await schemaSelectedCalendar.safeParse(body); + if (!safeQuery.success || !safeBody.success) throw new Error("Invalid request"); + const selectedCalendar = await prisma.selectedCalendar.update({ + where: { id: safeQuery.data.id }, + data: safeBody.data, + }); + const data = schemaSelectedCalendarPublic.parse(selectedCalendar); - if (method === "PATCH" && safeQuery.success && safeBody.success) { - const data = await prisma.selectedCalendar.update({ - where: { id: safeQuery.data.id }, - data: safeBody.data, - }); - if (data) res.status(200).json({ data }); - else - res - .status(404) - .json({ message: `Event type with ID ${safeQuery.data.id} not found and wasn't updated`, error }); - - // Reject any other HTTP method than POST - } else res.status(405).json({ message: "Only PATCH Method allowed for updating selectedCalendars" }); + if (data) res.status(200).json({ data }); + else + (error: Error) => + res.status(404).json({ + message: `Event type with ID ${safeQuery.data.id} not found and wasn't updated`, + error, + }); } -export default withValidQueryIdTransformParseInt(withValidSelectedCalendar(editSelectedCalendar)); +export default withMiddleware("HTTP_PATCH")( + withValidQueryIdTransformParseInt(withValidSelectedCalendar(editSelectedCalendar)) +); diff --git a/pages/api/selected-calendars/[id]/index.ts b/pages/api/selected-calendars/[id]/index.ts index 84a290ae20..cf932ac5a2 100644 --- a/pages/api/selected-calendars/[id]/index.ts +++ b/pages/api/selected-calendars/[id]/index.ts @@ -1,29 +1,45 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; -import { SelectedCalendar } from "@calcom/prisma/client"; +import { withMiddleware } from "@lib/helpers/withMiddleware"; +import type { SelectedCalendarResponse } from "@lib/types"; +import { schemaSelectedCalendarPublic } from "@lib/validations/selected-calendar"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, } from "@lib/validations/shared/queryIdTransformParseInt"; -type ResponseData = { - data?: SelectedCalendar; - message?: string; - error?: unknown; -}; +/** + * @swagger + * /api/selectedCalendars/:id: + * get: + * description: find selectedCalendar by ID + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: SelectedCalendar was not found + */ +export async function selectedCalendarById( + req: NextApiRequest, + res: NextApiResponse +) { + const safe = await schemaQueryIdParseInt.safeParse(req.query); + if (!safe.success) throw new Error("Invalid request query"); -export async function selectedCalendar(req: NextApiRequest, res: NextApiResponse) { - const { query, method } = req; - const safe = await schemaQueryIdParseInt.safeParse(query); - if (method === "GET" && safe.success) { - const data = await prisma.selectedCalendar.findUnique({ where: { id: safe.data.id } }); + const selectedCalendar = await prisma.selectedCalendar.findUnique({ where: { id: safe.data.id } }); + const data = schemaSelectedCalendarPublic.parse(selectedCalendar); - if (data) res.status(200).json({ data }); - else res.status(404).json({ message: "Event type not found" }); - // Reject any other HTTP method than POST - } else res.status(405).json({ message: "Only GET Method allowed" }); + if (selectedCalendar) res.status(200).json({ data }); + else + (error: Error) => + res.status(404).json({ + message: "SelectedCalendar was not found", + error, + }); } -export default withValidQueryIdTransformParseInt(selectedCalendar); +export default withMiddleware("HTTP_GET")(withValidQueryIdTransformParseInt(selectedCalendarById)); diff --git a/pages/api/selected-calendars/index.ts b/pages/api/selected-calendars/index.ts index 0a4bbd5012..ab80b3aa0f 100644 --- a/pages/api/selected-calendars/index.ts +++ b/pages/api/selected-calendars/index.ts @@ -1,15 +1,37 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; -import { SelectedCalendar } from "@calcom/prisma/client"; -type ResponseData = { - data?: SelectedCalendar[]; - error?: unknown; -}; +import { withMiddleware } from "@lib/helpers/withMiddleware"; +import { SelectedCalendarsResponse } from "@lib/types"; +import { schemaSelectedCalendarPublic } from "@lib/validations/selected-calendar"; + +/** + * @swagger + * /api/selectedCalendars: + * get: + * description: Returns all selected calendars + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: No selectedCalendars were found + */ +async function allSelectedCalendars(_: NextApiRequest, res: NextApiResponse) { + const selectedCalendars = await prisma.selectedCalendar.findMany(); + const data = selectedCalendars.map((selectedCalendar) => + schemaSelectedCalendarPublic.parse(selectedCalendar) + ); -export default async function selectedCalendar(req: NextApiRequest, res: NextApiResponse) { - const data = await prisma.selectedCalendar.findMany(); if (data) res.status(200).json({ data }); - else res.status(400).json({ error: "No data found" }); + else + (error: Error) => + res.status(404).json({ + message: "No SelectedCalendars were found", + error, + }); } + +export default withMiddleware("HTTP_GET")(allSelectedCalendars); diff --git a/pages/api/selected-calendars/new.ts b/pages/api/selected-calendars/new.ts index 712a936733..22ae2a8657 100644 --- a/pages/api/selected-calendars/new.ts +++ b/pages/api/selected-calendars/new.ts @@ -1,28 +1,43 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; -import { SelectedCalendar } from "@calcom/prisma/client"; -import { schemaSelectedCalendar, withValidSelectedCalendar } from "@lib/validations/selected-calendar"; +import { withMiddleware } from "@lib/helpers/withMiddleware"; +import type { SelectedCalendarResponse } from "@lib/types"; +import { + schemaSelectedCalendarBodyParams, + schemaSelectedCalendarPublic, + withValidSelectedCalendar, +} from "@lib/validations/selected-calendar"; -type ResponseData = { - data?: SelectedCalendar; - message?: string; - error?: string; -}; +/** + * @swagger + * /api/selectedCalendars/new: + * post: + * description: Creates a new selected calendar + * responses: + * 201: + * description: OK, selected calendar created + * model: SelectedCalendar + * 400: + * description: Bad request. SelectedCalendar body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ +async function createSelectedCalendar(req: NextApiRequest, res: NextApiResponse) { + const safe = schemaSelectedCalendarBodyParams.safeParse(req.body); + if (!safe.success) throw new Error("Invalid request body", safe.error); -async function createSelectedCalendar(req: NextApiRequest, res: NextApiResponse) { - const { body, method } = req; - const safe = schemaSelectedCalendar.safeParse(body); - if (method === "POST" && safe.success) { - await prisma.selectedCalendar - .create({ data: safe.data }) - .then((data) => res.status(201).json({ data })) - .catch((error) => - res.status(400).json({ message: "Could not create selectedCalendar type", error: error }) - ); - // Reject any other HTTP method than POST - } else res.status(405).json({ error: "Only POST Method allowed" }); + const selectedCalendar = await prisma.selectedCalendar.create({ data: safe.data }); + const data = schemaSelectedCalendarPublic.parse(selectedCalendar); + + if (data) res.status(201).json({ data, message: "SelectedCalendar created successfully" }); + else + (error: Error) => + res.status(400).json({ + message: "Could not create new selectedCalendar", + error, + }); } -export default withValidSelectedCalendar(createSelectedCalendar); +export default withMiddleware("HTTP_POST")(withValidSelectedCalendar(createSelectedCalendar)); diff --git a/pages/api/teams/[id]/delete.ts b/pages/api/teams/[id]/delete.ts index 3c88c02d28..cf3825fd96 100644 --- a/pages/api/teams/[id]/delete.ts +++ b/pages/api/teams/[id]/delete.ts @@ -3,19 +3,29 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; +import type { BaseResponse } from "@lib/types"; import { schemaQueryIdParseInt, withValidQueryIdTransformParseInt, } from "@lib/validations/shared/queryIdTransformParseInt"; -type ResponseData = { - message: string; - error?: object; -}; - -export async function deleteTeam(req: NextApiRequest, res: NextApiResponse) { +/** + * @swagger + * /api/teams/:id/delete: + * delete: + * description: Remove an existing team + * responses: + * 201: + * description: OK, team removed successfuly + * model: Team + * 400: + * description: Bad request. Team id is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ +export async function deleteTeam(req: NextApiRequest, res: NextApiResponse) { const safe = await schemaQueryIdParseInt.safeParse(req.query); - if (!safe.success) throw new Error("Invalid request query"); + if (!safe.success) throw new Error("Invalid request query", safe.error); const data = await prisma.team.delete({ where: { id: safe.data.id } }); @@ -28,4 +38,4 @@ export async function deleteTeam(req: NextApiRequest, res: NextApiResponse) { +/** + * @swagger + * /api/teams/:id/edit: + * patch: + * description: Edits an existing team + * responses: + * 201: + * description: OK, team edited successfuly + * model: Team + * 400: + * description: Bad request. Team body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ +export async function editTeam(req: NextApiRequest, res: NextApiResponse) { const safeQuery = await schemaQueryIdParseInt.safeParse(req.query); - const safeBody = await schemaTeam.safeParse(req.body); + const safeBody = await schemaTeamBodyParams.safeParse(req.body); if (!safeQuery.success || !safeBody.success) throw new Error("Invalid request"); - const data = await prisma.team.update({ + const team = await prisma.team.update({ where: { id: safeQuery.data.id }, data: safeBody.data, }); + const data = schemaTeamPublic.parse(team); if (data) res.status(200).json({ data }); else @@ -35,7 +44,4 @@ export async function editTeam(req: NextApiRequest, res: NextApiResponse) { +/** + * @swagger + * /api/teams/:id: + * get: + * description: find team by ID + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: Team was not found + */ +export async function teamById(req: NextApiRequest, res: NextApiResponse) { const safe = await schemaQueryIdParseInt.safeParse(req.query); if (!safe.success) throw new Error("Invalid request query"); - const data = await prisma.team.findUnique({ where: { id: safe.data.id } }); + const team = await prisma.team.findUnique({ where: { id: safe.data.id } }); + const data = schemaTeamPublic.parse(team); - if (data) res.status(200).json({ data }); + if (team) res.status(200).json({ data }); else (error: Error) => res.status(404).json({ diff --git a/pages/api/teams/index.ts b/pages/api/teams/index.ts index ce090dd0a5..7497539eeb 100644 --- a/pages/api/teams/index.ts +++ b/pages/api/teams/index.ts @@ -1,23 +1,32 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; -import { Team } from "@calcom/prisma/client"; import { withMiddleware } from "@lib/helpers/withMiddleware"; +import { TeamsResponse } from "@lib/types"; +import { schemaTeamPublic } from "@lib/validations/team"; -type ResponseData = { - data?: Team[]; - message?: string; - error?: object; -}; - -async function allTeams(req: NextApiRequest, res: NextApiResponse) { - const data = await prisma.team.findMany(); +/** + * @swagger + * /api/teams: + * get: + * description: Returns all teams + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: No teams were found + */ +async function allTeams(_: NextApiRequest, res: NextApiResponse) { + const teams = await prisma.team.findMany(); + const data = teams.map((team) => schemaTeamPublic.parse(team)); if (data) res.status(200).json({ data }); else (error: Error) => - res.status(400).json({ + res.status(404).json({ message: "No Teams were found", error, }); diff --git a/pages/api/teams/new.ts b/pages/api/teams/new.ts index a3412a7631..b78759d249 100644 --- a/pages/api/teams/new.ts +++ b/pages/api/teams/new.ts @@ -1,31 +1,39 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; -import { Team } from "@calcom/prisma/client"; import { withMiddleware } from "@lib/helpers/withMiddleware"; -import { schemaTeam, withValidTeam } from "@lib/validations/team"; +import type { TeamResponse } from "@lib/types"; +import { schemaTeamBodyParams, schemaTeamPublic, withValidTeam } from "@lib/validations/team"; -type ResponseData = { - data?: Team; - error?: object; -}; +/** + * @swagger + * /api/teams/new: + * post: + * description: Creates a new team + * responses: + * 201: + * description: OK, team created + * model: Team + * 400: + * description: Bad request. Team body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ +async function createTeam(req: NextApiRequest, res: NextApiResponse) { + const safe = schemaTeamBodyParams.safeParse(req.body); + if (!safe.success) throw new Error("Invalid request body", safe.error); -async function createTeam(req: NextApiRequest, res: NextApiResponse) { - const safe = schemaTeam.safeParse(req.body); - if (!safe.success) throw new Error("Invalid request body"); + const team = await prisma.team.create({ data: safe.data }); + const data = schemaTeamPublic.parse(team); - const data = await prisma.team.create({ data: safe.data }); - - if (data) res.status(201).json({ data }); + if (data) res.status(201).json({ data, message: "Team created successfully" }); else (error: Error) => res.status(400).json({ - error: { - message: "Could not create new team", - error, - }, + message: "Could not create new team", + error, }); } -export default withMiddleware("addRequestId", "HTTP_POST")(withValidTeam(createTeam)); +export default withMiddleware("HTTP_POST")(withValidTeam(createTeam)); diff --git a/pages/api/users/[id]/delete.ts b/pages/api/users/[id]/delete.ts index f35f50a36c..51b6e36cf6 100644 --- a/pages/api/users/[id]/delete.ts +++ b/pages/api/users/[id]/delete.ts @@ -9,6 +9,20 @@ import { withValidQueryIdTransformParseInt, } from "@lib/validations/shared/queryIdTransformParseInt"; +/** + * @swagger + * /api/users/:id/delete: + * delete: + * description: Remove an existing user + * responses: + * 201: + * description: OK, user removed successfuly + * model: User + * 400: + * description: Bad request. User id is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ export async function deleteUser(req: NextApiRequest, res: NextApiResponse) { const safe = await schemaQueryIdParseInt.safeParse(req.query); if (!safe.success) throw new Error("Invalid request query", safe.error); diff --git a/pages/api/users/[id]/edit.ts b/pages/api/users/[id]/edit.ts index bd0ed675de..b78cb1f83c 100644 --- a/pages/api/users/[id]/edit.ts +++ b/pages/api/users/[id]/edit.ts @@ -10,6 +10,20 @@ import { } from "@lib/validations/shared/queryIdTransformParseInt"; import { schemaUserBodyParams, schemaUserPublic, withValidUser } from "@lib/validations/user"; +/** + * @swagger + * /api/users/:id/edit: + * patch: + * description: Edits an existing user + * responses: + * 201: + * description: OK, user edited successfuly + * model: User + * 400: + * description: Bad request. User body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ export async function editUser(req: NextApiRequest, res: NextApiResponse) { const safeQuery = await schemaQueryIdParseInt.safeParse(req.query); const safeBody = await schemaUserBodyParams.safeParse(req.body); diff --git a/pages/api/users/[id]/index.ts b/pages/api/users/[id]/index.ts index 7e7c7cb8ff..04be633d5d 100644 --- a/pages/api/users/[id]/index.ts +++ b/pages/api/users/[id]/index.ts @@ -10,6 +10,19 @@ import { } from "@lib/validations/shared/queryIdTransformParseInt"; import { schemaUserPublic } from "@lib/validations/user"; +/** + * @swagger + * /api/users/:id: + * get: + * description: find user by ID + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: User was not found + */ export async function userById(req: NextApiRequest, res: NextApiResponse) { const safe = await schemaQueryIdParseInt.safeParse(req.query); if (!safe.success) throw new Error("Invalid request query"); diff --git a/pages/api/users/index.ts b/pages/api/users/index.ts index 365abe4df3..a748315a4a 100644 --- a/pages/api/users/index.ts +++ b/pages/api/users/index.ts @@ -6,6 +6,19 @@ import { withMiddleware } from "@lib/helpers/withMiddleware"; import { UsersResponse } from "@lib/types"; import { schemaUserPublic } from "@lib/validations/user"; +/** + * @swagger + * /api/users: + * get: + * description: Returns all users + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: No users were found + */ async function allUsers(_: NextApiRequest, res: NextApiResponse) { const users = await prisma.user.findMany(); const data = users.map((user) => schemaUserPublic.parse(user)); @@ -13,7 +26,7 @@ async function allUsers(_: NextApiRequest, res: NextApiResponse) if (data) res.status(200).json({ data }); else (error: Error) => - res.status(400).json({ + res.status(404).json({ message: "No Users were found", error, }); diff --git a/pages/api/users/new.ts b/pages/api/users/new.ts index adcf5d7658..fd717213e0 100644 --- a/pages/api/users/new.ts +++ b/pages/api/users/new.ts @@ -6,6 +6,20 @@ import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { UserResponse } from "@lib/types"; import { schemaUserBodyParams, schemaUserPublic, withValidUser } from "@lib/validations/user"; +/** + * @swagger + * /api/users/new: + * post: + * description: Creates a new user + * responses: + * 201: + * description: OK, user created + * model: User + * 400: + * description: Bad request. User body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ async function createUser(req: NextApiRequest, res: NextApiResponse) { const safe = schemaUserBodyParams.safeParse(req.body); if (!safe.success) throw new Error("Invalid request body", safe.error);