Migrate role and role target to v2 (#16009)
# Introduction close https://github.com/twentyhq/core-team-issues/issues/1930 close https://github.com/twentyhq/core-team-issues/issues/1929 Migrating role and roleTarget entities to the v2 core engine, allowing v2 caching leverage and allow migrating agent to v2 that needs role target in prior After agent we should be able to pass twenty standard app totally though workspace migration ## Role target assignation Please note that role target have 3 creation entrypoints: - Agent - User workspace - ApiKey Refactored all 3 of them to pass through a new role-target.service.ts that consumes the v2 under the hood. --------- Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
@@ -37,10 +37,10 @@ export class RoleDTO {
|
||||
label: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
description: string;
|
||||
description?: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
icon: string;
|
||||
icon?: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
isEditable: boolean;
|
||||
@@ -55,7 +55,7 @@ export class RoleDTO {
|
||||
canBeAssignedToApiKeys: boolean;
|
||||
|
||||
@HideField()
|
||||
roleTargets: Relation<RoleTargetsEntity[]>;
|
||||
roleTargets?: Relation<RoleTargetsEntity[]>;
|
||||
|
||||
@Field(() => [WorkspaceMemberDTO], { nullable: true })
|
||||
workspaceMembers?: WorkspaceMemberDTO[];
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class RoleTargetException extends CustomException {
|
||||
code: RoleTargetExceptionCode;
|
||||
constructor(message: string, code: RoleTargetExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
}
|
||||
|
||||
export enum RoleTargetExceptionCode {
|
||||
ROLE_TARGET_NOT_FOUND = 'ROLE_TARGET_NOT_FOUND',
|
||||
INVALID_ROLE_TARGET_DATA = 'INVALID_ROLE_TARGET_DATA',
|
||||
ROLE_TARGET_MISSING_IDENTIFIER = 'ROLE_TARGET_MISSING_IDENTIFIER',
|
||||
ROLE_CANNOT_BE_ASSIGNED_TO_ENTITY = 'ROLE_CANNOT_BE_ASSIGNED_TO_ENTITY',
|
||||
ROLE_NOT_FOUND = 'ROLE_NOT_FOUND',
|
||||
}
|
||||
@@ -12,16 +12,18 @@ import {
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/workspace-sync/interfaces/syncable-entity.interface';
|
||||
|
||||
import { ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
|
||||
@Entity('roleTargets')
|
||||
@Unique('IDX_ROLE_TARGETS_UNIQUE', [
|
||||
@Unique('IDX_ROLE_TARGETS_UNIQUE_USER_WORKSPACE', [
|
||||
'workspaceId',
|
||||
'userWorkspaceId',
|
||||
'roleId',
|
||||
'agentId',
|
||||
'apiKeyId',
|
||||
])
|
||||
@Unique('IDX_ROLE_TARGETS_UNIQUE_AGENT', ['workspaceId', 'agentId'])
|
||||
@Unique('IDX_ROLE_TARGETS_UNIQUE_API_KEY', ['workspaceId', 'apiKeyId'])
|
||||
@Index('IDX_ROLE_TARGETS_WORKSPACE_ID', ['userWorkspaceId', 'workspaceId'])
|
||||
@Index('IDX_ROLE_TARGETS_AGENT_ID', ['agentId'])
|
||||
@Index('IDX_ROLE_TARGETS_API_KEY_ID', ['apiKeyId'])
|
||||
@@ -29,7 +31,7 @@ import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
'CHK_role_targets_single_entity',
|
||||
'("agentId" IS NOT NULL AND "userWorkspaceId" IS NULL AND "apiKeyId" IS NULL) OR ("agentId" IS NULL AND "userWorkspaceId" IS NOT NULL AND "apiKeyId" IS NULL) OR ("agentId" IS NULL AND "userWorkspaceId" IS NULL AND "apiKeyId" IS NOT NULL)',
|
||||
)
|
||||
export class RoleTargetsEntity {
|
||||
export class RoleTargetsEntity extends SyncableEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@@ -46,13 +48,13 @@ export class RoleTargetsEntity {
|
||||
role: Relation<RoleEntity>;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
userWorkspaceId: string;
|
||||
userWorkspaceId: string | null;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
agentId: string;
|
||||
agentId: string | null;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
apiKeyId: string;
|
||||
apiKeyId: string | null;
|
||||
|
||||
@ManyToOne(() => ApiKeyEntity, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'apiKeyId' })
|
||||
|
||||
@@ -18,12 +18,12 @@ import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets
|
||||
|
||||
@Entity('role')
|
||||
@Unique('IDX_ROLE_LABEL_WORKSPACE_ID_UNIQUE', ['label', 'workspaceId'])
|
||||
export class RoleEntity extends SyncableEntity {
|
||||
export class RoleEntity extends SyncableEntity implements Required<RoleEntity> {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
standardId?: string;
|
||||
standardId: string | null;
|
||||
|
||||
@Column({ nullable: false })
|
||||
label: string;
|
||||
@@ -47,10 +47,10 @@ export class RoleEntity extends SyncableEntity {
|
||||
canDestroyAllObjectRecords: boolean;
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
description: string;
|
||||
description: string | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
icon: string;
|
||||
@Column({ nullable: true, type: 'varchar' })
|
||||
icon: string | null;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
workspaceId: string;
|
||||
|
||||
@@ -6,34 +6,61 @@ import { ApplicationModule } from 'src/engine/core-modules/application/applicati
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AiAgentRoleModule } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { WorkspaceFlatRoleTargetMapCacheService } from 'src/engine/metadata-modules/flat-role-target/services/workspace-flat-role-target-map-cache.service';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { ObjectPermissionModule } from 'src/engine/metadata-modules/object-permission/object-permission.module';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { PermissionFlagModule } from 'src/engine/metadata-modules/permission-flag/permission-flag.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { RoleResolver } from 'src/engine/metadata-modules/role/role.resolver';
|
||||
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
|
||||
import { WorkspaceFlatRoleMapCacheService } from 'src/engine/metadata-modules/role/services/workspace-flat-role-map-cache.service';
|
||||
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/workspace-migration-builder-graphql-api-exception.interceptor';
|
||||
import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([RoleEntity, RoleTargetsEntity]),
|
||||
TypeOrmModule.forFeature([UserWorkspaceEntity, WorkspaceEntity]),
|
||||
TypeOrmModule.forFeature([
|
||||
RoleEntity,
|
||||
RoleTargetsEntity,
|
||||
ObjectPermissionEntity,
|
||||
PermissionFlagEntity,
|
||||
FieldPermissionEntity,
|
||||
UserWorkspaceEntity,
|
||||
]),
|
||||
UserRoleModule,
|
||||
AiAgentRoleModule,
|
||||
ApplicationModule,
|
||||
ApiKeyModule,
|
||||
PermissionsModule,
|
||||
UserWorkspaceModule,
|
||||
ObjectPermissionModule,
|
||||
PermissionFlagModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
UserWorkspaceModule,
|
||||
FileModule,
|
||||
ApplicationModule,
|
||||
],
|
||||
providers: [RoleService, RoleResolver],
|
||||
exports: [RoleService],
|
||||
providers: [
|
||||
RoleService,
|
||||
RoleResolver,
|
||||
WorkspaceFlatRoleMapCacheService,
|
||||
WorkspaceFlatRoleTargetMapCacheService,
|
||||
WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor,
|
||||
],
|
||||
exports: [
|
||||
RoleService,
|
||||
WorkspaceFlatRoleMapCacheService,
|
||||
WorkspaceFlatRoleTargetMapCacheService,
|
||||
],
|
||||
})
|
||||
export class RoleModule {}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { UseFilters, UseGuards, UsePipes } from '@nestjs/common';
|
||||
import {
|
||||
UseFilters,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
UsePipes,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
Args,
|
||||
Mutation,
|
||||
@@ -12,6 +17,7 @@ import { msg } from '@lingui/core/macro';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/api-key-role.service';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||
@@ -49,7 +55,9 @@ import {
|
||||
} from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
import { UpdateRoleInput } from 'src/engine/metadata-modules/role/dtos/update-role-input.dto';
|
||||
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
|
||||
import { fromRoleEntitiesToRoleDtos } from 'src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/workspace-migration-builder-graphql-api-exception.interceptor';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@Resolver(() => RoleDTO)
|
||||
@@ -62,6 +70,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
PermissionsGraphqlApiExceptionFilter,
|
||||
PreventNestToAutoLogGraphqlErrorsFilter,
|
||||
)
|
||||
@UseInterceptors(WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor)
|
||||
export class RoleResolver {
|
||||
constructor(
|
||||
private readonly userRoleService: UserRoleService,
|
||||
@@ -72,13 +81,16 @@ export class RoleResolver {
|
||||
private readonly agentRoleService: AiAgentRoleService,
|
||||
private readonly apiKeyRoleService: ApiKeyRoleService,
|
||||
private readonly fieldPermissionService: FieldPermissionService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
) {}
|
||||
|
||||
@Query(() => [RoleDTO])
|
||||
async getRoles(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
): Promise<RoleDTO[]> {
|
||||
return this.roleService.getWorkspaceRoles(workspace.id);
|
||||
const roleEntities = await this.roleService.getWorkspaceRoles(workspace.id);
|
||||
|
||||
return fromRoleEntitiesToRoleDtos(roleEntities);
|
||||
}
|
||||
|
||||
@Mutation(() => WorkspaceMemberDTO)
|
||||
@@ -141,9 +153,18 @@ export class RoleResolver {
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@Args('createRoleInput') createRoleInput: CreateRoleInput,
|
||||
): Promise<RoleDTO> {
|
||||
const { id: workspaceId } = workspace;
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
return await this.roleService.createRole({
|
||||
workspaceId: workspace.id,
|
||||
workspaceId,
|
||||
input: createRoleInput,
|
||||
applicationId: workspaceCustomFlatApplication.id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -165,12 +186,12 @@ export class RoleResolver {
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@Args('roleId', { type: () => UUIDScalarType }) roleId: string,
|
||||
): Promise<string> {
|
||||
const deletedRoleId = await this.roleService.deleteRole(
|
||||
const deletedRole = await this.roleService.deleteRole({
|
||||
roleId,
|
||||
workspace.id,
|
||||
);
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
return deletedRoleId;
|
||||
return deletedRole.id;
|
||||
}
|
||||
|
||||
@Mutation(() => [ObjectPermissionDTO])
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { computeFlatEntityMapsFromTo } from 'src/engine/metadata-modules/flat-entity/utils/compute-flat-entity-maps-from-to.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
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 { MEMBER_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/member-role-label.constants';
|
||||
import {
|
||||
PermissionsException,
|
||||
@@ -14,24 +19,24 @@ import {
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { type CreateRoleInput } from 'src/engine/metadata-modules/role/dtos/create-role-input.dto';
|
||||
import {
|
||||
type UpdateRoleInput,
|
||||
type UpdateRolePayload,
|
||||
} from 'src/engine/metadata-modules/role/dtos/update-role-input.dto';
|
||||
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
import { type UpdateRoleInput } from 'src/engine/metadata-modules/role/dtos/update-role-input.dto';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { fromFlatRoleToRoleDto } from 'src/engine/metadata-modules/role/utils/fromFlatRoleToRoleDto.util';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { isArgDefinedIfProvidedOrThrow } from 'src/engine/metadata-modules/utils/is-arg-defined-if-provided-or-throw.util';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Injectable()
|
||||
export class RoleService {
|
||||
constructor(
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
private readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
@InjectRepository(RoleEntity)
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
private readonly userRoleService: UserRoleService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
) {}
|
||||
|
||||
public async getWorkspaceRoles(workspaceId: string): Promise<RoleEntity[]> {
|
||||
@@ -69,46 +74,66 @@ export class RoleService {
|
||||
public async createRole({
|
||||
input,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
}: {
|
||||
input: CreateRoleInput;
|
||||
workspaceId: string;
|
||||
}): Promise<RoleEntity> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
applicationId: string;
|
||||
}): Promise<RoleDTO> {
|
||||
const { flatRoleMaps: existingFlatRoleMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatRoleMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatRoleToCreate = fromCreateRoleInputToFlatRoleToCreate({
|
||||
createRoleInput: input,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatRoleMaps: computeFlatEntityMapsFromTo({
|
||||
flatEntityMaps: existingFlatRoleMaps,
|
||||
flatEntityToCreate: [flatRoleToCreate],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [],
|
||||
}),
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
await this.validateRoleInputOrThrow({ input, workspaceId });
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while creating role',
|
||||
);
|
||||
}
|
||||
|
||||
const id = input.id ?? v4();
|
||||
const role = await this.roleRepository.save({
|
||||
id,
|
||||
label: input.label,
|
||||
description: input.description,
|
||||
icon: input.icon,
|
||||
canUpdateAllSettings: input.canUpdateAllSettings,
|
||||
canAccessAllTools: input.canAccessAllTools,
|
||||
canReadAllObjectRecords: input.canReadAllObjectRecords,
|
||||
canUpdateAllObjectRecords: input.canUpdateAllObjectRecords,
|
||||
canSoftDeleteAllObjectRecords: input.canSoftDeleteAllObjectRecords,
|
||||
canDestroyAllObjectRecords: input.canDestroyAllObjectRecords,
|
||||
canBeAssignedToUsers: input.canBeAssignedToUsers,
|
||||
canBeAssignedToAgents: input.canBeAssignedToAgents,
|
||||
canBeAssignedToApiKeys: input.canBeAssignedToApiKeys,
|
||||
isEditable: true,
|
||||
workspaceId,
|
||||
applicationId: workspaceCustomFlatApplication.id,
|
||||
universalIdentifier: id,
|
||||
});
|
||||
const { flatRoleMaps: recomputedFlatRoleMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatRoleMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache({
|
||||
workspaceId,
|
||||
roleIds: [role.id],
|
||||
});
|
||||
|
||||
return role;
|
||||
return fromFlatRoleToRoleDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatRoleToCreate.id,
|
||||
flatEntityMaps: recomputedFlatRoleMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async updateRole({
|
||||
@@ -117,57 +142,69 @@ export class RoleService {
|
||||
}: {
|
||||
input: UpdateRoleInput;
|
||||
workspaceId: string;
|
||||
}): Promise<RoleEntity> {
|
||||
await this.validateRoleIsEditableOrThrow({
|
||||
roleId: input.id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const existingRole = await this.roleRepository.findOne({
|
||||
where: {
|
||||
id: input.id,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(existingRole)) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.ROLE_NOT_FOUND,
|
||||
PermissionsExceptionCode.ROLE_NOT_FOUND,
|
||||
}): Promise<RoleDTO> {
|
||||
const { flatRoleMaps: existingFlatRoleMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
userFriendlyMessage: msg`The role you are looking for could not be found. It may have been deleted or you may not have access to it.`,
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatRoleMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatRoleToUpdate = fromUpdateRoleInputToFlatRoleToUpdateOrThrow({
|
||||
flatRoleMaps: existingFlatRoleMaps,
|
||||
updateRoleInput: input,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatRoleMaps: computeFlatEntityMapsFromTo({
|
||||
flatEntityMaps: existingFlatRoleMaps,
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [flatRoleToUpdate],
|
||||
}),
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while updating role',
|
||||
);
|
||||
}
|
||||
|
||||
await this.validateRoleInputOrThrow({
|
||||
input: input.update,
|
||||
workspaceId,
|
||||
roleId: input.id,
|
||||
});
|
||||
const { flatRoleMaps: recomputedFlatRoleMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatRoleMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const updatedRole = await this.roleRepository.save({
|
||||
id: input.id,
|
||||
...input.update,
|
||||
});
|
||||
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache({
|
||||
workspaceId,
|
||||
roleIds: [input.id],
|
||||
});
|
||||
|
||||
return { ...existingRole, ...updatedRole };
|
||||
return fromFlatRoleToRoleDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: input.id,
|
||||
flatEntityMaps: recomputedFlatRoleMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteRole(
|
||||
roleId: string,
|
||||
workspaceId: string,
|
||||
): Promise<string> {
|
||||
await this.validateRoleIsEditableOrThrow({
|
||||
roleId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
public async deleteRole({
|
||||
roleId,
|
||||
workspaceId,
|
||||
}: {
|
||||
roleId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<RoleDTO> {
|
||||
const workspace = await this.workspaceRepository.findOne({
|
||||
where: {
|
||||
id: workspaceId,
|
||||
@@ -186,10 +223,15 @@ export class RoleService {
|
||||
);
|
||||
}
|
||||
|
||||
await this.validateRoleIsNotDefaultRoleOrThrow({
|
||||
roleId,
|
||||
defaultRoleId,
|
||||
});
|
||||
if (defaultRoleId === roleId) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.DEFAULT_ROLE_CANNOT_BE_DELETED,
|
||||
PermissionsExceptionCode.DEFAULT_ROLE_CANNOT_BE_DELETED,
|
||||
{
|
||||
userFriendlyMessage: msg`The default role cannot be deleted as it is required for the workspace to function properly.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await this.assignDefaultRoleToMembersWithRoleToDelete({
|
||||
roleId,
|
||||
@@ -197,16 +239,49 @@ export class RoleService {
|
||||
defaultRoleId,
|
||||
});
|
||||
|
||||
await this.roleRepository.delete({
|
||||
id: roleId,
|
||||
workspaceId,
|
||||
const { flatRoleMaps: existingFlatRoleMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatRoleMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatRoleToDelete = fromDeleteRoleInputToFlatRoleOrThrow({
|
||||
flatRoleMaps: existingFlatRoleMaps,
|
||||
roleId,
|
||||
});
|
||||
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache({
|
||||
workspaceId,
|
||||
});
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatRoleMaps: computeFlatEntityMapsFromTo({
|
||||
flatEntityMaps: existingFlatRoleMaps,
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [flatRoleToDelete],
|
||||
flatEntityToUpdate: [],
|
||||
}),
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {},
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingEntities: {
|
||||
role: true,
|
||||
},
|
||||
isSystemBuild: false,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
return roleId;
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while deleting role',
|
||||
);
|
||||
}
|
||||
|
||||
return fromFlatRoleToRoleDto(flatRoleToDelete);
|
||||
}
|
||||
|
||||
public async createMemberRole({
|
||||
@@ -215,165 +290,55 @@ export class RoleService {
|
||||
}: {
|
||||
applicationId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<RoleEntity> {
|
||||
const id = v4();
|
||||
|
||||
return this.roleRepository.save({
|
||||
label: MEMBER_ROLE_LABEL,
|
||||
description: 'Member role',
|
||||
icon: 'IconUser',
|
||||
canUpdateAllSettings: false,
|
||||
canAccessAllTools: true,
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: true,
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: false,
|
||||
canBeAssignedToApiKeys: false,
|
||||
isEditable: true,
|
||||
workspaceId,
|
||||
}): Promise<RoleDTO> {
|
||||
return this.createRole({
|
||||
input: {
|
||||
label: MEMBER_ROLE_LABEL,
|
||||
description: 'Member role',
|
||||
icon: 'IconUser',
|
||||
canUpdateAllSettings: false,
|
||||
canAccessAllTools: true,
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: true,
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: false,
|
||||
canBeAssignedToApiKeys: false,
|
||||
},
|
||||
applicationId,
|
||||
id,
|
||||
universalIdentifier: id,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
// Only used for dev seeding and testing
|
||||
public async createGuestRole({
|
||||
workspaceId,
|
||||
applicationId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
applicationId: string;
|
||||
}): Promise<RoleEntity> {
|
||||
const id = v4();
|
||||
|
||||
return this.roleRepository.save({
|
||||
label: 'Guest',
|
||||
description: 'Guest role',
|
||||
icon: 'IconUser',
|
||||
canUpdateAllSettings: false,
|
||||
canAccessAllTools: false,
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: false,
|
||||
canSoftDeleteAllObjectRecords: false,
|
||||
canDestroyAllObjectRecords: false,
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: false,
|
||||
canBeAssignedToApiKeys: false,
|
||||
isEditable: false,
|
||||
}): Promise<RoleDTO> {
|
||||
return this.createRole({
|
||||
input: {
|
||||
label: 'Guest',
|
||||
description: 'Guest role',
|
||||
icon: 'IconUser',
|
||||
canUpdateAllSettings: false,
|
||||
canAccessAllTools: false,
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: false,
|
||||
canSoftDeleteAllObjectRecords: false,
|
||||
canDestroyAllObjectRecords: false,
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: false,
|
||||
canBeAssignedToApiKeys: false,
|
||||
},
|
||||
workspaceId,
|
||||
applicationId,
|
||||
id,
|
||||
universalIdentifier: id,
|
||||
});
|
||||
}
|
||||
|
||||
private async validateRoleInputOrThrow({
|
||||
input,
|
||||
workspaceId,
|
||||
roleId,
|
||||
}: {
|
||||
input: CreateRoleInput | UpdateRolePayload;
|
||||
workspaceId: string;
|
||||
roleId?: string;
|
||||
}): Promise<void> {
|
||||
const keysToValidate = [
|
||||
'label',
|
||||
'canUpdateAllSettings',
|
||||
'canAccessAllTools',
|
||||
'canReadAllObjectRecords',
|
||||
'canUpdateAllObjectRecords',
|
||||
'canSoftDeleteAllObjectRecords',
|
||||
'canDestroyAllObjectRecords',
|
||||
];
|
||||
|
||||
for (const key of keysToValidate) {
|
||||
try {
|
||||
isArgDefinedIfProvidedOrThrow({
|
||||
input,
|
||||
key,
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
value: input[key],
|
||||
});
|
||||
} catch (error) {
|
||||
throw new PermissionsException(
|
||||
error.message,
|
||||
PermissionsExceptionCode.INVALID_ARG,
|
||||
{
|
||||
userFriendlyMessage: msg`Some of the information provided is invalid. Please check your input and try again.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const workspaceRoles = await this.getWorkspaceRoles(workspaceId);
|
||||
|
||||
if (isDefined(input.label)) {
|
||||
let rolesForLabelComparison = workspaceRoles;
|
||||
|
||||
if (isDefined(roleId)) {
|
||||
rolesForLabelComparison = workspaceRoles.filter(
|
||||
(role) => role.id !== roleId,
|
||||
);
|
||||
}
|
||||
|
||||
if (rolesForLabelComparison.some((role) => role.label === input.label)) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.ROLE_LABEL_ALREADY_EXISTS,
|
||||
PermissionsExceptionCode.ROLE_LABEL_ALREADY_EXISTS,
|
||||
{ userFriendlyMessage: msg`A role with this label already exists.` },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const existingRole = workspaceRoles.find((role) => role.id === roleId);
|
||||
|
||||
await this.validateRoleReadAndWirtePermissionsConsistencyOrThrow({
|
||||
input,
|
||||
existingRole,
|
||||
});
|
||||
}
|
||||
|
||||
private async validateRoleReadAndWirtePermissionsConsistencyOrThrow({
|
||||
input,
|
||||
existingRole,
|
||||
}: {
|
||||
input: CreateRoleInput | UpdateRolePayload;
|
||||
existingRole?: RoleEntity;
|
||||
}) {
|
||||
const hasReadingPermissionsAfterUpdate =
|
||||
input.canReadAllObjectRecords ?? existingRole?.canReadAllObjectRecords;
|
||||
|
||||
const hasUpdatePermissionsAfterUpdate =
|
||||
input.canUpdateAllObjectRecords ??
|
||||
existingRole?.canUpdateAllObjectRecords;
|
||||
|
||||
const hasSoftDeletePermissionsAfterUpdate =
|
||||
input.canSoftDeleteAllObjectRecords ??
|
||||
existingRole?.canSoftDeleteAllObjectRecords;
|
||||
|
||||
const hasDestroyPermissionsAfterUpdate =
|
||||
input.canDestroyAllObjectRecords ??
|
||||
existingRole?.canDestroyAllObjectRecords;
|
||||
|
||||
if (
|
||||
hasReadingPermissionsAfterUpdate === false &&
|
||||
(hasUpdatePermissionsAfterUpdate ||
|
||||
hasSoftDeletePermissionsAfterUpdate ||
|
||||
hasDestroyPermissionsAfterUpdate)
|
||||
) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.CANNOT_GIVE_WRITING_PERMISSION_WITHOUT_READING_PERMISSION,
|
||||
PermissionsExceptionCode.CANNOT_GIVE_WRITING_PERMISSION_WITHOUT_READING_PERMISSION,
|
||||
{
|
||||
userFriendlyMessage: msg`You cannot grant edit permissions without also granting read permissions. Please enable read access first.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move to migration side effect / To address for rollback of role deletion
|
||||
private async assignDefaultRoleToMembersWithRoleToDelete({
|
||||
roleId,
|
||||
workspaceId,
|
||||
@@ -399,54 +364,4 @@ export class RoleService {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private async getRole(
|
||||
roleId: string,
|
||||
workspaceId: string,
|
||||
): Promise<RoleEntity | null> {
|
||||
return this.roleRepository.findOne({
|
||||
where: {
|
||||
id: roleId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async validateRoleIsEditableOrThrow({
|
||||
roleId,
|
||||
workspaceId,
|
||||
}: {
|
||||
roleId: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const role = await this.getRole(roleId, workspaceId);
|
||||
|
||||
if (!role?.isEditable) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.ROLE_NOT_EDITABLE,
|
||||
PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||
{
|
||||
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async validateRoleIsNotDefaultRoleOrThrow({
|
||||
roleId,
|
||||
defaultRoleId,
|
||||
}: {
|
||||
roleId: string;
|
||||
defaultRoleId: string;
|
||||
}): Promise<void> {
|
||||
if (defaultRoleId === roleId) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.DEFAULT_ROLE_CANNOT_BE_DELETED,
|
||||
PermissionsExceptionCode.DEFAULT_ROLE_CANNOT_BE_DELETED,
|
||||
{
|
||||
userFriendlyMessage: msg`The default role cannot be deleted as it is required for the workspace to function properly.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type';
|
||||
import { fromRoleEntityToFlatRole } from 'src/engine/metadata-modules/flat-role/utils/from-role-entity-to-flat-role.util';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
import { regroupEntitiesByRelatedEntityId } from 'src/engine/workspace-flat-map-cache/utils/regroup-entities-by-related-entity-id';
|
||||
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration-v2/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceFlatMapCache('flatRoleMaps')
|
||||
export class WorkspaceFlatRoleMapCacheService extends WorkspaceFlatMapCacheService<
|
||||
FlatEntityMaps<FlatRole>
|
||||
> {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||
cacheStorageService: CacheStorageService,
|
||||
@InjectRepository(RoleEntity)
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
@InjectRepository(RoleTargetsEntity)
|
||||
private readonly roleTargetsRepository: Repository<RoleTargetsEntity>,
|
||||
@InjectRepository(ObjectPermissionEntity)
|
||||
private readonly objectPermissionRepository: Repository<ObjectPermissionEntity>,
|
||||
@InjectRepository(PermissionFlagEntity)
|
||||
private readonly permissionFlagRepository: Repository<PermissionFlagEntity>,
|
||||
@InjectRepository(FieldPermissionEntity)
|
||||
private readonly fieldPermissionRepository: Repository<FieldPermissionEntity>,
|
||||
) {
|
||||
super(cacheStorageService);
|
||||
}
|
||||
|
||||
protected async computeFlatMap({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<FlatEntityMaps<FlatRole>> {
|
||||
const [
|
||||
roles,
|
||||
roleTargets,
|
||||
objectPermissions,
|
||||
permissionFlags,
|
||||
fieldPermissions,
|
||||
] = await Promise.all([
|
||||
this.roleRepository.find({
|
||||
where: { workspaceId },
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.roleTargetsRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'roleId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.objectPermissionRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'roleId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.permissionFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'roleId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.fieldPermissionRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'roleId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const [
|
||||
roleTargetsByRoleId,
|
||||
objectPermissionsByRoleId,
|
||||
permissionFlagsByRoleId,
|
||||
fieldPermissionsByRoleId,
|
||||
] = (
|
||||
[
|
||||
{
|
||||
entities: roleTargets,
|
||||
foreignKey: 'roleId',
|
||||
},
|
||||
{
|
||||
entities: objectPermissions,
|
||||
foreignKey: 'roleId',
|
||||
},
|
||||
{
|
||||
entities: permissionFlags,
|
||||
foreignKey: 'roleId',
|
||||
},
|
||||
{
|
||||
entities: fieldPermissions,
|
||||
foreignKey: 'roleId',
|
||||
},
|
||||
] as const
|
||||
).map(regroupEntitiesByRelatedEntityId);
|
||||
|
||||
const flatRoleMaps = createEmptyFlatEntityMaps();
|
||||
|
||||
for (const roleEntity of roles) {
|
||||
const flatRole = fromRoleEntityToFlatRole({
|
||||
...roleEntity,
|
||||
roleTargets: roleTargetsByRoleId.get(roleEntity.id) || [],
|
||||
objectPermissions: objectPermissionsByRoleId.get(roleEntity.id) || [],
|
||||
permissionFlags: permissionFlagsByRoleId.get(roleEntity.id) || [],
|
||||
fieldPermissions: fieldPermissionsByRoleId.get(roleEntity.id) || [],
|
||||
} as RoleEntity);
|
||||
|
||||
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
|
||||
flatEntity: flatRole,
|
||||
flatEntityMapsToMutate: flatRoleMaps,
|
||||
});
|
||||
}
|
||||
|
||||
return flatRoleMaps;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type';
|
||||
import { type RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
|
||||
export const fromFlatRoleToRoleDto = ({
|
||||
canAccessAllTools,
|
||||
canBeAssignedToAgents,
|
||||
canBeAssignedToApiKeys,
|
||||
canBeAssignedToUsers,
|
||||
canDestroyAllObjectRecords,
|
||||
canReadAllObjectRecords,
|
||||
canSoftDeleteAllObjectRecords,
|
||||
canUpdateAllObjectRecords,
|
||||
canUpdateAllSettings,
|
||||
id,
|
||||
isEditable,
|
||||
label,
|
||||
description,
|
||||
icon,
|
||||
standardId,
|
||||
}: FlatRole): RoleDTO => {
|
||||
return {
|
||||
canAccessAllTools,
|
||||
canBeAssignedToAgents,
|
||||
canBeAssignedToApiKeys,
|
||||
canBeAssignedToUsers,
|
||||
canDestroyAllObjectRecords,
|
||||
canReadAllObjectRecords,
|
||||
canSoftDeleteAllObjectRecords,
|
||||
canUpdateAllObjectRecords,
|
||||
canUpdateAllSettings,
|
||||
id,
|
||||
isEditable,
|
||||
label,
|
||||
description: description ?? undefined,
|
||||
icon: icon ?? undefined,
|
||||
standardId: standardId ?? undefined,
|
||||
};
|
||||
};
|
||||
+6
-3
@@ -4,12 +4,12 @@ import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
export const fromRoleEntityToRoleDto = (role: RoleEntity): RoleDTO => {
|
||||
return {
|
||||
id: role.id,
|
||||
standardId: role.standardId,
|
||||
standardId: role.standardId ?? undefined,
|
||||
label: role.label,
|
||||
canUpdateAllSettings: role.canUpdateAllSettings,
|
||||
canAccessAllTools: role.canAccessAllTools,
|
||||
description: role.description,
|
||||
icon: role.icon,
|
||||
description: role.description ?? undefined,
|
||||
icon: role.icon ?? undefined,
|
||||
isEditable: role.isEditable,
|
||||
canReadAllObjectRecords: role.canReadAllObjectRecords,
|
||||
canUpdateAllObjectRecords: role.canUpdateAllObjectRecords,
|
||||
@@ -19,6 +19,9 @@ export const fromRoleEntityToRoleDto = (role: RoleEntity): RoleDTO => {
|
||||
canBeAssignedToAgents: role.canBeAssignedToAgents,
|
||||
canBeAssignedToApiKeys: role.canBeAssignedToApiKeys,
|
||||
roleTargets: role.roleTargets,
|
||||
permissionFlags: role.permissionFlags,
|
||||
objectPermissions: role.objectPermissions,
|
||||
fieldPermissions: role.fieldPermissions,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user