-
Notifications
You must be signed in to change notification settings - Fork 8.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make Google Meet default when DWD for google is enabled
- Loading branch information
1 parent
033332e
commit d9425a3
Showing
6 changed files
with
243 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 39 additions & 17 deletions
56
packages/features/bookings/lib/handleNewBooking/getLocationValuesForDb.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,52 @@ | ||
import { getFirstDwdConferencingCredentialAppLocation } from "@calcom/lib/domainWideDelegation/server"; | ||
import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils"; | ||
|
||
import type { loadAndValidateUsers } from "./loadAndValidateUsers"; | ||
|
||
type Users = Awaited<ReturnType<typeof loadAndValidateUsers>>; | ||
|
||
const sortUsersByDynamicList = (users: Users, dynamicUserList: string[]) => { | ||
const sortUsersByDynamicList = <TUser extends { username: string | null }>( | ||
users: TUser[], | ||
dynamicUserList: string[] | ||
) => { | ||
return users.sort((a, b) => { | ||
const aIndex = (a.username && dynamicUserList.indexOf(a.username)) || 0; | ||
const bIndex = (b.username && dynamicUserList.indexOf(b.username)) || 0; | ||
return aIndex - bIndex; | ||
}); | ||
}; | ||
|
||
export const getLocationValuesForDb = ( | ||
dynamicUserList: string[], | ||
users: Users, | ||
locationBodyString: string | ||
) => { | ||
export const getLocationValuesForDb = < | ||
TUser extends { | ||
username: string | null; | ||
metadata: Prisma.JsonValue; | ||
credentials: CredentialForCalendarService[]; | ||
} | ||
>({ | ||
dynamicUserList, | ||
users, | ||
location: locationBodyString, | ||
}: { | ||
dynamicUserList: string[]; | ||
users: TUser[]; | ||
location: string; | ||
}) => { | ||
const isDynamicGroupBookingCase = dynamicUserList.length > 1; | ||
let firstDynamicGroupMemberDefaultLocationUrl; | ||
// TODO: It's definition should be moved to getLocationValueForDb | ||
let organizerOrFirstDynamicGroupMemberDefaultLocationUrl; | ||
if (dynamicUserList.length > 1) { | ||
if (isDynamicGroupBookingCase) { | ||
users = sortUsersByDynamicList(users, dynamicUserList); | ||
const firstUsersMetadata = userMetadataSchema.parse(users[0].metadata); | ||
locationBodyString = firstUsersMetadata?.defaultConferencingApp?.appLink || locationBodyString; | ||
organizerOrFirstDynamicGroupMemberDefaultLocationUrl = | ||
firstUsersMetadata?.defaultConferencingApp?.appLink; | ||
const firstDynamicGroupMember = users[0]; | ||
const firstDynamicGroupMemberMetadata = userMetadataSchema.parse(firstDynamicGroupMember.metadata); | ||
const firstDynamicGroupMemberDwdConferencingAppLocation = getFirstDwdConferencingCredentialAppLocation({ | ||
credentials: firstDynamicGroupMember.credentials, | ||
}); | ||
|
||
firstDynamicGroupMemberDefaultLocationUrl = | ||
firstDynamicGroupMemberMetadata?.defaultConferencingApp?.appLink || | ||
firstDynamicGroupMemberDwdConferencingAppLocation; | ||
|
||
locationBodyString = firstDynamicGroupMemberDefaultLocationUrl || locationBodyString; | ||
} | ||
return { locationBodyString, organizerOrFirstDynamicGroupMemberDefaultLocationUrl }; | ||
|
||
return { | ||
locationBodyString, | ||
organizerOrFirstDynamicGroupMemberDefaultLocationUrl: firstDynamicGroupMemberDefaultLocationUrl, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
import { | ||
createBookingScenario, | ||
TestData, | ||
getDate, | ||
getOrganizer, | ||
getBooker, | ||
getScenarioData, | ||
|
@@ -31,7 +32,10 @@ import { | |
expectBookingToNotHaveReference, | ||
expectNoAttemptToCreateCalendarEvent, | ||
} from "@calcom/web/test/utils/bookingScenario/expects"; | ||
import { getMockRequestDataForBooking } from "@calcom/web/test/utils/bookingScenario/getMockRequestDataForBooking"; | ||
import { | ||
getMockRequestDataForBooking, | ||
getMockRequestDataForDynamicGroupBooking, | ||
} from "@calcom/web/test/utils/bookingScenario/getMockRequestDataForBooking"; | ||
import { setupAndTeardown } from "@calcom/web/test/utils/bookingScenario/setupAndTeardown"; | ||
|
||
import { describe, expect } from "vitest"; | ||
|
@@ -51,6 +55,7 @@ describe("handleNewBooking", () => { | |
`should create a successful booking using the domain wide delegation credential | ||
1. Should create a booking in the database with reference having DWD credential | ||
2. Should create an event in calendar with DWD credential | ||
3. Should use Google Meet as the location even when not explicitly set. | ||
`, | ||
async ({ emails }) => { | ||
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default; | ||
|
@@ -97,7 +102,6 @@ describe("handleNewBooking", () => { | |
id: 1, | ||
slotInterval: 30, | ||
length: 30, | ||
location: BookingLocations.GoogleMeet, | ||
users: [ | ||
{ | ||
id: organizer.id, | ||
|
@@ -394,5 +398,165 @@ describe("handleNewBooking", () => { | |
}, | ||
timeout | ||
); | ||
|
||
test( | ||
`should create a successful dynamic group booking using the domain wide delegation credential | ||
1. Should create a booking in the database with reference having DWD credential | ||
2. Should create an event in calendar with DWD credential for both users | ||
3. Should use Google Meet as the location even when not explicitly set. | ||
`, | ||
async () => { | ||
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default; | ||
|
||
const org = await createOrganization({ | ||
name: "Test Org", | ||
slug: "testorg", | ||
}); | ||
|
||
const payloadToMakePartOfOrganization = [ | ||
{ | ||
membership: { | ||
accepted: true, | ||
role: MembershipRole.ADMIN, | ||
}, | ||
team: { | ||
id: org.id, | ||
name: "Test Org", | ||
slug: "testorg", | ||
}, | ||
}, | ||
]; | ||
|
||
const booker = getBooker({ | ||
email: "[email protected]", | ||
name: "Booker", | ||
}); | ||
|
||
const groupUser1 = getOrganizer({ | ||
name: "group-user-1", | ||
username: "group-user-1", | ||
email: "[email protected]", | ||
id: 101, | ||
schedules: [TestData.schedules.IstWorkHours], | ||
selectedCalendars: [TestData.selectedCalendars.google], | ||
teams: payloadToMakePartOfOrganization, | ||
credentials: [], | ||
destinationCalendar: [TestData.selectedCalendars.google], | ||
}); | ||
|
||
const groupUser2 = getOrganizer({ | ||
name: "group-user-2", | ||
username: "group-user-2", | ||
email: "[email protected]", | ||
id: 102, | ||
schedules: [TestData.schedules.IstWorkHours], | ||
selectedCalendars: [TestData.selectedCalendars.google], | ||
teams: payloadToMakePartOfOrganization, | ||
credentials: [], | ||
destinationCalendar: [TestData.selectedCalendars.google], | ||
}); | ||
|
||
const dwd = await createDwdCredential(org.id); | ||
|
||
await createBookingScenario( | ||
getScenarioData({ | ||
webhooks: [ | ||
{ | ||
userId: groupUser1.id, | ||
eventTriggers: ["BOOKING_CREATED"], | ||
subscriberUrl: "http://my-webhook.example.com", | ||
active: true, | ||
eventTypeId: 0, | ||
appId: null, | ||
}, | ||
], | ||
workflows: [ | ||
{ | ||
userId: groupUser1.id, | ||
trigger: "NEW_EVENT", | ||
action: "EMAIL_HOST", | ||
template: "REMINDER", | ||
activeOn: [0], | ||
}, | ||
], | ||
eventTypes: [], | ||
users: [groupUser1, groupUser2], | ||
apps: [TestData.apps["daily-video"], TestData.apps["google-calendar"]], | ||
}) | ||
); | ||
|
||
// Mock a Scenario where iCalUID isn't returned by Google Calendar in which case booking UID is used as the ics UID | ||
const calendarMock = mockCalendarToHaveNoBusySlots("googlecalendar", { | ||
create: { | ||
id: "GOOGLE_CALENDAR_EVENT_ID", | ||
uid: "MOCK_ID", | ||
appSpecificData: { | ||
googleCalendar: { | ||
hangoutLink: "https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT", | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 }); | ||
|
||
const mockBookingData = getMockRequestDataForDynamicGroupBooking({ | ||
data: { | ||
start: `${plus1DateString}T05:00:00.000Z`, | ||
end: `${plus1DateString}T05:30:00.000Z`, | ||
eventTypeId: 0, | ||
eventTypeSlug: "group-user-1+group-user-2", | ||
user: "group-user-1+group-user-2", | ||
responses: { | ||
email: booker.email, | ||
name: booker.name, | ||
// There is no location option during booking for Dynamic Group Bookings | ||
// location: { optionValue: "", value: BookingLocations.CalVideo }, | ||
}, | ||
}, | ||
}); | ||
|
||
const { req } = createMockNextJsRequest({ | ||
method: "POST", | ||
body: mockBookingData, | ||
}); | ||
|
||
const createdBooking = await handleNewBooking(req); | ||
|
||
expect(createdBooking.responses).toEqual( | ||
expect.objectContaining({ | ||
email: booker.email, | ||
name: booker.name, | ||
}) | ||
); | ||
|
||
expect(createdBooking).toEqual( | ||
expect.objectContaining({ | ||
location: BookingLocations.GoogleMeet, | ||
}) | ||
); | ||
|
||
await expectBookingToBeInDatabase({ | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
uid: createdBooking.uid!, | ||
eventTypeId: null, | ||
status: BookingStatus.ACCEPTED, | ||
location: BookingLocations.GoogleMeet, | ||
references: [ | ||
{ | ||
type: appStoreMetadata.googlecalendar.type, | ||
uid: "GOOGLE_CALENDAR_EVENT_ID", | ||
meetingId: "GOOGLE_CALENDAR_EVENT_ID", | ||
meetingPassword: "MOCK_PASSWORD", | ||
meetingUrl: "https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT", | ||
// Verify DWD credential was used | ||
domainWideDelegationCredentialId: dwd.id, | ||
}, | ||
], | ||
iCalUID: createdBooking.iCalUID, | ||
}); | ||
}, | ||
timeout | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters