feat: adds full validations for users endpoint

pull/9078/head
Agusti Fernandez Pardo 2022-04-24 23:56:25 +02:00
parent dad70d5a12
commit 4c022d5d07
2 changed files with 68 additions and 3 deletions

View File

@ -1,4 +1,5 @@
import { withValidation } from "next-validations";
import * as tzdb from "tzdata";
import { z } from "zod";
import { _UserModel as User } from "@calcom/prisma/zod";
@ -14,11 +15,45 @@ enum weekdays {
SUNDAY = "Sunday",
}
// @note: extracted from apps/web/next-i18next.config.js, update if new locales.
enum locales {
EN = "en",
FR = "fr",
IT = "it",
RU = "ru",
ES = "es",
DE = "de",
PT = "pt",
RO = "ro",
NL = "nl",
PT_BR = "pt-BR",
ES_419 = "es-419",
KO = "ko",
JA = "ja",
PL = "pl",
AR = "ar",
IW = "iw",
ZH_CN = "zh-CN",
ZH_TW = "zh-TW",
CS = "cs",
SR = "sr",
SV = "sv",
VI = "vi",
}
enum theme {
DARK = "dark",
LIGHT = "light",
}
enum timeFormat {
TWELVE = 12,
TWENTY_FOUR = 24,
}
// @note: These are the values that are editable via PATCH method on the user Model
export const schemaUserBaseBodyParams = User.pick({
name: true,
bio: true,
avatar: true,
timeZone: true,
weekStart: true,
endTime: true,
@ -31,6 +66,8 @@ export const schemaUserBaseBodyParams = User.pick({
darkBrandColor: true,
allowDynamicBooking: true,
away: true,
// @note: disallowing avatar changes via API for now. We can add it later if needed. User should upload image via UI.
// avatar: true,
}).partial();
// @note: partial() is used to allow for the user to edit only the fields they want to edit making all optional,
// if want to make any required do it in the schemaRequiredParams
@ -39,9 +76,26 @@ export const schemaUserBaseBodyParams = User.pick({
// for example making weekStart only accept weekdays as input
const schemaUserRequiredParams = 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(),
bufferTime: z.number().min(0).max(86400).optional(),
startTime: z.number().min(0).max(86400).optional(),
endTime: z.number().min(0).max(86400).optional(),
theme: z.nativeEnum(theme).optional(),
timeFormat: z.nativeEnum(timeFormat).optional(),
defaultScheduleId: z
.number()
.refine((id: number) => id > 0)
.optional(),
locale: z.nativeEnum(locales),
});
// @note: These are the values that are editable via PATCH method on the user Model
// @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({});
// @note: These are the values that are always returned when reading a user

View File

@ -104,12 +104,23 @@ export async function userById(req: NextApiRequest, res: NextApiResponse<any>) {
break;
case "PATCH":
// FIXME: Validate body against different Edit validation, skip username.
const safeBody = schemaUserEditBodyParams.safeParse(body);
if (!safeBody.success) {
res.status(400).json({ message: "Bad request", error: safeBody.error });
throw new Error("Invalid request body");
}
const userSchedules = await prisma.schedule.findMany({
where: { userId },
});
const userSchedulesIds = userSchedules.map((schedule) => schedule.id);
// @note: here we make sure user can only make as default his own scheudles
if (!userSchedulesIds.includes(Number(safeBody?.data?.defaultScheduleId))) {
res.status(400).json({
message: "Bad request",
error: "Invalid default schedule id",
});
throw new Error("Invalid request body value: defaultScheduleId");
}
await prisma.user
.update({
where: { id: userId },