Skip to content

Commit edf9cd7

Browse files
authored
fix: hide cal branding on platform workflows (#27385)
* fix: hide cal branding on platform workflows * refactor: rely on existing with platform variables code * revert: comment * fix e2e * chore: remove unit results
1 parent f66fffd commit edf9cd7

9 files changed

Lines changed: 90 additions & 32 deletions

File tree

apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,10 @@ export class BookingsController_2024_04_15 {
611611
...clone.body,
612612
noEmail: oAuthParams === undefined ? false : !oAuthParams.arePlatformEmailsEnabled,
613613
creationSource: CreationSource.API_V2,
614+
metadata: {
615+
...(clone.body.metadata || {}),
616+
...(oAuthClientId && { platformClientId: oAuthClientId }),
617+
},
614618
};
615619
if (oAuthClientId) {
616620
await this.setPlatformAttendeesEmails(clone.body, oAuthClientId);

apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
515515
expect(updatedAtDate?.getTime()).toBeGreaterThanOrEqual(beforeCreate.getTime());
516516
expect(updatedAtDate?.getTime()).toBeLessThanOrEqual(afterCreate.getTime());
517517

518-
expect(data.metadata).toEqual(body.metadata);
518+
expect(data.metadata).toEqual({...body.metadata, platformClientId: oAuthClient.id});
519519
createdBooking = data;
520520
} else {
521521
throw new Error(
@@ -2115,7 +2115,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
21152115
expect(updatedAtDate?.getTime()).toBeGreaterThanOrEqual(beforeCreate.getTime());
21162116
expect(updatedAtDate?.getTime()).toBeLessThanOrEqual(afterCreate.getTime());
21172117

2118-
expect(data.metadata).toEqual(body.metadata);
2118+
expect(data.metadata).toEqual({...body.metadata, platformClientId: oAuthClient.id});
21192119
createdBooking = data;
21202120
} else {
21212121
throw new Error(
@@ -2662,16 +2662,22 @@ describe("Bookings Endpoints 2024-08-13", () => {
26622662
});
26632663

26642664
describe("calendar events", () => {
2665+
// Type assertion to access private methods for test spying
2666+
const eventManagerProto = EventManager.prototype as unknown as Record<
2667+
"createAllCalendarEvents" | "createAllCRMEvents",
2668+
(...args: unknown[]) => Promise<unknown[]>
2669+
>;
2670+
26652671
beforeEach(() => {
26662672
jest.restoreAllMocks();
26672673
jest
26682674
.spyOn(EventManager.prototype, "create")
26692675
.mockImplementation(() => Promise.resolve({ results: [], referencesToCreate: [] }));
26702676
jest
2671-
.spyOn(EventManager.prototype, "createAllCalendarEvents")
2677+
.spyOn(eventManagerProto, "createAllCalendarEvents")
26722678
.mockImplementation(() => Promise.resolve([]));
26732679
jest
2674-
.spyOn(EventManager.prototype, "createAllCRMEvents")
2680+
.spyOn(eventManagerProto, "createAllCRMEvents")
26752681
.mockImplementation(() => Promise.resolve([]));
26762682
});
26772683

@@ -2843,8 +2849,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
28432849
const data: BookingOutput_2024_08_13 = responseBody.data;
28442850
expect(data.id).toBeDefined();
28452851
expect(data.uid).toBeDefined();
2846-
expect(EventManager.prototype.createAllCalendarEvents).toHaveBeenCalledTimes(0);
2847-
expect(EventManager.prototype.createAllCRMEvents).toHaveBeenCalledTimes(0);
2852+
expect(eventManagerProto.createAllCalendarEvents).toHaveBeenCalledTimes(0);
2853+
expect(eventManagerProto.createAllCRMEvents).toHaveBeenCalledTimes(0);
28482854
} else {
28492855
throw new Error(
28502856
"Invalid response data - expected booking but received array of possibly recurring bookings"
@@ -2874,8 +2880,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
28742880
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
28752881
expect(responseBody.status).toEqual(SUCCESS_STATUS);
28762882
expect(responseBody.data).toBeDefined();
2877-
expect(EventManager.prototype.createAllCalendarEvents).toHaveBeenCalledTimes(0);
2878-
expect(EventManager.prototype.createAllCRMEvents).toHaveBeenCalledTimes(0);
2883+
expect(eventManagerProto.createAllCalendarEvents).toHaveBeenCalledTimes(0);
2884+
expect(eventManagerProto.createAllCRMEvents).toHaveBeenCalledTimes(0);
28792885
});
28802886
});
28812887

@@ -2909,8 +2915,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
29092915
const data: BookingOutput_2024_08_13 = responseBody.data;
29102916
expect(data.id).toBeDefined();
29112917
expect(data.uid).toBeDefined();
2912-
expect(EventManager.prototype.createAllCalendarEvents).toHaveBeenCalledTimes(0);
2913-
expect(EventManager.prototype.createAllCRMEvents).toHaveBeenCalledTimes(0);
2918+
expect(eventManagerProto.createAllCalendarEvents).toHaveBeenCalledTimes(0);
2919+
expect(eventManagerProto.createAllCRMEvents).toHaveBeenCalledTimes(0);
29142920
bookingThatRequiresConfirmationUid = data.uid;
29152921
} else {
29162922
throw new Error(
@@ -2935,8 +2941,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
29352941
const data: BookingOutput_2024_08_13 = responseBody.data;
29362942
expect(data.id).toBeDefined();
29372943
expect(data.uid).toBeDefined();
2938-
expect(EventManager.prototype.createAllCalendarEvents).toHaveBeenCalledTimes(0);
2939-
expect(EventManager.prototype.createAllCRMEvents).toHaveBeenCalledTimes(0);
2944+
expect(eventManagerProto.createAllCalendarEvents).toHaveBeenCalledTimes(0);
2945+
expect(eventManagerProto.createAllCRMEvents).toHaveBeenCalledTimes(0);
29402946
} else {
29412947
throw new Error(
29422948
"Invalid response data - expected booking but received array of possibly recurring bookings"

apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ export class InputBookingsService_2024_08_13 {
190190
eventTypeId: inputBooking.eventTypeId,
191191
timeZone: inputBooking.attendee.timeZone,
192192
language: inputBooking.attendee.language || "en",
193-
metadata: inputBooking.metadata || {},
193+
metadata: {
194+
...(inputBooking.metadata || {}),
195+
...(platformClientId && { platformClientId }),
196+
},
194197
hasHashedBookingLink: false,
195198
guests,
196199
verificationCode: inputBooking.emailVerificationCode,

packages/features/bookings/lib/service/RegularBookingService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2615,7 +2615,7 @@ async function handler(
26152615
workflows,
26162616
smsReminderNumber: smsReminderNumber || null,
26172617
calendarEvent: calendarEventForWorkflow,
2618-
hideBranding: !!eventType.owner?.hideBranding,
2618+
hideBranding: !!eventType.owner?.hideBranding || !!platformClientId,
26192619
seatReferenceUid: evt.attendeeSeatId,
26202620
isDryRun,
26212621
triggers: [WorkflowTriggerEvents.BOOKING_PAYMENT_INITIATED],
@@ -2790,7 +2790,7 @@ async function handler(
27902790
evt: evtWithMetadata,
27912791
workflows,
27922792
requiresConfirmation: !isConfirmedByDefault,
2793-
hideBranding: !!eventType.owner?.hideBranding,
2793+
hideBranding: !!eventType.owner?.hideBranding || !!platformClientId,
27942794
seatReferenceUid: evt.attendeeSeatId,
27952795
isPlatformNoEmail: noEmail && Boolean(platformClientId),
27962796
isDryRun,
@@ -2805,7 +2805,7 @@ async function handler(
28052805
workflows,
28062806
smsReminderNumber: smsReminderNumber || null,
28072807
calendarEvent: evtWithMetadata,
2808-
hideBranding: !!eventType.owner?.hideBranding,
2808+
hideBranding: !!eventType.owner?.hideBranding || !!platformClientId,
28092809
seatReferenceUid: evt.attendeeSeatId,
28102810
isDryRun,
28112811
isConfirmedByDefault,

packages/features/ee/workflows/api/scheduleEmailReminders.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import customTemplate from "../lib/reminders/templates/customTemplate";
3636
import emailRatingTemplate from "../lib/reminders/templates/emailRatingTemplate";
3737
import emailReminderTemplate from "../lib/reminders/templates/emailReminderTemplate";
3838

39-
export async function handler(req: NextRequest) {
39+
async function handler(req: NextRequest) {
4040
const apiKey = req.headers.get("authorization") || req.nextUrl.searchParams.get("apiKey");
4141

4242
if (process.env.CRON_API_KEY !== apiKey) {
@@ -243,9 +243,7 @@ export async function handler(req: NextRequest) {
243243
eventEndTimeInAttendeeTimezone: dayjs(reminder.booking?.endTime).tz(targetAttendee?.timeZone),
244244
};
245245
const emailLocale = locale || "en";
246-
const brandingDisabled = reminder.booking.eventType?.team
247-
? !!reminder.booking.eventType?.team?.hideBranding
248-
: !!reminder.booking.user?.hideBranding;
246+
const brandingDisabled = shouldHideBranding(reminder.booking);
249247

250248
const emailSubject = customTemplate(
251249
reminder.workflowStep.emailSubject || "",
@@ -271,9 +269,7 @@ export async function handler(req: NextRequest) {
271269
getTimeFormatStringFromUserTimeFormat(reminder.booking.user?.timeFormat)
272270
).text.length === 0;
273271
} else if (reminder.workflowStep.template === WorkflowTemplates.REMINDER) {
274-
const brandingDisabled = reminder.booking.eventType?.team
275-
? !!reminder.booking.eventType?.team?.hideBranding
276-
: !!reminder.booking.user?.hideBranding;
272+
const brandingDisabled = shouldHideBranding(reminder.booking);
277273
emailContent = emailReminderTemplate({
278274
isEditingMode: false,
279275
locale: reminder.booking.user?.locale || "en",
@@ -301,6 +297,7 @@ export async function handler(req: NextRequest) {
301297
const bookerUrl = await getBookerBaseUrl(
302298
reminder.booking.eventType?.team?.parentId ?? organizerOrganizationId ?? null
303299
);
300+
const brandingDisabled = shouldHideBranding(reminder.booking);
304301
emailContent = emailRatingTemplate({
305302
isEditingMode: true,
306303
locale: reminder.booking.user?.locale || "en",
@@ -313,6 +310,7 @@ export async function handler(req: NextRequest) {
313310
timeZone: timeZone || "",
314311
organizer: reminder.booking.user?.name || "",
315312
name: name || "",
313+
isBrandingDisabled: brandingDisabled,
316314
ratingUrl: `${bookerUrl}/booking/${reminder.booking.uid}?rating`,
317315
noShowUrl: `${bookerUrl}/booking/${reminder.booking.uid}?noShow=true`,
318316
});
@@ -430,9 +428,7 @@ export async function handler(req: NextRequest) {
430428

431429
const emailBodyEmpty = false;
432430

433-
const brandingDisabled = reminder.booking.eventType?.team
434-
? !!reminder.booking.eventType?.team?.hideBranding
435-
: !!reminder.booking.user?.hideBranding;
431+
const brandingDisabled = shouldHideBranding(reminder.booking);
436432

437433
emailContent = emailReminderTemplate({
438434
isEditingMode: false,
@@ -516,3 +512,23 @@ export async function handler(req: NextRequest) {
516512

517513
return NextResponse.json({ message: `${unscheduledReminders.length} Emails to schedule` }, { status: 200 });
518514
}
515+
516+
function shouldHideBranding(booking: {
517+
metadata: unknown;
518+
eventType?: { team?: { hideBranding?: boolean } | null } | null;
519+
user?: { hideBranding?: boolean } | null;
520+
}): boolean {
521+
const bookingMetadata = bookingMetadataSchema.parse(booking.metadata || {});
522+
523+
if (bookingMetadata?.platformClientId) {
524+
return true;
525+
}
526+
527+
if (booking.eventType?.team) {
528+
return !!booking.eventType.team.hideBranding;
529+
}
530+
531+
return !!booking.user?.hideBranding;
532+
}
533+
534+
export {handler};

packages/features/ee/workflows/lib/service/EmailWorkflowService.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ export class EmailWorkflowService {
8787
creditCheckFn,
8888
});
8989

90-
const hideBranding = await getHideBranding({
91-
userId: workflow.userId ?? undefined,
92-
teamId: workflow.teamId ?? undefined,
90+
const hideBranding = await this.shouldHideBranding({
91+
platformClientId: evt.platformClientId,
92+
userId: workflow.userId,
93+
teamId: workflow.teamId,
9394
});
9495

9596
const emailWorkflowContentParams = await this.generateParametersToBuildEmailWorkflowContent({
@@ -237,6 +238,27 @@ export class EmailWorkflowService {
237238
} as const;
238239
}
239240

241+
private async shouldHideBranding({
242+
platformClientId,
243+
userId,
244+
teamId,
245+
}: {
246+
platformClientId?: string | null;
247+
userId?: number | null;
248+
teamId?: number | null;
249+
}): Promise<boolean> {
250+
if (platformClientId) {
251+
return true;
252+
}
253+
254+
const hideBranding = await getHideBranding({
255+
userId: userId ?? undefined,
256+
teamId: teamId ?? undefined,
257+
});
258+
259+
return hideBranding;
260+
}
261+
240262
async generateEmailPayloadForEvtWorkflow({
241263
evt,
242264
sendTo,
@@ -412,6 +434,7 @@ export class EmailWorkflowService {
412434
meetingUrl,
413435
otherPerson: attendeeName,
414436
name,
437+
isBrandingDisabled: hideBranding,
415438
});
416439
} else if (matchedTemplate === WorkflowTemplates.RATING) {
417440
emailContent = emailRatingTemplate({
@@ -426,6 +449,7 @@ export class EmailWorkflowService {
426449
timeZone,
427450
organizer: evt.organizer.name,
428451
name,
452+
isBrandingDisabled: hideBranding,
429453
ratingUrl: `${bookerUrl}/booking/${evt.uid}?rating`,
430454
noShowUrl: `${bookerUrl}/booking/${evt.uid}?noShow=true`,
431455
});
@@ -554,4 +578,4 @@ export class EmailWorkflowService {
554578
sender,
555579
};
556580
}
557-
}
581+
}

packages/features/ee/workflows/lib/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export type BookingInfo = {
8989
videoCallData?: {
9090
url?: string;
9191
};
92+
platformClientId?: string | null;
9293
};
9394

9495
export type WorkflowContextData =

packages/features/tasker/tasks/sendWorkflowEmails.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,17 @@ export async function sendWorkflowEmails(payload: string): Promise<void> {
4949
throw new Error("Booking not found");
5050
}
5151

52-
const calendarEvent = (await CalendarEventBuilder.fromBooking(booking, {})).build();
52+
const bookingMetadata = bookingMetadataSchema.parse(booking.metadata || {});
53+
54+
const calendarEvent = (await CalendarEventBuilder.fromBooking(booking, {
55+
platformClientId: bookingMetadata?.platformClientId,
56+
})).build();
5357

5458
if (!calendarEvent) {
5559
throw new Error("Calendar event could not be built");
5660
}
5761

5862
// Check if videoCallUrl exists in booking metadata and add it to evt.metadata
59-
const bookingMetadata = bookingMetadataSchema.parse(booking.metadata || {});
6063
const metadata = bookingMetadata?.videoCallUrl
6164
? {
6265
videoCallUrl: bookingMetadata.videoCallUrl,

packages/prisma/zod-utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ export const teamMetadataStrictSchema = baseTeamMetadataSchema
523523
export const bookingMetadataSchema = z
524524
.object({
525525
videoCallUrl: z.string().optional(),
526+
platformClientId: z.string().optional(),
526527
})
527528
.and(z.record(z.string()))
528529
.nullable()

0 commit comments

Comments
 (0)