fix: event is not created on the Zoho Calendar when hideOrganizerEmail is enabled (#27675)
* fix: event not created * Update CalendarManager.ts * Add tests: packages/features/calendars/lib/CalendarManager.ts Generated by Paragon from proposal for PR #27675 * Add tests: packages/features/calendars/lib/CalendarManager.test.ts Generated by Paragon from proposal for PR #27675 * Export processEvent for testing and refactor logic * Fix missing newline at end of CalendarManager.test.ts Add missing newline at the end of the file. * fix test
This commit is contained in:
@@ -466,13 +466,13 @@ class ZohoCalendarService implements Calendar {
|
||||
timezone: event.organizer.timeZone,
|
||||
},
|
||||
attendees: event.attendees.map((attendee) => ({ email: attendee.email })),
|
||||
isprivate: event.seatsShowAttendees,
|
||||
isprivate: event.hideCalendarEventDetails ?? false,
|
||||
reminders: [
|
||||
{
|
||||
minutes: "-15",
|
||||
action: "popup",
|
||||
},
|
||||
],
|
||||
action: "popup",
|
||||
},
|
||||
],
|
||||
location: event.location
|
||||
? getLocation({
|
||||
videoCallData: event.videoCallData,
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
import { prisma } from "@calcom/prisma/__mocks__/prisma";
|
||||
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
|
||||
import { getCalendarCredentials, deduplicateCredentialsBasedOnSelectedCalendars } from "./CalendarManager";
|
||||
import {
|
||||
getCalendarCredentials,
|
||||
deduplicateCredentialsBasedOnSelectedCalendars,
|
||||
processEvent,
|
||||
} from "./CalendarManager";
|
||||
|
||||
vi.mock("@calcom/prisma", () => ({
|
||||
prisma,
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/lib/constants", () => ({
|
||||
ORGANIZER_EMAIL_EXEMPT_DOMAINS: "",
|
||||
IS_PRODUCTION: false,
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/app-store/locations", () => ({
|
||||
MeetLocationType: "integrations:google:meet",
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/lib/CalEventParser", () => ({
|
||||
getRichDescription: vi.fn(() => "Test description"),
|
||||
}));
|
||||
|
||||
function buildCredential(data: {
|
||||
type: string;
|
||||
appId: string;
|
||||
@@ -32,7 +49,178 @@ function buildCredential(data: {
|
||||
};
|
||||
}
|
||||
|
||||
function buildCalendarEvent(overrides = {}) {
|
||||
return {
|
||||
type: "test-event",
|
||||
title: "Test Event",
|
||||
startTime: "2024-01-01T10:00:00Z",
|
||||
endTime: "2024-01-01T11:00:00Z",
|
||||
organizer: {
|
||||
name: "Organizer",
|
||||
email: "organizer@example.com",
|
||||
timeZone: "UTC",
|
||||
language: { translate: (x: string) => x, locale: "en" },
|
||||
},
|
||||
attendees: [
|
||||
{
|
||||
name: "Attendee 1",
|
||||
email: "attendee1@example.com",
|
||||
timeZone: "UTC",
|
||||
language: { translate: (x: string) => x, locale: "en" },
|
||||
},
|
||||
{
|
||||
name: "Attendee 2",
|
||||
email: "attendee2@example.com",
|
||||
timeZone: "UTC",
|
||||
language: { translate: (x: string) => x, locale: "en" },
|
||||
},
|
||||
],
|
||||
destinationCalendar: null,
|
||||
hideOrganizerEmail: false,
|
||||
location: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("CalendarManager tests", () => {
|
||||
describe("fn: processEvent", () => {
|
||||
it("should clear attendees when hideOrganizerEmail is true and no Zoho Calendar destination", () => {
|
||||
const calEvent = buildCalendarEvent({
|
||||
hideOrganizerEmail: true,
|
||||
destinationCalendar: [
|
||||
{
|
||||
integration: "google_calendar",
|
||||
externalId: "calendar-1",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
expect(result.attendees).toEqual([]);
|
||||
});
|
||||
|
||||
it("should NOT clear attendees when hideOrganizerEmail is true and destination is Zoho Calendar", () => {
|
||||
const calEvent = buildCalendarEvent({
|
||||
hideOrganizerEmail: true,
|
||||
destinationCalendar: [
|
||||
{
|
||||
integration: "zoho_calendar",
|
||||
externalId: "calendar-1",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
// Zoho Calendar requires at least one attendee, so attendees should NOT be cleared
|
||||
expect(result.attendees).toHaveLength(2);
|
||||
expect(result.attendees[0].email).toBe("attendee1@example.com");
|
||||
expect(result.attendees[1].email).toBe("attendee2@example.com");
|
||||
});
|
||||
|
||||
it("should NOT clear attendees when hideOrganizerEmail is true and one of multiple destinations is Zoho Calendar", () => {
|
||||
const calEvent = buildCalendarEvent({
|
||||
hideOrganizerEmail: true,
|
||||
destinationCalendar: [
|
||||
{
|
||||
integration: "google_calendar",
|
||||
externalId: "google-calendar-1",
|
||||
},
|
||||
{
|
||||
integration: "zoho_calendar",
|
||||
externalId: "zoho-calendar-1",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
// Zoho Calendar is in the list, so attendees should NOT be cleared
|
||||
expect(result.attendees).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should NOT clear attendees when hideOrganizerEmail is false", () => {
|
||||
const calEvent = buildCalendarEvent({
|
||||
hideOrganizerEmail: false,
|
||||
destinationCalendar: [
|
||||
{
|
||||
integration: "google_calendar",
|
||||
externalId: "calendar-1",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
expect(result.attendees).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should NOT clear attendees when location is MeetLocationType even with hideOrganizerEmail true", () => {
|
||||
const calEvent = buildCalendarEvent({
|
||||
hideOrganizerEmail: true,
|
||||
location: "integrations:google:meet",
|
||||
destinationCalendar: [
|
||||
{
|
||||
integration: "google_calendar",
|
||||
externalId: "calendar-1",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
expect(result.attendees).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should handle null destinationCalendar with hideOrganizerEmail true", () => {
|
||||
const calEvent = buildCalendarEvent({
|
||||
hideOrganizerEmail: true,
|
||||
destinationCalendar: null,
|
||||
});
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
expect(result.attendees).toEqual([]);
|
||||
});
|
||||
|
||||
it("should handle empty destinationCalendar array with hideOrganizerEmail true", () => {
|
||||
const calEvent = buildCalendarEvent({
|
||||
hideOrganizerEmail: true,
|
||||
destinationCalendar: [],
|
||||
});
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
expect(result.attendees).toEqual([]);
|
||||
});
|
||||
|
||||
it("should include calendarDescription from getRichDescription", () => {
|
||||
const calEvent = buildCalendarEvent();
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
expect(result.calendarDescription).toBe("Test description");
|
||||
});
|
||||
|
||||
it("should clear responses for seatsPerTimeSlot events", () => {
|
||||
const calEvent = buildCalendarEvent({
|
||||
seatsPerTimeSlot: 5,
|
||||
responses: { field1: { label: "Field 1", value: "test" } },
|
||||
userFieldsResponses: { field1: { label: "Field 1", value: "test" } },
|
||||
additionalNotes: "Test notes",
|
||||
customInputs: { input1: "value1" },
|
||||
});
|
||||
|
||||
const result = processEvent(calEvent as any);
|
||||
|
||||
expect(result.responses).toBeNull();
|
||||
expect(result.userFieldsResponses).toBeNull();
|
||||
expect(result.additionalNotes).toBeNull();
|
||||
expect(result.customInputs).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("fn: getCalendarCredentials", () => {
|
||||
it("should only return credentials for calendar apps", async () => {
|
||||
const googleCalendarCredentials = {
|
||||
|
||||
@@ -31,7 +31,7 @@ const log = logger.getSubLogger({ prefix: ["CalendarManager"] });
|
||||
/**
|
||||
* Process the calendar event by generating description and removing attendees if needed
|
||||
*/
|
||||
const processEvent = (calEvent: CalendarEvent): CalendarServiceEvent => {
|
||||
export const processEvent = (calEvent: CalendarEvent): CalendarServiceEvent => {
|
||||
if (calEvent.seatsPerTimeSlot) {
|
||||
calEvent.responses = null;
|
||||
calEvent.userFieldsResponses = null;
|
||||
@@ -51,7 +51,10 @@ const processEvent = (calEvent: CalendarEvent): CalendarServiceEvent => {
|
||||
.filter((domain) => domain.trim() !== "")
|
||||
.some((domain) => calEvent.organizer.email.toLowerCase().endsWith(domain.toLowerCase()));
|
||||
|
||||
if (calEvent.hideOrganizerEmail && !isOrganizerExempt && !isMeetLocationType) {
|
||||
// Zoho Calendar requires at least one attendee, so don't empty attendees array for Zoho Calendar
|
||||
const hasZohoCalendar = calEvent.destinationCalendar?.some((cal) => cal.integration === "zoho_calendar") ?? false;
|
||||
|
||||
if (calEvent.hideOrganizerEmail && !isOrganizerExempt && !isMeetLocationType && !hasZohoCalendar) {
|
||||
calendarEvent.attendees = [];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user