refactor: convert UserRepository to use dependency injection pattern (#22360)
* refactor: convert UserRepository to use dependency injection pattern - Convert all static methods to public instance methods - Add constructor that takes PrismaClient parameter - Update all usage sites to use new instantiation pattern: new UserRepository(prisma).method() - Follow same pattern as PrismaOOORepository for consistency - Maintain all existing method logic and signatures unchanged - Update 125+ files across the codebase to adapt to new pattern Co-Authored-By: morgan@cal.com <morgan@cal.com> * optimize: reuse UserRepository instances within same function scope - Create single UserRepository instance per function scope - Reuse instance for multiple method calls within same function - Reduces object instantiation overhead and improves performance - Apply optimization pattern consistently across codebase Co-Authored-By: morgan@cal.com <morgan@cal.com> * fix: repository * fixup! fix: repository * fixup! fixup! fix: repository * fixup! fixup! fixup! fix: repository * fix: update test mocking strategies for UserRepository dependency injection - Convert static method mocks to instance method mocks in userCreationService.test.ts - Update vi.spyOn calls to work with constructor injection pattern in getAllCredentials.test.ts - Fix UserRepository mocking in getRoutedUrl.test.ts to use constructor injection - Ensure consistent mocking approach across all test files - Fix 'UserRepository is not a constructor' errors in tests Co-Authored-By: morgan@cal.com <morgan@cal.com> * feat: optimize UserRepository instance reuse and add SessionUser type - Reuse UserRepository instance in OrganizationRepository.createWithNonExistentOwner - Add comprehensive SessionUser type definition for type safety - Improve type constraints in enrichUserWithTheProfile and enrichUserWithItsProfile - Ensure proper return types with profile information Co-Authored-By: morgan@cal.com <morgan@cal.com> * fix: make UserRepository mocking strategy more robust for CI environments - Add defensive checks for vi.mocked() to handle CI environment differences - Ensure mockImplementation is available before calling it - Maintain consistent mocking pattern across all test files - Fix 'Cannot read properties of undefined' error in CI Co-Authored-By: morgan@cal.com <morgan@cal.com> * fixup! fix: make UserRepository mocking strategy more robust for CI environments * refactor: convert direct UserRepository instantiations to two-step pattern - Change await new UserRepository(prisma).method(...) to const userRepo = new UserRepository(prisma); await userRepo.method(...) - Optimize instance reuse within same function scopes - Apply pattern consistently across all modified files in PR - Fix type errors in organization.ts and sessionMiddleware.ts Co-Authored-By: morgan@cal.com <morgan@cal.com> * refactor: complete two-step UserRepository pattern for remaining files - Apply two-step instantiation pattern to all remaining modified files in PR - Ensure consistent UserRepository usage across entire codebase - Maintain instance reuse optimization within function scopes Co-Authored-By: morgan@cal.com <morgan@cal.com> * chore: bump platform libs --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: morgan@cal.com <morgan@cal.com> Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
This commit is contained in:
co-authored by
morgan@cal.com <morgan@cal.com>
Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
morgan@cal.com <morgan@cal.com>
Morgan
parent
89e7fca183
commit
e4c47640fc
@@ -105,7 +105,9 @@ async function getHandler(req: NextApiRequest) {
|
||||
|
||||
const userIds = req.query.userId ? extractUserIdsFromQuery(req) : [userId];
|
||||
|
||||
const usersWithCalendars = await UserRepository.findManyByIdsIncludeDestinationAndSelectedCalendars({
|
||||
const usersWithCalendars = await new UserRepository(
|
||||
prisma
|
||||
).findManyByIdsIncludeDestinationAndSelectedCalendars({
|
||||
ids: userIds,
|
||||
});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"@axiomhq/winston": "^1.2.0",
|
||||
"@calcom/platform-constants": "*",
|
||||
"@calcom/platform-enums": "*",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.249",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.252",
|
||||
"@calcom/platform-types": "*",
|
||||
"@calcom/platform-utils": "*",
|
||||
"@calcom/prisma": "*",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { cookies, headers } from "next/headers";
|
||||
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { AppCategories } from "@calcom/prisma/enums";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
@@ -25,7 +26,8 @@ const ServerPage = async () => {
|
||||
const session = await getServerSession({ req });
|
||||
let appStore, userAdminTeamsIds: number[];
|
||||
if (session?.user?.id) {
|
||||
const userAdminTeams = await UserRepository.getUserAdminTeams(session.user.id);
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const userAdminTeams = await userRepo.getUserAdminTeams({ userId: session.user.id });
|
||||
userAdminTeamsIds = userAdminTeams?.teams?.map(({ team }) => team.id) ?? [];
|
||||
appStore = await getAppRegistryWithCredentials(session.user.id, userAdminTeamsIds);
|
||||
} else {
|
||||
|
||||
+5
-2
@@ -6,6 +6,7 @@ import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequir
|
||||
import { UsersEditView } from "@calcom/features/ee/users/pages/users-edit-view";
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
const userIdSchema = z.object({ id: z.coerce.number() });
|
||||
|
||||
@@ -21,7 +22,8 @@ export const generateMetadata = async ({ params }: { params: Params }) => {
|
||||
);
|
||||
}
|
||||
|
||||
const user = await UserRepository.adminFindById(input.data.id);
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const user = await userRepo.adminFindById(input.data.id);
|
||||
|
||||
return await _generateMetadata(
|
||||
(t) => `${t("editing_user")}: ${user.username}`,
|
||||
@@ -37,7 +39,8 @@ const Page = async ({ params }: { params: Params }) => {
|
||||
|
||||
if (!input.success) throw new Error("Invalid access");
|
||||
|
||||
const user = await UserRepository.adminFindById(input.data.id);
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const user = await userRepo.adminFindById(input.data.id);
|
||||
const t = await getTranslate();
|
||||
|
||||
return (
|
||||
|
||||
@@ -11,6 +11,7 @@ import { HttpError } from "@calcom/lib/http-error";
|
||||
import notEmpty from "@calcom/lib/notEmpty";
|
||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
@@ -29,7 +30,10 @@ async function authMiddleware() {
|
||||
throw new HttpError({ statusCode: 401, message: "Not authenticated" });
|
||||
}
|
||||
|
||||
const userWithCredentials = await UserRepository.findUserWithCredentials({ id: session.user.id });
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const userWithCredentials = await userRepo.findUserWithCredentials({
|
||||
id: session.user.id,
|
||||
});
|
||||
|
||||
if (!userWithCredentials) {
|
||||
throw new HttpError({ statusCode: 401, message: "Not authenticated" });
|
||||
|
||||
@@ -8,6 +8,7 @@ import { z } from "zod";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { userMetadata } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { CardComponent } from "@lib/plain/card-components";
|
||||
@@ -474,7 +475,8 @@ async function handler(request: NextRequest) {
|
||||
// Validate request body
|
||||
const { cardKeys, customer } = inputSchema.parse(requestBody);
|
||||
|
||||
const user = await UserRepository.findByEmail({ email: customer.email });
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const user = await userRepo.findByEmail({ email: customer.email });
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({
|
||||
@@ -502,7 +504,7 @@ async function handler(request: NextRequest) {
|
||||
}
|
||||
|
||||
// Fetch team details including userId and team name
|
||||
const teamMemberships = await UserRepository.findTeamsByUserId({ userId: user.id });
|
||||
const teamMemberships = await userRepo.findTeamsByUserId({ userId: user.id });
|
||||
const firstTeam = teamMemberships.teams[0] ?? null;
|
||||
|
||||
// Parse user metadata
|
||||
|
||||
@@ -18,7 +18,8 @@ import { STEPS } from "~/apps/installation/[[...step]]/constants";
|
||||
import type { OnboardingPageProps, TEventTypeGroup } from "~/apps/installation/[[...step]]/step-view";
|
||||
|
||||
const getUser = async (userId: number) => {
|
||||
const userAdminTeams = await UserRepository.getUserAdminTeams(userId);
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const userAdminTeams = await userRepo.getUserAdminTeams({ userId });
|
||||
|
||||
if (!userAdminTeams?.id) {
|
||||
return null;
|
||||
|
||||
@@ -101,7 +101,8 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
|
||||
|
||||
name = profileUsername || username;
|
||||
|
||||
const [user] = await UserRepository.findUsersByUsername({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const [user] = await userRepo.findUsersByUsername({
|
||||
usernameList: [name],
|
||||
orgSlug: org,
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { GetServerSidePropsContext } from "next";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const { req } = context;
|
||||
@@ -12,7 +13,8 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||
}
|
||||
|
||||
const user = await UserRepository.findUserTeams({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const user = await userRepo.findUserTeams({
|
||||
id: session.user.id,
|
||||
});
|
||||
|
||||
|
||||
@@ -101,8 +101,9 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
|
||||
const eventType = booking.eventType ? booking.eventType : getDefaultEvent(dynamicEventSlugRef);
|
||||
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const enrichedBookingUser = booking.user
|
||||
? await UserRepository.enrichUserWithItsProfile({ user: booking.user })
|
||||
? await userRepo.enrichUserWithItsProfile({ user: booking.user })
|
||||
: null;
|
||||
|
||||
const eventUrl = await buildEventUrlFromBooking({
|
||||
|
||||
@@ -140,9 +140,10 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
})
|
||||
: false;
|
||||
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const profile = booking.user
|
||||
? (
|
||||
await UserRepository.enrichUserWithItsProfile({
|
||||
await userRepo.enrichUserWithItsProfile({
|
||||
user: booking.user,
|
||||
})
|
||||
).profile
|
||||
|
||||
@@ -135,7 +135,8 @@ async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
|
||||
}
|
||||
}
|
||||
|
||||
const usersInOrgContext = await UserRepository.findUsersByUsername({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const usersInOrgContext = await userRepo.findUsersByUsername({
|
||||
usernameList: usernames,
|
||||
orgSlug: isValidOrgDomain ? currentOrgDomain : null,
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import { stripMarkdown } from "@calcom/lib/stripMarkdown";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { RedirectType, type EventType, type User } from "@calcom/prisma/client";
|
||||
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
import type { UserProfile } from "@calcom/types/UserProfile";
|
||||
@@ -199,7 +200,9 @@ export const getServerSideProps: GetServerSideProps<UserPageProps> = async (cont
|
||||
};
|
||||
|
||||
export async function getUsersInOrgContext(usernameList: string[], orgSlug: string | null) {
|
||||
const usersInOrgContext = await UserRepository.findUsersByUsername({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
|
||||
const usersInOrgContext = await userRepo.findUsersByUsername({
|
||||
usernameList,
|
||||
orgSlug,
|
||||
});
|
||||
@@ -212,7 +215,7 @@ export async function getUsersInOrgContext(usernameList: string[], orgSlug: stri
|
||||
// the platform organization does not have a domain. In this case there is no org domain but also platform member
|
||||
// "User.organization" is not null so "UserRepository.findUsersByUsername" returns empty array and we do this as a last resort
|
||||
// call to find platform member.
|
||||
return await UserRepository.findPlatformMembersByUsernames({
|
||||
return await userRepo.findPlatformMembersByUsernames({
|
||||
usernameList,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import logger from "@calcom/lib/logger";
|
||||
import { uploadAvatar } from "@calcom/lib/server/avatar";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import { resizeBase64Image } from "@calcom/lib/server/resizeBase64Image";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
export async function updateProfilePhotoGoogle(oAuth2Client: OAuth2Client, userId: number) {
|
||||
try {
|
||||
@@ -25,11 +26,13 @@ export async function updateProfilePhotoGoogle(oAuth2Client: OAuth2Client, userI
|
||||
avatar: await resizeBase64Image(avatarUrl),
|
||||
userId,
|
||||
});
|
||||
await UserRepository.updateAvatar({ id: userId, avatarUrl: resizedAvatarUrl });
|
||||
const userRepo = new UserRepository(prisma);
|
||||
await userRepo.updateAvatar({ id: userId, avatarUrl: resizedAvatarUrl });
|
||||
return;
|
||||
}
|
||||
|
||||
await UserRepository.updateAvatar({ id: userId, avatarUrl });
|
||||
const userRepo = new UserRepository(prisma);
|
||||
await userRepo.updateAvatar({ id: userId, avatarUrl });
|
||||
} catch (error) {
|
||||
logger.error("Error updating avatarUrl from google calendar connect", error);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
export const throwIfNotHaveAdminAccessToTeam = async ({
|
||||
teamId,
|
||||
@@ -11,7 +12,8 @@ export const throwIfNotHaveAdminAccessToTeam = async ({
|
||||
if (!teamId) {
|
||||
return;
|
||||
}
|
||||
const userAdminTeams = await UserRepository.getUserAdminTeams(userId);
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const userAdminTeams = await userRepo.getUserAdminTeams({ userId });
|
||||
const teamsUserHasAdminAccessFor = userAdminTeams?.teams?.map(({ team }) => team.id) ?? [];
|
||||
const hasAdminAccessToTeam = teamsUserHasAdminAccessFor.some((id) => id === teamId);
|
||||
|
||||
|
||||
@@ -99,9 +99,10 @@ export const getServerSidePropsForSingleFormView = async function getServerSideP
|
||||
|
||||
const { UserRepository } = await import("@calcom/lib/server/repository/user");
|
||||
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const formWithUserInfoProfile = {
|
||||
...form,
|
||||
user: await UserRepository.enrichUserWithItsProfile({ user: form.user }),
|
||||
user: await userRepo.enrichUserWithItsProfile({ user: form.user }),
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -67,9 +67,10 @@ export const getServerSideProps = async function getServerSideProps(
|
||||
}
|
||||
|
||||
const { UserRepository } = await import("@calcom/lib/server/repository/user");
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const formWithUserProfile = {
|
||||
...form,
|
||||
user: await UserRepository.enrichUserWithItsProfile({ user: form.user }),
|
||||
user: await userRepo.enrichUserWithItsProfile({ user: form.user }),
|
||||
};
|
||||
|
||||
if (
|
||||
|
||||
@@ -83,9 +83,10 @@ async function getResponseWithFormFieldsHandler({ ctx, input }: GetResponseWithF
|
||||
}
|
||||
|
||||
const { UserRepository } = await import("@calcom/lib/server/repository/user");
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const formWithUserProfile = {
|
||||
...form,
|
||||
user: await UserRepository.enrichUserWithItsProfile({ user: form.user }),
|
||||
user: await userRepo.enrichUserWithItsProfile({ user: form.user }),
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -80,7 +80,8 @@ export async function getServerSession(options: {
|
||||
return null;
|
||||
}
|
||||
|
||||
const user = await UserRepository.enrichUserWithTheProfile({
|
||||
const userRepository = new UserRepository(prisma);
|
||||
const user = await userRepository.enrichUserWithTheProfile({
|
||||
user: userFromDb,
|
||||
upId,
|
||||
});
|
||||
|
||||
@@ -116,7 +116,8 @@ const providers: Provider[] = [
|
||||
throw new Error(ErrorCode.InternalServerError);
|
||||
}
|
||||
|
||||
const user = await UserRepository.findByEmailAndIncludeProfilesAndPassword({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const user = await userRepo.findByEmailAndIncludeProfilesAndPassword({
|
||||
email: credentials.email,
|
||||
});
|
||||
// Don't leak information about it being username or password that is invalid
|
||||
@@ -291,7 +292,8 @@ if (isSAMLLoginEnabled) {
|
||||
locale?: string;
|
||||
}) => {
|
||||
log.debug("BoxyHQ:profile", safeStringify({ profile }));
|
||||
const user = await UserRepository.findByEmailAndIncludeProfilesAndPassword({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const user = await userRepo.findByEmailAndIncludeProfilesAndPassword({
|
||||
email: profile.email || "",
|
||||
});
|
||||
return {
|
||||
@@ -355,9 +357,8 @@ if (isSAMLLoginEnabled) {
|
||||
|
||||
const { id, firstName, lastName } = userInfo;
|
||||
const email = userInfo.email.toLowerCase();
|
||||
let user = !email
|
||||
? undefined
|
||||
: await UserRepository.findByEmailAndIncludeProfilesAndPassword({ email });
|
||||
const userRepo = new UserRepository(prisma);
|
||||
let user = !email ? undefined : await userRepo.findByEmailAndIncludeProfilesAndPassword({ email });
|
||||
if (!user) {
|
||||
const hostedCal = Boolean(HOSTED_CAL_FEATURES);
|
||||
if (hostedCal && email) {
|
||||
@@ -373,7 +374,7 @@ if (isSAMLLoginEnabled) {
|
||||
createUsersAndConnectToOrgProps,
|
||||
org,
|
||||
});
|
||||
user = await UserRepository.findByEmailAndIncludeProfilesAndPassword({
|
||||
user = await userRepo.findByEmailAndIncludeProfilesAndPassword({
|
||||
email: email,
|
||||
});
|
||||
}
|
||||
|
||||
+73
-11
@@ -9,18 +9,30 @@ import { describe, test, expect, vi } from "vitest";
|
||||
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
|
||||
// vi.mock("@calcom/lib/server/repository/user", () => {
|
||||
// return {
|
||||
// enrichUserWithItsProfile
|
||||
// }
|
||||
// })
|
||||
vi.mock("@calcom/lib/server/repository/user", () => {
|
||||
return {
|
||||
UserRepository: vi.fn().mockImplementation(() => ({
|
||||
enrichUserWithItsProfile: vi.fn(),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
describe("getAllCredentialsIncludeServiceAccountKey", () => {
|
||||
test("Get an individual's credentials", async () => {
|
||||
vi.spyOn(UserRepository, "enrichUserWithItsProfile").mockReturnValue({
|
||||
const mockEnrichUserWithItsProfile = vi.fn().mockReturnValue({
|
||||
profile: null,
|
||||
});
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
enrichUserWithItsProfile: mockEnrichUserWithItsProfile,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const getAllCredentialsIncludeServiceAccountKey = (await import("./getAllCredentials"))
|
||||
.getAllCredentialsIncludeServiceAccountKey;
|
||||
|
||||
@@ -62,10 +74,20 @@ describe("getAllCredentialsIncludeServiceAccountKey", () => {
|
||||
describe("If CRM is enabled on the event type", () => {
|
||||
describe("With _crm credentials", () => {
|
||||
test("For users", async () => {
|
||||
vi.spyOn(UserRepository, "enrichUserWithItsProfile").mockReturnValue({
|
||||
const mockEnrichUserWithItsProfile = vi.fn().mockReturnValue({
|
||||
profile: null,
|
||||
});
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
enrichUserWithItsProfile: mockEnrichUserWithItsProfile,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const getAllCredentialsIncludeServiceAccountKey = (await import("./getAllCredentials"))
|
||||
.getAllCredentialsIncludeServiceAccountKey;
|
||||
|
||||
@@ -136,10 +158,20 @@ describe("getAllCredentialsIncludeServiceAccountKey", () => {
|
||||
expect(credentials).toContainEqual(expect.objectContaining({ userId: 1, type: "salesforce_crm" }));
|
||||
});
|
||||
test("For teams", async () => {
|
||||
vi.spyOn(UserRepository, "enrichUserWithItsProfile").mockReturnValue({
|
||||
const mockEnrichUserWithItsProfile = vi.fn().mockReturnValue({
|
||||
profile: null,
|
||||
});
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
enrichUserWithItsProfile: mockEnrichUserWithItsProfile,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const getAllCredentialsIncludeServiceAccountKey = (await import("./getAllCredentials"))
|
||||
.getAllCredentialsIncludeServiceAccountKey;
|
||||
|
||||
@@ -203,10 +235,20 @@ describe("getAllCredentialsIncludeServiceAccountKey", () => {
|
||||
expect(credentials).toContainEqual(expect.objectContaining({ teamId: 1, type: "salesforce_crm" }));
|
||||
});
|
||||
test("For child of managed event type", async () => {
|
||||
vi.spyOn(UserRepository, "enrichUserWithItsProfile").mockReturnValue({
|
||||
const mockEnrichUserWithItsProfile = vi.fn().mockReturnValue({
|
||||
profile: null,
|
||||
});
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
enrichUserWithItsProfile: mockEnrichUserWithItsProfile,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const getAllCredentialsIncludeServiceAccountKey = (await import("./getAllCredentials"))
|
||||
.getAllCredentialsIncludeServiceAccountKey;
|
||||
|
||||
@@ -298,10 +340,20 @@ describe("getAllCredentialsIncludeServiceAccountKey", () => {
|
||||
const getAllCredentialsIncludeServiceAccountKey = (await import("./getAllCredentials"))
|
||||
.getAllCredentialsIncludeServiceAccountKey;
|
||||
const orgId = 3;
|
||||
vi.spyOn(UserRepository, "enrichUserWithItsProfile").mockReturnValue({
|
||||
const mockEnrichUserWithItsProfile = vi.fn().mockReturnValue({
|
||||
profile: { organizationId: orgId },
|
||||
});
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
enrichUserWithItsProfile: mockEnrichUserWithItsProfile,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const crmCredential = {
|
||||
id: 1,
|
||||
type: "salesforce_crm",
|
||||
@@ -611,10 +663,20 @@ describe("getAllCredentialsIncludeServiceAccountKey", () => {
|
||||
const getAllCredentialsIncludeServiceAccountKey = (await import("./getAllCredentials"))
|
||||
.getAllCredentialsIncludeServiceAccountKey;
|
||||
const orgId = 3;
|
||||
vi.spyOn(UserRepository, "enrichUserWithItsProfile").mockReturnValue({
|
||||
const mockEnrichUserWithItsProfile = vi.fn().mockReturnValue({
|
||||
profile: { organizationId: orgId },
|
||||
});
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
enrichUserWithItsProfile: mockEnrichUserWithItsProfile,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const crmCredential = {
|
||||
id: 1,
|
||||
type: "salesforce_crm",
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ export const getAllCredentialsIncludeServiceAccountKey = async (
|
||||
}
|
||||
}
|
||||
|
||||
const { profile } = await UserRepository.enrichUserWithItsProfile({
|
||||
const { profile } = await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: user,
|
||||
});
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ export const findUsersByUsername = async ({
|
||||
usernameList: string[];
|
||||
}) => {
|
||||
log.debug("findUsersByUsername", { usernameList, orgSlug });
|
||||
const { where, profiles } = await UserRepository._getWhereClauseForFindingUsersByUsername({
|
||||
const { where, profiles } = await new UserRepository(prisma)._getWhereClauseForFindingUsersByUsername({
|
||||
orgSlug,
|
||||
usernameList,
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { CredentialRepository } from "@calcom/lib/server/repository/credential";
|
||||
import { DestinationCalendarRepository } from "@calcom/lib/server/repository/destinationCalendar";
|
||||
import { EventTypeRepository } from "@calcom/lib/server/repository/eventType";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
const testUser = {
|
||||
email: "test@test.com",
|
||||
@@ -38,7 +39,7 @@ describe("deleteCredential", () => {
|
||||
test("Delete video credential", async () => {
|
||||
const handleDeleteCredential = (await import("./handleDeleteCredential")).default;
|
||||
|
||||
const user = await UserRepository.create({
|
||||
const user = await new UserRepository(prisma).create({
|
||||
...testUser,
|
||||
});
|
||||
|
||||
@@ -74,7 +75,7 @@ describe("deleteCredential", () => {
|
||||
test("Delete calendar credential", async () => {
|
||||
const handleDeleteCredential = (await import("./handleDeleteCredential")).default;
|
||||
|
||||
const user = await UserRepository.create({
|
||||
const user = await new UserRepository(prisma).create({
|
||||
...testUser,
|
||||
});
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ const handleUserEvents = async (event: DirectorySyncEvent, organizationId: numbe
|
||||
|
||||
if (user) {
|
||||
if (eventData.active) {
|
||||
if (UserRepository.isAMemberOfOrganization({ user, organizationId })) {
|
||||
if (await new UserRepository(prisma).isAMemberOfOrganization({ user, organizationId })) {
|
||||
await syncCustomAttributesToUser({
|
||||
event,
|
||||
userEmail,
|
||||
|
||||
@@ -105,7 +105,7 @@ export class OrganizationPaymentService {
|
||||
|
||||
const stripeCustomerId = customer.stripeCustomerId;
|
||||
if (existingCustomer && parsedMetadata) {
|
||||
await UserRepository.updateStripeCustomerId({
|
||||
await new UserRepository(prisma).updateStripeCustomerId({
|
||||
id: existingCustomer.id,
|
||||
stripeCustomerId,
|
||||
existingMetadata: parsedMetadata,
|
||||
|
||||
@@ -317,7 +317,7 @@ async function ensureStripeCustomerIdIsUpdated({
|
||||
}) {
|
||||
const parsedMetadata = userMetadata.parse(owner.metadata);
|
||||
|
||||
await UserRepository.updateStripeCustomerId({
|
||||
await new UserRepository(prisma).updateStripeCustomerId({
|
||||
id: owner.id,
|
||||
stripeCustomerId: stripeCustomerId,
|
||||
existingMetadata: parsedMetadata,
|
||||
|
||||
@@ -279,7 +279,7 @@ export const findUserToBeOrgOwner = async (email: string) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await UserRepository.enrichUserWithItsProfile({
|
||||
return await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -237,7 +237,7 @@ export const getPublicEvent = async (
|
||||
const orgQuery = org ? getSlugOrRequestedSlug(org) : null;
|
||||
// In case of dynamic group event, we fetch user's data and use the default event.
|
||||
if (usernameList.length > 1) {
|
||||
const usersInOrgContext = await UserRepository.findUsersByUsername({
|
||||
const usersInOrgContext = await new UserRepository(prisma).findUsersByUsername({
|
||||
usernameList,
|
||||
orgSlug: org,
|
||||
});
|
||||
@@ -397,7 +397,7 @@ export const getPublicEvent = async (
|
||||
const usersAsHosts = event.hosts.map((host) => host.user);
|
||||
|
||||
// Enrich users in a single batch call
|
||||
const enrichedUsers = await UserRepository.enrichUsersWithTheirProfiles(usersAsHosts);
|
||||
const enrichedUsers = await new UserRepository(prisma).enrichUsersWithTheirProfiles(usersAsHosts);
|
||||
|
||||
// Map enriched users back to the hosts
|
||||
const hosts = event.hosts.map((host, index) => ({
|
||||
@@ -408,7 +408,7 @@ export const getPublicEvent = async (
|
||||
const eventWithUserProfiles = {
|
||||
...event,
|
||||
owner: event.owner
|
||||
? await UserRepository.enrichUserWithItsProfile({
|
||||
? await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: event.owner,
|
||||
})
|
||||
: null,
|
||||
@@ -640,7 +640,7 @@ async function getOwnerFromUsersArray(prisma: PrismaClient, eventTypeId: number)
|
||||
if (!users.length) return null;
|
||||
|
||||
// Batch enrich users in a single call
|
||||
const enrichedUsers = await UserRepository.enrichUsersWithTheirProfiles(users);
|
||||
const enrichedUsers = await new UserRepository(prisma).enrichUsersWithTheirProfiles(users);
|
||||
|
||||
// Map the enriched users back to include the organization info
|
||||
const usersWithUserProfile = enrichedUsers.map((user) => ({
|
||||
|
||||
@@ -47,7 +47,8 @@ export const createAProfileForAnExistingUser = async ({
|
||||
movedFromUserId: user.id,
|
||||
});
|
||||
|
||||
await UserRepository.updateWhereId({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
await userRepo.updateWhereId({
|
||||
whereId: user.id,
|
||||
data: {
|
||||
movedToProfileId: profile.id,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { CredentialRepository } from "@calcom/lib/server/repository/credential";
|
||||
import type { ServiceAccountKey } from "@calcom/lib/server/repository/delegationCredential";
|
||||
import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { CredentialForCalendarService, CredentialPayload } from "@calcom/types/Credential";
|
||||
|
||||
import { UserRepository } from "../server/repository/user";
|
||||
@@ -593,7 +594,7 @@ export async function findUniqueDelegationCalendarCredential({
|
||||
}) {
|
||||
const [delegationCredential, user] = await Promise.all([
|
||||
DelegationCredentialRepository.findByIdIncludeSensitiveServiceAccountKey({ id: delegationCredentialId }),
|
||||
UserRepository.findById({ id: userId }),
|
||||
new UserRepository(prisma).findById({ id: userId }),
|
||||
]);
|
||||
|
||||
if (!delegationCredential) {
|
||||
|
||||
@@ -66,11 +66,12 @@ export const getEventTypeById = async ({
|
||||
const newMetadata = eventTypeMetaDataSchemaWithTypedApps.parse(metadata || {}) || {};
|
||||
const apps = newMetadata?.apps || {};
|
||||
const eventTypeWithParsedMetadata = { ...rawEventType, metadata: newMetadata };
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const eventTeamMembershipsWithUserProfile = [];
|
||||
for (const eventTeamMembership of rawEventType.team?.members || []) {
|
||||
eventTeamMembershipsWithUserProfile.push({
|
||||
...eventTeamMembership,
|
||||
user: await UserRepository.enrichUserWithItsProfile({
|
||||
user: await userRepo.enrichUserWithItsProfile({
|
||||
user: eventTeamMembership.user,
|
||||
}),
|
||||
});
|
||||
@@ -81,7 +82,7 @@ export const getEventTypeById = async ({
|
||||
childrenWithUserProfile.push({
|
||||
...child,
|
||||
owner: child.owner
|
||||
? await UserRepository.enrichUserWithItsProfile({
|
||||
? await userRepo.enrichUserWithItsProfile({
|
||||
user: child.owner,
|
||||
})
|
||||
: null,
|
||||
@@ -91,7 +92,7 @@ export const getEventTypeById = async ({
|
||||
const eventTypeUsersWithUserProfile = [];
|
||||
for (const eventTypeUser of rawEventType.users) {
|
||||
eventTypeUsersWithUserProfile.push(
|
||||
await UserRepository.enrichUserWithItsProfile({
|
||||
await userRepo.enrichUserWithItsProfile({
|
||||
user: eventTypeUser,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -13,6 +13,7 @@ import { EventTypeRepository } from "@calcom/lib/server/repository/eventType";
|
||||
import { MembershipRepository } from "@calcom/lib/server/repository/membership";
|
||||
import { ProfileRepository } from "@calcom/lib/server/repository/profile";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { MembershipRole, SchedulingType } from "@calcom/prisma/enums";
|
||||
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { eventTypeMetaDataSchemaWithUntypedApps } from "@calcom/prisma/zod-utils";
|
||||
@@ -105,32 +106,35 @@ export const getEventTypesByViewer = async (user: User, filters?: Filters, forRo
|
||||
|
||||
type UserEventTypes = (typeof profileEventTypes)[number];
|
||||
|
||||
const mapEventType = async (eventType: UserEventTypes) => ({
|
||||
...eventType,
|
||||
safeDescription: eventType?.description ? markdownToSafeHTML(eventType.description) : undefined,
|
||||
users: await Promise.all(
|
||||
(!!eventType?.hosts?.length ? eventType?.hosts.map((host) => host.user) : eventType.users).map(
|
||||
async (u) =>
|
||||
await UserRepository.enrichUserWithItsProfile({
|
||||
user: u,
|
||||
})
|
||||
)
|
||||
),
|
||||
metadata: eventType.metadata ? eventTypeMetaDataSchemaWithUntypedApps.parse(eventType.metadata) : null,
|
||||
children: await Promise.all(
|
||||
(eventType.children || []).map(async (c) => ({
|
||||
...c,
|
||||
users: await Promise.all(
|
||||
c.users.map(
|
||||
async (u) =>
|
||||
await UserRepository.enrichUserWithItsProfile({
|
||||
user: u,
|
||||
})
|
||||
)
|
||||
),
|
||||
}))
|
||||
),
|
||||
});
|
||||
const mapEventType = async (eventType: UserEventTypes) => {
|
||||
const userRepo = new UserRepository(prisma);
|
||||
return {
|
||||
...eventType,
|
||||
safeDescription: eventType?.description ? markdownToSafeHTML(eventType.description) : undefined,
|
||||
users: await Promise.all(
|
||||
(!!eventType?.hosts?.length ? eventType?.hosts.map((host) => host.user) : eventType.users).map(
|
||||
async (u) =>
|
||||
await userRepo.enrichUserWithItsProfile({
|
||||
user: u,
|
||||
})
|
||||
)
|
||||
),
|
||||
metadata: eventType.metadata ? eventTypeMetaDataSchemaWithUntypedApps.parse(eventType.metadata) : null,
|
||||
children: await Promise.all(
|
||||
(eventType.children || []).map(async (c) => ({
|
||||
...c,
|
||||
users: await Promise.all(
|
||||
c.users.map(
|
||||
async (u) =>
|
||||
await userRepo.enrichUserWithItsProfile({
|
||||
user: u,
|
||||
})
|
||||
)
|
||||
),
|
||||
}))
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const userEventTypes = (await Promise.all(profileEventTypes.map(mapEventType))).filter((eventType) => {
|
||||
const isAChildEvent = eventType.parentId;
|
||||
|
||||
@@ -23,7 +23,13 @@ import { getRoutedUrl } from "./getRoutedUrl";
|
||||
vi.mock("@calcom/lib/checkRateLimitAndThrowError");
|
||||
vi.mock("@calcom/app-store/routing-forms/lib/handleResponse");
|
||||
vi.mock("@calcom/lib/server/repository/routingForm");
|
||||
vi.mock("@calcom/lib/server/repository/user");
|
||||
vi.mock("@calcom/lib/server/repository/user", () => {
|
||||
return {
|
||||
UserRepository: vi.fn().mockImplementation(() => ({
|
||||
enrichUserWithItsProfile: vi.fn(),
|
||||
})),
|
||||
};
|
||||
});
|
||||
vi.mock("@calcom/features/ee/organizations/lib/orgDomains");
|
||||
vi.mock("@calcom/features/routing-forms/lib/isAuthorizedToViewForm");
|
||||
vi.mock("@calcom/app-store/routing-forms/lib/getSerializableForm");
|
||||
@@ -79,7 +85,17 @@ describe("getRoutedUrl", () => {
|
||||
// Provide default mock implementations
|
||||
vi.mocked(orgDomainConfig).mockReturnValue({ currentOrgDomain: null });
|
||||
vi.mocked(RoutingFormRepository.findFormByIdIncludeUserTeamAndOrg).mockResolvedValue(null);
|
||||
vi.mocked(UserRepository.enrichUserWithItsProfile).mockImplementation(async ({ user }) => user);
|
||||
|
||||
const mockEnrichUserWithItsProfile = vi.fn().mockImplementation(async ({ user }) => user);
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
enrichUserWithItsProfile: mockEnrichUserWithItsProfile,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
vi.mocked(isAuthorizedToViewFormOnOrgDomain).mockReturnValue(true);
|
||||
vi.mocked(getSerializableForm).mockResolvedValue(mockSerializableForm as never);
|
||||
vi.mocked(findMatchingRoute).mockReturnValue(null);
|
||||
|
||||
@@ -22,6 +22,7 @@ import logger from "@calcom/lib/logger";
|
||||
import { withReporting } from "@calcom/lib/sentryWrapper";
|
||||
import { RoutingFormRepository } from "@calcom/lib/server/repository/routingForm";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
@@ -102,9 +103,10 @@ const _getRoutedUrl = async (context: Pick<GetServerSidePropsContext, "query" |
|
||||
}
|
||||
|
||||
const profileEnrichmentStart = performance.now();
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const formWithUserProfile = {
|
||||
...form,
|
||||
user: await UserRepository.enrichUserWithItsProfile({ user: form.user }),
|
||||
user: await userRepo.enrichUserWithItsProfile({ user: form.user }),
|
||||
};
|
||||
timeTaken.profileEnrichment = performance.now() - profileEnrichmentStart;
|
||||
|
||||
|
||||
@@ -183,10 +183,11 @@ export async function getTeamWithMembers(args: {
|
||||
if (!teamOrOrg) return null;
|
||||
|
||||
const teamOrOrgMemberships = [];
|
||||
const userRepo = new UserRepository(prisma);
|
||||
for (const membership of teamOrOrg.members) {
|
||||
teamOrOrgMemberships.push({
|
||||
...membership,
|
||||
user: await UserRepository.enrichUserWithItsProfile({
|
||||
user: await userRepo.enrichUserWithItsProfile({
|
||||
user: membership.user,
|
||||
}),
|
||||
});
|
||||
@@ -236,7 +237,7 @@ export async function getTeamWithMembers(args: {
|
||||
const usersWithUserProfile = [];
|
||||
for (const { user } of eventType.hosts) {
|
||||
usersWithUserProfile.push(
|
||||
await UserRepository.enrichUserWithItsProfile({
|
||||
await userRepo.enrichUserWithItsProfile({
|
||||
user,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -145,7 +145,8 @@ export class BookingRepository {
|
||||
if (!booking.eventType || !booking.eventType.teamId) return false;
|
||||
|
||||
// TODO add checks for team and org
|
||||
const isAdminOrUser = await UserRepository.isAdminOfTeamOrParentOrg({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const isAdminOrUser = await userRepo.isAdminOfTeamOrParentOrg({
|
||||
userId,
|
||||
teamId: booking.eventType.teamId,
|
||||
});
|
||||
|
||||
@@ -93,7 +93,8 @@ export class OrganizationRepository {
|
||||
logger.debug("createWithNonExistentOwner", safeStringify({ orgData, owner }));
|
||||
const organization = await this.create(orgData);
|
||||
const ownerUsernameInOrg = getOrgUsernameFromEmail(owner.email, orgData.autoAcceptEmail);
|
||||
const ownerInDb = await UserRepository.create({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const ownerInDb = await userRepo.create({
|
||||
email: owner.email,
|
||||
username: ownerUsernameInOrg,
|
||||
organizationId: organization.id,
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("UserRepository", () => {
|
||||
|
||||
describe("create", () => {
|
||||
test("Should create a user without a password", async () => {
|
||||
const user = await UserRepository.create({
|
||||
const user = await new UserRepository(prismock).create({
|
||||
username: "test",
|
||||
email: "test@example.com",
|
||||
organizationId: null,
|
||||
@@ -50,7 +50,7 @@ describe("UserRepository", () => {
|
||||
});
|
||||
|
||||
test("If locked param is passed, user should be locked", async () => {
|
||||
const user = await UserRepository.create({
|
||||
const user = await new UserRepository(prismock).create({
|
||||
username: "test",
|
||||
email: "test@example.com",
|
||||
organizationId: null,
|
||||
@@ -78,7 +78,7 @@ describe("UserRepository", () => {
|
||||
const organizationId = 123;
|
||||
const username = "test";
|
||||
|
||||
const user = await UserRepository.create({
|
||||
const user = await new UserRepository(prismock).create({
|
||||
username,
|
||||
email: "test@example.com",
|
||||
organizationId,
|
||||
|
||||
@@ -4,9 +4,9 @@ import { whereClauseForOrgWithSlugOrRequestedSlug } from "@calcom/ee/organizatio
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import prisma, { availabilityUserSelect } from "@calcom/prisma";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
import type { User as UserType } from "@calcom/prisma/client";
|
||||
import { availabilityUserSelect } from "@calcom/prisma";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
import type { Prisma, User as UserType } from "@calcom/prisma/client";
|
||||
import type { CreationSource } from "@calcom/prisma/enums";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
||||
@@ -23,6 +23,46 @@ export type { UserWithLegacySelectedCalendars } from "../withSelectedCalendars";
|
||||
export { withSelectedCalendars };
|
||||
export type UserAdminTeams = number[];
|
||||
|
||||
export type SessionUser = {
|
||||
id: number;
|
||||
username: string | null;
|
||||
name: string | null;
|
||||
email: string;
|
||||
emailVerified: Date | null;
|
||||
bio: string | null;
|
||||
avatarUrl: string | null;
|
||||
timeZone: string;
|
||||
weekStart: string;
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
defaultScheduleId: number | null;
|
||||
bufferTime: number;
|
||||
theme: string | null;
|
||||
appTheme: string | null;
|
||||
createdDate: Date;
|
||||
hideBranding: boolean;
|
||||
twoFactorEnabled: boolean;
|
||||
disableImpersonation: boolean;
|
||||
identityProvider: string | null;
|
||||
identityProviderId: string | null;
|
||||
brandColor: string | null;
|
||||
darkBrandColor: string | null;
|
||||
movedToProfileId: number | null;
|
||||
completedOnboarding: boolean;
|
||||
destinationCalendar: any;
|
||||
locale: string;
|
||||
timeFormat: number | null;
|
||||
trialEndsAt: Date | null;
|
||||
metadata: any;
|
||||
role: string;
|
||||
allowDynamicBooking: boolean;
|
||||
allowSEOIndexing: boolean;
|
||||
receiveMonthlyDigestEmail: boolean;
|
||||
profiles: any[];
|
||||
allSelectedCalendars: any[];
|
||||
userLevelSelectedCalendars: any[];
|
||||
};
|
||||
|
||||
const log = logger.getSubLogger({ prefix: ["[repository/user]"] });
|
||||
|
||||
export const ORGANIZATION_ID_UNKNOWN = "ORGANIZATION_ID_UNKNOWN";
|
||||
@@ -80,8 +120,10 @@ const userSelect = {
|
||||
} satisfies Prisma.UserSelect;
|
||||
|
||||
export class UserRepository {
|
||||
static async findTeamsByUserId({ userId }: { userId: UserType["id"] }) {
|
||||
const teamMemberships = await prisma.membership.findMany({
|
||||
constructor(private prismaClient: PrismaClient) {}
|
||||
|
||||
async findTeamsByUserId({ userId }: { userId: UserType["id"] }) {
|
||||
const teamMemberships = await this.prismaClient.membership.findMany({
|
||||
where: {
|
||||
userId: userId,
|
||||
},
|
||||
@@ -103,8 +145,8 @@ export class UserRepository {
|
||||
};
|
||||
}
|
||||
|
||||
static async findOrganizations({ userId }: { userId: UserType["id"] }) {
|
||||
const { acceptedTeamMemberships } = await UserRepository.findTeamsByUserId({
|
||||
async findOrganizations({ userId }: { userId: UserType["id"] }) {
|
||||
const { acceptedTeamMemberships } = await this.findTeamsByUserId({
|
||||
userId,
|
||||
});
|
||||
|
||||
@@ -122,20 +164,14 @@ export class UserRepository {
|
||||
/**
|
||||
* It is aware of the fact that a user can be part of multiple organizations.
|
||||
*/
|
||||
static async findUsersByUsername({
|
||||
orgSlug,
|
||||
usernameList,
|
||||
}: {
|
||||
orgSlug: string | null;
|
||||
usernameList: string[];
|
||||
}) {
|
||||
const { where, profiles } = await UserRepository._getWhereClauseForFindingUsersByUsername({
|
||||
async findUsersByUsername({ orgSlug, usernameList }: { orgSlug: string | null; usernameList: string[] }) {
|
||||
const { where, profiles } = await this._getWhereClauseForFindingUsersByUsername({
|
||||
orgSlug,
|
||||
usernameList,
|
||||
});
|
||||
|
||||
return (
|
||||
await prisma.user.findMany({
|
||||
await this.prismaClient.user.findMany({
|
||||
select: userSelect,
|
||||
where,
|
||||
})
|
||||
@@ -161,9 +197,9 @@ export class UserRepository {
|
||||
});
|
||||
}
|
||||
|
||||
static async findPlatformMembersByUsernames({ usernameList }: { usernameList: string[] }) {
|
||||
async findPlatformMembersByUsernames({ usernameList }: { usernameList: string[] }) {
|
||||
return (
|
||||
await prisma.user.findMany({
|
||||
await this.prismaClient.user.findMany({
|
||||
select: userSelect,
|
||||
where: {
|
||||
username: {
|
||||
@@ -187,7 +223,7 @@ export class UserRepository {
|
||||
});
|
||||
}
|
||||
|
||||
static async _getWhereClauseForFindingUsersByUsername({
|
||||
async _getWhereClauseForFindingUsersByUsername({
|
||||
orgSlug,
|
||||
usernameList,
|
||||
}: {
|
||||
@@ -229,8 +265,8 @@ export class UserRepository {
|
||||
return { where, profiles };
|
||||
}
|
||||
|
||||
static async findByEmail({ email }: { email: string }) {
|
||||
const user = await prisma.user.findUnique({
|
||||
async findByEmail({ email }: { email: string }) {
|
||||
const user = await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
email: email.toLowerCase(),
|
||||
},
|
||||
@@ -239,8 +275,8 @@ export class UserRepository {
|
||||
return user;
|
||||
}
|
||||
|
||||
static async findByEmailAndIncludeProfilesAndPassword({ email }: { email: string }) {
|
||||
const user = await prisma.user.findUnique({
|
||||
async findByEmailAndIncludeProfilesAndPassword({ email }: { email: string }) {
|
||||
const user = await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
email: email.toLowerCase(),
|
||||
},
|
||||
@@ -280,8 +316,8 @@ export class UserRepository {
|
||||
};
|
||||
}
|
||||
|
||||
static async findById({ id }: { id: number }) {
|
||||
const user = await prisma.user.findUnique({
|
||||
async findById({ id }: { id: number }) {
|
||||
const user = await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
@@ -297,8 +333,8 @@ export class UserRepository {
|
||||
};
|
||||
}
|
||||
|
||||
static async findByIds({ ids }: { ids: number[] }) {
|
||||
return prisma.user.findMany({
|
||||
async findByIds({ ids }: { ids: number[] }) {
|
||||
return this.prismaClient.user.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: ids,
|
||||
@@ -308,20 +344,20 @@ export class UserRepository {
|
||||
});
|
||||
}
|
||||
|
||||
static async findByIdOrThrow({ id }: { id: number }) {
|
||||
const user = await UserRepository.findById({ id });
|
||||
async findByIdOrThrow({ id }: { id: number }) {
|
||||
const user = await this.findById({ id });
|
||||
if (!user) {
|
||||
throw new Error(`User with id ${id} not found`);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
static async findManyByOrganization({ organizationId }: { organizationId: number }) {
|
||||
async findManyByOrganization({ organizationId }: { organizationId: number }) {
|
||||
const profiles = await ProfileRepository.findManyForOrg({ organizationId });
|
||||
return profiles.map((profile) => profile.user);
|
||||
}
|
||||
|
||||
static isAMemberOfOrganization({
|
||||
isAMemberOfOrganization({
|
||||
user,
|
||||
organizationId,
|
||||
}: {
|
||||
@@ -331,7 +367,7 @@ export class UserRepository {
|
||||
return user.profiles.some((profile) => profile.organizationId === organizationId);
|
||||
}
|
||||
|
||||
static async findIfAMemberOfSomeOrganization({ user }: { user: { id: number } }) {
|
||||
async findIfAMemberOfSomeOrganization({ user }: { user: { id: number } }) {
|
||||
return !!(
|
||||
await ProfileRepository.findManyForUser({
|
||||
id: user.id,
|
||||
@@ -339,7 +375,7 @@ export class UserRepository {
|
||||
).length;
|
||||
}
|
||||
|
||||
static isMigratedToOrganization({
|
||||
isMigratedToOrganization({
|
||||
user,
|
||||
}: {
|
||||
user: {
|
||||
@@ -351,11 +387,11 @@ export class UserRepository {
|
||||
return !!user.metadata?.migratedToOrgFrom;
|
||||
}
|
||||
|
||||
static async isMovedToAProfile({ user }: { user: Pick<UserType, "movedToProfileId"> }) {
|
||||
async isMovedToAProfile({ user }: { user: Pick<UserType, "movedToProfileId"> }) {
|
||||
return !!user.movedToProfileId;
|
||||
}
|
||||
|
||||
static async enrichUserWithTheProfile<T extends { username: string | null; id: number }>({
|
||||
async enrichUserWithTheProfile<T extends { username: string | null; id: number }>({
|
||||
user,
|
||||
upId,
|
||||
}: {
|
||||
@@ -382,7 +418,13 @@ export class UserRepository {
|
||||
* 2. While dealing with a User that has been moved to a Profile i.e. he was invited to an organization when he was an existing user.
|
||||
* 3. We haven't added profileId to all the entities, so they aren't aware of which profile they belong to. So, we still mostly use this function to enrich the user with its profile.
|
||||
*/
|
||||
static async enrichUserWithItsProfile<T extends { id: number; username: string | null }>({
|
||||
async enrichUserWithItsProfile<
|
||||
T extends {
|
||||
id: number;
|
||||
username: string | null;
|
||||
[key: string]: any;
|
||||
}
|
||||
>({
|
||||
user,
|
||||
}: {
|
||||
user: T;
|
||||
@@ -420,7 +462,7 @@ export class UserRepository {
|
||||
};
|
||||
}
|
||||
|
||||
static async enrichUsersWithTheirProfiles<T extends { id: number; username: string | null }>(
|
||||
async enrichUsersWithTheirProfiles<T extends { id: number; username: string | null }>(
|
||||
users: T[]
|
||||
): Promise<
|
||||
Array<
|
||||
@@ -479,7 +521,7 @@ export class UserRepository {
|
||||
});
|
||||
}
|
||||
|
||||
static enrichUserWithItsProfileBuiltFromUser<T extends { id: number; username: string | null }>({
|
||||
enrichUserWithItsProfileBuiltFromUser<T extends { id: number; username: string | null }>({
|
||||
user,
|
||||
}: {
|
||||
user: T;
|
||||
@@ -495,7 +537,7 @@ export class UserRepository {
|
||||
};
|
||||
}
|
||||
|
||||
static async enrichEntityWithProfile<
|
||||
async enrichEntityWithProfile<
|
||||
T extends
|
||||
| {
|
||||
profile: {
|
||||
@@ -554,7 +596,7 @@ export class UserRepository {
|
||||
}
|
||||
}
|
||||
|
||||
static async updateWhereId({
|
||||
async updateWhereId({
|
||||
whereId,
|
||||
data,
|
||||
}: {
|
||||
@@ -563,7 +605,7 @@ export class UserRepository {
|
||||
movedToProfileId?: number | null;
|
||||
};
|
||||
}) {
|
||||
return prisma.user.update({
|
||||
return this.prismaClient.user.update({
|
||||
where: {
|
||||
id: whereId,
|
||||
},
|
||||
@@ -579,7 +621,7 @@ export class UserRepository {
|
||||
});
|
||||
}
|
||||
|
||||
static async create(
|
||||
async create(
|
||||
data: Omit<Prisma.UserCreateInput, "password" | "organization" | "movedToProfile"> & {
|
||||
username: string;
|
||||
hashedPassword?: string;
|
||||
@@ -595,7 +637,7 @@ export class UserRepository {
|
||||
const t = await getTranslation("en", "common");
|
||||
const availability = getAvailabilityFromSchedule(DEFAULT_SCHEDULE);
|
||||
|
||||
const user = await prisma.user.create({
|
||||
const user = await this.prismaClient.user.create({
|
||||
data: {
|
||||
username,
|
||||
email: email,
|
||||
@@ -635,8 +677,8 @@ export class UserRepository {
|
||||
|
||||
return user;
|
||||
}
|
||||
static async getUserAdminTeams(userId: number) {
|
||||
return prisma.user.findUnique({
|
||||
async getUserAdminTeams({ userId }: { userId: number }) {
|
||||
return await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
@@ -687,7 +729,7 @@ export class UserRepository {
|
||||
},
|
||||
});
|
||||
}
|
||||
static async isAdminOfTeamOrParentOrg({ userId, teamId }: { userId: number; teamId: number }) {
|
||||
async isAdminOfTeamOrParentOrg({ userId, teamId }: { userId: number; teamId: number }) {
|
||||
const membershipQuery = {
|
||||
members: {
|
||||
some: {
|
||||
@@ -696,7 +738,7 @@ export class UserRepository {
|
||||
},
|
||||
},
|
||||
};
|
||||
const teams = await prisma.team.findMany({
|
||||
const teams = await this.prismaClient.team.findMany({
|
||||
where: {
|
||||
id: teamId,
|
||||
OR: [
|
||||
@@ -712,8 +754,8 @@ export class UserRepository {
|
||||
});
|
||||
return !!teams.length;
|
||||
}
|
||||
static async isAdminOrOwnerOfTeam({ userId, teamId }: { userId: number; teamId: number }) {
|
||||
const isAdminOrOwnerOfTeam = await prisma.membership.findUnique({
|
||||
async isAdminOrOwnerOfTeam({ userId, teamId }: { userId: number; teamId: number }) {
|
||||
const isAdminOrOwnerOfTeam = await this.prismaClient.membership.findUnique({
|
||||
where: {
|
||||
userId_teamId: {
|
||||
userId,
|
||||
@@ -728,8 +770,8 @@ export class UserRepository {
|
||||
});
|
||||
return !!isAdminOrOwnerOfTeam;
|
||||
}
|
||||
static async getTimeZoneAndDefaultScheduleId({ userId }: { userId: number }) {
|
||||
return await prisma.user.findUnique({
|
||||
async getTimeZoneAndDefaultScheduleId({ userId }: { userId: number }) {
|
||||
return await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
@@ -740,16 +782,16 @@ export class UserRepository {
|
||||
});
|
||||
}
|
||||
|
||||
static async adminFindById(userId: number) {
|
||||
return await prisma.user.findUniqueOrThrow({
|
||||
async adminFindById(userId: number) {
|
||||
return await this.prismaClient.user.findUniqueOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
static async findUserTeams({ id }: { id: number }) {
|
||||
const user = await prisma.user.findUnique({
|
||||
async findUserTeams({ id }: { id: number }) {
|
||||
const user = await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
@@ -776,10 +818,10 @@ export class UserRepository {
|
||||
return user;
|
||||
}
|
||||
|
||||
static async updateAvatar({ id, avatarUrl }: { id: number; avatarUrl: string }) {
|
||||
async updateAvatar({ id, avatarUrl }: { id: number; avatarUrl: string }) {
|
||||
// Using updateMany here since if the user already has a profile it would throw an error
|
||||
// because no records were found to update the profile picture
|
||||
await prisma.user.updateMany({
|
||||
await this.prismaClient.user.updateMany({
|
||||
where: {
|
||||
id,
|
||||
avatarUrl: {
|
||||
@@ -791,8 +833,8 @@ export class UserRepository {
|
||||
},
|
||||
});
|
||||
}
|
||||
static async findUserWithCredentials({ id }: { id: number }) {
|
||||
const user = await prisma.user.findUnique({
|
||||
async findUserWithCredentials({ id }: { id: number }) {
|
||||
const user = await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
@@ -817,8 +859,8 @@ export class UserRepository {
|
||||
};
|
||||
}
|
||||
|
||||
static async findUnlockedUserForSession({ userId }: { userId: number }) {
|
||||
const user = await prisma.user.findUnique({
|
||||
async findUnlockedUserForSession({ userId }: { userId: number }) {
|
||||
const user = await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
// Locked users can't login
|
||||
@@ -877,8 +919,8 @@ export class UserRepository {
|
||||
return withSelectedCalendars(user);
|
||||
}
|
||||
|
||||
static async getUserStats({ userId }: { userId: number }) {
|
||||
const user = await prisma.user.findUnique({
|
||||
async getUserStats({ userId }: { userId: number }) {
|
||||
const user = await this.prismaClient.user.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
@@ -927,8 +969,8 @@ export class UserRepository {
|
||||
};
|
||||
}
|
||||
|
||||
static async findManyByIdsIncludeDestinationAndSelectedCalendars({ ids }: { ids: number[] }) {
|
||||
const users = await prisma.user.findMany({
|
||||
async findManyByIdsIncludeDestinationAndSelectedCalendars({ ids }: { ids: number[] }) {
|
||||
const users = await this.prismaClient.user.findMany({
|
||||
where: { id: { in: ids } },
|
||||
include: {
|
||||
selectedCalendars: true,
|
||||
@@ -938,7 +980,7 @@ export class UserRepository {
|
||||
return users.map(withSelectedCalendars);
|
||||
}
|
||||
|
||||
static async updateStripeCustomerId({
|
||||
async updateStripeCustomerId({
|
||||
id,
|
||||
stripeCustomerId,
|
||||
existingMetadata,
|
||||
@@ -947,39 +989,33 @@ export class UserRepository {
|
||||
stripeCustomerId: string;
|
||||
existingMetadata: z.infer<typeof userMetadata>;
|
||||
}) {
|
||||
return prisma.user.update({
|
||||
return this.prismaClient.user.update({
|
||||
where: { id },
|
||||
data: { metadata: { ...existingMetadata, stripeCustomerId } },
|
||||
});
|
||||
}
|
||||
|
||||
static async updateWhitelistWorkflows({
|
||||
id,
|
||||
whitelistWorkflows,
|
||||
}: {
|
||||
id: number;
|
||||
whitelistWorkflows: boolean;
|
||||
}) {
|
||||
return prisma.user.update({
|
||||
async updateWhitelistWorkflows({ id, whitelistWorkflows }: { id: number; whitelistWorkflows: boolean }) {
|
||||
return this.prismaClient.user.update({
|
||||
where: { id },
|
||||
data: { whitelistWorkflows },
|
||||
});
|
||||
}
|
||||
|
||||
static async findManyUsersForDynamicEventType({
|
||||
async findManyUsersForDynamicEventType({
|
||||
currentOrgDomain,
|
||||
usernameList,
|
||||
}: {
|
||||
currentOrgDomain: string | null;
|
||||
usernameList: string[];
|
||||
}) {
|
||||
const { where } = await UserRepository._getWhereClauseForFindingUsersByUsername({
|
||||
const { where } = await this._getWhereClauseForFindingUsersByUsername({
|
||||
orgSlug: currentOrgDomain,
|
||||
usernameList,
|
||||
});
|
||||
|
||||
// TODO: Should be moved to UserRepository
|
||||
return prisma.user.findMany({
|
||||
return this.prismaClient.user.findMany({
|
||||
where,
|
||||
select: {
|
||||
allowDynamicBooking: true,
|
||||
|
||||
@@ -21,11 +21,11 @@ vi.mock("@calcom/features/auth/lib/hashPassword", () => ({
|
||||
hashPassword: vi.fn().mockResolvedValue("hashed-password"),
|
||||
}));
|
||||
|
||||
vi.mock("../repository/user", async () => {
|
||||
vi.mock("../repository/user", () => {
|
||||
return {
|
||||
UserRepository: {
|
||||
UserRepository: vi.fn().mockImplementation(() => ({
|
||||
create: vi.fn(),
|
||||
},
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -48,15 +48,25 @@ describe("UserCreationService", () => {
|
||||
});
|
||||
|
||||
test("should create user", async () => {
|
||||
vi.spyOn(UserRepository, "create").mockResolvedValue({
|
||||
const mockCreate = vi.fn().mockResolvedValue({
|
||||
username: "test",
|
||||
locked: false,
|
||||
organizationId: null,
|
||||
} as any);
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
create: mockCreate,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const user = await UserCreationService.createUser({ data: mockUserData });
|
||||
|
||||
expect(UserRepository.create).toHaveBeenCalledWith(
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
username: "test",
|
||||
locked: false,
|
||||
@@ -70,9 +80,25 @@ describe("UserCreationService", () => {
|
||||
test("should lock user when email is in watchlist", async () => {
|
||||
vi.mocked(checkIfEmailIsBlockedInWatchlistController).mockResolvedValue(true);
|
||||
|
||||
const mockCreate = vi.fn().mockResolvedValue({
|
||||
username: "test",
|
||||
locked: true,
|
||||
organizationId: null,
|
||||
} as any);
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
create: mockCreate,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const user = await UserCreationService.createUser({ data: mockUserData });
|
||||
|
||||
expect(UserRepository.create).toHaveBeenCalledWith(
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
locked: true,
|
||||
})
|
||||
@@ -85,12 +111,28 @@ describe("UserCreationService", () => {
|
||||
const mockPassword = "password";
|
||||
vi.mocked(hashPassword).mockResolvedValue("hashed_password");
|
||||
|
||||
const mockCreate = vi.fn().mockResolvedValue({
|
||||
username: "test",
|
||||
locked: false,
|
||||
organizationId: null,
|
||||
} as any);
|
||||
|
||||
const mockUserRepository = vi.mocked(UserRepository);
|
||||
if (mockUserRepository && typeof mockUserRepository.mockImplementation === "function") {
|
||||
mockUserRepository.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
create: mockCreate,
|
||||
} as any)
|
||||
);
|
||||
}
|
||||
|
||||
const user = await UserCreationService.createUser({
|
||||
data: { ...mockUserData, password: mockPassword },
|
||||
});
|
||||
|
||||
expect(hashPassword).toHaveBeenCalledWith(mockPassword);
|
||||
expect(UserRepository.create).toHaveBeenCalledWith(
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
hashedPassword: "hashed_password",
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { hashPassword } from "@calcom/features/auth/lib/hashPassword";
|
||||
import { checkIfEmailIsBlockedInWatchlistController } from "@calcom/features/watchlist/operations/check-if-email-in-watchlist.controller";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { CreationSource, UserPermissionRole, IdentityProvider } from "@calcom/prisma/enums";
|
||||
|
||||
import slugify from "../../slugify";
|
||||
@@ -37,7 +38,8 @@ export class UserCreationService {
|
||||
|
||||
const hashedPassword = password ? await hashPassword(password) : null;
|
||||
|
||||
const user = await UserRepository.create({
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const user = await userRepo.create({
|
||||
...data,
|
||||
username: slugify(username),
|
||||
...(hashedPassword && { hashedPassword }),
|
||||
|
||||
@@ -6,6 +6,7 @@ import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { ProfileRepository } from "@calcom/lib/server/repository/profile";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { teamMetadataSchema, userMetadata } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
@@ -24,7 +25,8 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe<S
|
||||
return null;
|
||||
}
|
||||
|
||||
const userFromDb = await UserRepository.findUnlockedUserForSession({ userId: session.user.id });
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const userFromDb = await userRepo.findUnlockedUserForSession({ userId: session.user.id });
|
||||
|
||||
// some hacks to make sure `username` and `email` are never inferred as `null`
|
||||
if (!userFromDb) {
|
||||
@@ -33,7 +35,7 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe<S
|
||||
|
||||
const upId = session.upId;
|
||||
|
||||
const user = await UserRepository.enrichUserWithTheProfile({
|
||||
const user = await userRepo.enrichUserWithTheProfile({
|
||||
user: userFromDb,
|
||||
upId,
|
||||
});
|
||||
@@ -54,7 +56,7 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe<S
|
||||
|
||||
const locale = user?.locale ?? ctx.locale;
|
||||
const { members = [], ..._organization } = user.profile?.organization || {};
|
||||
const isOrgAdmin = members.some((member) => ["OWNER", "ADMIN"].includes(member.role));
|
||||
const isOrgAdmin = members.some((member: any) => ["OWNER", "ADMIN"].includes(member.role));
|
||||
|
||||
if (isOrgAdmin) {
|
||||
logger.debug("User is an org admin", safeStringify({ userId: user.id }));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import type { TrpcSessionUser } from "../../../types";
|
||||
import type { TWhitelistUserWorkflows } from "./whitelistUserWorkflows.schema";
|
||||
@@ -13,7 +14,7 @@ type GetOptions = {
|
||||
export const whitelistUserWorkflows = async ({ input }: GetOptions) => {
|
||||
const { userId, whitelistWorkflows } = input;
|
||||
|
||||
const user = await UserRepository.updateWhitelistWorkflows({
|
||||
const user = await new UserRepository(prisma).updateWhitelistWorkflows({
|
||||
id: userId,
|
||||
whitelistWorkflows,
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ type AppCredentialsByTypeOptions = {
|
||||
/** Used for grabbing credentials on specific app pages */
|
||||
export const appCredentialsByTypeHandler = async ({ ctx, input }: AppCredentialsByTypeOptions) => {
|
||||
const { user } = ctx;
|
||||
const userAdminTeams = await UserRepository.getUserAdminTeams(ctx.user.id);
|
||||
const userAdminTeams = await new UserRepository(prisma).getUserAdminTeams({ userId: ctx.user.id });
|
||||
const { user: _, ...safeCredentialSelectWithoutUser } = safeCredentialSelect;
|
||||
const userAdminTeamsIds = userAdminTeams?.teams?.map(({ team }) => team.id) ?? [];
|
||||
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ export const findTeamMembersMatchingAttributeLogicHandler = async ({
|
||||
}
|
||||
|
||||
const matchingTeamMembersIds = matchingTeamMembersWithResult.map((member) => member.userId);
|
||||
const matchingTeamMembers = await UserRepository.findByIds({ ids: matchingTeamMembersIds });
|
||||
const matchingTeamMembers = await new UserRepository(ctx.prisma).findByIds({ ids: matchingTeamMembersIds });
|
||||
|
||||
return {
|
||||
mainWarnings,
|
||||
|
||||
+2
-1
@@ -71,11 +71,12 @@ async function getTeamMembers({
|
||||
distinct: ["userId"],
|
||||
});
|
||||
|
||||
const userRepo = new UserRepository(prisma);
|
||||
const membershipWithUserProfile = [];
|
||||
for (const membership of memberships) {
|
||||
membershipWithUserProfile.push({
|
||||
...membership,
|
||||
user: await UserRepository.enrichUserWithItsProfile({
|
||||
user: await userRepo.enrichUserWithItsProfile({
|
||||
user: membership.user,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -248,7 +248,7 @@ export async function editLocationHandler({ ctx, input }: EditLocationOptions) {
|
||||
const { newLocation, credentialId: conferenceCredentialId } = input;
|
||||
const { booking, user: loggedInUser } = ctx;
|
||||
|
||||
const organizer = await UserRepository.findByIdOrThrow({ id: booking.userId || 0 });
|
||||
const organizer = await new UserRepository(prisma).findByIdOrThrow({ id: booking.userId || 0 });
|
||||
|
||||
const newLocationInEvtFormat = await getLocationInEvtFormatOrThrow({
|
||||
location: newLocation,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import type { EventTypeRepository } from "@calcom/lib/server/repository/eventType";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { PeriodType } from "@calcom/prisma/enums";
|
||||
import type { CustomInputSchema } from "@calcom/prisma/zod-utils";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
@@ -202,7 +203,7 @@ export const mapEventType = async (eventType: EventType) => ({
|
||||
users: await Promise.all(
|
||||
(!!eventType?.hosts?.length ? eventType?.hosts.map((host) => host.user) : eventType.users).map(
|
||||
async (u) =>
|
||||
await UserRepository.enrichUserWithItsProfile({
|
||||
await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: u,
|
||||
})
|
||||
)
|
||||
@@ -214,7 +215,7 @@ export const mapEventType = async (eventType: EventType) => ({
|
||||
users: await Promise.all(
|
||||
c.users.map(
|
||||
async (u) =>
|
||||
await UserRepository.enrichUserWithItsProfile({
|
||||
await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: u,
|
||||
})
|
||||
)
|
||||
|
||||
@@ -27,7 +27,7 @@ export const getHandler = async ({ ctx, input }: MeOptions) => {
|
||||
sessionUser
|
||||
);
|
||||
|
||||
const user = await UserRepository.enrichUserWithTheProfile({
|
||||
const user = await new UserRepository(prisma).enrichUserWithTheProfile({
|
||||
user: sessionUser,
|
||||
upId: session.upId,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Session } from "next-auth";
|
||||
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||
|
||||
type MyStatsOptions = {
|
||||
@@ -13,7 +14,7 @@ type MyStatsOptions = {
|
||||
export const myStatsHandler = async ({ ctx }: MyStatsOptions) => {
|
||||
const { user: sessionUser } = ctx;
|
||||
|
||||
const additionalUserInfo = await UserRepository.getUserStats({ userId: sessionUser.id });
|
||||
const additionalUserInfo = await new UserRepository(prisma).getUserStats({ userId: sessionUser.id });
|
||||
|
||||
const sumOfTeamEventTypes = additionalUserInfo?.teams.reduce(
|
||||
(sum, team) => sum + team.team.eventTypes.length,
|
||||
|
||||
@@ -262,7 +262,7 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => {
|
||||
});
|
||||
}
|
||||
|
||||
const user = await UserRepository.enrichUserWithItsProfile({
|
||||
const user = await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: { ...orgOwner, organizationId: organization.id },
|
||||
});
|
||||
|
||||
@@ -308,7 +308,7 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => {
|
||||
}
|
||||
|
||||
if (!organization.id) throw Error("User not created");
|
||||
const user = await UserRepository.enrichUserWithItsProfile({
|
||||
const user = await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: { ...orgOwner, organizationId: organization.id },
|
||||
});
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ export const createTeamsHandler = async ({ ctx, input }: CreateTeamsOptions) =>
|
||||
|
||||
const [teamSlugs, userSlugs] = [
|
||||
await prisma.team.findMany({ where: { parentId: orgId }, select: { slug: true } }),
|
||||
await UserRepository.findManyByOrganization({ organizationId: orgId }),
|
||||
await new UserRepository(prisma).findManyByOrganization({ organizationId: orgId }),
|
||||
];
|
||||
|
||||
const existingSlugs = teamSlugs
|
||||
|
||||
@@ -250,7 +250,7 @@ export const listMembersHandler = async ({ ctx, input }: GetOptions) => {
|
||||
|
||||
const members = await Promise.all(
|
||||
teamMembers?.map(async (membership) => {
|
||||
const user = await UserRepository.enrichUserWithItsProfile({ user: membership.user });
|
||||
const user = await new UserRepository(prisma).enrichUserWithItsProfile({ user: membership.user });
|
||||
let attributes;
|
||||
|
||||
if (expand?.includes("attributes")) {
|
||||
|
||||
@@ -108,7 +108,7 @@ export const listOtherTeamMembers = async ({ input }: ListOptions) => {
|
||||
for (const membership of members) {
|
||||
enrichedMemberships.push({
|
||||
...membership,
|
||||
user: await UserRepository.enrichUserWithItsProfile({
|
||||
user: await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: membership.user,
|
||||
}),
|
||||
});
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import { getOrderedListOfLuckyUsers } from "@calcom/lib/server/getLuckyUser";
|
||||
import { EventTypeRepository } from "@calcom/lib/server/repository/eventType";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { getAbsoluteEventTypeRedirectUrl } from "@calcom/routing-forms/getEventTypeRedirectUrl";
|
||||
import { getSerializableForm } from "@calcom/routing-forms/lib/getSerializableForm";
|
||||
import { getServerTimingHeader } from "@calcom/routing-forms/lib/getServerTimingHeader";
|
||||
@@ -54,7 +55,7 @@ async function getEnrichedSerializableForm<
|
||||
>(form: TForm) {
|
||||
const formWithUserInfoProfile = {
|
||||
...form,
|
||||
user: await UserRepository.enrichUserWithItsProfile({ user: form.user }),
|
||||
user: await new UserRepository(prisma).enrichUserWithItsProfile({ user: form.user }),
|
||||
};
|
||||
|
||||
const serializableForm = await getSerializableForm({
|
||||
|
||||
@@ -50,6 +50,7 @@ import { SelectedSlotsRepository } from "@calcom/lib/server/repository/selectedS
|
||||
import { TeamRepository } from "@calcom/lib/server/repository/team";
|
||||
import { UserRepository, withSelectedCalendars } from "@calcom/lib/server/repository/user";
|
||||
import getSlots from "@calcom/lib/slots";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { PeriodType } from "@calcom/prisma/client";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import type { EventBusyDate, EventBusyDetails } from "@calcom/types/Calendar";
|
||||
@@ -140,7 +141,7 @@ export class AvailableSlotsService {
|
||||
}
|
||||
const dynamicEventType = getDefaultEvent(input.eventTypeSlug);
|
||||
|
||||
const usersForDynamicEventType = await UserRepository.findManyUsersForDynamicEventType({
|
||||
const usersForDynamicEventType = await new UserRepository(prisma).findManyUsersForDynamicEventType({
|
||||
currentOrgDomain: isValidOrgDomain ? currentOrgDomain : null,
|
||||
usernameList: Array.isArray(input.usernameList)
|
||||
? input.usernameList
|
||||
@@ -246,7 +247,7 @@ export class AvailableSlotsService {
|
||||
) {
|
||||
const { currentOrgDomain, isValidOrgDomain } = organizationDetails;
|
||||
log.info("getUserIdFromUsername", safeStringify({ organizationDetails, username }));
|
||||
const [user] = await UserRepository.findUsersByUsername({
|
||||
const [user] = await new UserRepository(prisma).findUsersByUsername({
|
||||
usernameList: [username],
|
||||
orgSlug: isValidOrgDomain ? currentOrgDomain : null,
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import { isOrganisationOwner } from "@calcom/lib/server/queries/organisations";
|
||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
import type { CreationSource } from "@calcom/prisma/enums";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
||||
@@ -266,7 +267,10 @@ const inviteMembers = async ({ ctx, input }: InviteMemberOptions) => {
|
||||
if (isPlatform) {
|
||||
inviterOrgId = team.id;
|
||||
orgSlug = team ? team.slug || requestedSlugForTeam : null;
|
||||
isInviterOrgAdmin = await UserRepository.isAdminOrOwnerOfTeam({ userId: inviter.id, teamId: team.id });
|
||||
isInviterOrgAdmin = await new UserRepository(prisma).isAdminOrOwnerOfTeam({
|
||||
userId: inviter.id,
|
||||
teamId: team.id,
|
||||
});
|
||||
}
|
||||
|
||||
await ensureAtleastAdminPermissions({
|
||||
|
||||
@@ -150,7 +150,7 @@ export function canBeInvited(invitee: UserWithMembership, team: TeamWithParent)
|
||||
// If he is invited to a sub-team and is already part of the organization.
|
||||
if (
|
||||
team.parentId &&
|
||||
UserRepository.isAMemberOfOrganization({ user: invitee, organizationId: team.parentId })
|
||||
new UserRepository(prisma).isAMemberOfOrganization({ user: invitee, organizationId: team.parentId })
|
||||
) {
|
||||
return INVITE_STATUS.CAN_BE_INVITED;
|
||||
}
|
||||
@@ -565,7 +565,7 @@ export function getAutoJoinStatus({
|
||||
|
||||
const isAutoAcceptEmail = connectionInfoMap[invitee.email].autoAccept;
|
||||
const isUserMemberOfTheTeamsParentOrganization = team.parentId
|
||||
? UserRepository.isAMemberOfOrganization({ user: invitee, organizationId: team.parentId })
|
||||
? new UserRepository(prisma).isAMemberOfOrganization({ user: invitee, organizationId: team.parentId })
|
||||
: null;
|
||||
|
||||
if (isUserMemberOfTheTeamsParentOrganization) {
|
||||
|
||||
@@ -106,7 +106,7 @@ export const legacyListMembers = async ({ ctx, input }: ListMembersOptions) => {
|
||||
|
||||
const enrichedMembers = await Promise.all(
|
||||
memberships.map(async (membership) =>
|
||||
UserRepository.enrichUserWithItsProfile({
|
||||
new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: {
|
||||
...membership.user,
|
||||
accepted: membership.accepted,
|
||||
|
||||
@@ -78,7 +78,7 @@ export const listMembersHandler = async ({ ctx, input }: ListMembersHandlerOptio
|
||||
|
||||
const membersWithApps = await Promise.all(
|
||||
teamMembers.map(async (member) => {
|
||||
const user = await UserRepository.enrichUserWithItsProfile({
|
||||
const user = await new UserRepository(prisma).enrichUserWithItsProfile({
|
||||
user: member.user,
|
||||
});
|
||||
const { profile, ...restUser } = user;
|
||||
|
||||
@@ -2519,7 +2519,7 @@ __metadata:
|
||||
"@axiomhq/winston": ^1.2.0
|
||||
"@calcom/platform-constants": "*"
|
||||
"@calcom/platform-enums": "*"
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.249"
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.252"
|
||||
"@calcom/platform-types": "*"
|
||||
"@calcom/platform-utils": "*"
|
||||
"@calcom/prisma": "*"
|
||||
@@ -3565,13 +3565,13 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.249":
|
||||
version: 0.0.249
|
||||
resolution: "@calcom/platform-libraries@npm:0.0.249"
|
||||
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.252":
|
||||
version: 0.0.252
|
||||
resolution: "@calcom/platform-libraries@npm:0.0.252"
|
||||
dependencies:
|
||||
"@calcom/features": "*"
|
||||
"@calcom/lib": "*"
|
||||
checksum: 5ca9eec4c30ffa78b817979202dc33fa96f701b97f54ed946311b0f6a35d1a3c6f37947229cef3b3133438134bf82bbea15dfe47b32156f0da6b1f59c9ed9763
|
||||
checksum: 70a3a5013e59c4df218464ad63c074e958f53388f7552085048f34083bf2dfac987cc953d21a4ab447d61d08c5e2be74c7557262b8f960885a284f046606af31
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user