[permissions] Improve performances using a cache for userWorkspaces roles (#11587)

In this PR we are 

- introducing a cached map `{ userworkspaceId: roleId } `to reduce calls
to get a userWorkspace's role (we were having N+1 around that with
combinedFindMany queries and generally having a lot of avoidable
queries)
- using the roles permissions cache (`{ roleId: { objectNameSingular:
{ canRead: bool, canUpdate: bool, ...} } `) in Permissions V1's
userHasObjectPermission, in order to 1) improve performances to avoid
calls to get roles 2) start using our permissions cache
This commit is contained in:
Marie
2025-04-16 17:07:43 +02:00
committed by GitHub
parent ab277476a8
commit 4d78f5f97f
20 changed files with 692 additions and 350 deletions

View File

@ -12,6 +12,7 @@ import {
} from 'src/engine/metadata-modules/permissions/permissions.exception';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
@ -24,6 +25,7 @@ export class UserRoleService {
@InjectRepository(UserWorkspace, 'core')
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
) {}
public async assignRoleToUserWorkspace({
@ -56,6 +58,12 @@ export class UserRoleService {
workspaceId,
id: Not(newUserWorkspaceRole.id),
});
await this.workspacePermissionsCacheService.recomputeUserWorkspaceRoleMapCache(
{
workspaceId,
},
);
}
public async getRoleIdForUserWorkspace({
@ -69,11 +77,14 @@ export class UserRoleService {
return;
}
const userWorkspaceRole = await this.userWorkspaceRoleRepository.findOne({
where: { userWorkspaceId, workspaceId },
});
const userWorkspaceRoleMap =
await this.workspacePermissionsCacheService.getUserWorkspaceRoleMapFromCache(
{
workspaceId,
},
);
return userWorkspaceRole?.roleId;
return userWorkspaceRoleMap.data[userWorkspaceId];
}
public async getRolesByUserWorkspaces({
@ -125,21 +136,13 @@ export class UserRoleService {
roleId: string,
workspaceId: string,
): Promise<WorkspaceMemberWorkspaceEntity[]> {
const userWorkspaceRoles = await this.userWorkspaceRoleRepository.find({
where: {
roleId,
workspaceId,
},
});
const userWorkspaceIdsWithRole =
await this.getUserWorkspaceIdsAssignedToRole(roleId, workspaceId);
const userIds = await this.userWorkspaceRepository
.find({
where: {
id: In(
userWorkspaceRoles.map(
(userWorkspaceRole) => userWorkspaceRole.userWorkspaceId,
),
),
id: In(userWorkspaceIdsWithRole),
},
})
.then((userWorkspaces) =>
@ -161,6 +164,22 @@ export class UserRoleService {
return workspaceMembers;
}
public async getUserWorkspaceIdsAssignedToRole(
roleId: string,
workspaceId: string,
): Promise<string[]> {
const userWorkspaceRoleMap =
await this.workspacePermissionsCacheService.getUserWorkspaceRoleMapFromCache(
{
workspaceId,
},
);
return Object.entries(userWorkspaceRoleMap.data)
.filter(([_, roleIdFromMap]) => roleIdFromMap === roleId)
.map(([userWorkspaceId]) => userWorkspaceId);
}
public async validateUserWorkspaceIsNotUniqueAdminOrThrow({
userWorkspaceId,
workspaceId,