diff --git a/README.md b/README.md index c2320f50c9..f8834ae82d 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,6 @@ We aim to provide a fully tested API for our peace of mind, this is accomplished Since this will only support an API, we redirect the requests to root to the /api folder. We also added a redirect for future-proofing API versioning when we might need it, without having to resort to dirty hacks like a v1/v2 folders with lots of duplicated code, instead we redirect /api/v*/:rest to /api/:rest?version=* + + +The priority is the booking-related API routes so people can build their own booking flow, then event type management routes, then availability management routes etc \ No newline at end of file diff --git a/lib/validations/apiKey.ts b/lib/validations/apiKey.ts new file mode 100644 index 0000000000..5ed6e12b76 --- /dev/null +++ b/lib/validations/apiKey.ts @@ -0,0 +1,16 @@ +import { withValidation } from "next-validations"; +import { z } from "zod"; + +const schemaApiKey = z + .object({ + expiresAt: z.date().optional(), // default is 30 days + note: z.string().min(1).optional(), + }) + .strict(); // Adding strict so that we can disallow passing in extra fields +const withValidApiKey = withValidation({ + schema: schemaApiKey, + type: "Zod", + mode: "body", +}); + +export { schemaApiKey, withValidApiKey }; diff --git a/next.config.js b/next.config.js index 1e6d5beae6..3f24426e89 100644 --- a/next.config.js +++ b/next.config.js @@ -1,4 +1,9 @@ -module.exports = { +const withTM = require("next-transpile-modules")([ + "@calcom/prisma", +]); + + +module.exports = withTM({ async rewrites() { return [ // This redirects requests recieved at / the root to the /api/ folder. @@ -13,4 +18,4 @@ module.exports = { }, ]; }, -}; +}); diff --git a/package.json b/package.json index 12a5753dc7..f829c22786 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,10 @@ "node-mocks-http": "^1.11.0" }, "dependencies": { - "typescript": "^4.6.3", "next": "^12.1.0", + "next-transpile-modules": "^9.0.0", "next-validations": "^0.1.11", + "typescript": "^4.6.3", "zod": "^3.14.2" } } diff --git a/pages/api/api-keys/[id]/delete.ts b/pages/api/api-keys/[id]/delete.ts new file mode 100644 index 0000000000..7912907b51 --- /dev/null +++ b/pages/api/api-keys/[id]/delete.ts @@ -0,0 +1,36 @@ +import { PrismaClient } from "@prisma/client"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { schemaQueryId, withValidQueryId } from "@lib/validations/queryIdTransformParseInt"; + +const prisma = new PrismaClient(); + +type ResponseData = { + message?: string; + error?: unknown; +}; + +export async function eventType(req: NextApiRequest, res: NextApiResponse) { + const { query, method } = req; + const safe = await schemaQueryId.safeParse(query); + if (safe.success) { + if (method === "DELETE") { + // DELETE WILL DELETE THE EVENT TYPE + prisma.eventType + .delete({ where: { id: safe.data.id } }) + .then(() => { + // We only remove the event type from the database if there's an existing resource. + res.status(200).json({ message: `event-type with id: ${safe.data.id} deleted successfully` }); + }) + .catch((error) => { + // This catches the error thrown by prisma.eventType.delete() if the resource is not found. + res.status(400).json({ message: `Resource with id:${safe.data.id} was not found`, error: error }); + }); + } else { + // Reject any other HTTP method than POST + res.status(405).json({ message: "Only DELETE Method allowed in /event-types/[id]/delete endpoint" }); + } + } +} + +export default withValidQueryId(eventType); diff --git a/pages/api/api-keys/[id]/edit.ts b/pages/api/api-keys/[id]/edit.ts new file mode 100644 index 0000000000..c4eac80975 --- /dev/null +++ b/pages/api/api-keys/[id]/edit.ts @@ -0,0 +1,37 @@ +import { PrismaClient, EventType } from "@prisma/client"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { schemaEventType, withValidEventType } from "@lib/validations/eventType"; +import { schemaQueryId, withValidQueryId } from "@lib/validations/queryIdTransformParseInt"; + +const prisma = new PrismaClient(); + +type ResponseData = { + data?: EventType; + message?: string; + error?: unknown; +}; + +export async function editEventType(req: NextApiRequest, res: NextApiResponse) { + const { query, body, method } = req; + const safeQuery = await schemaQueryId.safeParse(query); + const safeBody = await schemaEventType.safeParse(body); + + if (method === "PATCH") { + if (safeQuery.success && safeBody.success) { + await prisma.eventType.update({ + where: { id: safeQuery.data.id }, + data: safeBody.data, + }).then(event => { + res.status(200).json({ data: event }); + }).catch(error => { + res.status(404).json({ message: `Event type with ID ${safeQuery.data.id} not found and wasn't updated`, error }) + }); + } + } else { + // Reject any other HTTP method than POST + res.status(405).json({ message: "Only PATCH Method allowed for updating event-types" }); + } +} + +export default withValidQueryId(withValidEventType(editEventType)); diff --git a/pages/api/api-keys/[id]/index.ts b/pages/api/api-keys/[id]/index.ts new file mode 100644 index 0000000000..087a30cd55 --- /dev/null +++ b/pages/api/api-keys/[id]/index.ts @@ -0,0 +1,31 @@ +import { PrismaClient, EventType } from "@prisma/client"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { schemaQueryId, withValidQueryId } from "@lib/validations/queryIdTransformParseInt"; + +const prisma = new PrismaClient(); + +type ResponseData = { + data?: EventType; + message?: string; + error?: unknown; +}; + +export async function eventType(req: NextApiRequest, res: NextApiResponse) { + const { query, method } = req; + const safe = await schemaQueryId.safeParse(query); + if (safe.success) { + if (method === "GET") { + const event = await prisma.eventType.findUnique({ where: { id: safe.data.id } }); + + if (event) res.status(200).json({ data: event }); + if (!event) res.status(404).json({ message: "Event type not found" }); + } else { + // Reject any other HTTP method than POST + res.status(405).json({ message: "Only GET Method allowed" }); + } + } +} + + +export default withValidQueryId(eventType); diff --git a/pages/api/api-keys/index.ts b/pages/api/api-keys/index.ts new file mode 100644 index 0000000000..257fa060a1 --- /dev/null +++ b/pages/api/api-keys/index.ts @@ -0,0 +1,19 @@ +import { PrismaClient, ApiKey } from "@prisma/client"; +import type { NextApiRequest, NextApiResponse } from "next"; + +const prisma = new PrismaClient(); +type ResponseData = { + data?: ApiKey[]; + error?: unknown; +}; + +export default async function apiKey(req: NextApiRequest, res: NextApiResponse) { + try { + const apiKeys = await prisma.apiKey.findMany({ where: { id: `${req.query.eventTypeId}` } }); + res.status(200).json({ data: { ...apiKeys } }); + } catch (error) { + console.log(error); + // FIXME: Add zod for validation/error handling + res.status(400).json({ error: error }); + } +} diff --git a/pages/api/api-keys/new.ts b/pages/api/api-keys/new.ts new file mode 100644 index 0000000000..5c4e210cca --- /dev/null +++ b/pages/api/api-keys/new.ts @@ -0,0 +1,30 @@ +import { PrismaClient, ApiKey } from "@prisma/client"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { schemaApiKey, withValidApiKey } from "@lib/validations/apiKey"; + +const prisma = new PrismaClient(); +type ResponseData = { + data?: ApiKey; + message?: string; + error?: string; +}; + +async function createApiKey(req: NextApiRequest, res: NextApiResponse) { + const { body, method } = req; + if (method === "POST") { + const safe = schemaApiKey.safeParse(body); + if (safe.success && safe.data) { + await prisma.apiKey + .create({ data: { ...safe.data, user: { connect: { id: 1 } } +}}) + .then((event) => res.status(201).json({ data: event })) + .catch((error) => res.status(400).json({ message: "Could not create event type", error: error })); + } + } else { + // Reject any other HTTP method than POST + res.status(405).json({ error: "Only POST Method allowed" }); + } +} + +export default withValidApiKey(createApiKey); diff --git a/tsconfig.json b/tsconfig.json index b9b2164ce0..951cbeb88c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,9 @@ "jsx": "preserve", "paths": { "@api/*": ["pages/api/*"], + "@prisma/client/*": ["@calcom/prisma/client/*"], "@lib/*": ["lib/*"] + }, }, "include": [