feat: made project role multi support for org invite

This commit is contained in:
=
2024-09-09 16:17:59 +05:30
parent eb7c804bb9
commit e7191c2f71
7 changed files with 40 additions and 26 deletions

View File

@@ -21,7 +21,7 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
projects: z projects: z
.object({ .object({
id: z.string(), id: z.string(),
projectRoleSlug: z.string().default(ProjectMembershipRole.Member) projectRoleSlug: z.string().array().default([ProjectMembershipRole.Member])
}) })
.array() .array()
.optional(), .optional(),

View File

@@ -48,7 +48,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
projects: [ projects: [
{ {
id: req.params.projectId, id: req.params.projectId,
projectRoleSlug: ProjectMembershipRole.Member projectRoleSlug: [ProjectMembershipRole.Member]
} }
] ]
}); });

View File

@@ -12,6 +12,7 @@ import {
SecretKeyEncoding, SecretKeyEncoding,
TableName, TableName,
TProjectMemberships, TProjectMemberships,
TProjectUserMembershipRolesInsert,
TUsers TUsers
} from "@app/db/schemas"; } from "@app/db/schemas";
import { TProjects } from "@app/db/schemas/projects"; import { TProjects } from "@app/db/schemas/projects";
@@ -88,7 +89,7 @@ type TOrgServiceFactoryDep = {
"getPlan" | "updateSubscriptionOrgMemberCount" | "generateOrgCustomerId" | "removeOrgCustomer" "getPlan" | "updateSubscriptionOrgMemberCount" | "generateOrgCustomerId" | "removeOrgCustomer"
>; >;
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">; projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
projectRoleDAL: Pick<TProjectRoleDALFactory, "findOne">; projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "findUserGroupMembershipsInProject">; userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "findUserGroupMembershipsInProject">;
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">; projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany">; projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany">;
@@ -652,12 +653,15 @@ export const orgServiceFactory = ({
if (!userWithEncryptionKeyInvitedToProject.length) continue; if (!userWithEncryptionKeyInvitedToProject.length) continue;
// validate custom project role // validate custom project role
const invitedProjectRole = const invitedProjectRoles = invitedProjects.find((el) => el.id === project.id)?.projectRoleSlug || [
invitedProjects.find((el) => el.id === project.id)?.projectRoleSlug || ProjectMembershipRole.Member; ProjectMembershipRole.Member
const isCustomProjectRole = !Object.values(ProjectMembershipRole).includes( ];
invitedProjectRole as ProjectMembershipRole
const customProjectRoles = invitedProjectRoles.filter(
(role) => !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole)
); );
if (isCustomProjectRole) { const hasCustomRole = Boolean(customProjectRoles.length);
if (hasCustomRole) {
if (!plan?.rbac) if (!plan?.rbac)
throw new BadRequestError({ throw new BadRequestError({
message: message:
@@ -665,6 +669,17 @@ export const orgServiceFactory = ({
}); });
} }
const customRoles = hasCustomRole
? await projectRoleDAL.find({
projectId,
$in: { slug: customProjectRoles.map((role) => role) }
})
: [];
if (customRoles.length !== customProjectRoles.length)
throw new BadRequestError({ message: "Custom role not found" });
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx); const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx);
if (!ghostUser) { if (!ghostUser) {
throw new BadRequestError({ throw new BadRequestError({
@@ -698,7 +713,7 @@ export const orgServiceFactory = ({
userPrivateKey: botPrivateKey, userPrivateKey: botPrivateKey,
members: userWithEncryptionKeyInvitedToProject.map((userEnc) => ({ members: userWithEncryptionKeyInvitedToProject.map((userEnc) => ({
orgMembershipId: userEnc.userId, orgMembershipId: userEnc.userId,
projectMembershipRole: invitedProjectRole as ProjectMembershipRole, projectMembershipRole: ProjectMembershipRole.Admin,
userPublicKey: userEnc.publicKey userPublicKey: userEnc.publicKey
})) }))
}); });
@@ -712,17 +727,18 @@ export const orgServiceFactory = ({
); );
newProjectMemberships.push(...projectMemberships); newProjectMemberships.push(...projectMemberships);
let customRoleId: string; const sanitizedProjectMembershipRoles: TProjectUserMembershipRolesInsert[] = [];
const projectRole = isCustomProjectRole ? ProjectMembershipRole.Custom : invitedProjectRole; invitedProjectRoles.forEach((projectRole) => {
if (isCustomProjectRole) { const isCustomRole = Boolean(customRolesGroupBySlug?.[projectRole]?.[0]);
const customRole = await projectRoleDAL.findOne({ slug: invitedProjectRole, projectId }); projectMemberships.forEach((membership) => {
if (!customRole) throw new BadRequestError({ name: "Invite membership", message: "Project role not found" }); sanitizedProjectMembershipRoles.push({
customRoleId = customRole.id; projectMembershipId: membership.id,
} role: isCustomRole ? ProjectMembershipRole.Custom : projectRole,
await projectUserMembershipRoleDAL.insertMany( customRoleId: customRolesGroupBySlug[projectRole] ? customRolesGroupBySlug[projectRole][0].id : null
projectMemberships.map(({ id }) => ({ projectMembershipId: id, role: projectRole, customRoleId })), });
tx });
); });
await projectUserMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx);
await projectKeyDAL.insertMany( await projectKeyDAL.insertMany(
newWsMembers.map((el) => ({ newWsMembers.map((el) => ({

View File

@@ -34,7 +34,7 @@ export type TInviteUserToOrgDTO = {
organizationRoleSlug: string; organizationRoleSlug: string;
projects?: { projects?: {
id: string; id: string;
projectRoleSlug?: string; projectRoleSlug?: string[];
}[]; }[];
}; };

View File

@@ -150,7 +150,7 @@ export type DeletOrgMembershipDTO = {
export type AddUserToOrgDTO = { export type AddUserToOrgDTO = {
inviteeEmails: string[]; inviteeEmails: string[];
projects?: { id: string; projectRoleSlug: string }[]; projects?: { id: string; projectRoleSlug: string[] }[];
organizationRoleSlug: string; organizationRoleSlug: string;
organizationId: string; organizationId: string;
}; };

View File

@@ -129,7 +129,7 @@ export const AddOrgMemberModal = ({
organizationId: currentOrg?.id, organizationId: currentOrg?.id,
inviteeEmails: emails.split(",").map((email) => email.trim()), inviteeEmails: emails.split(",").map((email) => email.trim()),
organizationRoleSlug, organizationRoleSlug,
projects: projectIds.map((id) => ({ id, projectRoleSlug })) projects: projectIds.map((id) => ({ id, projectRoleSlug: [projectRoleSlug] }))
}); });
setCompleteInviteLinks(data?.completeInviteLinks ?? null); setCompleteInviteLinks(data?.completeInviteLinks ?? null);

View File

@@ -101,9 +101,7 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
members?.forEach((member) => { members?.forEach((member) => {
wsUserUsernames.set(member.user.username, true); wsUserUsernames.set(member.user.username, true);
}); });
return (orgUsers || []).filter( return (orgUsers || []).filter(({ user: u }) => !wsUserUsernames.has(u.username));
({ status, user: u }) => status === "accepted" && !wsUserUsernames.has(u.username)
);
}, [orgUsers, members]); }, [orgUsers, members]);
return ( return (