fix: booking reference moved to pick-first create/edit separate validations
This commit is contained in:
1 parent
6997606a4c
commit
14e2964a6d
8 files changed
+85
-56
No files matched your search
@@ -1,7 +1,7 @@
|
||||
# Cal.com Public API (Enterprise Only)
|
||||
|
||||
This is the public REST api for cal.com.
|
||||
It exposes CRUD Endpoints of all our most important resources.
|
||||
This is the public REST api for cal.com.
|
||||
It exposes CRUD Endpoints of all our most important resources.
|
||||
And it makes it easy for anyone to integrate with Cal.com at the application programming level.
|
||||
|
||||
## Stack
|
||||
@@ -63,9 +63,10 @@ API Keys optionally may have expiry dates, if they are expired they won't work.
|
||||
In the future we might add support for header Bearer Auth if we need to or if our customers require it.
|
||||
|
||||
## Middlewares
|
||||
|
||||
We don't use the new NextJS 12 Beta Middlewares, mainly because they run on the edge, and are not able to call prisma from api endpoints. We use instead a very nifty library called next-api-middleware that let's us use a similar approach building our own middlewares and applying them as we see fit.
|
||||
|
||||
* withMiddleware() requires some default middlewares (verifyApiKey, etc...)
|
||||
- withMiddleware() requires some default middlewares (verifyApiKey, etc...)
|
||||
|
||||
## Next.config.js
|
||||
|
||||
@@ -121,21 +122,21 @@ We aim to provide a fully tested API for our peace of mind, this is accomplished
|
||||
|
||||
## Endpoints matrix
|
||||
|
||||
| resource | get [id] | get all | create | edit | delete |
|
||||
|--------------------------|----------|----------|---------|-------|--------|
|
||||
| attendees | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| availabilities | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| booking-references | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| daily-event-references | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| destination-calendars | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| event-type-custom-inputs | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| event-types | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| memberships | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| payments | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| schedules | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| selected-calendars | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| teams | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| users | ✅ | 👤[1] | ✅ | ✅ | ✅ |
|
||||
| resource | get [id] | get all | create | edit | delete |
|
||||
| ------------------------ | -------- | ------- | ------ | ---- | ------ |
|
||||
| attendees | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| availabilities | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| booking-references | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| daily-event-references | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| destination-calendars | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| event-type-custom-inputs | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| event-types | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| memberships | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| payments | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| schedules | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| selected-calendars | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| teams | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| users | ✅ | 👤[1] | ✅ | ✅ | ✅ |
|
||||
|
||||
## Models from database that are not exposed
|
||||
|
||||
@@ -170,5 +171,6 @@ DATABASE_URL=DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"
|
||||
|
||||
## Optional
|
||||
|
||||
API_KEY_PREFIX=cal_# This can be changed per envirorment so cal_test_ for staging for example.
|
||||
> If you're self-hosting under our commercial license, you can use any prefix you want for api keys. either leave the default cal_ (not providing any envirorment variable) or modify it
|
||||
API*KEY_PREFIX=cal*# This can be changed per envirorment so cal*test* for staging for example.
|
||||
|
||||
> If you're self-hosting under our commercial license, you can use any prefix you want for api keys. either leave the default cal\_ (not providing any envirorment variable) or modify it
|
||||
@@ -22,12 +22,8 @@ const schemaAttendeeEditParams = z.object({
|
||||
name: z.string(),
|
||||
timeZone: z.string(),
|
||||
});
|
||||
export const schemaAttendeeEditBodyParams = schemaAttendeeBaseBodyParams.merge(
|
||||
schemaAttendeeEditParams
|
||||
);
|
||||
export const schemaAttendeeCreateBodyParams = schemaAttendeeBaseBodyParams.merge(
|
||||
schemaAttendeeCreateParams
|
||||
);
|
||||
export const schemaAttendeeEditBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeEditParams);
|
||||
export const schemaAttendeeCreateBodyParams = schemaAttendeeBaseBodyParams.merge(schemaAttendeeCreateParams);
|
||||
|
||||
export const schemaAttendeeReadPublic = Attendee.pick({
|
||||
bookingId: true,
|
||||
|
||||
@@ -11,7 +11,7 @@ export const schemaAvailabilityBaseBodyParams = Availability.pick({
|
||||
days: true,
|
||||
}).partial();
|
||||
|
||||
export const schemaAvailabilityPublic = Availability.pick({
|
||||
export const schemaAvailabilityReadPublic = Availability.pick({
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
date: true,
|
||||
|
||||
@@ -3,21 +3,45 @@ import { z } from "zod";
|
||||
|
||||
import { _BookingReferenceModel as BookingReference } from "@calcom/prisma/zod";
|
||||
|
||||
export const schemaBookingReferenceBaseBodyParams = BookingReference.omit({ id: true }).partial();
|
||||
export const schemaBookingReferenceBaseBodyParams = BookingReference.pick({
|
||||
type: true,
|
||||
bookingId: true,
|
||||
uid: true,
|
||||
meetingId: true,
|
||||
meetingPassword: true,
|
||||
meetingUrl: true,
|
||||
deleted: true,
|
||||
}).partial();
|
||||
|
||||
export const schemaBookingReferencePublic = BookingReference.omit({});
|
||||
export const schemaBookingReferenceReadPublic = BookingReference.pick({
|
||||
type: true,
|
||||
bookingId: true,
|
||||
uid: true,
|
||||
meetingId: true,
|
||||
meetingPassword: true,
|
||||
meetingUrl: true,
|
||||
deleted: true,
|
||||
});
|
||||
|
||||
const schemaBookingReferenceRequiredParams = z.object({
|
||||
const schemaBookingReferenceEditParams = z.object({
|
||||
type: z.string(),
|
||||
uid: z.string(),
|
||||
meetingId: z.string(),
|
||||
meetingPassword: z.string(),
|
||||
meetingUrl: z.string(),
|
||||
deleted: z.boolean(),
|
||||
});
|
||||
|
||||
export const schemaBookingReferenceBodyParams = schemaBookingReferenceBaseBodyParams.merge(
|
||||
schemaBookingReferenceRequiredParams
|
||||
const schemaBookingReferenceCreateParams = z.object({
|
||||
type: z.string(),
|
||||
uid: z.string(),
|
||||
meetingId: z.string(),
|
||||
meetingPassword: z.string(),
|
||||
meetingUrl: z.string(),
|
||||
deleted: z.boolean(),
|
||||
});
|
||||
export const schemaBookingCreateBodyParams = schemaBookingReferenceBaseBodyParams.merge(
|
||||
schemaBookingReferenceCreateParams
|
||||
);
|
||||
export const schemaBookingEditBodyParams = schemaBookingReferenceBaseBodyParams.merge(
|
||||
schemaBookingReferenceEditParams
|
||||
);
|
||||
|
||||
export const withValidBookingReference = withValidation({
|
||||
schema: schemaBookingReferenceBodyParams,
|
||||
type: "Zod",
|
||||
mode: "body",
|
||||
});
|
||||
@@ -4,7 +4,10 @@ import prisma from "@calcom/prisma";
|
||||
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import type { AvailabilityResponse } from "@lib/types";
|
||||
import { schemaAvailabilityEditBodyParams, schemaAvailabilityReadPublic } from "@lib/validations/availability";
|
||||
import {
|
||||
schemaAvailabilityEditBodyParams,
|
||||
schemaAvailabilityReadPublic,
|
||||
} from "@lib/validations/availability";
|
||||
import {
|
||||
schemaQueryIdParseInt,
|
||||
withValidQueryIdTransformParseInt,
|
||||
|
||||
@@ -4,7 +4,10 @@ import prisma from "@calcom/prisma";
|
||||
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import { AvailabilityResponse, AvailabilitiesResponse } from "@lib/types";
|
||||
import { schemaAvailabilityCreateBodyParams, schemaAvailabilityReadPublic } from "@lib/validations/availability";
|
||||
import {
|
||||
schemaAvailabilityCreateBodyParams,
|
||||
schemaAvailabilityReadPublic,
|
||||
} from "@lib/validations/availability";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
|
||||
@@ -6,8 +6,8 @@ import prisma from "@calcom/prisma";
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import type { BookingReferenceResponse } from "@lib/types";
|
||||
import {
|
||||
schemaBookingReferenceBodyParams,
|
||||
schemaBookingReferencePublic,
|
||||
schemaBookingEditBodyParams,
|
||||
schemaBookingReferenceReadPublic,
|
||||
} from "@lib/validations/booking-reference";
|
||||
import {
|
||||
schemaQueryIdParseInt,
|
||||
@@ -18,14 +18,14 @@ import {
|
||||
* @swagger
|
||||
* /booking-references/{id}:
|
||||
* get:
|
||||
* summary: Get a daily event reference by ID
|
||||
* summary: Get a booking reference by ID
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: Numeric ID of the daily event reference to get
|
||||
* description: Numeric ID of the booking reference to get
|
||||
* security:
|
||||
* - ApiKeyAuth: []
|
||||
* tags:
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
* 404:
|
||||
* description: BookingReference was not found
|
||||
* patch:
|
||||
* summary: Edit an existing daily event reference
|
||||
* summary: Edit an existing booking reference
|
||||
* consumes:
|
||||
* - application/json
|
||||
* parameters:
|
||||
@@ -54,7 +54,7 @@ import {
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: Numeric ID of the daily event reference to edit
|
||||
* description: Numeric ID of the booking reference to edit
|
||||
* security:
|
||||
* - ApiKeyAuth: []
|
||||
* tags:
|
||||
@@ -68,14 +68,14 @@ import {
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
* delete:
|
||||
* summary: Remove an existing daily event reference
|
||||
* summary: Remove an existing booking reference
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: Numeric ID of the daily event reference to delete
|
||||
* description: Numeric ID of the booking reference to delete
|
||||
* security:
|
||||
* - ApiKeyAuth: []
|
||||
* tags:
|
||||
@@ -95,7 +95,6 @@ export async function bookingReferenceById(
|
||||
) {
|
||||
const { method, query, body } = req;
|
||||
const safeQuery = schemaQueryIdParseInt.safeParse(query);
|
||||
const safeBody = schemaBookingReferenceBodyParams.safeParse(body);
|
||||
if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error);
|
||||
const userId = req.userId;
|
||||
const userWithBookings = await prisma.user.findUnique({
|
||||
@@ -111,7 +110,7 @@ export async function bookingReferenceById(
|
||||
case "GET":
|
||||
await prisma.bookingReference
|
||||
.findUnique({ where: { id: safeQuery.data.id } })
|
||||
.then((data) => schemaBookingReferencePublic.parse(data))
|
||||
.then((data) => schemaBookingReferenceReadPublic.parse(data))
|
||||
.then((booking_reference) => res.status(200).json({ booking_reference }))
|
||||
.catch((error: Error) =>
|
||||
res.status(404).json({
|
||||
@@ -122,12 +121,14 @@ export async function bookingReferenceById(
|
||||
break;
|
||||
|
||||
case "PATCH":
|
||||
const safeBody = schemaBookingEditBodyParams.safeParse(body);
|
||||
|
||||
if (!safeBody.success) {
|
||||
throw new Error("Invalid request body");
|
||||
}
|
||||
await prisma.bookingReference
|
||||
.update({ where: { id: safeQuery.data.id }, data: safeBody.data })
|
||||
.then((data) => schemaBookingReferencePublic.parse(data))
|
||||
.then((data) => schemaBookingReferenceReadPublic.parse(data))
|
||||
.then((booking_reference) => res.status(200).json({ booking_reference }))
|
||||
.catch((error: Error) =>
|
||||
res.status(404).json({
|
||||
|
||||
@@ -5,8 +5,8 @@ import prisma from "@calcom/prisma";
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import { BookingReferenceResponse, BookingReferencesResponse } from "@lib/types";
|
||||
import {
|
||||
schemaBookingReferenceBodyParams,
|
||||
schemaBookingReferencePublic,
|
||||
schemaBookingCreateBodyParams,
|
||||
schemaBookingReferenceReadPublic,
|
||||
} from "@lib/validations/booking-reference";
|
||||
|
||||
/**
|
||||
@@ -55,7 +55,7 @@ async function createOrlistAllBookingReferences(
|
||||
if (method === "GET") {
|
||||
const data = await prisma.bookingReference.findMany({ where: { id: { in: userBookingIds } } });
|
||||
const booking_references = data.map((bookingReference) =>
|
||||
schemaBookingReferencePublic.parse(bookingReference)
|
||||
schemaBookingReferenceReadPublic.parse(bookingReference)
|
||||
);
|
||||
if (booking_references) res.status(200).json({ booking_references });
|
||||
else
|
||||
@@ -65,7 +65,7 @@ async function createOrlistAllBookingReferences(
|
||||
error,
|
||||
});
|
||||
} else if (method === "POST") {
|
||||
const safe = schemaBookingReferenceBodyParams.safeParse(req.body);
|
||||
const safe = schemaBookingCreateBodyParams.safeParse(req.body);
|
||||
if (!safe.success) {
|
||||
throw new Error("Invalid request body");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user