extracting out schemaQuery validation to lib, extracting out delete/edit to it's own files for clarity
parent
e4d9f7bc7d
commit
737a8897ba
|
@ -2,14 +2,14 @@ import { createMocks } from "node-mocks-http";
|
||||||
|
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import handleEvent from "../pages/api/event-types/[id]";
|
import handleEvent from "../../../pages/api/event-types/[id]";
|
||||||
|
|
||||||
afterAll((done) => {
|
afterAll((done) => {
|
||||||
prisma.$disconnect().then();
|
prisma.$disconnect().then();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("/api/event-types/[id] with valid id as string returns an event-type", () => {
|
describe("GET /api/event-types/[id] with valid id as string returns an event-type", () => {
|
||||||
it("returns a message with the specified events", async () => {
|
it("returns a message with the specified events", async () => {
|
||||||
const { req, res } = createMocks({
|
const { req, res } = createMocks({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -27,7 +27,7 @@ describe("/api/event-types/[id] with valid id as string returns an event-type",
|
||||||
|
|
||||||
// This can never happen under our normal nextjs setup where query is always a string | string[].
|
// This can never happen under our normal nextjs setup where query is always a string | string[].
|
||||||
// But seemed a good example for testing an error validation
|
// But seemed a good example for testing an error validation
|
||||||
describe("/api/event-types/[id] errors if query id is number, requires a string", () => {
|
describe("GET /api/event-types/[id] errors if query id is number, requires a string", () => {
|
||||||
it("returns a message with the specified events", async () => {
|
it("returns a message with the specified events", async () => {
|
||||||
const { req, res } = createMocks({
|
const { req, res } = createMocks({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -50,7 +50,7 @@ describe("/api/event-types/[id] errors if query id is number, requires a string"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("/api/event-types/[id] an id not present in db like 0, throws 404 not found", () => {
|
describe("GET /api/event-types/[id] an id not present in db like 0, throws 404 not found", () => {
|
||||||
it("returns a message with the specified events", async () => {
|
it("returns a message with the specified events", async () => {
|
||||||
const { req, res } = createMocks({
|
const { req, res } = createMocks({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -65,7 +65,7 @@ describe("/api/event-types/[id] an id not present in db like 0, throws 404 not f
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("/api/event-types/[id] only allow GET, fails with POST", () => {
|
describe("POST /api/event-types/[id] fails, only GET allowed", () => {
|
||||||
it("returns a message with the specified events", async () => {
|
it("returns a message with the specified events", async () => {
|
||||||
const { req, res } = createMocks({
|
const { req, res } = createMocks({
|
||||||
method: "POST", // This POST method is not allowed
|
method: "POST", // This POST method is not allowed
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// Extracted out as utility function so can be reused
|
||||||
|
// at different endpoints that require this validation.
|
||||||
|
const schema = z
|
||||||
|
.object({
|
||||||
|
// since nextjs parses query params as strings,
|
||||||
|
// we need to cast them to numbers using z.transform() and parseInt()
|
||||||
|
id: z
|
||||||
|
.string()
|
||||||
|
.regex(/^\d+$/)
|
||||||
|
.transform((id) => parseInt(id)),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
export default schema;
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { PrismaClient, EventType } from "@prisma/client";
|
||||||
|
import schema from "lib/validations/queryIdTransformParseInt";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import { withValidation } from "next-validations";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
type ResponseData = {
|
||||||
|
message?: string;
|
||||||
|
error?: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function eventType(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
|
||||||
|
const { query, method } = req;
|
||||||
|
const safe = await schema.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({ error: error });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Reject any other HTTP method than POST
|
||||||
|
res.status(405).json({ error: "Only DELETE Method allowed in /event-types/[id]/delete endpoint" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = withValidation({
|
||||||
|
schema,
|
||||||
|
type: "Zod",
|
||||||
|
mode: "query",
|
||||||
|
});
|
||||||
|
|
||||||
|
export default validate(eventType);
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { PrismaClient, EventType } from "@prisma/client";
|
||||||
|
import schemaQuery from "lib/validations/queryIdTransformParseInt";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import { withValidation } from "next-validations";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
const schema = z
|
||||||
|
.object({
|
||||||
|
title: z.string().min(3),
|
||||||
|
slug: z.string().min(3),
|
||||||
|
length: z.number().min(1).max(1440), // max is a full day.
|
||||||
|
description: z.string().min(3).optional(),
|
||||||
|
})
|
||||||
|
.strict(); // Adding strict so that we can disallow passing in extra fields
|
||||||
|
|
||||||
|
type ResponseData = {
|
||||||
|
data?: EventType;
|
||||||
|
error?: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function editEventType(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
|
||||||
|
const { query, body, method } = req;
|
||||||
|
const safeQuery = await schemaQuery.safeParse(query);
|
||||||
|
const safeBody = await schema.safeParse(body);
|
||||||
|
|
||||||
|
if (safeQuery.success && safeBody.success) {
|
||||||
|
if (method === "PATCH") {
|
||||||
|
const event = await prisma.eventType.update({
|
||||||
|
where: { id: safeQuery.data.id },
|
||||||
|
data: { ...safeBody.data },
|
||||||
|
});
|
||||||
|
if (event) res.status(200).json({ data: event });
|
||||||
|
if (!event) res.status(404).json({ error: "Event type not found" });
|
||||||
|
} else {
|
||||||
|
// Reject any other HTTP method than POST
|
||||||
|
res.status(405).json({ error: "Only GET Method allowed" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = withValidation({
|
||||||
|
schema,
|
||||||
|
type: "Zod",
|
||||||
|
mode: "body",
|
||||||
|
});
|
||||||
|
|
||||||
|
export default validate(editEventType);
|
|
@ -1,27 +1,10 @@
|
||||||
import { PrismaClient, EventType } from "@prisma/client";
|
import { PrismaClient, EventType } from "@prisma/client";
|
||||||
|
import schema from "lib/validations/queryIdTransformParseInt";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
import { withValidation } from "next-validations";
|
import { withValidation } from "next-validations";
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
const schema = z
|
|
||||||
.object({
|
|
||||||
// since nextjs parses query params as strings,
|
|
||||||
// we need to cast them to numbers using z.transform() and parseInt()
|
|
||||||
id: z
|
|
||||||
.string()
|
|
||||||
.regex(/^\d+$/)
|
|
||||||
.transform((id) => parseInt(id)),
|
|
||||||
})
|
|
||||||
.strict();
|
|
||||||
|
|
||||||
const validate = withValidation({
|
|
||||||
schema,
|
|
||||||
type: "Zod",
|
|
||||||
mode: "query",
|
|
||||||
});
|
|
||||||
|
|
||||||
type ResponseData = {
|
type ResponseData = {
|
||||||
data?: EventType;
|
data?: EventType;
|
||||||
error?: any;
|
error?: any;
|
||||||
|
@ -29,18 +12,31 @@ type ResponseData = {
|
||||||
|
|
||||||
export async function eventType(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
|
export async function eventType(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
|
||||||
const { query, method } = req;
|
const { query, method } = req;
|
||||||
if (method === "GET") {
|
const safe = await schema.safeParse(query);
|
||||||
const safe = await schema.safeParse(query);
|
if (safe.success) {
|
||||||
if (safe.success) {
|
if (method === "GET") {
|
||||||
const event = await prisma.eventType.findUnique({ where: { id: safe.data.id } });
|
const event = await prisma.eventType.findUnique({ where: { id: safe.data.id } });
|
||||||
|
|
||||||
if (event) res.status(200).json({ data: event });
|
if (event) res.status(200).json({ data: event });
|
||||||
if (!event) res.status(404).json({ error: "Event type not found" });
|
if (!event) res.status(404).json({ error: "Event type not found" });
|
||||||
|
} else if (method === "PATCH") {
|
||||||
|
const event = await prisma.eventType.update({
|
||||||
|
where: { id: safe.data.id },
|
||||||
|
data: { title: "Updated title" },
|
||||||
|
});
|
||||||
|
if (event) res.status(200).json({ data: event });
|
||||||
|
if (!event) res.status(404).json({ error: "Event type not found" });
|
||||||
|
} else {
|
||||||
|
// Reject any other HTTP method than POST
|
||||||
|
res.status(405).json({ error: "Only GET Method allowed" });
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Reject any other HTTP method than POST
|
|
||||||
res.status(405).json({ error: "Only GET Method allowed" });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validate = withValidation({
|
||||||
|
schema,
|
||||||
|
type: "Zod",
|
||||||
|
mode: "query",
|
||||||
|
});
|
||||||
|
|
||||||
export default validate(eventType);
|
export default validate(eventType);
|
|
@ -14,7 +14,7 @@ const schema = z
|
||||||
})
|
})
|
||||||
.strict(); // Adding strict so that we can disallow passing in extra fields
|
.strict(); // Adding strict so that we can disallow passing in extra fields
|
||||||
|
|
||||||
type schema = z.infer<typeof schema>;
|
// type schema = z.infer<typeof schema>;
|
||||||
const validate = withValidation({
|
const validate = withValidation({
|
||||||
schema,
|
schema,
|
||||||
type: "Zod",
|
type: "Zod",
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
],
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"baseUrl": "./",
|
"baseUrl": ".",
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
|
@ -17,9 +17,9 @@
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"jsx": "preserve"
|
"jsx": "preserve",
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./"
|
"./**/*.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue