fix: Add attendees to getLuckyUser algorithm (#10285)

* Add attendees to getLuckyUser algorithm

* Fix unit test for getLuckyUser
pull/10289/head
Alex van Andel 2023-07-20 22:51:15 +01:00 committed by GitHub
parent 56f4310c24
commit 3818093652
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 9 deletions

View File

@ -33,6 +33,7 @@ it("can find lucky user with maximize availability", async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
prismaMock.user.findMany.mockResolvedValue(users); prismaMock.user.findMany.mockResolvedValue(users);
prismaMock.booking.findMany.mockResolvedValue([]);
await expect( await expect(
getLuckyUser("MAXIMIZE_AVAILABILITY", { getLuckyUser("MAXIMIZE_AVAILABILITY", {

View File

@ -2,14 +2,15 @@ import type { User } from "@prisma/client";
import prisma from "@calcom/prisma"; import prisma from "@calcom/prisma";
async function leastRecentlyBookedUser<T extends Pick<User, "id">>({ async function leastRecentlyBookedUser<T extends Pick<User, "id" | "email">>({
availableUsers, availableUsers,
eventTypeId, eventTypeId,
}: { }: {
availableUsers: T[]; availableUsers: T[];
eventTypeId: number; eventTypeId: number;
}) { }) {
const usersWithLastCreated = await prisma.user.findMany({ // First we get all organizers (fixed host/single round robin user)
const organizersWithLastCreated = await prisma.user.findMany({
where: { where: {
id: { id: {
in: availableUsers.map((user) => user.id), in: availableUsers.map((user) => user.id),
@ -32,18 +33,64 @@ async function leastRecentlyBookedUser<T extends Pick<User, "id">>({
}, },
}); });
if (!usersWithLastCreated) { const organizerIdAndAtCreatedPair = organizersWithLastCreated.reduce(
throw new Error("Unable to find users by availableUser ids."); // should never happen. (keyValuePair: { [userId: number]: Date }, user) => {
}
const userIdAndAtCreatedPair = usersWithLastCreated.reduce(
(keyValuePair: { [key: number]: Date }, user) => {
keyValuePair[user.id] = user.bookings[0]?.createdAt || new Date(0); keyValuePair[user.id] = user.bookings[0]?.createdAt || new Date(0);
return keyValuePair; return keyValuePair;
}, },
{} {}
); );
const bookings = await prisma.booking.findMany({
where: {
AND: [
{
eventTypeId,
},
{
attendees: {
some: {
email: {
in: availableUsers.map((user) => user.email),
},
},
},
},
],
},
select: {
id: true,
createdAt: true,
attendees: {
select: {
email: true,
},
},
},
orderBy: {
createdAt: "desc",
},
});
const attendeeUserIdAndAtCreatedPair = bookings.reduce((aggregate: { [userId: number]: Date }, booking) => {
availableUsers.forEach((user) => {
if (aggregate[user.id]) return; // Bookings are ordered DESC, so if the reducer aggregate
// contains the user id, it's already got the most recent booking marked.
if (!booking.attendees.map((attendee) => attendee.email).includes(user.email)) return;
aggregate[user.id] = booking.createdAt;
});
return aggregate;
}, {});
const userIdAndAtCreatedPair = {
...organizerIdAndAtCreatedPair,
...attendeeUserIdAndAtCreatedPair,
};
if (!userIdAndAtCreatedPair) {
throw new Error("Unable to find users by availableUser ids."); // should never happen.
}
const leastRecentlyBookedUser = availableUsers.sort((a, b) => { const leastRecentlyBookedUser = availableUsers.sort((a, b) => {
return userIdAndAtCreatedPair[a.id] > userIdAndAtCreatedPair[b.id] ? 1 : -1; return userIdAndAtCreatedPair[a.id] > userIdAndAtCreatedPair[b.id] ? 1 : -1;
})[0]; })[0];
@ -53,7 +100,7 @@ async function leastRecentlyBookedUser<T extends Pick<User, "id">>({
// TODO: Configure distributionAlgorithm from the event type configuration // TODO: Configure distributionAlgorithm from the event type configuration
// TODO: Add 'MAXIMIZE_FAIRNESS' algorithm. // TODO: Add 'MAXIMIZE_FAIRNESS' algorithm.
export async function getLuckyUser<T extends Pick<User, "id">>( export async function getLuckyUser<T extends Pick<User, "id" | "email">>(
distributionAlgorithm: "MAXIMIZE_AVAILABILITY" = "MAXIMIZE_AVAILABILITY", distributionAlgorithm: "MAXIMIZE_AVAILABILITY" = "MAXIMIZE_AVAILABILITY",
{ availableUsers, eventTypeId }: { availableUsers: T[]; eventTypeId: number } { availableUsers, eventTypeId }: { availableUsers: T[]; eventTypeId: number }
) { ) {