diff --git a/packages/twenty-server/src/engine/core-modules/user/dtos/workspace-member.dto.ts b/packages/twenty-server/src/engine/core-modules/user/dtos/workspace-member.dto.ts index fdcccf582..c501774da 100644 --- a/packages/twenty-server/src/engine/core-modules/user/dtos/workspace-member.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/user/dtos/workspace-member.dto.ts @@ -47,6 +47,6 @@ export class WorkspaceMember { @Field(() => [RoleDTO], { nullable: true }) roles?: RoleDTO[]; - @Field(() => String) - userWorkspaceId: string; + @Field(() => String, { nullable: true }) + userWorkspaceId?: string; } diff --git a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts index 43468b7b7..4912c8d1c 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts @@ -13,7 +13,6 @@ import crypto from 'crypto'; import { GraphQLJSONObject } from 'graphql-type-json'; import { FileUpload, GraphQLUpload } from 'graphql-upload'; -import { isDefined } from 'twenty-shared'; import { In, Repository } from 'typeorm'; import { SupportDriver } from 'src/engine/core-modules/environment/interfaces/support.interface'; @@ -49,6 +48,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator'; import { DemoEnvGuard } from 'src/engine/guards/demo.env.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; +import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto'; import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service'; import { AccountsToReconnectKeys } from 'src/modules/connected-account/types/accounts-to-reconnect-key-value.type'; import { streamToBuffer } from 'src/utils/stream-to-buffer'; @@ -173,20 +173,35 @@ export class UserResolver { const workspaceMembers: WorkspaceMember[] = []; - const userWorkspaces = await this.userWorkspaceRepository.find({ - where: { - userId: In(workspaceMemberEntities.map((entity) => entity.userId)), - workspaceId: workspace.id, - }, - }); - - const userWorkspacesByUserId = new Map( - userWorkspaces.map((userWorkspace) => [ - userWorkspace.userId, - userWorkspace, - ]), + const permissionsEnabled = await this.featureFlagService.isFeatureEnabled( + FeatureFlagKey.IsPermissionsEnabled, + workspace.id, ); + let userWorkspacesByUserId = new Map(); + let rolesByUserWorkspaces = new Map(); + + if (permissionsEnabled === true) { + const userWorkspaces = await this.userWorkspaceRepository.find({ + where: { + userId: In(workspaceMemberEntities.map((entity) => entity.userId)), + workspaceId: workspace.id, + }, + }); + + userWorkspacesByUserId = new Map( + userWorkspaces.map((userWorkspace) => [ + userWorkspace.userId, + userWorkspace, + ]), + ); + + rolesByUserWorkspaces = + await this.userRoleService.getRolesByUserWorkspaces( + userWorkspaces.map((userWorkspace) => userWorkspace.id), + ); + } + for (const workspaceMemberEntity of workspaceMemberEntities) { if (workspaceMemberEntity.avatarUrl) { const avatarUrlToken = await this.fileService.encodeFileToken({ @@ -197,45 +212,33 @@ export class UserResolver { workspaceMemberEntity.avatarUrl = `${workspaceMemberEntity.avatarUrl}?token=${avatarUrlToken}`; } - const userWorkspace = userWorkspacesByUserId.get( - workspaceMemberEntity.userId, - ); - - if (!userWorkspace) { - throw new Error('User workspace not found'); - } - - const permissionsEnabled = await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IsPermissionsEnabled, - workspace.id, - ); - - const workspaceMember: WorkspaceMember = { - ...workspaceMemberEntity, - userWorkspaceId: userWorkspace.id, - } as WorkspaceMember; + const workspaceMember = workspaceMemberEntity as WorkspaceMember; if (permissionsEnabled === true) { - const roles = await this.userRoleService - .getRolesForUserWorkspace(userWorkspace.id) - .then(([roleEntity]) => { - if (!isDefined(roleEntity)) { - return []; - } + const userWorkspace = userWorkspacesByUserId.get( + workspaceMemberEntity.userId, + ); - return [ - { - id: roleEntity.id, - label: roleEntity.label, - canUpdateAllSettings: roleEntity.canUpdateAllSettings, - description: roleEntity.description, - isEditable: roleEntity.isEditable, - userWorkspaceRoles: roleEntity.userWorkspaceRoles, - }, - ]; - }); + if (!userWorkspace) { + throw new Error('User workspace not found'); + } - workspaceMember.roles = roles; + workspaceMember.userWorkspaceId = userWorkspace.id; + + const workspaceMemberRoles = ( + rolesByUserWorkspaces.get(userWorkspace.id) ?? [] + ).map((roleEntity) => { + return { + id: roleEntity.id, + label: roleEntity.label, + canUpdateAllSettings: roleEntity.canUpdateAllSettings, + description: roleEntity.description, + isEditable: roleEntity.isEditable, + userWorkspaceRoles: roleEntity.userWorkspaceRoles, + }; + }); + + workspaceMember.roles = workspaceMemberRoles; } workspaceMembers.push(workspaceMember); diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts index 053933dda..788fd5ab5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts @@ -21,8 +21,9 @@ export class PermissionsService { }: { userWorkspaceId: string; }): Promise> { - const [roleOfUserWorkspace] = - await this.userRoleService.getRolesForUserWorkspace(userWorkspaceId); + const [roleOfUserWorkspace] = await this.userRoleService + .getRolesByUserWorkspaces([userWorkspaceId]) + .then((roles) => roles?.get(userWorkspaceId) ?? []); let hasPermissionOnSettingFeature = false; @@ -46,10 +47,11 @@ export class PermissionsService { userWorkspaceId: string; setting: SettingsFeatures; }): Promise { - const [userWorkspaceRole] = - await this.userRoleService.getRolesForUserWorkspace(userWorkspaceId); + const [roleOfUserWorkspace] = await this.userRoleService + .getRolesByUserWorkspaces([userWorkspaceId]) + .then((roles) => roles?.get(userWorkspaceId) ?? []); - if (userWorkspaceRole?.canUpdateAllSettings === true) { + if (roleOfUserWorkspace?.canUpdateAllSettings === true) { return; } diff --git a/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts index df58b3349..b2cffa448 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts @@ -101,9 +101,12 @@ export class RoleResolver { }); } - const roles = await this.userRoleService.getRolesForUserWorkspace( - userWorkspace.id, - ); + const roles = await this.userRoleService + .getRolesByUserWorkspaces([userWorkspace.id]) + .then( + (rolesByUserWorkspaces) => + rolesByUserWorkspaces?.get(userWorkspace.id) ?? [], + ); return { ...workspaceMember, diff --git a/packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts b/packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts index 88c06d8a2..6bd3dc59e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts @@ -60,7 +60,9 @@ export class UserRoleService { ); } - const [currentRole] = await this.getRolesForUserWorkspace(userWorkspace.id); + const roles = await this.getRolesByUserWorkspaces([userWorkspace.id]); + + const currentRole = roles.get(userWorkspace.id)?.[0]; if (currentRole?.id === roleId) { return; @@ -96,33 +98,42 @@ export class UserRoleService { }); } - public async getRolesForUserWorkspace( - userWorkspaceId: string, - ): Promise { - const userWorkspaceRole = await this.userWorkspaceRoleRepository.findOne({ - where: { - userWorkspaceId, - }, - }); - - if (!isDefined(userWorkspaceRole)) { - return []; + public async getRolesByUserWorkspaces( + userWorkspaceIds: string[], + ): Promise> { + if (!userWorkspaceIds.length) { + return new Map(); } - const role = await this.roleRepository.findOne({ + const allUserWorkspaceRoles = await this.userWorkspaceRoleRepository.find({ where: { - id: userWorkspaceRole.roleId, + userWorkspaceId: In(userWorkspaceIds), + }, + relations: { + role: true, }, }); - if (!isDefined(role)) { - throw new PermissionsException( - 'Role not found', - PermissionsExceptionCode.ROLE_NOT_FOUND, + if (!allUserWorkspaceRoles.length) { + return new Map(); + } + + const rolesMap = new Map(); + + for (const userWorkspaceId of userWorkspaceIds) { + const userWorkspaceRolesOfUserWorkspace = allUserWorkspaceRoles.filter( + (userWorkspaceRole) => + userWorkspaceRole.userWorkspaceId === userWorkspaceId, ); + + const rolesOfUserWorkspace = userWorkspaceRolesOfUserWorkspace + .map((userWorkspaceRole) => userWorkspaceRole.role) + .filter(isDefined); + + rolesMap.set(userWorkspaceId, rolesOfUserWorkspace); } - return [role]; + return rolesMap; } public async getWorkspaceMembersAssignedToRole( @@ -169,9 +180,13 @@ export class UserRoleService { userWorkspaceId: string, workspaceId: string, ): Promise { - const roles = await this.getRolesForUserWorkspace(userWorkspaceId); + const roles = await this.getRolesByUserWorkspaces([userWorkspaceId]); - const adminRole = roles.find((role: RoleDTO) => role.isEditable === false); + const currentRoles = roles.get(userWorkspaceId); + + const adminRole = currentRoles?.find( + (role: RoleDTO) => role.isEditable === false, + ); if (isDefined(adminRole)) { const workspaceMembersWithAdminRole =