Files
calendar/packages/lib/server/repository/membership.ts
T
19634073e9 refactor: improve team members page performance (#16155)
* fix: simplify workflow page and improve load time

* chore: use new endpoint

* chore: save progress

* refactor: code

* refactor: remove not requried code

* chore: remove schema

* chore: fix typ

* chore: improve

* chore: change name

* chore: remove unused

* chore: remove page

* refactor: teams page

* feat: add auto scroll

* chore: create validate unique invite

* fix: auth check

* fix: optimistic update

* chore

* fix: add loading

* fix: improvements

* chore: remove

* chore

* chore: fix teams page

* fix: team profile page

* fix: appearance page

* fix: sso view

* fix: type err

* feat: defer loading connected Apps

* fix: type err

* fix: type error

* fix: type err

* fix: connectedApps type

* chore: move

* chore: missing export

* feat: add search by name

* fix: display role change

* fix: use setInfiniteData

* chore: save progress

* test: add unit tests for loading members

* fix: test

* chore: update name

* fix: bugs and improvements

* chore: change variable name

* test: add tests for checkCanAccessMembers

* refactor: performance

---------

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2024-08-29 13:32:54 +05:30

235 lines
5.8 KiB
TypeScript

import { prisma } from "@calcom/prisma";
import type { MembershipRole } from "@calcom/prisma/client";
import { Prisma } from "@calcom/prisma/client";
import logger from "../../logger";
import { safeStringify } from "../../safeStringify";
import { eventTypeSelect } from "../eventTypeSelect";
import { LookupTarget, ProfileRepository } from "./profile";
const log = logger.getSubLogger({ prefix: ["repository/membership"] });
type IMembership = {
teamId: number;
userId: number;
accepted: boolean;
role: MembershipRole;
};
const membershipSelect = Prisma.validator<Prisma.MembershipSelect>()({
id: true,
teamId: true,
userId: true,
accepted: true,
role: true,
disableImpersonation: true,
});
const teamParentSelect = Prisma.validator<Prisma.TeamSelect>()({
id: true,
name: true,
slug: true,
logoUrl: true,
parentId: true,
metadata: true,
});
const userSelect = Prisma.validator<Prisma.UserSelect>()({
name: true,
avatarUrl: true,
username: true,
id: true,
});
const getWhereForfindAllByUpId = async (upId: string, where?: Prisma.MembershipWhereInput) => {
const lookupTarget = ProfileRepository.getLookupTarget(upId);
let prismaWhere;
if (lookupTarget.type === LookupTarget.Profile) {
/**
* TODO: When we add profileId to membership, we lookup by profileId
* If the profile is movedFromUser, we lookup all memberships without profileId as well.
*/
const profile = await ProfileRepository.findById(lookupTarget.id);
if (!profile) {
return [];
}
prismaWhere = {
userId: profile.user.id,
...where,
};
} else {
prismaWhere = {
userId: lookupTarget.id,
...where,
};
}
return prismaWhere;
};
export class MembershipRepository {
static async create(data: IMembership) {
return await prisma.membership.create({
data,
});
}
static async createMany(data: IMembership[]) {
return await prisma.membership.createMany({
data,
});
}
/**
* TODO: Using a specific function for specific tasks so that we don't have to focus on TS magic at the moment. May be try to make it a a generic findAllByProfileId with various options.
*/
static async findAllByUpIdIncludeTeamWithMembersAndEventTypes(
{ upId }: { upId: string },
{ where }: { where?: Prisma.MembershipWhereInput } = {}
) {
const prismaWhere = await getWhereForfindAllByUpId(upId, where);
if (Array.isArray(prismaWhere)) {
return prismaWhere;
}
log.debug(
"findAllByUpIdIncludeTeamWithMembersAndEventTypes",
safeStringify({
prismaWhere,
})
);
return await prisma.membership.findMany({
where: prismaWhere,
include: {
team: {
include: {
members: {
select: membershipSelect,
},
parent: {
select: teamParentSelect,
},
eventTypes: {
select: {
...eventTypeSelect,
hashedLink: true,
users: { select: userSelect },
children: {
include: {
users: { select: userSelect },
},
},
hosts: {
include: {
user: { select: userSelect },
},
},
},
// As required by getByViewHandler - Make it configurable
orderBy: [
{
position: "desc",
},
{
id: "asc",
},
],
},
},
},
},
});
}
static async findAllByUpIdIncludeMinimalEventTypes(
{ upId }: { upId: string },
{ where, skipEventTypes = false }: { where?: Prisma.MembershipWhereInput; skipEventTypes?: boolean } = {}
) {
const prismaWhere = await getWhereForfindAllByUpId(upId, where);
if (Array.isArray(prismaWhere)) {
return prismaWhere;
}
log.debug(
"findAllByUpIdIncludeMinimalEventTypes",
safeStringify({
prismaWhere,
})
);
const select = Prisma.validator<Prisma.MembershipSelect>()({
id: true,
teamId: true,
userId: true,
accepted: true,
role: true,
disableImpersonation: true,
team: {
select: {
...teamParentSelect,
isOrganization: true,
parent: {
select: teamParentSelect,
},
...(!skipEventTypes
? {
eventTypes: {
select: {
...eventTypeSelect,
hashedLink: true,
children: { select: { id: true } },
},
orderBy: [
{
position: "desc",
},
{
id: "asc",
},
],
},
}
: {}),
},
},
});
return await prisma.membership.findMany({
where: prismaWhere,
select,
});
}
static async findAllByUpIdIncludeTeam(
{ upId }: { upId: string },
{ where }: { where?: Prisma.MembershipWhereInput } = {}
) {
const prismaWhere = await getWhereForfindAllByUpId(upId, where);
if (Array.isArray(prismaWhere)) {
return prismaWhere;
}
return await prisma.membership.findMany({
where: prismaWhere,
include: {
team: {
include: {
parent: {
select: teamParentSelect,
},
},
},
},
});
}
static async findFirstByUserIdAndTeamId({ userId, teamId }: { userId: number; teamId: number }) {
return await prisma.membership.findFirst({
where: {
userId,
teamId,
},
});
}
}