Fix role lookup query and align calendar layout behavior (#22928)
## Summary Fixes slow getRoles requests caused by loading several one-to-many role relations in a single TypeORM query. The previous query produced a Cartesian product across role targets, permission flags, object permissions, and field permissions. In production, a workspace with 9 roles expanded into more than 32,000 database rows before TypeORM hydration. This change: - Reads roles and their related permissions from the existing flat-map caches. - Hydrates relations using the same findManyWithRelationsFromCache pattern as ViewService. - Removes the expensive joined query from the getRoles path. - Preserves the existing GraphQL response shape. ## Performance ### Before: 9 roles 32,257 SQL result rows Approximately 10 seconds observed request latency ### After: No Cartesian SQL query Cache-backed lookups and in-memory relation hydration <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22928?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -57,7 +57,6 @@ import {
|
||||
} from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
import { UpdateRoleInput } from 'src/engine/metadata-modules/role/dtos/update-role.input';
|
||||
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
|
||||
import { fromRoleEntitiesToRoleDtos } from 'src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util';
|
||||
import { UpsertRowLevelPermissionPredicatesInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/upsert-row-level-permission-predicates.input';
|
||||
import { RowLevelPermissionPredicateGroupDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto';
|
||||
import { RowLevelPermissionPredicateDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto';
|
||||
@@ -103,9 +102,7 @@ export class RoleResolver {
|
||||
async getRoles(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
): Promise<RoleDTO[]> {
|
||||
const roleEntities = await this.roleService.getWorkspaceRoles(workspace.id);
|
||||
|
||||
return fromRoleEntitiesToRoleDtos(roleEntities);
|
||||
return this.roleService.getWorkspaceRoles(workspace.id);
|
||||
}
|
||||
|
||||
@Mutation(() => WorkspaceMemberDTO)
|
||||
|
||||
@@ -21,9 +21,13 @@ import {
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type';
|
||||
import { fromCreateRoleInputToFlatRoleToCreate } from 'src/engine/metadata-modules/flat-role/utils/from-create-role-input-to-flat-role-to-create.util';
|
||||
import { fromDeleteRoleInputToFlatRoleOrThrow } from 'src/engine/metadata-modules/flat-role/utils/from-delete-role-input-to-flat-role-or-throw.util';
|
||||
import { fromUpdateRoleInputToFlatRoleToUpdateOrThrow } from 'src/engine/metadata-modules/flat-role/utils/from-update-role-input-to-flat-role-to-update-or-throw.util';
|
||||
import { fromFlatFieldPermissionToFieldPermissionDto } from 'src/engine/metadata-modules/object-permission/utils/from-flat-field-permission-to-field-permission-dto.util';
|
||||
import { fromFlatObjectPermissionToObjectPermissionDto } from 'src/engine/metadata-modules/object-permission/utils/from-flat-object-permission-to-object-permission-dto.util';
|
||||
import { MEMBER_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/member-role-label.constants';
|
||||
import {
|
||||
PermissionsException,
|
||||
@@ -35,6 +39,7 @@ import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
import { type UpdateRoleInput } from 'src/engine/metadata-modules/role/dtos/update-role.input';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { fromFlatRoleToRoleDto } from 'src/engine/metadata-modules/role/utils/fromFlatRoleToRoleDto.util';
|
||||
import { fromFlatRolePermissionFlagToRolePermissionFlagDto } from 'src/engine/metadata-modules/role-permission-flag/utils/from-flat-role-permission-flag-to-role-permission-flag-dto.util';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/inject-workspace-scoped-repository.decorator';
|
||||
import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository';
|
||||
@@ -56,16 +61,67 @@ export class RoleService {
|
||||
private readonly aiAgentRoleService: AiAgentRoleService,
|
||||
) {}
|
||||
|
||||
public async getWorkspaceRoles(workspaceId: string): Promise<RoleEntity[]> {
|
||||
return this.roleRepository.find(workspaceId, {
|
||||
relations: {
|
||||
roleTargets: true,
|
||||
rolePermissionFlags: {
|
||||
permissionFlag: true,
|
||||
public async getWorkspaceRoles(workspaceId: string): Promise<RoleDTO[]> {
|
||||
const { flatRoleMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatRoleMaps'],
|
||||
},
|
||||
objectPermissions: true,
|
||||
fieldPermissions: true,
|
||||
},
|
||||
);
|
||||
|
||||
return this.findManyWithRelationsFromCache(
|
||||
Object.values(flatRoleMaps.byUniversalIdentifier).filter(isDefined),
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async findManyWithRelationsFromCache(
|
||||
flatRoles: FlatRole[],
|
||||
workspaceId: string,
|
||||
): Promise<RoleDTO[]> {
|
||||
const {
|
||||
flatRolePermissionFlagMaps,
|
||||
flatPermissionFlagMaps,
|
||||
flatObjectPermissionMaps,
|
||||
flatFieldPermissionMaps,
|
||||
} =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatRolePermissionFlagMaps',
|
||||
'flatPermissionFlagMaps',
|
||||
'flatObjectPermissionMaps',
|
||||
'flatFieldPermissionMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
return flatRoles.map((flatRole) => {
|
||||
const roleDto = fromFlatRoleToRoleDto(flatRole);
|
||||
|
||||
roleDto.permissionFlags = findManyFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityIds: flatRole.rolePermissionFlagIds,
|
||||
flatEntityMaps: flatRolePermissionFlagMaps,
|
||||
}).map((flatRolePermissionFlag) =>
|
||||
fromFlatRolePermissionFlagToRolePermissionFlagDto(
|
||||
flatRolePermissionFlag,
|
||||
flatPermissionFlagMaps,
|
||||
),
|
||||
);
|
||||
|
||||
roleDto.objectPermissions = findManyFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityIds: flatRole.objectPermissionIds,
|
||||
flatEntityMaps: flatObjectPermissionMaps,
|
||||
}).map(fromFlatObjectPermissionToObjectPermissionDto);
|
||||
|
||||
roleDto.fieldPermissions = findManyFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityIds: flatRole.fieldPermissionIds,
|
||||
flatEntityMaps: flatFieldPermissionMaps,
|
||||
}).map(fromFlatFieldPermissionToFieldPermissionDto);
|
||||
|
||||
return roleDto;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user