Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: loadUsers and move to separate file #15532

Merged
merged 8 commits into from
Jun 25, 2024
75 changes: 1 addition & 74 deletions packages/features/bookings/lib/handleNewBooking.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { App, DestinationCalendar, EventTypeCustomInput } from "@prisma/client";
import { Prisma } from "@prisma/client";
import type { IncomingMessage } from "http";
import { isValidPhoneNumber } from "libphonenumber-js";
// eslint-disable-next-line no-restricted-imports
import { cloneDeep } from "lodash";
Expand Down Expand Up @@ -41,7 +40,6 @@ import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import {
allowDisablingAttendeeConfirmationEmails,
allowDisablingHostConfirmationEmails,
Expand Down Expand Up @@ -81,7 +79,6 @@ import { getPiiFreeCalendarEvent, getPiiFreeEventType, getPiiFreeUser } from "@c
import { safeStringify } from "@calcom/lib/safeStringify";
import { checkBookingLimits, checkDurationLimits, getLuckyUser } from "@calcom/lib/server";
import { getTranslation } from "@calcom/lib/server/i18n";
import { UserRepository } from "@calcom/lib/server/repository/user";
import { slugify } from "@calcom/lib/slugify";
import { updateWebUser as syncServicesUpdateWebUser } from "@calcom/lib/sync/SyncServiceManager";
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
Expand Down Expand Up @@ -110,6 +107,7 @@ import { checkForConflicts } from "./conflictChecker/checkForConflicts";
import { getAllCredentials } from "./getAllCredentialsForUsersOnEvent/getAllCredentials";
import { refreshCredentials } from "./getAllCredentialsForUsersOnEvent/refreshCredentials";
import getBookingDataSchema from "./getBookingDataSchema";
import { loadUsers } from "./handleNewBooking/loadUsers";
import handleSeats from "./handleSeats/handleSeats";
import type { BookingSeat } from "./handleSeats/types";

Expand Down Expand Up @@ -292,40 +290,6 @@ type IsFixedAwareUser = User & {
priority?: number;
};

const loadUsers = async (eventType: NewBookingEventType, dynamicUserList: string[], req: IncomingMessage) => {
try {
if (!eventType.id) {
if (!Array.isArray(dynamicUserList) || dynamicUserList.length === 0) {
throw new Error("dynamicUserList is not properly defined or empty.");
}
const { isValidOrgDomain, currentOrgDomain } = orgDomainConfig(req);
const users = await findUsersByUsername({
usernameList: dynamicUserList,
orgSlug: isValidOrgDomain ? currentOrgDomain : null,
});
return users;
}
const hosts = eventType.hosts || [];

if (!Array.isArray(hosts)) {
throw new Error("eventType.hosts is not properly defined.");
}

const users = hosts.map(({ user, isFixed, priority }) => ({
...user,
isFixed,
priority,
}));

return users.length ? users : eventType.users;
} catch (error) {
if (error instanceof HttpError || error instanceof Prisma.PrismaClientKnownRequestError) {
throw new HttpError({ statusCode: 400, message: error.message });
}
throw new HttpError({ statusCode: 500, message: "Unable to load users" });
}
};

export async function ensureAvailableUsers(
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
users: IsFixedAwareUser[];
Expand Down Expand Up @@ -2650,40 +2614,3 @@ function handleCustomInputs(
}
});
}

/**
* This method is mostly same as the one in UserRepository but it includes a lot more relations which are specific requirement here
* TODO: Figure out how to keep it in UserRepository and use it here
*/
export const findUsersByUsername = async ({
usernameList,
orgSlug,
}: {
orgSlug: string | null;
usernameList: string[];
}) => {
log.debug("findUsersByUsername", { usernameList, orgSlug });
const { where, profiles } = await UserRepository._getWhereClauseForFindingUsersByUsername({
orgSlug,
usernameList,
});
return (
await prisma.user.findMany({
where,
select: {
...userSelect.select,
credentials: {
select: credentialForCalendarServiceSelect,
},
metadata: true,
},
})
).map((user) => {
const profile = profiles?.find((profile) => profile.user.id === user.id) ?? null;
return {
...user,
organizationId: profile?.organizationId ?? null,
profile,
};
});
};
87 changes: 87 additions & 0 deletions packages/features/bookings/lib/handleNewBooking/loadUsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Prisma } from "@prisma/client";
import type { IncomingMessage } from "http";

import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { UserRepository } from "@calcom/lib/server/repository/user";
import prisma, { userSelect } from "@calcom/prisma";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";

import type { NewBookingEventType } from "../handleNewBooking";

const log = logger.getSubLogger({ prefix: ["[loadUsers]:handleNewBooking "] });

type EventType = Pick<NewBookingEventType, "hosts" | "users" | "id">;

export const loadUsers = async (eventType: EventType, dynamicUserList: string[], req: IncomingMessage) => {
try {
const { currentOrgDomain } = orgDomainConfig(req);

return eventType.id
? await loadUsersByEventType(eventType)
: await loadDynamicUsers(dynamicUserList, currentOrgDomain);
} catch (error) {
if (error instanceof HttpError || error instanceof Prisma.PrismaClientKnownRequestError) {
throw new HttpError({ statusCode: 400, message: error.message });
}
throw new HttpError({ statusCode: 500, message: "Unable to load users" });
}
};

const loadUsersByEventType = async (eventType: EventType): Promise<NewBookingEventType["users"]> => {
const hosts = eventType.hosts || [];
const users = hosts.map(({ user, isFixed, priority }) => ({
...user,
isFixed,
priority,
}));
return users.length ? users : eventType.users;
};

const loadDynamicUsers = async (dynamicUserList: string[], currentOrgDomain: string | null) => {
if (!Array.isArray(dynamicUserList) || dynamicUserList.length === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!Array.isArray(dynamicUserList) || dynamicUserList.length === 0) {
if (dynamicUserList.length === 0) {

throw new Error("dynamicUserList is not properly defined or empty.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new Error("dynamicUserList is not properly defined or empty.");
throw new Error("dynamicUserList is empty.");

}
return findUsersByUsername({
usernameList: dynamicUserList,
orgSlug: !!currentOrgDomain ? currentOrgDomain : null,
});
};

/**
* This method is mostly same as the one in UserRepository but it includes a lot more relations which are specific requirement here
* TODO: Figure out how to keep it in UserRepository and use it here
*/
export const findUsersByUsername = async ({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no change in the code here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this logic is already in the UserRepository. Can we delete this duplicate code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function returns few more fields(credentials and metadata) than the static function in UserRepository.

usernameList,
orgSlug,
}: {
orgSlug: string | null;
usernameList: string[];
}) => {
log.debug("findUsersByUsername", { usernameList, orgSlug });
const { where, profiles } = await UserRepository._getWhereClauseForFindingUsersByUsername({
orgSlug,
usernameList,
});
return (
await prisma.user.findMany({
where,
select: {
...userSelect.select,
credentials: {
select: credentialForCalendarServiceSelect,
},
metadata: true,
},
})
).map((user) => {
const profile = profiles?.find((profile) => profile.user.id === user.id) ?? null;
return {
...user,
organizationId: profile?.organizationId ?? null,
profile,
};
});
};
Loading