From 4a53ec0b745b0bd129d645781e411e022f80fa9f Mon Sep 17 00:00:00 2001 From: Lauris Skraucis Date: Fri, 13 Jun 2025 16:15:47 +0200 Subject: [PATCH] feat: platform toggle calendar event creation (#21788) * feat: add areCalendarEventsEnabled to PlatformOAuthClient table * feat: areCalendarEvents enabled in handleNewBooking and v2 * feat: v2 handle areCalendarEventsEnabled * feat: toggle areCalendarEventsEnabled on frontend * feat: enable areCalendarEventsEnabled for recurring,confirm,paid * refactor: use placeholder calendar event * remove merge conflicts * chore: bump libraries * chore: bump libraries --- apps/api/v2/package.json | 2 +- .../controllers/bookings.controller.ts | 6 +- .../controllers/e2e/user-bookings.e2e-spec.ts | 293 ++++++++++++++++++ .../2024-08-13/services/bookings.service.ts | 5 + .../2024-08-13/services/input.service.ts | 1 + .../shared/platform-bookings.service.ts | 2 + .../oauth-clients-output.service.ts | 1 + apps/api/v2/swagger/documentation.json | 17 +- .../oauth-client.repository.fixture.ts | 7 + .../dashboard/oauth-clients-list/index.tsx | 1 + .../oauth-clients/OAuthClientCard.tsx | 6 + .../oauth-clients/oauth-client-form/index.tsx | 22 ++ .../[clientId]/edit/edit-view.tsx | 2 + .../oauth-clients/create-new-view.tsx | 20 +- docs/api-reference/v2/openapi.json | 17 +- .../bookings/lib/handleConfirmation.ts | 5 +- .../features/bookings/lib/handleNewBooking.ts | 6 +- .../bookings/lib/handleNewRecurringBooking.ts | 2 + packages/features/ee/payments/api/webhook.ts | 18 +- .../get-platform-params.ts | 12 + ...tform-oauth-client.repository.interface.ts | 5 + .../platform-oauth-client.repository.ts | 24 ++ packages/lib/EventManager.ts | 2 + packages/lib/payment/getBooking.ts | 1 + packages/lib/payment/handlePaymentSuccess.ts | 19 +- packages/platform/libraries/event-types.ts | 4 +- .../inputs/create-oauth-client.input.ts | 14 + .../inputs/update-oauth-client.input.ts | 13 +- .../outputs/oauth-client.output.ts | 6 + .../migration.sql | 2 + packages/prisma/schema.prisma | 1 + packages/prisma/zod-utils.ts | 1 + yarn.lock | 10 +- 33 files changed, 525 insertions(+), 22 deletions(-) create mode 100644 packages/features/platform-oauth-client/get-platform-params.ts create mode 100644 packages/features/platform-oauth-client/platform-oauth-client.repository.interface.ts create mode 100644 packages/features/platform-oauth-client/platform-oauth-client.repository.ts create mode 100644 packages/prisma/migrations/20250611095054_add_are_calendar_events_enabled_for_oauth_client/migration.sql diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json index 6e55f6588c..a9a14c26a8 100644 --- a/apps/api/v2/package.json +++ b/apps/api/v2/package.json @@ -37,7 +37,7 @@ "@axiomhq/winston": "^1.2.0", "@calcom/platform-constants": "*", "@calcom/platform-enums": "*", - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.220", + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.222", "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2", "@calcom/platform-types": "*", "@calcom/platform-utils": "*", diff --git a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts index f279e3f481..7e21b011ff 100644 --- a/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts +++ b/apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts @@ -77,6 +77,7 @@ type OAuthRequestParams = { platformBookingUrl: string; platformBookingLocation?: string; arePlatformEmailsEnabled: boolean; + areCalendarEventsEnabled: boolean; }; const DEFAULT_PLATFORM_PARAMS = { @@ -86,6 +87,7 @@ const DEFAULT_PLATFORM_PARAMS = { platformBookingUrl: "", arePlatformEmailsEnabled: false, platformBookingLocation: undefined, + areCalendarEventsEnabled: false, }; @Controller({ @@ -195,6 +197,7 @@ export class BookingsController_2024_04_15 { platformCancelUrl: bookingRequest.platformCancelUrl, platformBookingUrl: bookingRequest.platformBookingUrl, platformBookingLocation: bookingRequest.platformBookingLocation, + areCalendarEventsEnabled: bookingRequest.areCalendarEventsEnabled, }); if (booking.userId && booking.uid && booking.startTime) { void (await this.billingService.increaseUsageByUserId(booking.userId, { @@ -428,7 +431,7 @@ export class BookingsController_2024_04_15 { if (isEmbed) { // embed should ignore oauth client settings and enable emails by default - return { ...res, arePlatformEmailsEnabled: true }; + return { ...res, arePlatformEmailsEnabled: true, areCalendarEventsEnabled: true }; } try { @@ -440,6 +443,7 @@ export class BookingsController_2024_04_15 { res.platformRescheduleUrl = client.bookingRescheduleRedirectUri ?? ""; res.platformBookingUrl = client.bookingRedirectUri ?? ""; res.arePlatformEmailsEnabled = client.areEmailsEnabled ?? false; + res.areCalendarEventsEnabled = client.areCalendarEventsEnabled; } return res; } catch (err) { diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts index f06465cc20..e57b2a93f4 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts @@ -35,6 +35,7 @@ import { X_CAL_CLIENT_ID, } from "@calcom/platform-constants"; import { AttendeeScheduledEmail, OrganizerScheduledEmail } from "@calcom/platform-libraries/emails"; +import { EventManager } from "@calcom/platform-libraries/event-types"; import { CreateEventTypeInput_2024_06_14, FAILED_EVENT_TYPE_IDENTIFICATION_ERROR_MESSAGE, @@ -78,6 +79,8 @@ describe("Bookings Endpoints 2024-08-13", () => { const eventTypeSlug = `user-bookings-event-type-${randomString()}`; let recurringEventTypeId: number; const recurringEventTypeSlug = `user-bookings-event-type-${randomString()}`; + let eventTypeRequiresConfirmationId: number; + const eventTypeRequiresConfirmationSlug = `user-bookings-event-type-requires-confirmation-${randomString()}`; let createdBooking: BookingOutput_2024_08_13; let rescheduledBooking: BookingOutput_2024_08_13; @@ -153,6 +156,17 @@ describe("Bookings Endpoints 2024-08-13", () => { ); recurringEventTypeId = recurringEvent.id; + const eventTypeRequiresConfirmation = await eventTypesRepositoryFixture.create( + { + title: `user-bookings-2024-08-13-event-type-requires-confirmation-${randomString()}`, + slug: eventTypeRequiresConfirmationSlug, + length: 60, + requiresConfirmation: true, + }, + user.id + ); + eventTypeRequiresConfirmationId = eventTypeRequiresConfirmation.id; + bookingInThePast = await bookingsRepositoryFixture.create({ user: { connect: { @@ -2539,6 +2553,285 @@ describe("Bookings Endpoints 2024-08-13", () => { }); }); + describe("calendar events", () => { + beforeEach(() => { + jest.restoreAllMocks(); + jest + .spyOn(EventManager.prototype, "create") + .mockImplementation(() => Promise.resolve({ results: [], referencesToCreate: [] })); + }); + + describe("platform oAuth client has calendar events enabled", () => { + beforeAll(async () => { + await oauthClientRepositoryFixture.update(oAuthClient.id, { + areCalendarEventsEnabled: true, + }); + }); + + it("should create calendar event when booking normal event", async () => { + const body: CreateBookingInput_2024_08_13 = { + start: new Date(Date.UTC(2040, 0, 9, 9, 0, 0)).toISOString(), + eventTypeId, + attendee: { + name: "Mr Proper", + email: "mr_proper@gmail.com", + timeZone: "Europe/Rome", + language: "it", + }, + }; + + return request(app.getHttpServer()) + .post("/v2/bookings") + .send(body) + .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) + .expect(201) + .then(async (response) => { + const responseBody: CreateBookingOutput_2024_08_13 = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(responseDataIsBooking(responseBody.data)).toBe(true); + + if (responseDataIsBooking(responseBody.data)) { + const data: BookingOutput_2024_08_13 = responseBody.data; + expect(data.id).toBeDefined(); + expect(data.uid).toBeDefined(); + expect(EventManager.prototype.create).toHaveBeenCalledTimes(1); + } else { + throw new Error( + "Invalid response data - expected booking but received array of possibly recurring bookings" + ); + } + }); + }); + + it("should create calendar events when booking recurring event", async () => { + const body: CreateBookingInput_2024_08_13 = { + start: new Date(Date.UTC(2040, 0, 10, 9, 0, 0)).toISOString(), + eventTypeId: recurringEventTypeId, + attendee: { + name: "Mr Proper", + email: "mr_proper@gmail.com", + timeZone: "Europe/Rome", + language: "it", + }, + }; + + return request(app.getHttpServer()) + .post("/v2/bookings") + .send(body) + .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) + .expect(201) + .then(async (response) => { + const responseBody: CreateBookingOutput_2024_08_13 = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(EventManager.prototype.create).toHaveBeenCalledTimes(3); + }); + }); + + describe("event type requires confirmation", () => { + let bookingThatRequiresConfirmationUid: string; + + it("should not create calendar event when booking event that requires confirmation", async () => { + const body: CreateBookingInput_2024_08_13 = { + start: new Date(Date.UTC(2040, 0, 12, 9, 0, 0)).toISOString(), + eventTypeId: eventTypeRequiresConfirmationId, + attendee: { + name: "Mr Proper", + email: "mr_proper@gmail.com", + timeZone: "Europe/Rome", + language: "it", + }, + }; + + return request(app.getHttpServer()) + .post("/v2/bookings") + .send(body) + .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) + .expect(201) + .then(async (response) => { + const responseBody: CreateBookingOutput_2024_08_13 = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(responseDataIsBooking(responseBody.data)).toBe(true); + + if (responseDataIsBooking(responseBody.data)) { + const data: BookingOutput_2024_08_13 = responseBody.data; + expect(data.id).toBeDefined(); + expect(data.uid).toBeDefined(); + expect(EventManager.prototype.create).toHaveBeenCalledTimes(0); + bookingThatRequiresConfirmationUid = data.uid; + } else { + throw new Error( + "Invalid response data - expected booking but received array of possibly recurring bookings" + ); + } + }); + }); + + it("should create calendar event when confirming event that requires confirmation", async () => { + return request(app.getHttpServer()) + .post(`/v2/bookings/${bookingThatRequiresConfirmationUid}/confirm`) + .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) + .expect(200) + .then(async (response) => { + const responseBody: CreateBookingOutput_2024_08_13 = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(responseDataIsBooking(responseBody.data)).toBe(true); + + if (responseDataIsBooking(responseBody.data)) { + const data: BookingOutput_2024_08_13 = responseBody.data; + expect(data.id).toBeDefined(); + expect(data.uid).toBeDefined(); + expect(EventManager.prototype.create).toHaveBeenCalledTimes(1); + } else { + throw new Error( + "Invalid response data - expected booking but received array of possibly recurring bookings" + ); + } + }); + }); + }); + }); + + describe("platform oAuth client has calendar events disabled", () => { + beforeAll(async () => { + await oauthClientRepositoryFixture.update(oAuthClient.id, { + areCalendarEventsEnabled: false, + }); + }); + + it("should not create calendar event when booking", async () => { + const body: CreateBookingInput_2024_08_13 = { + start: new Date(Date.UTC(2040, 0, 9, 10, 0, 0)).toISOString(), + eventTypeId, + attendee: { + name: "Mr Proper", + email: "mr_proper@gmail.com", + timeZone: "Europe/Rome", + language: "it", + }, + }; + + const beforeCreate = new Date(); + return request(app.getHttpServer()) + .post("/v2/bookings") + .send(body) + .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) + .expect(201) + .then(async (response) => { + const afterCreate = new Date(); + const responseBody: CreateBookingOutput_2024_08_13 = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(responseDataIsBooking(responseBody.data)).toBe(true); + + if (responseDataIsBooking(responseBody.data)) { + const data: BookingOutput_2024_08_13 = responseBody.data; + expect(data.id).toBeDefined(); + expect(data.uid).toBeDefined(); + expect(EventManager.prototype.create).toHaveBeenCalledTimes(0); + } else { + throw new Error( + "Invalid response data - expected booking but received array of possibly recurring bookings" + ); + } + }); + }); + + it("should not create calendar events when booking recurring event", async () => { + const body: CreateBookingInput_2024_08_13 = { + start: new Date(Date.UTC(2040, 0, 11, 10, 0, 0)).toISOString(), + eventTypeId: recurringEventTypeId, + attendee: { + name: "Mr Proper", + email: "mr_proper@gmail.com", + timeZone: "Europe/Rome", + language: "it", + }, + }; + + return request(app.getHttpServer()) + .post("/v2/bookings") + .send(body) + .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) + .expect(201) + .then(async (response) => { + const responseBody: CreateBookingOutput_2024_08_13 = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(EventManager.prototype.create).toHaveBeenCalledTimes(0); + }); + }); + + describe("event type requires confirmation", () => { + let bookingThatRequiresConfirmationUid: string; + + it("should not create calendar event when booking event that requires confirmation", async () => { + const body: CreateBookingInput_2024_08_13 = { + start: new Date(Date.UTC(2040, 0, 12, 10, 0, 0)).toISOString(), + eventTypeId: eventTypeRequiresConfirmationId, + attendee: { + name: "Mr Proper", + email: "mr_proper@gmail.com", + timeZone: "Europe/Rome", + language: "it", + }, + }; + + return request(app.getHttpServer()) + .post("/v2/bookings") + .send(body) + .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) + .expect(201) + .then(async (response) => { + const responseBody: CreateBookingOutput_2024_08_13 = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(responseDataIsBooking(responseBody.data)).toBe(true); + + if (responseDataIsBooking(responseBody.data)) { + const data: BookingOutput_2024_08_13 = responseBody.data; + expect(data.id).toBeDefined(); + expect(data.uid).toBeDefined(); + expect(EventManager.prototype.create).toHaveBeenCalledTimes(0); + bookingThatRequiresConfirmationUid = data.uid; + } else { + throw new Error( + "Invalid response data - expected booking but received array of possibly recurring bookings" + ); + } + }); + }); + + it("should not create calendar event when confirming event that requires confirmation", async () => { + return request(app.getHttpServer()) + .post(`/v2/bookings/${bookingThatRequiresConfirmationUid}/confirm`) + .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) + .expect(200) + .then(async (response) => { + const responseBody: CreateBookingOutput_2024_08_13 = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + expect(responseBody.data).toBeDefined(); + expect(responseDataIsBooking(responseBody.data)).toBe(true); + + if (responseDataIsBooking(responseBody.data)) { + const data: BookingOutput_2024_08_13 = responseBody.data; + expect(data.id).toBeDefined(); + expect(data.uid).toBeDefined(); + expect(EventManager.prototype.create).toHaveBeenCalledTimes(0); + } else { + throw new Error( + "Invalid response data - expected booking but received array of possibly recurring bookings" + ); + } + }); + }); + }); + }); + }); + function responseDataIsRecurranceBooking(data: any): data is RecurringBookingOutput_2024_08_13 { return ( !Array.isArray(data) && diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts index 342e99f458..ce26ba6b62 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts @@ -404,6 +404,7 @@ export class BookingsService_2024_08_13 { platformBookingUrl: bookingRequest.platformBookingUrl, platformBookingLocation: bookingRequest.platformBookingLocation, noEmail: bookingRequest.noEmail, + areCalendarEventsEnabled: bookingRequest.areCalendarEventsEnabled, }); const ids = bookings.map((booking) => booking.id || 0); return this.outputService.getOutputRecurringBookings(ids); @@ -424,6 +425,7 @@ export class BookingsService_2024_08_13 { platformCancelUrl: bookingRequest.platformCancelUrl, platformBookingUrl: bookingRequest.platformBookingUrl, platformBookingLocation: bookingRequest.platformBookingLocation, + areCalendarEventsEnabled: bookingRequest.areCalendarEventsEnabled, }); return this.outputService.getOutputCreateRecurringSeatedBookings( bookings.map((booking) => ({ uid: booking.uid || "", seatUid: booking.seatReferenceUid || "" })) @@ -445,6 +447,7 @@ export class BookingsService_2024_08_13 { platformCancelUrl: bookingRequest.platformCancelUrl, platformBookingUrl: bookingRequest.platformBookingUrl, platformBookingLocation: bookingRequest.platformBookingLocation, + areCalendarEventsEnabled: bookingRequest.areCalendarEventsEnabled, }); if (!booking.uid) { @@ -475,6 +478,7 @@ export class BookingsService_2024_08_13 { platformCancelUrl: bookingRequest.platformCancelUrl, platformBookingUrl: bookingRequest.platformBookingUrl, platformBookingLocation: bookingRequest.platformBookingLocation, + areCalendarEventsEnabled: bookingRequest.areCalendarEventsEnabled, }); if (!booking.uid) { @@ -652,6 +656,7 @@ export class BookingsService_2024_08_13 { platformCancelUrl: bookingRequest.platformCancelUrl, platformBookingUrl: bookingRequest.platformBookingUrl, platformBookingLocation: bookingRequest.platformBookingLocation, + areCalendarEventsEnabled: bookingRequest.areCalendarEventsEnabled, }); if (!booking.uid) { throw new Error("Booking missing uid"); diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts b/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts index 0e33ec40c7..e7905c0e98 100644 --- a/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts +++ b/apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts @@ -59,6 +59,7 @@ type OAuthRequestParams = { platformBookingUrl: string; platformBookingLocation?: string; arePlatformEmailsEnabled: boolean; + areCalendarEventsEnabled: boolean; }; export enum Frequency { diff --git a/apps/api/v2/src/ee/bookings/shared/platform-bookings.service.ts b/apps/api/v2/src/ee/bookings/shared/platform-bookings.service.ts index 6873221308..756100dcbb 100644 --- a/apps/api/v2/src/ee/bookings/shared/platform-bookings.service.ts +++ b/apps/api/v2/src/ee/bookings/shared/platform-bookings.service.ts @@ -54,6 +54,7 @@ export class PlatformBookingsService { platformRescheduleUrl: oAuthClient.bookingRescheduleRedirectUri, platformBookingUrl: oAuthClient.bookingRedirectUri, arePlatformEmailsEnabled: oAuthClient.areEmailsEnabled, + areCalendarEventsEnabled: oAuthClient.areCalendarEventsEnabled, }; } @@ -78,6 +79,7 @@ export class PlatformBookingsService { platformRescheduleUrl: oAuthClient.bookingRescheduleRedirectUri, platformBookingUrl: oAuthClient.bookingRedirectUri, arePlatformEmailsEnabled: oAuthClient.areEmailsEnabled, + areCalendarEventsEnabled: oAuthClient.areCalendarEventsEnabled, }; } diff --git a/apps/api/v2/src/modules/oauth-clients/services/oauth-clients/oauth-clients-output.service.ts b/apps/api/v2/src/modules/oauth-clients/services/oauth-clients/oauth-clients-output.service.ts index 9aefacd52d..c5f9447065 100644 --- a/apps/api/v2/src/modules/oauth-clients/services/oauth-clients/oauth-clients-output.service.ts +++ b/apps/api/v2/src/modules/oauth-clients/services/oauth-clients/oauth-clients-output.service.ts @@ -25,6 +25,7 @@ export class OAuthClientsOutputService { bookingRescheduleRedirectUri: client.bookingRescheduleRedirectUri ?? undefined, areEmailsEnabled: client.areEmailsEnabled, areDefaultEventTypesEnabled: client.areDefaultEventTypesEnabled, + areCalendarEventsEnabled: client.areCalendarEventsEnabled, }; } diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index 71e0cb3d29..3d35b87293 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -13912,6 +13912,11 @@ "type": "boolean", "default": false, "description": "If true, when creating a managed user the managed user will have 4 default event types: 30 and 60 minutes without Cal video, 30 and 60 minutes with Cal video. Set this as false if you want to create a managed user and then manually create event types for the user." + }, + "areCalendarEventsEnabled": { + "type": "boolean", + "default": true, + "description": "If true and if managed user has calendar connected, calendar events will be created. Disable it if you manually create calendar events. Default to true." } }, "required": [ @@ -14034,6 +14039,11 @@ "example": true, "description": "If enabled, when creating a managed user the managed user will have 4 default event types: 30 and 60 minutes without Cal video, 30 and 60 minutes with Cal video. Leave this disabled if you want to create a managed user and then manually create event types for the user." }, + "areCalendarEventsEnabled": { + "type": "boolean", + "example": true, + "description": "If true and if managed user has calendar connected, calendar events will be created. Disable it if you manually create calendar events. Default to true." + }, "bookingRedirectUri": { "type": "string", "example": "https://example.com/booking-redirect" @@ -14056,7 +14066,8 @@ "organizationId", "createdAt", "areEmailsEnabled", - "areDefaultEventTypesEnabled" + "areDefaultEventTypesEnabled", + "areCalendarEventsEnabled" ] }, "GetOAuthClientsResponseDto": { @@ -14132,6 +14143,10 @@ "areDefaultEventTypesEnabled": { "type": "boolean", "description": "If true, when creating a managed user the managed user will have 4 default event types: 30 and 60 minutes without Cal video, 30 and 60 minutes with Cal video. Set this as false if you want to create a managed user and then manually create event types for the user." + }, + "areCalendarEventsEnabled": { + "type": "boolean", + "description": "If true and if managed user has calendar connected, calendar events will be created. Disable it if you manually create calendar events. Default to true." } } }, diff --git a/apps/api/v2/test/fixtures/repository/oauth-client.repository.fixture.ts b/apps/api/v2/test/fixtures/repository/oauth-client.repository.fixture.ts index c76acc2f2f..0c89a7a29a 100644 --- a/apps/api/v2/test/fixtures/repository/oauth-client.repository.fixture.ts +++ b/apps/api/v2/test/fixtures/repository/oauth-client.repository.fixture.ts @@ -47,6 +47,13 @@ export class OAuthClientRepositoryFixture { }); } + async update(clientId: PlatformOAuthClient["id"], data: Prisma.PlatformOAuthClientUpdateInput) { + return this.prismaWriteClient.platformOAuthClient.update({ + where: { id: clientId }, + data, + }); + } + async delete(clientId: PlatformOAuthClient["id"]) { return this.prismaWriteClient.platformOAuthClient.delete({ where: { id: clientId } }); } diff --git a/apps/web/components/settings/platform/dashboard/oauth-clients-list/index.tsx b/apps/web/components/settings/platform/dashboard/oauth-clients-list/index.tsx index 6dae578f6f..bfbf086ae4 100644 --- a/apps/web/components/settings/platform/dashboard/oauth-clients-list/index.tsx +++ b/apps/web/components/settings/platform/dashboard/oauth-clients-list/index.tsx @@ -48,6 +48,7 @@ export const OAuthClientsList = ({ oauthClients, isDeleting, handleDelete }: OAu onDelete={handleDelete} areEmailsEnabled={client.areEmailsEnabled} areDefaultEventTypesEnabled={client.areDefaultEventTypesEnabled} + areCalendarEventsEnabled={client.areCalendarEventsEnabled} organizationId={client.organizationId} /> ); diff --git a/apps/web/components/settings/platform/oauth-clients/OAuthClientCard.tsx b/apps/web/components/settings/platform/oauth-clients/OAuthClientCard.tsx index 2eb83e9fc1..bc5d9bd2da 100644 --- a/apps/web/components/settings/platform/oauth-clients/OAuthClientCard.tsx +++ b/apps/web/components/settings/platform/oauth-clients/OAuthClientCard.tsx @@ -18,6 +18,7 @@ type OAuthClientCardProps = { bookingRescheduleRedirectUri: string | null | undefined; areEmailsEnabled: boolean | undefined; areDefaultEventTypesEnabled: boolean; + areCalendarEventsEnabled: boolean; permissions: Array; lastItem: boolean; id: string; @@ -42,6 +43,7 @@ export const OAuthClientCard = ({ isLoading, areEmailsEnabled, areDefaultEventTypesEnabled, + areCalendarEventsEnabled, organizationId, }: OAuthClientCardProps) => { const router = useRouter(); @@ -173,6 +175,10 @@ export const OAuthClientCard = ({ Default event types enabled:{" "} {areDefaultEventTypesEnabled ? "Yes" : "No"} +
+ Calendar events enabled:{" "} + {areCalendarEventsEnabled ? "Yes" : "No"} +
+
+
+ + + +
+
+
+
+
({ uri })) ?? [{ uri: "" }], bookingRedirectUri: data?.bookingRedirectUri ?? "", bookingCancelRedirectUri: data?.bookingCancelRedirectUri ?? "", diff --git a/apps/web/modules/settings/platform/oauth-clients/create-new-view.tsx b/apps/web/modules/settings/platform/oauth-clients/create-new-view.tsx index 15e2e8b420..3a931714cb 100644 --- a/apps/web/modules/settings/platform/oauth-clients/create-new-view.tsx +++ b/apps/web/modules/settings/platform/oauth-clients/create-new-view.tsx @@ -56,6 +56,7 @@ export default function CreateOAuthClient() { bookingRescheduleRedirectUri: data.bookingRescheduleRedirectUri, areEmailsEnabled: data.areEmailsEnabled, areDefaultEventTypesEnabled: data.areDefaultEventTypesEnabled, + areCalendarEventsEnabled: data.areCalendarEventsEnabled, }); }; @@ -76,7 +77,24 @@ export default function CreateOAuthClient() {

- + diff --git a/docs/api-reference/v2/openapi.json b/docs/api-reference/v2/openapi.json index e248185be3..91b1fa7de2 100644 --- a/docs/api-reference/v2/openapi.json +++ b/docs/api-reference/v2/openapi.json @@ -13254,6 +13254,11 @@ "type": "boolean", "default": false, "description": "If true, when creating a managed user the managed user will have 4 default event types: 30 and 60 minutes without Cal video, 30 and 60 minutes with Cal video. Set this as false if you want to create a managed user and then manually create event types for the user." + }, + "areCalendarEventsEnabled": { + "type": "boolean", + "default": true, + "description": "If true and if managed user has calendar connected, calendar events will be created. Disable it if you manually create calendar events. Default to true." } }, "required": ["name", "redirectUris", "permissions"] @@ -13358,6 +13363,11 @@ "example": true, "description": "If enabled, when creating a managed user the managed user will have 4 default event types: 30 and 60 minutes without Cal video, 30 and 60 minutes with Cal video. Leave this disabled if you want to create a managed user and then manually create event types for the user." }, + "areCalendarEventsEnabled": { + "type": "boolean", + "example": true, + "description": "If true and if managed user has calendar connected, calendar events will be created. Disable it if you manually create calendar events. Default to true." + }, "bookingRedirectUri": { "type": "string", "example": "https://example.com/booking-redirect" @@ -13380,7 +13390,8 @@ "organizationId", "createdAt", "areEmailsEnabled", - "areDefaultEventTypesEnabled" + "areDefaultEventTypesEnabled", + "areCalendarEventsEnabled" ] }, "GetOAuthClientsResponseDto": { @@ -13444,6 +13455,10 @@ "areDefaultEventTypesEnabled": { "type": "boolean", "description": "If true, when creating a managed user the managed user will have 4 default event types: 30 and 60 minutes without Cal video, 30 and 60 minutes with Cal video. Set this as false if you want to create a managed user and then manually create event types for the user." + }, + "areCalendarEventsEnabled": { + "type": "boolean", + "description": "If true and if managed user has calendar connected, calendar events will be created. Disable it if you manually create calendar events. Default to true." } } }, diff --git a/packages/features/bookings/lib/handleConfirmation.ts b/packages/features/bookings/lib/handleConfirmation.ts index 01acd9e779..853a0bdd5f 100644 --- a/packages/features/bookings/lib/handleConfirmation.ts +++ b/packages/features/bookings/lib/handleConfirmation.ts @@ -14,7 +14,7 @@ import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload"; import type { EventPayloadType, EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload"; import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser"; import type { EventManagerUser } from "@calcom/lib/EventManager"; -import EventManager from "@calcom/lib/EventManager"; +import EventManager, { placeholderCreatedEvent } from "@calcom/lib/EventManager"; import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server"; import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId"; import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType"; @@ -88,7 +88,8 @@ export async function handleConfirmation(args: { const eventTypeMetadata = EventTypeMetaDataSchema.parse(eventType?.metadata || {}); const apps = eventTypeAppMetadataOptionalSchema.parse(eventTypeMetadata?.apps); const eventManager = new EventManager(user, apps); - const scheduleResult = await eventManager.create(evt); + const areCalendarEventsEnabled = platformClientParams?.areCalendarEventsEnabled ?? true; + const scheduleResult = areCalendarEventsEnabled ? await eventManager.create(evt) : placeholderCreatedEvent; const results = scheduleResult.results; const metadata: AdditionalInformation = {}; const workflows = await getAllWorkflowsFromEventType(eventType, booking.userId); diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index b102f878d6..73e0b1397e 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -43,7 +43,7 @@ import { scheduleTrigger, } from "@calcom/features/webhooks/lib/scheduleTrigger"; import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser"; -import EventManager from "@calcom/lib/EventManager"; +import EventManager, { placeholderCreatedEvent } from "@calcom/lib/EventManager"; import { handleAnalyticsEvents } from "@calcom/lib/analyticsManager/handleAnalyticsEvents"; import { shouldIgnoreContactOwner } from "@calcom/lib/bookings/routing/utils"; import { getUsernameList } from "@calcom/lib/defaultEvents"; @@ -373,6 +373,7 @@ export type PlatformParams = { platformBookingUrl?: string; platformRescheduleUrl?: string; platformBookingLocation?: string; + areCalendarEventsEnabled?: boolean; }; export type BookingHandlerInput = { @@ -397,6 +398,7 @@ async function handler( platformBookingLocation, hostname, forcedSlug, + areCalendarEventsEnabled = true, } = input; const isPlatformBooking = !!platformClientId; @@ -1737,7 +1739,7 @@ async function handler( // Create a booking } else if (isConfirmedByDefault) { // Use EventManager to conditionally use all needed integrations. - const createManager = await eventManager.create(evt); + const createManager = areCalendarEventsEnabled ? await eventManager.create(evt) : placeholderCreatedEvent; if (evt.location) { booking.location = evt.location; } diff --git a/packages/features/bookings/lib/handleNewRecurringBooking.ts b/packages/features/bookings/lib/handleNewRecurringBooking.ts index dca7019257..b593c98f28 100644 --- a/packages/features/bookings/lib/handleNewRecurringBooking.ts +++ b/packages/features/bookings/lib/handleNewRecurringBooking.ts @@ -9,6 +9,7 @@ export type PlatformParams = { platformBookingUrl?: string; platformRescheduleUrl?: string; platformBookingLocation?: string; + areCalendarEventsEnabled?: boolean; }; export type BookingHandlerInput = { @@ -45,6 +46,7 @@ export const handleNewRecurringBooking = async (input: BookingHandlerInput): Pro platformCancelUrl: input.platformCancelUrl, platformBookingUrl: input.platformBookingUrl, platformBookingLocation: input.platformBookingLocation, + areCalendarEventsEnabled: input.areCalendarEventsEnabled, }; if (isRoundRobin) { diff --git a/packages/features/ee/payments/api/webhook.ts b/packages/features/ee/payments/api/webhook.ts index 7853587922..286d822d4b 100644 --- a/packages/features/ee/payments/api/webhook.ts +++ b/packages/features/ee/payments/api/webhook.ts @@ -8,7 +8,9 @@ import { doesBookingRequireConfirmation } from "@calcom/features/bookings/lib/do import { getAllCredentialsIncludeServiceAccountKey } from "@calcom/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials"; import { handleConfirmation } from "@calcom/features/bookings/lib/handleConfirmation"; import stripe from "@calcom/features/ee/payments/server/stripe"; -import EventManager from "@calcom/lib/EventManager"; +import { getPlatformParams } from "@calcom/features/platform-oauth-client/get-platform-params"; +import { PlatformOAuthClientRepository } from "@calcom/features/platform-oauth-client/platform-oauth-client.repository"; +import EventManager, { placeholderCreatedEvent } from "@calcom/lib/EventManager"; import { IS_PRODUCTION } from "@calcom/lib/constants"; import { getErrorFromUnknown } from "@calcom/lib/errors"; import { HttpError as HttpCode } from "@calcom/lib/http-error"; @@ -80,9 +82,18 @@ const handleSetupSuccess = async (event: Stripe.Event) => { metadata, }); + const platformOAuthClientRepository = new PlatformOAuthClientRepository(); + const platformOAuthClient = user.isPlatformManaged + ? await platformOAuthClientRepository.getByUserId(user.id) + : null; + const areCalendarEventsEnabled = platformOAuthClient?.areCalendarEventsEnabled ?? true; + const areEmailsEnabled = platformOAuthClient?.areEmailsEnabled ?? true; + if (!requiresConfirmation) { const eventManager = new EventManager({ ...user, credentials: allCredentials }, metadata?.apps); - const scheduleResult = await eventManager.create(evt); + const scheduleResult = areCalendarEventsEnabled + ? await eventManager.create(evt) + : placeholderCreatedEvent; bookingData.references = { create: scheduleResult.referencesToCreate }; bookingData.status = BookingStatus.ACCEPTED; } @@ -114,8 +125,9 @@ const handleSetupSuccess = async (event: Stripe.Event) => { bookingId: booking.id, booking, paid: true, + platformClientParams: platformOAuthClient ? getPlatformParams(platformOAuthClient) : undefined, }); - } else { + } else if (areEmailsEnabled) { await sendOrganizerRequestEmail({ ...evt }, eventType.metadata); await sendAttendeeRequestEmailAndSMS({ ...evt }, evt.attendees[0], eventType.metadata); } diff --git a/packages/features/platform-oauth-client/get-platform-params.ts b/packages/features/platform-oauth-client/get-platform-params.ts new file mode 100644 index 0000000000..205d9170b0 --- /dev/null +++ b/packages/features/platform-oauth-client/get-platform-params.ts @@ -0,0 +1,12 @@ +import type { PlatformOAuthClient } from "@prisma/client"; + +export function getPlatformParams(platformOAuthClient: PlatformOAuthClient) { + return { + platformClientId: platformOAuthClient.id, + platformCancelUrl: platformOAuthClient.bookingCancelRedirectUri, + platformRescheduleUrl: platformOAuthClient.bookingRescheduleRedirectUri, + platformBookingUrl: platformOAuthClient.bookingRedirectUri, + arePlatformEmailsEnabled: platformOAuthClient.areEmailsEnabled, + areCalendarEventsEnabled: platformOAuthClient.areCalendarEventsEnabled, + }; +} diff --git a/packages/features/platform-oauth-client/platform-oauth-client.repository.interface.ts b/packages/features/platform-oauth-client/platform-oauth-client.repository.interface.ts new file mode 100644 index 0000000000..f188517729 --- /dev/null +++ b/packages/features/platform-oauth-client/platform-oauth-client.repository.interface.ts @@ -0,0 +1,5 @@ +import type { PlatformOAuthClient } from "@prisma/client"; + +export interface IPlatformOAuthClientRepository { + getByUserId(userId: number): Promise; +} diff --git a/packages/features/platform-oauth-client/platform-oauth-client.repository.ts b/packages/features/platform-oauth-client/platform-oauth-client.repository.ts new file mode 100644 index 0000000000..f194e9ba32 --- /dev/null +++ b/packages/features/platform-oauth-client/platform-oauth-client.repository.ts @@ -0,0 +1,24 @@ +import { captureException } from "@sentry/nextjs"; + +import prisma from "@calcom/prisma"; + +import type { IPlatformOAuthClientRepository } from "./platform-oauth-client.repository.interface"; + +export class PlatformOAuthClientRepository implements IPlatformOAuthClientRepository { + async getByUserId(userId: number) { + try { + return prisma.platformOAuthClient.findFirst({ + where: { + users: { + some: { + id: userId, + }, + }, + }, + }); + } catch (err) { + captureException(err); + throw err; + } + } +} diff --git a/packages/lib/EventManager.ts b/packages/lib/EventManager.ts index 192dd0a5d4..1731a34cdf 100644 --- a/packages/lib/EventManager.ts +++ b/packages/lib/EventManager.ts @@ -1142,3 +1142,5 @@ export default class EventManager { return this.appOptions[credential.appId as keyof typeof this.appOptions]; } } + +export const placeholderCreatedEvent = { results: [], referencesToCreate: [] }; diff --git a/packages/lib/payment/getBooking.ts b/packages/lib/payment/getBooking.ts index b7962b6332..4e03480871 100644 --- a/packages/lib/payment/getBooking.ts +++ b/packages/lib/payment/getBooking.ts @@ -91,6 +91,7 @@ export async function getBooking(bookingId: number) { name: true, locale: true, destinationCalendar: true, + isPlatformManaged: true, }, }, }, diff --git a/packages/lib/payment/handlePaymentSuccess.ts b/packages/lib/payment/handlePaymentSuccess.ts index 7ec2cc6344..7860865562 100644 --- a/packages/lib/payment/handlePaymentSuccess.ts +++ b/packages/lib/payment/handlePaymentSuccess.ts @@ -5,7 +5,9 @@ import { doesBookingRequireConfirmation } from "@calcom/features/bookings/lib/do import { getAllCredentialsIncludeServiceAccountKey } from "@calcom/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials"; import { handleBookingRequested } from "@calcom/features/bookings/lib/handleBookingRequested"; import { handleConfirmation } from "@calcom/features/bookings/lib/handleConfirmation"; -import EventManager from "@calcom/lib/EventManager"; +import { getPlatformParams } from "@calcom/features/platform-oauth-client/get-platform-params"; +import { PlatformOAuthClientRepository } from "@calcom/features/platform-oauth-client/platform-oauth-client.repository"; +import EventManager, { placeholderCreatedEvent } from "@calcom/lib/EventManager"; import { HttpError as HttpCode } from "@calcom/lib/http-error"; import { getBooking } from "@calcom/lib/payment/getBooking"; import prisma from "@calcom/prisma"; @@ -33,10 +35,20 @@ export async function handlePaymentSuccess(paymentId: number, bookingId: number) }); const isConfirmed = booking.status === BookingStatus.ACCEPTED; + + const platformOAuthClientRepository = new PlatformOAuthClientRepository(); + const platformOAuthClient = userWithCredentials.isPlatformManaged + ? await platformOAuthClientRepository.getByUserId(userWithCredentials.id) + : null; + const areCalendarEventsEnabled = platformOAuthClient?.areCalendarEventsEnabled ?? true; + const areEmailsEnabled = platformOAuthClient?.areEmailsEnabled ?? true; + if (isConfirmed) { const apps = eventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); const eventManager = new EventManager({ ...userWithCredentials, credentials: allCredentials }, apps); - const scheduleResult = await eventManager.create(evt); + const scheduleResult = areCalendarEventsEnabled + ? await eventManager.create(evt) + : placeholderCreatedEvent; bookingData.references = { create: scheduleResult.referencesToCreate }; } @@ -76,6 +88,7 @@ export async function handlePaymentSuccess(paymentId: number, bookingId: number) bookingId: booking.id, booking, paid: true, + platformClientParams: platformOAuthClient ? getPlatformParams(platformOAuthClient) : undefined, }); } else { await handleBookingRequested({ @@ -84,7 +97,7 @@ export async function handlePaymentSuccess(paymentId: number, bookingId: number) }); log.debug(`handling booking request for eventId ${eventType.id}`); } - } else { + } else if (areEmailsEnabled) { await sendScheduledEmailsAndSMS({ ...evt }, undefined, undefined, undefined, eventType.metadata); } diff --git a/packages/platform/libraries/event-types.ts b/packages/platform/libraries/event-types.ts index ebdd425b2b..2d7d06590f 100644 --- a/packages/platform/libraries/event-types.ts +++ b/packages/platform/libraries/event-types.ts @@ -1,3 +1,5 @@ +import EventManager from "@calcom/lib/EventManager"; + export { getPublicEvent, type PublicEventType } from "@calcom/features/eventtypes/lib/getPublicEvent"; export { getBulkUserEventTypes, getBulkTeamEventTypes } from "@calcom/lib/event-types/getBulkEventTypes"; @@ -17,7 +19,7 @@ export { } from "@calcom/prisma/zod-utils"; export { validateCustomEventName } from "@calcom/lib/event"; - +export { EventManager }; export { getEventTypeById } from "@calcom/lib/event-types/getEventTypeById"; export { getEventTypesByViewer } from "@calcom/lib/event-types/getEventTypesByViewer"; export type { EventType } from "@calcom/lib/event-types/getEventTypeById"; diff --git a/packages/platform/types/oauth-clients/inputs/create-oauth-client.input.ts b/packages/platform/types/oauth-clients/inputs/create-oauth-client.input.ts index 1ddf23e567..d8a5415d1f 100644 --- a/packages/platform/types/oauth-clients/inputs/create-oauth-client.input.ts +++ b/packages/platform/types/oauth-clients/inputs/create-oauth-client.input.ts @@ -6,6 +6,10 @@ import { PERMISSION_MAP } from "@calcom/platform-constants"; export const ARE_DEFAULT_EVENT_TYPES_ENABLED_DOCS = "If true, when creating a managed user the managed user will have 4 default event types: 30 and 60 minutes without Cal video, 30 and 60 minutes with Cal video. Set this as false if you want to create a managed user and then manually create event types for the user."; + +export const ARE_CALENDAR_EVENTS_ENABLED_DOCS = + "If true and if managed user has calendar connected, calendar events will be created. Disable it if you manually create calendar events. Default to true."; + export class CreateOAuthClientInput { @IsOptional() @IsString() @@ -60,4 +64,14 @@ export class CreateOAuthClientInput { description: ARE_DEFAULT_EVENT_TYPES_ENABLED_DOCS, }) areDefaultEventTypesEnabled? = false; + + @IsOptional() + @IsBoolean() + @Transform(({ value }) => value ?? true) + @ApiPropertyOptional({ + type: Boolean, + default: true, + description: ARE_CALENDAR_EVENTS_ENABLED_DOCS, + }) + areCalendarEventsEnabled? = true; } diff --git a/packages/platform/types/oauth-clients/inputs/update-oauth-client.input.ts b/packages/platform/types/oauth-clients/inputs/update-oauth-client.input.ts index 58cc453de1..2081071378 100644 --- a/packages/platform/types/oauth-clients/inputs/update-oauth-client.input.ts +++ b/packages/platform/types/oauth-clients/inputs/update-oauth-client.input.ts @@ -1,7 +1,10 @@ import { ApiPropertyOptional } from "@nestjs/swagger"; import { IsArray, IsBoolean, IsOptional, IsString } from "class-validator"; -import { ARE_DEFAULT_EVENT_TYPES_ENABLED_DOCS } from "./create-oauth-client.input"; +import { + ARE_CALENDAR_EVENTS_ENABLED_DOCS, + ARE_DEFAULT_EVENT_TYPES_ENABLED_DOCS, +} from "./create-oauth-client.input"; export class UpdateOAuthClientInput { @IsOptional() @@ -47,4 +50,12 @@ export class UpdateOAuthClientInput { description: ARE_DEFAULT_EVENT_TYPES_ENABLED_DOCS, }) areDefaultEventTypesEnabled?: boolean; + + @IsOptional() + @IsBoolean() + @ApiPropertyOptional({ + type: Boolean, + description: ARE_CALENDAR_EVENTS_ENABLED_DOCS, + }) + areCalendarEventsEnabled?: boolean; } diff --git a/packages/platform/types/oauth-clients/outputs/oauth-client.output.ts b/packages/platform/types/oauth-clients/outputs/oauth-client.output.ts index 6f423aeaeb..aef525be4a 100644 --- a/packages/platform/types/oauth-clients/outputs/oauth-client.output.ts +++ b/packages/platform/types/oauth-clients/outputs/oauth-client.output.ts @@ -3,6 +3,8 @@ import { IsArray, IsEnum, IsString, IsNumber, IsOptional, IsDate, IsBoolean, IsU import { PERMISSION_MAP } from "@calcom/platform-constants"; +import { ARE_CALENDAR_EVENTS_ENABLED_DOCS } from "../inputs"; + export class PlatformOAuthClientDto { @ApiProperty({ example: "clsx38nbl0001vkhlwin9fmt0" }) @IsString() @@ -56,6 +58,10 @@ export class PlatformOAuthClientDto { }) areDefaultEventTypesEnabled!: boolean; + @IsBoolean() + @ApiProperty({ example: true, description: ARE_CALENDAR_EVENTS_ENABLED_DOCS }) + areCalendarEventsEnabled!: boolean; + @ApiPropertyOptional({ example: "https://example.com/booking-redirect" }) @IsOptional() @IsUrl() diff --git a/packages/prisma/migrations/20250611095054_add_are_calendar_events_enabled_for_oauth_client/migration.sql b/packages/prisma/migrations/20250611095054_add_are_calendar_events_enabled_for_oauth_client/migration.sql new file mode 100644 index 0000000000..a7850e7164 --- /dev/null +++ b/packages/prisma/migrations/20250611095054_add_are_calendar_events_enabled_for_oauth_client/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "PlatformOAuthClient" ADD COLUMN "areCalendarEventsEnabled" BOOLEAN NOT NULL DEFAULT true; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 70a3c2b740..b04a4460b3 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -1786,6 +1786,7 @@ model PlatformOAuthClient { bookingRescheduleRedirectUri String? areEmailsEnabled Boolean @default(false) areDefaultEventTypesEnabled Boolean @default(true) + areCalendarEventsEnabled Boolean @default(true) createdAt DateTime @default(now()) } diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 164cffa678..5897005e71 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -259,6 +259,7 @@ const PlatformClientParamsSchema = z.object({ platformCancelUrl: z.string().nullable().optional(), platformBookingUrl: z.string().nullable().optional(), platformBookingLocation: z.string().optional(), + areCalendarEventsEnabled: z.boolean().optional(), }); export type PlatformClientParams = z.infer; diff --git a/yarn.lock b/yarn.lock index 046a096525..f8b3b75d86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2736,7 +2736,7 @@ __metadata: "@axiomhq/winston": ^1.2.0 "@calcom/platform-constants": "*" "@calcom/platform-enums": "*" - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.218" + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.222" "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2" "@calcom/platform-types": "*" "@calcom/platform-utils": "*" @@ -3831,13 +3831,13 @@ __metadata: languageName: node linkType: hard -"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.218": - version: 0.0.218 - resolution: "@calcom/platform-libraries@npm:0.0.218" +"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.222": + version: 0.0.222 + resolution: "@calcom/platform-libraries@npm:0.0.222" dependencies: "@calcom/features": "*" "@calcom/lib": "*" - checksum: 8f73aeffaec3b15707aeaab40d0f056e0156e162d22ba44a19e537d897f654adc32f4bafaa0f7bab846b94211cebeaadc7bb7180ce98204c646c24ed48bdf3ef + checksum: c02cc43e0726cb2587bac6424d69af1cb42f48f5de725ec5096c5c5397c85928409ecf0c7fbc23092ef6ccf6235501f59449c6f89383d2874c794bf2259c0c1b languageName: node linkType: hard