Batch create, update, and delete navigation menu items (#18882)
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+59
@@ -65,6 +65,26 @@ export class NavigationMenuItemResolver {
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => [NavigationMenuItemDTO])
|
||||
@UseGuards(NoPermissionGuard)
|
||||
async createManyNavigationMenuItems(
|
||||
@Args('inputs', { type: () => [CreateNavigationMenuItemInput] })
|
||||
inputs: CreateNavigationMenuItemInput[],
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@AuthUserWorkspaceId({ allowUndefined: true })
|
||||
userWorkspaceId: string | undefined,
|
||||
@AuthApiKey() apiKey: ApiKeyEntity | undefined,
|
||||
@Context() context: { req: { application?: ApplicationEntity } },
|
||||
): Promise<NavigationMenuItemDTO[]> {
|
||||
return await this.navigationMenuItemService.createMany({
|
||||
inputs,
|
||||
workspaceId: workspace.id,
|
||||
authUserWorkspaceId: userWorkspaceId,
|
||||
authApiKeyId: apiKey?.id,
|
||||
authApplicationId: context.req.application?.id,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => NavigationMenuItemDTO)
|
||||
@UseGuards(NoPermissionGuard)
|
||||
async createNavigationMenuItem(
|
||||
@@ -84,6 +104,26 @@ export class NavigationMenuItemResolver {
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => [NavigationMenuItemDTO])
|
||||
@UseGuards(NoPermissionGuard)
|
||||
async updateManyNavigationMenuItems(
|
||||
@Args('inputs', { type: () => [UpdateOneNavigationMenuItemInput] })
|
||||
inputs: UpdateOneNavigationMenuItemInput[],
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@AuthUserWorkspaceId({ allowUndefined: true })
|
||||
userWorkspaceId: string | undefined,
|
||||
@AuthApiKey() apiKey: ApiKeyEntity | undefined,
|
||||
@Context() context: { req: { application?: ApplicationEntity } },
|
||||
): Promise<NavigationMenuItemDTO[]> {
|
||||
return await this.navigationMenuItemService.updateMany({
|
||||
inputs,
|
||||
workspaceId: workspace.id,
|
||||
authUserWorkspaceId: userWorkspaceId,
|
||||
authApiKeyId: apiKey?.id,
|
||||
authApplicationId: context.req.application?.id,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => NavigationMenuItemDTO)
|
||||
@UseGuards(NoPermissionGuard)
|
||||
async updateNavigationMenuItem(
|
||||
@@ -103,6 +143,25 @@ export class NavigationMenuItemResolver {
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => [NavigationMenuItemDTO])
|
||||
@UseGuards(NoPermissionGuard)
|
||||
async deleteManyNavigationMenuItems(
|
||||
@Args('ids', { type: () => [UUIDScalarType] }) ids: string[],
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@AuthUserWorkspaceId({ allowUndefined: true })
|
||||
userWorkspaceId: string | undefined,
|
||||
@AuthApiKey() apiKey: ApiKeyEntity | undefined,
|
||||
@Context() context: { req: { application?: ApplicationEntity } },
|
||||
): Promise<NavigationMenuItemDTO[]> {
|
||||
return await this.navigationMenuItemService.deleteMany({
|
||||
ids,
|
||||
workspaceId: workspace.id,
|
||||
authUserWorkspaceId: userWorkspaceId,
|
||||
authApiKeyId: apiKey?.id,
|
||||
authApplicationId: context.req.application?.id,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => NavigationMenuItemDTO)
|
||||
@UseGuards(NoPermissionGuard)
|
||||
async deleteNavigationMenuItem(
|
||||
|
||||
+290
-80
@@ -7,6 +7,8 @@ import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/wo
|
||||
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 { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatNavigationMenuItem } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item.type';
|
||||
import { addFlatNavigationMenuItemToMapsAndUpdateIndex } from 'src/engine/metadata-modules/flat-navigation-menu-item/utils/add-flat-navigation-menu-item-to-maps-and-update-index.util';
|
||||
import { fromCreateNavigationMenuItemInputToFlatNavigationMenuItemToCreate } from 'src/engine/metadata-modules/flat-navigation-menu-item/utils/from-create-navigation-menu-item-input-to-flat-navigation-menu-item-to-create.util';
|
||||
import { fromDeleteNavigationMenuItemInputToFlatNavigationMenuItemOrThrow } from 'src/engine/metadata-modules/flat-navigation-menu-item/utils/from-delete-navigation-menu-item-input-to-flat-navigation-menu-item-or-throw.util';
|
||||
import { fromFlatNavigationMenuItemToNavigationMenuItemDto } from 'src/engine/metadata-modules/flat-navigation-menu-item/utils/from-flat-navigation-menu-item-to-navigation-menu-item-dto.util';
|
||||
@@ -14,7 +16,10 @@ import { fromUpdateNavigationMenuItemInputToFlatNavigationMenuItemToUpdateOrThro
|
||||
import { type CreateNavigationMenuItemInput } from 'src/engine/metadata-modules/navigation-menu-item/dtos/create-navigation-menu-item.input';
|
||||
import { type NavigationMenuItemDTO } from 'src/engine/metadata-modules/navigation-menu-item/dtos/navigation-menu-item.dto';
|
||||
import { RecordIdentifierDTO } from 'src/engine/metadata-modules/navigation-menu-item/dtos/record-identifier.dto';
|
||||
import { type UpdateNavigationMenuItemInput } from 'src/engine/metadata-modules/navigation-menu-item/dtos/update-navigation-menu-item.input';
|
||||
import {
|
||||
type UpdateNavigationMenuItemInput,
|
||||
type UpdateOneNavigationMenuItemInput,
|
||||
} from 'src/engine/metadata-modules/navigation-menu-item/dtos/update-navigation-menu-item.input';
|
||||
import { NavigationMenuItemType } from 'src/engine/metadata-modules/navigation-menu-item/enums/navigation-menu-item-type.enum';
|
||||
import {
|
||||
NavigationMenuItemException,
|
||||
@@ -123,13 +128,55 @@ export class NavigationMenuItemService {
|
||||
authApiKeyId?: string;
|
||||
authApplicationId?: string;
|
||||
}): Promise<NavigationMenuItemDTO> {
|
||||
await this.navigationMenuItemAccessService.canUserCreateNavigationMenuItem({
|
||||
userWorkspaceId: authUserWorkspaceId,
|
||||
const createdItems = await this.createMany({
|
||||
inputs: [input],
|
||||
workspaceId,
|
||||
apiKeyId: authApiKeyId,
|
||||
applicationId: authApplicationId,
|
||||
inputUserWorkspaceId: input.userWorkspaceId,
|
||||
authUserWorkspaceId,
|
||||
authApiKeyId,
|
||||
authApplicationId,
|
||||
});
|
||||
|
||||
const created = createdItems[0];
|
||||
|
||||
if (!isDefined(created)) {
|
||||
throw new NavigationMenuItemException(
|
||||
'Failed to create navigation menu item',
|
||||
NavigationMenuItemExceptionCode.INVALID_NAVIGATION_MENU_ITEM_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
async createMany({
|
||||
inputs,
|
||||
workspaceId,
|
||||
authUserWorkspaceId,
|
||||
authApiKeyId,
|
||||
authApplicationId,
|
||||
}: {
|
||||
inputs: CreateNavigationMenuItemInput[];
|
||||
workspaceId: string;
|
||||
authUserWorkspaceId?: string;
|
||||
authApiKeyId?: string;
|
||||
authApplicationId?: string;
|
||||
}): Promise<NavigationMenuItemDTO[]> {
|
||||
if (inputs.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const input of inputs) {
|
||||
await this.navigationMenuItemAccessService.canUserCreateNavigationMenuItem(
|
||||
{
|
||||
userWorkspaceId: authUserWorkspaceId,
|
||||
workspaceId,
|
||||
apiKeyId: authApiKeyId,
|
||||
applicationId: authApplicationId,
|
||||
inputUserWorkspaceId: input.userWorkspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
@@ -150,30 +197,57 @@ export class NavigationMenuItemService {
|
||||
},
|
||||
);
|
||||
|
||||
const normalizedInput: CreateNavigationMenuItemInput = {
|
||||
...input,
|
||||
userWorkspaceId:
|
||||
isDefined(input.userWorkspaceId) && isDefined(authUserWorkspaceId)
|
||||
? authUserWorkspaceId
|
||||
: input.userWorkspaceId,
|
||||
};
|
||||
const optimisticFlatNavigationMenuItemMaps = structuredClone(
|
||||
existingFlatNavigationMenuItemMaps,
|
||||
);
|
||||
|
||||
const flatNavigationMenuItemToCreate =
|
||||
fromCreateNavigationMenuItemInputToFlatNavigationMenuItemToCreate({
|
||||
createNavigationMenuItemInput: normalizedInput,
|
||||
workspaceId,
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
flatNavigationMenuItemMaps: existingFlatNavigationMenuItemMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatViewMaps,
|
||||
const foldersFirst = [...inputs].sort((a, b) => {
|
||||
const aIsFolder = a.type === NavigationMenuItemType.FOLDER ? 0 : 1;
|
||||
const bIsFolder = b.type === NavigationMenuItemType.FOLDER ? 0 : 1;
|
||||
|
||||
return aIsFolder - bIsFolder;
|
||||
});
|
||||
|
||||
const flatEntityByInput = new Map<
|
||||
CreateNavigationMenuItemInput,
|
||||
FlatNavigationMenuItem
|
||||
>();
|
||||
|
||||
const flatNavigationMenuItemsToCreate = foldersFirst.map((input) => {
|
||||
const normalizedInput: CreateNavigationMenuItemInput = {
|
||||
...input,
|
||||
userWorkspaceId:
|
||||
isDefined(input.userWorkspaceId) && isDefined(authUserWorkspaceId)
|
||||
? authUserWorkspaceId
|
||||
: input.userWorkspaceId,
|
||||
};
|
||||
|
||||
const flatNavigationMenuItemToCreate =
|
||||
fromCreateNavigationMenuItemInputToFlatNavigationMenuItemToCreate({
|
||||
createNavigationMenuItemInput: normalizedInput,
|
||||
workspaceId,
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
flatNavigationMenuItemMaps: optimisticFlatNavigationMenuItemMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatViewMaps,
|
||||
});
|
||||
|
||||
addFlatNavigationMenuItemToMapsAndUpdateIndex({
|
||||
flatNavigationMenuItem: flatNavigationMenuItemToCreate,
|
||||
flatNavigationMenuItemMaps: optimisticFlatNavigationMenuItemMaps,
|
||||
});
|
||||
|
||||
flatEntityByInput.set(input, flatNavigationMenuItemToCreate);
|
||||
|
||||
return flatNavigationMenuItemToCreate;
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
navigationMenuItem: {
|
||||
flatEntityToCreate: [flatNavigationMenuItemToCreate],
|
||||
flatEntityToCreate: flatNavigationMenuItemsToCreate,
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
@@ -188,7 +262,7 @@ export class NavigationMenuItemService {
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while creating navigation menu item',
|
||||
'Multiple validation errors occurred while creating navigation menu items',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -200,11 +274,13 @@ export class NavigationMenuItemService {
|
||||
},
|
||||
);
|
||||
|
||||
return fromFlatNavigationMenuItemToNavigationMenuItemDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatNavigationMenuItemToCreate.id,
|
||||
flatEntityMaps: recomputedFlatNavigationMenuItemMaps,
|
||||
}),
|
||||
return inputs.map((input) =>
|
||||
fromFlatNavigationMenuItemToNavigationMenuItemDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatEntityByInput.get(input)!.id,
|
||||
flatEntityMaps: recomputedFlatNavigationMenuItemMaps,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -221,6 +297,45 @@ export class NavigationMenuItemService {
|
||||
authApiKeyId?: string;
|
||||
authApplicationId?: string;
|
||||
}): Promise<NavigationMenuItemDTO> {
|
||||
const { id, ...update } = input;
|
||||
|
||||
const updatedItems = await this.updateMany({
|
||||
inputs: [{ id, update }],
|
||||
workspaceId,
|
||||
authUserWorkspaceId,
|
||||
authApiKeyId,
|
||||
authApplicationId,
|
||||
});
|
||||
|
||||
const updated = updatedItems[0];
|
||||
|
||||
if (!isDefined(updated)) {
|
||||
throw new NavigationMenuItemException(
|
||||
'Failed to update navigation menu item',
|
||||
NavigationMenuItemExceptionCode.INVALID_NAVIGATION_MENU_ITEM_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
async updateMany({
|
||||
inputs,
|
||||
workspaceId,
|
||||
authUserWorkspaceId,
|
||||
authApiKeyId,
|
||||
authApplicationId,
|
||||
}: {
|
||||
inputs: UpdateOneNavigationMenuItemInput[];
|
||||
workspaceId: string;
|
||||
authUserWorkspaceId?: string;
|
||||
authApiKeyId?: string;
|
||||
authApplicationId?: string;
|
||||
}): Promise<NavigationMenuItemDTO[]> {
|
||||
if (inputs.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
@@ -234,29 +349,41 @@ export class NavigationMenuItemService {
|
||||
},
|
||||
);
|
||||
|
||||
const existingNavigationMenuItem = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: input.id,
|
||||
flatEntityMaps: existingFlatNavigationMenuItemMaps,
|
||||
});
|
||||
const flatNavigationMenuItemsToUpdate: FlatNavigationMenuItem[] = [];
|
||||
|
||||
if (isDefined(existingNavigationMenuItem)) {
|
||||
await this.navigationMenuItemAccessService.canUserUpdateNavigationMenuItem(
|
||||
{
|
||||
userWorkspaceId: authUserWorkspaceId,
|
||||
workspaceId,
|
||||
apiKeyId: authApiKeyId,
|
||||
applicationId: authApplicationId,
|
||||
existingUserWorkspaceId: existingNavigationMenuItem.userWorkspaceId,
|
||||
},
|
||||
for (const { id, update } of inputs) {
|
||||
const updateInput: UpdateNavigationMenuItemInput & { id: string } = {
|
||||
...update,
|
||||
id,
|
||||
};
|
||||
|
||||
const existingNavigationMenuItem = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: id,
|
||||
flatEntityMaps: existingFlatNavigationMenuItemMaps,
|
||||
});
|
||||
|
||||
if (isDefined(existingNavigationMenuItem)) {
|
||||
await this.navigationMenuItemAccessService.canUserUpdateNavigationMenuItem(
|
||||
{
|
||||
userWorkspaceId: authUserWorkspaceId,
|
||||
workspaceId,
|
||||
apiKeyId: authApiKeyId,
|
||||
applicationId: authApplicationId,
|
||||
existingUserWorkspaceId: existingNavigationMenuItem.userWorkspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
flatNavigationMenuItemsToUpdate.push(
|
||||
fromUpdateNavigationMenuItemInputToFlatNavigationMenuItemToUpdateOrThrow(
|
||||
{
|
||||
flatNavigationMenuItemMaps: existingFlatNavigationMenuItemMaps,
|
||||
updateNavigationMenuItemInput: updateInput,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const flatNavigationMenuItemToUpdate =
|
||||
fromUpdateNavigationMenuItemInputToFlatNavigationMenuItemToUpdateOrThrow({
|
||||
flatNavigationMenuItemMaps: existingFlatNavigationMenuItemMaps,
|
||||
updateNavigationMenuItemInput: input,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
@@ -264,7 +391,7 @@ export class NavigationMenuItemService {
|
||||
navigationMenuItem: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [flatNavigationMenuItemToUpdate],
|
||||
flatEntityToUpdate: flatNavigationMenuItemsToUpdate,
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
@@ -277,7 +404,7 @@ export class NavigationMenuItemService {
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while updating navigation menu item',
|
||||
'Multiple validation errors occurred while updating navigation menu items',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -289,11 +416,13 @@ export class NavigationMenuItemService {
|
||||
},
|
||||
);
|
||||
|
||||
return fromFlatNavigationMenuItemToNavigationMenuItemDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: input.id,
|
||||
flatEntityMaps: recomputedFlatNavigationMenuItemMaps,
|
||||
}),
|
||||
return inputs.map(({ id }) =>
|
||||
fromFlatNavigationMenuItemToNavigationMenuItemDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: id,
|
||||
flatEntityMaps: recomputedFlatNavigationMenuItemMaps,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -310,6 +439,53 @@ export class NavigationMenuItemService {
|
||||
authApiKeyId?: string;
|
||||
authApplicationId?: string;
|
||||
}): Promise<NavigationMenuItemDTO> {
|
||||
const deletedItems = await this.deleteMany({
|
||||
ids: [id],
|
||||
workspaceId,
|
||||
authUserWorkspaceId,
|
||||
authApiKeyId,
|
||||
authApplicationId,
|
||||
});
|
||||
|
||||
const deleted = deletedItems[0];
|
||||
|
||||
if (!isDefined(deleted)) {
|
||||
throw new NavigationMenuItemException(
|
||||
'Failed to delete navigation menu item',
|
||||
NavigationMenuItemExceptionCode.NAVIGATION_MENU_ITEM_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
async deleteMany({
|
||||
ids,
|
||||
workspaceId,
|
||||
authUserWorkspaceId,
|
||||
authApiKeyId,
|
||||
authApplicationId,
|
||||
}: {
|
||||
ids: string[];
|
||||
workspaceId: string;
|
||||
authUserWorkspaceId?: string;
|
||||
authApiKeyId?: string;
|
||||
authApplicationId?: string;
|
||||
}): Promise<NavigationMenuItemDTO[]> {
|
||||
if (ids.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const uniqueOrderedIds: string[] = [];
|
||||
const seenId = new Set<string>();
|
||||
|
||||
for (const id of ids) {
|
||||
if (!seenId.has(id)) {
|
||||
seenId.add(id);
|
||||
uniqueOrderedIds.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
@@ -323,30 +499,55 @@ export class NavigationMenuItemService {
|
||||
},
|
||||
);
|
||||
|
||||
const flatNavigationMenuItemToDelete =
|
||||
fromDeleteNavigationMenuItemInputToFlatNavigationMenuItemOrThrow({
|
||||
flatNavigationMenuItemMaps: existingFlatNavigationMenuItemMaps,
|
||||
navigationMenuItemId: id,
|
||||
});
|
||||
const flatEntitiesToDeleteOrdered: FlatNavigationMenuItem[] = [];
|
||||
const seenDeleteId = new Set<string>();
|
||||
const deletedNavigationMenuItemDtoByRequestedId = new Map<
|
||||
string,
|
||||
NavigationMenuItemDTO
|
||||
>();
|
||||
|
||||
await this.navigationMenuItemAccessService.canUserDeleteNavigationMenuItem({
|
||||
userWorkspaceId: authUserWorkspaceId,
|
||||
workspaceId,
|
||||
apiKeyId: authApiKeyId,
|
||||
applicationId: authApplicationId,
|
||||
existingUserWorkspaceId: flatNavigationMenuItemToDelete.userWorkspaceId,
|
||||
});
|
||||
for (const requestedId of uniqueOrderedIds) {
|
||||
const flatNavigationMenuItemRoot =
|
||||
fromDeleteNavigationMenuItemInputToFlatNavigationMenuItemOrThrow({
|
||||
flatNavigationMenuItemMaps: existingFlatNavigationMenuItemMaps,
|
||||
navigationMenuItemId: requestedId,
|
||||
});
|
||||
|
||||
const flatEntitiesToDelete = [flatNavigationMenuItemToDelete];
|
||||
await this.navigationMenuItemAccessService.canUserDeleteNavigationMenuItem(
|
||||
{
|
||||
userWorkspaceId: authUserWorkspaceId,
|
||||
workspaceId,
|
||||
apiKeyId: authApiKeyId,
|
||||
applicationId: authApplicationId,
|
||||
existingUserWorkspaceId: flatNavigationMenuItemRoot.userWorkspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (flatNavigationMenuItemToDelete.type === NavigationMenuItemType.FOLDER) {
|
||||
const userWorkspaceIdKey =
|
||||
flatNavigationMenuItemToDelete.userWorkspaceId ?? 'null';
|
||||
const folderChildren =
|
||||
existingFlatNavigationMenuItemMaps.byUserWorkspaceIdAndFolderId[
|
||||
userWorkspaceIdKey
|
||||
]?.[id] ?? [];
|
||||
flatEntitiesToDelete.unshift(...folderChildren);
|
||||
deletedNavigationMenuItemDtoByRequestedId.set(
|
||||
requestedId,
|
||||
fromFlatNavigationMenuItemToNavigationMenuItemDto(
|
||||
flatNavigationMenuItemRoot,
|
||||
),
|
||||
);
|
||||
|
||||
const flatEntitiesForRoot = [flatNavigationMenuItemRoot];
|
||||
|
||||
if (flatNavigationMenuItemRoot.type === NavigationMenuItemType.FOLDER) {
|
||||
const userWorkspaceIdKey =
|
||||
flatNavigationMenuItemRoot.userWorkspaceId ?? 'null';
|
||||
const folderChildren =
|
||||
existingFlatNavigationMenuItemMaps.byUserWorkspaceIdAndFolderId[
|
||||
userWorkspaceIdKey
|
||||
]?.[requestedId] ?? [];
|
||||
flatEntitiesForRoot.unshift(...folderChildren);
|
||||
}
|
||||
|
||||
for (const flatEntity of flatEntitiesForRoot) {
|
||||
if (!seenDeleteId.has(flatEntity.id)) {
|
||||
seenDeleteId.add(flatEntity.id);
|
||||
flatEntitiesToDeleteOrdered.push(flatEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const validateAndBuildResult =
|
||||
@@ -355,7 +556,7 @@ export class NavigationMenuItemService {
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
navigationMenuItem: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: flatEntitiesToDelete,
|
||||
flatEntityToDelete: flatEntitiesToDeleteOrdered,
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
@@ -369,13 +570,22 @@ export class NavigationMenuItemService {
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while deleting navigation menu item',
|
||||
'Multiple validation errors occurred while deleting navigation menu items',
|
||||
);
|
||||
}
|
||||
|
||||
return fromFlatNavigationMenuItemToNavigationMenuItemDto(
|
||||
flatNavigationMenuItemToDelete,
|
||||
);
|
||||
return uniqueOrderedIds.map((requestedId) => {
|
||||
const dto = deletedNavigationMenuItemDtoByRequestedId.get(requestedId);
|
||||
|
||||
if (!isDefined(dto)) {
|
||||
throw new NavigationMenuItemException(
|
||||
'Failed to resolve deleted navigation menu item in batch',
|
||||
NavigationMenuItemExceptionCode.NAVIGATION_MENU_ITEM_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return dto;
|
||||
});
|
||||
}
|
||||
|
||||
async findTargetRecord({
|
||||
|
||||
Reference in New Issue
Block a user