From cfe49be676877567de9704e8d7968d611fa77fc9 Mon Sep 17 00:00:00 2001 From: Stephen Smith Date: Tue, 23 May 2023 23:59:01 -0400 Subject: [PATCH] 20031: Prevent movng collection items to itself, show error toast if attempted Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- .../collection-service/collection-service.ts | 35 ++++++++++++++----- .../common-service/common-resource-service.ts | 7 ++-- .../collection-partial-move-actions.ts | 5 ++- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/services/collection-service/collection-service.ts b/src/services/collection-service/collection-service.ts index 7d734fa6..b6687985 100644 --- a/src/services/collection-service/collection-service.ts +++ b/src/services/collection-service/collection-service.ts @@ -13,12 +13,14 @@ import { ApiActions } from "services/api/api-actions"; import { Session } from "models/session"; import { CommonService } from "services/common-service/common-service"; import { snakeCase } from "lodash"; +import { CommonResourceServiceError } from "services/common-service/common-resource-service"; export type UploadProgress = (fileId: number, loaded: number, total: number, currentTime: number) => void; type CollectionPartialUpdateOrCreate = Partial & Pick | Partial & Pick; export const emptyCollectionPdh = 'd41d8cd98f00b204e9800998ecf8427e+0'; +export const SOURCE_DESTINATION_EQUAL_ERROR_MESSAGE = 'Source and destination cannot be the same'; export class CollectionService extends TrashableResourceService { constructor(serverApi: AxiosInstance, private webdavClient: WebDAV, private authService: AuthService, actions: ApiActions) { @@ -172,10 +174,10 @@ export class CollectionService extends TrashableResourceService { - const sourceFileName = sourceFile.split('/').filter(Boolean).slice(-1).join(""); + const fileBasename = sourceFile.split('/').filter(Boolean).slice(-1).join(""); return { ...obj, - [this.combineFilePath([destinationPath, sourceFileName])]: `${sourcePdh}${this.combineFilePath([sourceFile])}` + [this.combineFilePath([destinationPath, fileBasename])]: `${sourcePdh}${this.combineFilePath([sourceFile])}` }; }, {}); @@ -184,16 +186,31 @@ export class CollectionService extends TrashableResourceService { - const sourceFileName = sourceFile.split('/').filter(Boolean).slice(-1).join(""); - return { - ...obj, - [this.combineFilePath([destinationPath, sourceFileName])]: `${sourcePdh}${this.combineFilePath([sourceFile])}`, - [this.combineFilePath([sourceFile])]: '', - }; + const fileBasename = sourceFile.split('/').filter(Boolean).slice(-1).join(""); + const fileDestinationPath = this.combineFilePath([destinationPath, fileBasename]); + const fileSourcePath = this.combineFilePath([sourceFile]); + const fileSourceUri = `${sourcePdh}${fileSourcePath}`; + + + if (fileDestinationPath !== fileSourcePath) { + return { + ...obj, + [fileDestinationPath]: fileSourceUri, + [fileSourcePath]: '', + }; + } else { + errors.push(CommonResourceServiceError.SOURCE_DESTINATION_CANNOT_BE_SAME); + return obj; + } }, {}); - return this.replaceFiles({uuid: sourceUuid}, fileMap, showErrors) + if (errors.length === 0) { + return this.replaceFiles({uuid: sourceUuid}, fileMap, showErrors) + } else { + return Promise.reject({errors}); + } } else { return this.copyFiles(sourcePdh, files, destinationCollection, destinationPath, showErrors) .then(() => { diff --git a/src/services/common-service/common-resource-service.ts b/src/services/common-service/common-resource-service.ts index d9be8dae..11bfec38 100644 --- a/src/services/common-service/common-resource-service.ts +++ b/src/services/common-service/common-resource-service.ts @@ -13,6 +13,7 @@ export enum CommonResourceServiceError { OWNERSHIP_CYCLE = 'OwnershipCycle', MODIFYING_CONTAINER_REQUEST_FINAL_STATE = 'ModifyingContainerRequestFinalState', NAME_HAS_ALREADY_BEEN_TAKEN = 'NameHasAlreadyBeenTaken', + SOURCE_DESTINATION_CANNOT_BE_SAME = 'SourceDestinationCannotBeSame', UNKNOWN = 'Unknown', NONE = 'None' } @@ -53,7 +54,7 @@ export class CommonResourceService extends CommonService } export const getCommonResourceServiceError = (errorResponse: any) => { - if ('errors' in errorResponse) { + if (errorResponse && 'errors' in errorResponse) { const error = errorResponse.errors.join(''); switch (true) { case /UniqueViolation/.test(error): @@ -64,11 +65,11 @@ export const getCommonResourceServiceError = (errorResponse: any) => { return CommonResourceServiceError.MODIFYING_CONTAINER_REQUEST_FINAL_STATE; case /Name has already been taken/.test(error): return CommonResourceServiceError.NAME_HAS_ALREADY_BEEN_TAKEN; + case new RegExp(CommonResourceServiceError.SOURCE_DESTINATION_CANNOT_BE_SAME).test(error): + return CommonResourceServiceError.SOURCE_DESTINATION_CANNOT_BE_SAME; default: return CommonResourceServiceError.UNKNOWN; } } return CommonResourceServiceError.NONE; }; - - diff --git a/src/store/collections/collection-partial-move-actions.ts b/src/store/collections/collection-partial-move-actions.ts index b8b4eeb8..1d979f31 100644 --- a/src/store/collections/collection-partial-move-actions.ts +++ b/src/store/collections/collection-partial-move-actions.ts @@ -17,6 +17,7 @@ import { SnackbarKind, snackbarActions } from "store/snackbar/snackbar-actions"; import { RootState } from "store/store"; import { FileOperationLocation } from "store/tree-picker/tree-picker-actions"; import { CollectionResource } from "models/collection"; +import { SOURCE_DESTINATION_EQUAL_ERROR_MESSAGE } from "services/collection-service/collection-service"; export const COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_NEW_DIALOG'; export const COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_SELECTED_DIALOG'; @@ -170,7 +171,9 @@ export const moveCollectionPartialToExistingCollection = (fileSelection: Collect dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION)); } catch (e) { const error = getCommonResourceServiceError(e); - if (error === CommonResourceServiceError.UNKNOWN) { + if (error === CommonResourceServiceError.SOURCE_DESTINATION_CANNOT_BE_SAME) { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: SOURCE_DESTINATION_EQUAL_ERROR_MESSAGE, hideDuration: 2000, kind: SnackbarKind.ERROR })); + } else if (error === CommonResourceServiceError.UNKNOWN) { dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION })); dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not copy this files to selected collection', hideDuration: 2000, kind: SnackbarKind.ERROR })); } -- 2.30.2