refactor: v2 event types endpoint paths (#14545)

* refactor: rename event-types/:username/public to event-types/public/:username

* refactor: move /events/public to /event-types/public/:username/:eventSlug

* fix: remove EVENT_TYPE_READ permission from public GET event-types/public/:username

* docs: add response DTO to generate docs for /public/:username/:slug aka ex. events/public

* tests: public event types endpoints

* refactor: have /public at the end of event-types routes

* correct swagger doc
This commit is contained in:
Lauris Skraucis
2024-04-13 12:47:35 +03:00
committed by GitHub
parent 296678046c
commit 8ad80f7ac3
10 changed files with 994 additions and 236 deletions
@@ -3,7 +3,9 @@ import { AppModule } from "@/app.module";
import { EventTypesModule } from "@/ee/event-types/event-types.module";
import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
import { UpdateEventTypeInput } from "@/ee/event-types/inputs/update-event-type.input";
import { GetEventTypePublicOutput } from "@/ee/event-types/outputs/get-event-type-public.output";
import { GetEventTypeOutput } from "@/ee/event-types/outputs/get-event-type.output";
import { GetEventTypesPublicOutput } from "@/ee/event-types/outputs/get-event-types-public.output";
import { HttpExceptionFilter } from "@/filters/http-exception.filter";
import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
@@ -64,7 +66,7 @@ describe("Event types Endpoints", () => {
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
const userEmail = "event-types-test-e2e@api.com";
const name = "bob the builder";
const name = "bob-the-builder";
const username = name;
let eventType: EventType;
let user: User;
@@ -183,6 +185,41 @@ describe("Event types Endpoints", () => {
expect(responseBody.data.eventType.userId).toEqual(user.id);
});
it(`/GET/:username/public`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types/${username}/public`)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
.set("Authorization", `Bearer whatever`)
.expect(200);
const responseBody: GetEventTypesPublicOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseBody.data?.length).toEqual(1);
expect(responseBody.data?.[0]?.id).toEqual(eventType.id);
expect(responseBody.data?.[0]?.title).toEqual(eventType.title);
expect(responseBody.data?.[0]?.slug).toEqual(eventType.slug);
expect(responseBody.data?.[0]?.length).toEqual(eventType.length);
});
it(`/GET/:username/:eventSlug/public`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types/${username}/${eventType.slug}/public`)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
.set("Authorization", `Bearer whatever`)
.expect(200);
const responseBody: GetEventTypePublicOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseBody.data?.id).toEqual(eventType.id);
expect(responseBody.data?.title).toEqual(eventType.title);
expect(responseBody.data?.slug).toEqual(eventType.slug);
expect(responseBody.data?.length).toEqual(eventType.length);
});
it(`/GET/`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types`)
@@ -202,7 +239,7 @@ describe("Event types Endpoints", () => {
expect(responseBody.data.profiles?.[0]?.name).toEqual(name);
});
it(`/GET/:username/public`, async () => {
it(`/GET/public/:username/`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types/${username}/public`)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
@@ -1,7 +1,9 @@
import { CreateEventTypeInput } from "@/ee/event-types/inputs/create-event-type.input";
import { GetPublicEventTypeQueryParams } from "@/ee/event-types/inputs/get-public-event-type-query-params.input";
import { UpdateEventTypeInput } from "@/ee/event-types/inputs/update-event-type.input";
import { CreateEventTypeOutput } from "@/ee/event-types/outputs/create-event-type.output";
import { DeleteEventTypeOutput } from "@/ee/event-types/outputs/delete-event-type.output";
import { GetEventTypePublicOutput } from "@/ee/event-types/outputs/get-event-type-public.output";
import { GetEventTypeOutput } from "@/ee/event-types/outputs/get-event-type.output";
import { GetEventTypesPublicOutput } from "@/ee/event-types/outputs/get-event-types-public.output";
import { GetEventTypesOutput } from "@/ee/event-types/outputs/get-event-types.output";
@@ -11,6 +13,7 @@ import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { AccessTokenGuard } from "@/modules/auth/guards/access-token/access-token.guard";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import {
Controller,
@@ -24,11 +27,15 @@ import {
HttpCode,
HttpStatus,
Delete,
Query,
InternalServerErrorException,
} from "@nestjs/common";
import { ApiTags as DocsTags } from "@nestjs/swagger";
import { EVENT_TYPE_READ, EVENT_TYPE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
import { getPublicEvent } from "@calcom/platform-libraries";
import { getEventTypesByViewer } from "@calcom/platform-libraries";
import { PrismaClient } from "@calcom/prisma";
@Controller({
path: "event-types",
@@ -37,7 +44,10 @@ import { getEventTypesByViewer } from "@calcom/platform-libraries";
@UseGuards(PermissionsGuard)
@DocsTags("Event types")
export class EventTypesController {
constructor(private readonly eventTypesService: EventTypesService) {}
constructor(
private readonly eventTypesService: EventTypesService,
private readonly prismaReadService: PrismaReadService
) {}
@Post("/")
@Permissions([EVENT_TYPE_WRITE])
@@ -90,6 +100,35 @@ export class EventTypesController {
};
}
@Get("/:username/:eventSlug/public")
async getPublicEventType(
@Param("username") username: string,
@Param("eventSlug") eventSlug: string,
@Query() queryParams: GetPublicEventTypeQueryParams
): Promise<GetEventTypePublicOutput> {
try {
const event = await getPublicEvent(
username.toLowerCase(),
eventSlug,
queryParams.isTeamEvent,
queryParams.org || null,
this.prismaReadService.prisma as unknown as PrismaClient,
// We should be fine allowing unpublished orgs events to be servable through platform because Platform access is behind license
// If there is ever a need to restrict this, we can introduce a new query param `fromRedirectOfNonOrgLink`
true
);
return {
data: event,
status: SUCCESS_STATUS,
};
} catch (err) {
if (err instanceof Error) {
throw new NotFoundException(err.message);
}
}
throw new InternalServerErrorException("Could not find public event.");
}
@Get("/:username/public")
async getPublicEventTypes(@Param("username") username: string): Promise<GetEventTypesPublicOutput> {
const eventTypes = await this.eventTypesService.getEventTypesPublicByUsername(username);
@@ -2,16 +2,7 @@ import { ApiProperty } from "@nestjs/swagger";
import { Transform } from "class-transformer";
import { IsBoolean, IsOptional, IsString } from "class-validator";
export class GetPublicEventInput {
@IsString()
@Transform(({ value }: { value: string }) => value.toLowerCase())
@ApiProperty({ required: true })
username!: string;
@IsString()
@ApiProperty({ required: true })
eventSlug!: string;
export class GetPublicEventTypeQueryParams {
@Transform(({ value }: { value: string }) => value === "true")
@IsBoolean()
@IsOptional()
@@ -0,0 +1,355 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import {
IsBoolean,
IsInt,
IsOptional,
IsString,
IsUrl,
ValidateNested,
IsArray,
IsObject,
IsNumber,
IsEnum,
} from "class-validator";
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
class Location {
@IsString()
type!: string;
}
class Source {
@IsString()
id!: string;
@IsString()
type!: string;
@IsString()
label!: string;
}
class OptionInput {
@IsString()
type!: string;
@IsBoolean()
@IsOptional()
required?: boolean;
@IsString()
@IsOptional()
placeholder?: string;
}
class BookingField {
@IsString()
name!: string;
@IsString()
type!: string;
@IsOptional()
@IsString()
defaultLabel?: string;
@IsString()
@IsOptional()
label?: string;
@IsString()
@IsOptional()
placeholder?: string;
@IsBoolean()
@IsOptional()
required?: boolean;
@IsOptional()
getOptionsAt?: string;
@IsObject()
@IsOptional()
optionsInputs?: { [key: string]: OptionInput };
@IsBoolean()
@IsOptional()
hideWhenJustOneOption?: boolean;
@IsString()
@IsOptional()
editable?: string;
@IsArray()
@ValidateNested({ each: true })
@Type(() => Source)
@IsOptional()
sources?: Source[];
}
class Organization {
@IsInt()
id!: number;
@IsString()
@IsOptional()
slug?: string | null;
@IsString()
name!: string;
@IsOptional()
metadata!: Record<string, any>;
}
class Profile {
@IsString()
username!: string | null;
@IsInt()
id!: number | null;
@IsInt()
@IsOptional()
userId?: number;
@IsString()
@IsOptional()
uid?: string;
@IsOptional()
@IsString()
name?: string;
@IsInt()
organizationId!: number | null;
@ValidateNested()
@Type(() => Organization)
organization?: Organization | null;
@IsString()
upId!: string;
@IsString()
@IsOptional()
image?: string;
@IsString()
@IsOptional()
brandColor?: string;
@IsString()
@IsOptional()
darkBrandColor?: string;
@IsString()
@IsOptional()
theme?: string;
@IsOptional()
bookerLayouts?: any;
}
class Owner {
@IsInt()
id!: number;
@IsString()
@IsOptional()
avatarUrl?: string | null;
@IsString()
username!: string | null;
@IsString()
name!: string | null;
@IsString()
weekStart!: string;
@IsString()
@IsOptional()
brandColor?: string | null;
@IsString()
@IsOptional()
darkBrandColor?: string | null;
@IsString()
@IsOptional()
theme?: string | null;
@IsOptional()
metadata!: any;
@IsInt()
@IsOptional()
defaultScheduleId?: number | null;
@IsString()
nonProfileUsername!: string | null;
@ValidateNested()
@Type(() => Profile)
profile!: Profile;
}
class User {
@IsString()
username!: string | null;
@IsString()
name!: string | null;
@IsString()
weekStart!: string;
@IsInt()
organizationId?: number;
@IsString()
@IsOptional()
avatarUrl?: string | null;
@ValidateNested()
profile!: Profile;
@IsString()
bookerUrl!: string;
}
class Schedule {
@IsInt()
id!: number;
@IsString()
timeZone!: string | null;
}
class PublicEventTypeOutput {
@IsInt()
id!: number;
@IsString()
title!: string;
@IsString()
description!: string;
@IsString()
@IsOptional()
eventName?: string | null;
@IsString()
slug!: string;
@IsBoolean()
isInstantEvent!: boolean;
@IsOptional()
aiPhoneCallConfig?: any;
@IsOptional()
schedulingType?: any;
@IsInt()
length!: number;
@IsArray()
@ValidateNested({ each: true })
@Type(() => Location)
locations!: Location[];
@IsArray()
customInputs!: any[];
@IsBoolean()
disableGuests!: boolean;
@IsObject()
metadata!: object | null;
@IsBoolean()
lockTimeZoneToggleOnBookingPage!: boolean;
@IsBoolean()
requiresConfirmation!: boolean;
@IsBoolean()
requiresBookerEmailVerification!: boolean;
@IsOptional()
recurringEvent?: any;
@IsNumber()
price!: number;
@IsString()
currency!: string;
@IsOptional()
seatsPerTimeSlot?: number | null;
@IsBoolean()
seatsShowAvailabilityCount!: boolean | null;
@IsArray()
@ValidateNested({ each: true })
@Type(() => BookingField)
bookingFields!: BookingField[];
@IsOptional()
team?: any;
@IsOptional()
@IsUrl()
successRedirectUrl?: string | null;
@IsArray()
workflows!: any[];
@IsArray()
hosts!: any[];
@ValidateNested()
@Type(() => Owner)
owner!: Owner | null;
@ValidateNested()
@Type(() => Schedule)
schedule!: Schedule | null;
@IsBoolean()
hidden!: boolean;
@IsBoolean()
assignAllTeamMembers!: boolean;
@IsOptional()
bookerLayouts?: any;
@IsArray()
@ValidateNested({ each: true })
@Type(() => User)
users!: User[];
@IsObject()
entity!: object;
@IsBoolean()
isDynamic!: boolean;
}
export class GetEventTypePublicOutput {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS])
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
@ValidateNested({ each: true })
@Type(() => PublicEventTypeOutput)
@IsArray()
data!: PublicEventTypeOutput | null;
}
+1 -2
View File
@@ -1,12 +1,11 @@
import { PlatformEndpointsModule } from "@/ee/platform-endpoints-module";
import { EventsModule } from "@/modules/events/events.module";
import { OAuthClientModule } from "@/modules/oauth-clients/oauth-client.module";
import { TimezoneModule } from "@/modules/timezones/timezones.module";
import type { MiddlewareConsumer, NestModule } from "@nestjs/common";
import { Module } from "@nestjs/common";
@Module({
imports: [EventsModule, OAuthClientModule, PlatformEndpointsModule, TimezoneModule],
imports: [OAuthClientModule, PlatformEndpointsModule, TimezoneModule],
})
export class EndpointsModule implements NestModule {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1,43 +0,0 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { Controller, Get, NotFoundException, InternalServerErrorException, Query } from "@nestjs/common";
import { ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { getPublicEvent } from "@calcom/platform-libraries";
import type { PublicEventType } from "@calcom/platform-libraries";
import { ApiResponse, GetPublicEventInput } from "@calcom/platform-types";
import { PrismaClient } from "@calcom/prisma";
@Controller({
path: "events",
version: "2",
})
@DocsTags("Event types")
export class EventsController {
constructor(private readonly prismaReadService: PrismaReadService) {}
@Get("/public")
async getPublicEvent(@Query() queryParams: GetPublicEventInput): Promise<ApiResponse<PublicEventType>> {
try {
const event = await getPublicEvent(
queryParams.username,
queryParams.eventSlug,
queryParams.isTeamEvent,
queryParams.org || null,
this.prismaReadService.prisma as unknown as PrismaClient,
// We should be fine allowing unpublished orgs events to be servable through platform because Platform access is behind license
// If there is ever a need to restrict this, we can introduce a new query param `fromRedirectOfNonOrgLink`
true
);
return {
data: event,
status: SUCCESS_STATUS,
};
} catch (err) {
if (err instanceof Error) {
throw new NotFoundException(err.message);
}
}
throw new InternalServerErrorException("Could not find public event.");
}
}
@@ -1,9 +0,0 @@
import { EventsController } from "@/modules/events/controllers/events.controller";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { Module } from "@nestjs/common";
@Module({
imports: [PrismaModule],
controllers: [EventsController],
})
export class EventsModule {}
+545 -159
View File
@@ -22,61 +22,7 @@
]
}
},
"/v2/events/public": {
"get": {
"operationId": "EventsController_getPublicEvent",
"parameters": [
{
"name": "username",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "eventSlug",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "isTeamEvent",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "org",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"Event types"
]
}
},
"/v2/oauth-clients/{clientId}/users": {
"/api/v2/oauth-clients/{clientId}/users": {
"get": {
"operationId": "OAuthClientUsersController_getManagedUsers",
"parameters": [
@@ -144,7 +90,7 @@
]
}
},
"/v2/oauth-clients/{clientId}/users/{userId}": {
"/api/v2/oauth-clients/{clientId}/users/{userId}": {
"get": {
"operationId": "OAuthClientUsersController_getUserById",
"parameters": [
@@ -264,7 +210,7 @@
]
}
},
"/v2/oauth-clients/{clientId}/users/{userId}/force-refresh": {
"/api/v2/oauth-clients/{clientId}/users/{userId}/force-refresh": {
"post": {
"operationId": "OAuthClientUsersController_forceRefresh",
"parameters": [
@@ -302,7 +248,7 @@
]
}
},
"/v2/oauth-clients": {
"/api/v2/oauth-clients": {
"post": {
"operationId": "OAuthClientsController_createOAuthClient",
"summary": "",
@@ -356,7 +302,7 @@
]
}
},
"/v2/oauth-clients/{clientId}": {
"/api/v2/oauth-clients/{clientId}": {
"get": {
"operationId": "OAuthClientsController_getOAuthClientById",
"summary": "",
@@ -458,7 +404,7 @@
]
}
},
"/v2/oauth/{clientId}/authorize": {
"/api/v2/oauth/{clientId}/authorize": {
"post": {
"operationId": "OAuthFlowController_authorize",
"summary": "Authorize an OAuth client",
@@ -496,7 +442,7 @@
]
}
},
"/v2/oauth/{clientId}/exchange": {
"/api/v2/oauth/{clientId}/exchange": {
"post": {
"operationId": "OAuthFlowController_exchange",
"summary": "Exchange authorization code for access tokens",
@@ -549,7 +495,7 @@
]
}
},
"/v2/oauth/{clientId}/refresh": {
"/api/v2/oauth/{clientId}/refresh": {
"post": {
"operationId": "OAuthFlowController_refreshAccessToken",
"parameters": [
@@ -597,7 +543,7 @@
]
}
},
"/v2/event-types": {
"/api/v2/event-types": {
"post": {
"operationId": "EventTypesController_createEventType",
"parameters": [],
@@ -647,7 +593,7 @@
]
}
},
"/v2/event-types/{eventTypeId}": {
"/api/v2/event-types/{eventTypeId}": {
"get": {
"operationId": "EventTypesController_getEventType",
"parameters": [
@@ -743,7 +689,61 @@
]
}
},
"/v2/event-types/{username}/public": {
"/api/v2/event-types/{username}/{eventSlug}/public": {
"get": {
"operationId": "EventTypesController_getPublicEventType",
"parameters": [
{
"name": "username",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "eventSlug",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "isTeamEvent",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "org",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetEventTypePublicOutput"
}
}
}
}
},
"tags": [
"Event types"
]
}
},
"/api/v2/event-types/{username}/public": {
"get": {
"operationId": "EventTypesController_getPublicEventTypes",
"parameters": [
@@ -773,7 +773,7 @@
]
}
},
"/v2/ee/gcal/oauth/auth-url": {
"/api/v2/ee/gcal/oauth/auth-url": {
"get": {
"operationId": "GcalController_redirect",
"parameters": [
@@ -803,7 +803,7 @@
]
}
},
"/v2/ee/gcal/oauth/save": {
"/api/v2/ee/gcal/oauth/save": {
"get": {
"operationId": "GcalController_save",
"parameters": [
@@ -841,7 +841,7 @@
]
}
},
"/v2/ee/gcal/check": {
"/api/v2/ee/gcal/check": {
"get": {
"operationId": "GcalController_check",
"parameters": [],
@@ -862,7 +862,7 @@
]
}
},
"/v2/ee/provider/{clientId}": {
"/api/v2/ee/provider/{clientId}": {
"get": {
"operationId": "CalProviderController_verifyClientId",
"parameters": [
@@ -892,7 +892,7 @@
]
}
},
"/v2/ee/provider/{clientId}/access-token": {
"/api/v2/ee/provider/{clientId}/access-token": {
"get": {
"operationId": "CalProviderController_verifyAccessToken",
"parameters": [
@@ -922,7 +922,7 @@
]
}
},
"/v2/schedules": {
"/api/v2/schedules": {
"post": {
"operationId": "SchedulesController_createSchedule",
"parameters": [],
@@ -972,7 +972,7 @@
]
}
},
"/v2/schedules/default": {
"/api/v2/schedules/default": {
"get": {
"operationId": "SchedulesController_getDefaultSchedule",
"parameters": [],
@@ -993,7 +993,7 @@
]
}
},
"/v2/schedules/{scheduleId}": {
"/api/v2/schedules/{scheduleId}": {
"get": {
"operationId": "SchedulesController_getSchedule",
"parameters": [
@@ -1089,7 +1089,7 @@
]
}
},
"/v2/ee/me": {
"/api/v2/ee/me": {
"get": {
"operationId": "MeController_getMe",
"parameters": [],
@@ -1139,7 +1139,7 @@
]
}
},
"/v2/ee/calendars/busy-times": {
"/api/v2/ee/calendars/busy-times": {
"get": {
"operationId": "CalendarsController_getBusyTimes",
"parameters": [],
@@ -1160,7 +1160,7 @@
]
}
},
"/v2/ee/calendars": {
"/api/v2/ee/calendars": {
"get": {
"operationId": "CalendarsController_getCalendars",
"parameters": [],
@@ -1181,7 +1181,7 @@
]
}
},
"/v2/ee/bookings": {
"/api/v2/ee/bookings": {
"get": {
"operationId": "BookingsController_getBookings",
"parameters": [
@@ -1235,16 +1235,7 @@
},
"post": {
"operationId": "BookingsController_createBooking",
"parameters": [
{
"name": "x-cal-client-id",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
}
],
"parameters": [],
"requestBody": {
"required": true,
"content": {
@@ -1272,7 +1263,7 @@
]
}
},
"/v2/ee/bookings/{bookingUid}": {
"/api/v2/ee/bookings/{bookingUid}": {
"get": {
"operationId": "BookingsController_getBooking",
"parameters": [
@@ -1302,7 +1293,7 @@
]
}
},
"/v2/ee/bookings/{bookingUid}/reschedule": {
"/api/v2/ee/bookings/{bookingUid}/reschedule": {
"get": {
"operationId": "BookingsController_getBookingForReschedule",
"parameters": [
@@ -1332,7 +1323,7 @@
]
}
},
"/v2/ee/bookings/{bookingId}/cancel": {
"/api/v2/ee/bookings/{bookingId}/cancel": {
"post": {
"operationId": "BookingsController_cancelBooking",
"parameters": [
@@ -1343,14 +1334,6 @@
"schema": {
"type": "string"
}
},
{
"name": "x-cal-client-id",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
}
],
"requestBody": {
@@ -1380,19 +1363,10 @@
]
}
},
"/v2/ee/bookings/reccuring": {
"/api/v2/ee/bookings/reccuring": {
"post": {
"operationId": "BookingsController_createReccuringBooking",
"parameters": [
{
"name": "x-cal-client-id",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
}
],
"parameters": [],
"requestBody": {
"required": true,
"content": {
@@ -1423,19 +1397,10 @@
]
}
},
"/v2/ee/bookings/instant": {
"/api/v2/ee/bookings/instant": {
"post": {
"operationId": "BookingsController_createInstantBooking",
"parameters": [
{
"name": "x-cal-client-id",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
}
],
"parameters": [],
"requestBody": {
"required": true,
"content": {
@@ -1463,7 +1428,7 @@
]
}
},
"/v2/slots/reserve": {
"/api/v2/slots/reserve": {
"post": {
"operationId": "SlotsController_reserveSlot",
"parameters": [],
@@ -1494,7 +1459,7 @@
]
}
},
"/v2/slots/selected-slot": {
"/api/v2/slots/selected-slot": {
"delete": {
"operationId": "SlotsController_deleteSelectedSlot",
"parameters": [],
@@ -1515,7 +1480,7 @@
]
}
},
"/v2/slots/available": {
"/api/v2/slots/available": {
"get": {
"operationId": "SlotsController_getAvailableSlots",
"parameters": [],
@@ -1536,7 +1501,7 @@
]
}
},
"/v2/timezones": {
"/api/v2/timezones": {
"get": {
"operationId": "TimezonesController_getTimeZones",
"parameters": [],
@@ -2184,6 +2149,462 @@
"data"
]
},
"Location": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
},
"required": [
"type"
]
},
"Source": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"label": {
"type": "string"
}
},
"required": [
"id",
"type",
"label"
]
},
"BookingField": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"defaultLabel": {
"type": "string"
},
"label": {
"type": "string"
},
"placeholder": {
"type": "string"
},
"required": {
"type": "boolean"
},
"getOptionsAt": {
"type": "string"
},
"hideWhenJustOneOption": {
"type": "boolean"
},
"editable": {
"type": "string"
},
"sources": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Source"
}
}
},
"required": [
"name",
"type"
]
},
"Organization": {
"type": "object",
"properties": {
"id": {
"type": "number"
},
"slug": {
"type": "string",
"nullable": true
},
"name": {
"type": "string"
},
"metadata": {
"type": "object"
}
},
"required": [
"id",
"name",
"metadata"
]
},
"Profile": {
"type": "object",
"properties": {
"username": {
"type": "string",
"nullable": true
},
"id": {
"type": "number",
"nullable": true
},
"userId": {
"type": "number"
},
"uid": {
"type": "string"
},
"name": {
"type": "string"
},
"organizationId": {
"type": "number",
"nullable": true
},
"organization": {
"nullable": true,
"allOf": [
{
"$ref": "#/components/schemas/Organization"
}
]
},
"upId": {
"type": "string"
},
"image": {
"type": "string"
},
"brandColor": {
"type": "string"
},
"darkBrandColor": {
"type": "string"
},
"theme": {
"type": "string"
},
"bookerLayouts": {
"type": "object"
}
},
"required": [
"username",
"id",
"organizationId",
"upId"
]
},
"Owner": {
"type": "object",
"properties": {
"id": {
"type": "number"
},
"avatarUrl": {
"type": "string",
"nullable": true
},
"username": {
"type": "string",
"nullable": true
},
"name": {
"type": "string",
"nullable": true
},
"weekStart": {
"type": "string"
},
"brandColor": {
"type": "string",
"nullable": true
},
"darkBrandColor": {
"type": "string",
"nullable": true
},
"theme": {
"type": "string",
"nullable": true
},
"metadata": {
"type": "object"
},
"defaultScheduleId": {
"type": "number",
"nullable": true
},
"nonProfileUsername": {
"type": "string",
"nullable": true
},
"profile": {
"$ref": "#/components/schemas/Profile"
}
},
"required": [
"id",
"username",
"name",
"weekStart",
"metadata",
"nonProfileUsername",
"profile"
]
},
"Schedule": {
"type": "object",
"properties": {
"id": {
"type": "number"
},
"timeZone": {
"type": "string",
"nullable": true
}
},
"required": [
"id",
"timeZone"
]
},
"User": {
"type": "object",
"properties": {
"username": {
"type": "string",
"nullable": true
},
"name": {
"type": "string",
"nullable": true
},
"weekStart": {
"type": "string"
},
"organizationId": {
"type": "number"
},
"avatarUrl": {
"type": "string",
"nullable": true
},
"profile": {
"$ref": "#/components/schemas/Profile"
},
"bookerUrl": {
"type": "string"
}
},
"required": [
"username",
"name",
"weekStart",
"profile",
"bookerUrl"
]
},
"PublicEventTypeOutput": {
"type": "object",
"properties": {
"id": {
"type": "number"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"eventName": {
"type": "string",
"nullable": true
},
"slug": {
"type": "string"
},
"isInstantEvent": {
"type": "boolean"
},
"aiPhoneCallConfig": {
"type": "object"
},
"schedulingType": {
"type": "object"
},
"length": {
"type": "number"
},
"locations": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Location"
}
},
"customInputs": {
"type": "array",
"items": {
"type": "object"
}
},
"disableGuests": {
"type": "boolean"
},
"metadata": {
"type": "object",
"nullable": true
},
"lockTimeZoneToggleOnBookingPage": {
"type": "boolean"
},
"requiresConfirmation": {
"type": "boolean"
},
"requiresBookerEmailVerification": {
"type": "boolean"
},
"recurringEvent": {
"type": "object"
},
"price": {
"type": "number"
},
"currency": {
"type": "string"
},
"seatsPerTimeSlot": {
"type": "number",
"nullable": true
},
"seatsShowAvailabilityCount": {
"type": "boolean",
"nullable": true
},
"bookingFields": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BookingField"
}
},
"team": {
"type": "object"
},
"successRedirectUrl": {
"type": "string",
"nullable": true
},
"workflows": {
"type": "array",
"items": {
"type": "object"
}
},
"hosts": {
"type": "array",
"items": {
"type": "object"
}
},
"owner": {
"nullable": true,
"allOf": [
{
"$ref": "#/components/schemas/Owner"
}
]
},
"schedule": {
"nullable": true,
"allOf": [
{
"$ref": "#/components/schemas/Schedule"
}
]
},
"hidden": {
"type": "boolean"
},
"assignAllTeamMembers": {
"type": "boolean"
},
"bookerLayouts": {
"type": "object"
},
"users": {
"type": "array",
"items": {
"$ref": "#/components/schemas/User"
}
},
"entity": {
"type": "object"
},
"isDynamic": {
"type": "boolean"
}
},
"required": [
"id",
"title",
"description",
"slug",
"isInstantEvent",
"length",
"locations",
"customInputs",
"disableGuests",
"metadata",
"lockTimeZoneToggleOnBookingPage",
"requiresConfirmation",
"requiresBookerEmailVerification",
"price",
"currency",
"seatsShowAvailabilityCount",
"bookingFields",
"workflows",
"hosts",
"owner",
"schedule",
"hidden",
"assignAllTeamMembers",
"users",
"entity",
"isDynamic"
]
},
"GetEventTypePublicOutput": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "success",
"enum": [
"success",
"error"
]
},
"data": {
"nullable": true,
"allOf": [
{
"$ref": "#/components/schemas/PublicEventTypeOutput"
}
]
}
},
"required": [
"status",
"data"
]
},
"PublicEventType": {
"type": "object",
"properties": {
@@ -3404,26 +3825,6 @@
"credentialId"
]
},
"User": {
"type": "object",
"properties": {
"id": {
"type": "number"
},
"name": {
"type": "string",
"nullable": true
},
"email": {
"type": "string"
}
},
"required": [
"id",
"name",
"email"
]
},
"GetBookingsDataEntry": {
"type": "object",
"properties": {
@@ -3710,21 +4111,6 @@
"data"
]
},
"Location": {
"type": "object",
"properties": {
"optionValue": {
"type": "string"
},
"value": {
"type": "string"
}
},
"required": [
"optionValue",
"value"
]
},
"Response": {
"type": "object",
"properties": {
@@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query";
import { shallow } from "zustand/shallow";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { SUCCESS_STATUS, V2_ENDPOINTS } from "@calcom/platform-constants";
import type { PublicEventType } from "@calcom/platform-libraries";
import type { ApiResponse } from "@calcom/platform-types";
@@ -16,18 +16,22 @@ export const usePublicEvent = (props: { username: string; eventSlug: string }) =
const isTeamEvent = useBookerStore((state) => state.isTeamEvent);
const org = useBookerStore((state) => state.org);
const requestUsername = username ?? props.username;
const requestEventSlug = eventSlug ?? props.eventSlug;
const event = useQuery({
queryKey: [QUERY_KEY, username ?? props.username, eventSlug ?? props.eventSlug],
queryFn: () => {
return http
.get<ApiResponse<PublicEventType>>("/events/public", {
params: {
username: username ?? props.username,
eventSlug: eventSlug ?? props.eventSlug,
isTeamEvent,
org: org ?? null,
},
})
.get<ApiResponse<PublicEventType>>(
`/${V2_ENDPOINTS.eventTypes}/${requestUsername}/${requestEventSlug}/public`,
{
params: {
isTeamEvent,
org: org ?? null,
},
}
)
.then((res) => {
if (res.data.status === SUCCESS_STATUS) {
return res.data.data;
-1
View File
@@ -1,7 +1,6 @@
export * from "./permissions";
export * from "./api";
export * from "./oauth-clients";
export * from "./events";
export * from "./slots";
export * from "./calendars";
export * from "./schedules";