Fix seeding perf + batch role targets creation (#16337)
This commit is contained in:
+48
-18
@@ -37,6 +37,25 @@ export class RoleTargetService {
|
||||
createRoleTargetInput: CreateRoleTargetInput;
|
||||
workspaceId: string;
|
||||
}): Promise<FlatRoleTarget> {
|
||||
const [flatRoleTarget] = await this.createMany({
|
||||
createRoleTargetInputs: [createRoleTargetInput],
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return flatRoleTarget;
|
||||
}
|
||||
|
||||
async createMany({
|
||||
createRoleTargetInputs,
|
||||
workspaceId,
|
||||
}: {
|
||||
createRoleTargetInputs: CreateRoleTargetInput[];
|
||||
workspaceId: string;
|
||||
}): Promise<FlatRoleTarget[]> {
|
||||
if (createRoleTargetInputs.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { flatRoleTargetMaps, flatRoleMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
@@ -44,6 +63,7 @@ export class RoleTargetService {
|
||||
flatMapsKeys: ['flatRoleTargetMaps', 'flatRoleMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{
|
||||
@@ -51,17 +71,25 @@ export class RoleTargetService {
|
||||
},
|
||||
);
|
||||
|
||||
const { flatRoleTargetToCreate, flatRoleTargetsToDelete } =
|
||||
fromCreateRoleTargetInputToFlatRoleTargetToCreate({
|
||||
createRoleTargetInput: {
|
||||
...createRoleTargetInput,
|
||||
applicationId:
|
||||
createRoleTargetInput.applicationId ??
|
||||
workspaceCustomFlatApplication.id,
|
||||
},
|
||||
flatRoleTargetMaps,
|
||||
workspaceId,
|
||||
});
|
||||
const allFlatRoleTargetsToCreate: FlatRoleTarget[] = [];
|
||||
const allFlatRoleTargetsToDelete: FlatRoleTarget[] = [];
|
||||
|
||||
for (const createRoleTargetInput of createRoleTargetInputs) {
|
||||
const { flatRoleTargetToCreate, flatRoleTargetsToDelete } =
|
||||
fromCreateRoleTargetInputToFlatRoleTargetToCreate({
|
||||
createRoleTargetInput: {
|
||||
...createRoleTargetInput,
|
||||
applicationId:
|
||||
createRoleTargetInput.applicationId ??
|
||||
workspaceCustomFlatApplication.id,
|
||||
},
|
||||
flatRoleTargetMaps,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
allFlatRoleTargetsToCreate.push(flatRoleTargetToCreate);
|
||||
allFlatRoleTargetsToDelete.push(...flatRoleTargetsToDelete);
|
||||
}
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
@@ -69,8 +97,8 @@ export class RoleTargetService {
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatRoleTargetMaps: computeFlatEntityMapsFromTo({
|
||||
flatEntityMaps: flatRoleTargetMaps,
|
||||
flatEntityToCreate: [flatRoleTargetToCreate],
|
||||
flatEntityToDelete: flatRoleTargetsToDelete,
|
||||
flatEntityToCreate: allFlatRoleTargetsToCreate,
|
||||
flatEntityToDelete: allFlatRoleTargetsToDelete,
|
||||
flatEntityToUpdate: [],
|
||||
}),
|
||||
},
|
||||
@@ -90,7 +118,7 @@ export class RoleTargetService {
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderExceptionV2(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while creating role target',
|
||||
'Multiple validation errors occurred while creating role targets',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,10 +130,12 @@ export class RoleTargetService {
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatRoleTargetToCreate.id,
|
||||
flatEntityMaps: recomputedFlatRoleTargetMaps,
|
||||
});
|
||||
return allFlatRoleTargetsToCreate.map((flatRoleTargetToCreate) =>
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatRoleTargetToCreate.id,
|
||||
flatEntityMaps: recomputedFlatRoleTargetMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async delete({ id, workspaceId }: DeleteRoleTargetInput): Promise<void> {
|
||||
|
||||
@@ -126,8 +126,8 @@ export class RoleResolver {
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
await this.userRoleService.assignRoleToUserWorkspace({
|
||||
userWorkspaceId: userWorkspace.id,
|
||||
await this.userRoleService.assignRoleToManyUserWorkspace({
|
||||
userWorkspaceIds: [userWorkspace.id],
|
||||
workspaceId: workspace.id,
|
||||
roleId,
|
||||
});
|
||||
|
||||
@@ -354,14 +354,10 @@ export class RoleService {
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
userWorkspaceIds.map((userWorkspaceId) =>
|
||||
this.userRoleService.assignRoleToUserWorkspace({
|
||||
userWorkspaceId,
|
||||
roleId: defaultRoleId,
|
||||
workspaceId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
await this.userRoleService.assignRoleToManyUserWorkspace({
|
||||
userWorkspaceIds,
|
||||
roleId: defaultRoleId,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,31 +29,38 @@ export class UserRoleService {
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
) {}
|
||||
|
||||
public async assignRoleToUserWorkspace({
|
||||
public async assignRoleToManyUserWorkspace({
|
||||
workspaceId,
|
||||
userWorkspaceId,
|
||||
userWorkspaceIds,
|
||||
roleId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
userWorkspaceId: string;
|
||||
userWorkspaceIds: string[];
|
||||
roleId: string;
|
||||
}): Promise<void> {
|
||||
const validationResult = await this.validateAssignRoleInput({
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
roleId,
|
||||
});
|
||||
|
||||
if (validationResult?.roleToAssignIsSameAsCurrentRole) {
|
||||
if (userWorkspaceIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.roleTargetService.create({
|
||||
createRoleTargetInput: {
|
||||
const userWorkspaceIdsToAssign =
|
||||
await this.validateAssignRoleInputsAndGetUserWorkspaceIdsToAssign({
|
||||
userWorkspaceIds,
|
||||
workspaceId,
|
||||
roleId,
|
||||
targetId: userWorkspaceId,
|
||||
targetMetadataForeignKey: 'userWorkspaceId',
|
||||
},
|
||||
});
|
||||
|
||||
if (userWorkspaceIdsToAssign.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.roleTargetService.createMany({
|
||||
createRoleTargetInputs: userWorkspaceIdsToAssign.map(
|
||||
(userWorkspaceId) => ({
|
||||
roleId,
|
||||
targetId: userWorkspaceId,
|
||||
targetMetadataForeignKey: 'userWorkspaceId' as const,
|
||||
}),
|
||||
),
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
@@ -208,57 +215,72 @@ export class UserRoleService {
|
||||
}
|
||||
}
|
||||
|
||||
private async validateAssignRoleInput({
|
||||
userWorkspaceId,
|
||||
private async validateAssignRoleInputsAndGetUserWorkspaceIdsToAssign({
|
||||
userWorkspaceIds,
|
||||
workspaceId,
|
||||
roleId,
|
||||
}: {
|
||||
userWorkspaceId: string;
|
||||
userWorkspaceIds: string[];
|
||||
workspaceId: string;
|
||||
roleId: string;
|
||||
}) {
|
||||
const userWorkspace = await this.userWorkspaceRepository.findOne({
|
||||
}): Promise<string[]> {
|
||||
const userWorkspaces = await this.userWorkspaceRepository.find({
|
||||
where: {
|
||||
id: userWorkspaceId,
|
||||
id: In(userWorkspaceIds),
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(userWorkspace)) {
|
||||
const foundUserWorkspaceIds = new Set(
|
||||
userWorkspaces.map((userWorkspace) => userWorkspace.id),
|
||||
);
|
||||
|
||||
const missingUserWorkspaceIds = userWorkspaceIds.filter(
|
||||
(id) => !foundUserWorkspaceIds.has(id),
|
||||
);
|
||||
|
||||
if (missingUserWorkspaceIds.length > 0) {
|
||||
throw new PermissionsException(
|
||||
'User workspace not found',
|
||||
`User workspaces not found: ${missingUserWorkspaceIds.join(', ')}`,
|
||||
PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND,
|
||||
{
|
||||
userFriendlyMessage: msg`Your workspace membership could not be found. You may no longer have access to this workspace.`,
|
||||
userFriendlyMessage: msg`Some workspace memberships could not be found. They may no longer have access to this workspace.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const roles = await this.getRolesByUserWorkspaces({
|
||||
userWorkspaceIds: [userWorkspace.id],
|
||||
const rolesByUserWorkspaces = await this.getRolesByUserWorkspaces({
|
||||
userWorkspaceIds,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const currentRole = roles.get(userWorkspace.id)?.[0];
|
||||
const userWorkspaceIdsToAssign: string[] = [];
|
||||
let adminRoleIdToValidate: string | undefined;
|
||||
|
||||
if (currentRole?.id === roleId) {
|
||||
return {
|
||||
roleToAssignIsSameAsCurrentRole: true,
|
||||
};
|
||||
}
|
||||
for (const userWorkspaceId of userWorkspaceIds) {
|
||||
const currentRole = rolesByUserWorkspaces.get(userWorkspaceId)?.[0];
|
||||
|
||||
if (
|
||||
!(
|
||||
if (currentRole?.id === roleId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(currentRole) &&
|
||||
currentRole.standardId === ADMIN_ROLE.standardId
|
||||
)
|
||||
) {
|
||||
return;
|
||||
) {
|
||||
adminRoleIdToValidate = currentRole.id;
|
||||
}
|
||||
|
||||
userWorkspaceIdsToAssign.push(userWorkspaceId);
|
||||
}
|
||||
|
||||
await this.validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({
|
||||
workspaceId,
|
||||
adminRoleId: currentRole.id,
|
||||
});
|
||||
if (isDefined(adminRoleIdToValidate)) {
|
||||
await this.validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({
|
||||
workspaceId,
|
||||
adminRoleId: adminRoleIdToValidate,
|
||||
});
|
||||
}
|
||||
|
||||
return userWorkspaceIdsToAssign;
|
||||
}
|
||||
|
||||
private async validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({
|
||||
|
||||
Reference in New Issue
Block a user