diff --git a/frontend/src/hooks/api/secretFolders/queries.tsx b/frontend/src/hooks/api/secretFolders/queries.tsx index 71c63f3eb..9bf266ccf 100644 --- a/frontend/src/hooks/api/secretFolders/queries.tsx +++ b/frontend/src/hooks/api/secretFolders/queries.tsx @@ -16,6 +16,7 @@ import { TGetFoldersByEnvDTO, TGetProjectFoldersDTO, TSecretFolder, + TUpdateFolderBatchDTO, TUpdateFolderDTO } from "./types"; @@ -190,3 +191,43 @@ export const useDeleteFolder = () => { } }); }; + +export const useUpdateFolderBatch = () => { + const queryClient = useQueryClient(); + + return useMutation<{}, {}, TUpdateFolderBatchDTO>({ + mutationFn: async ({ projectId, folders }) => { + const { data } = await apiRequest.patch("/api/v1/folders/batch", { + folders, + workspaceId: projectId + }); + + return data; + }, + onSuccess: (_, { projectId, folders }) => { + folders.forEach((folder) => { + queryClient.invalidateQueries( + folderQueryKeys.getSecretFolders({ + projectId, + environment: folder.environment, + path: folder.path + }) + ); + queryClient.invalidateQueries( + secretSnapshotKeys.list({ + workspaceId: projectId, + environment: folder.environment, + directory: folder.path + }) + ); + queryClient.invalidateQueries( + secretSnapshotKeys.count({ + workspaceId: projectId, + environment: folder.environment, + directory: folder.path + }) + ); + }); + } + }); +}; diff --git a/frontend/src/hooks/api/secretFolders/types.ts b/frontend/src/hooks/api/secretFolders/types.ts index eac202389..0ae2cfef2 100644 --- a/frontend/src/hooks/api/secretFolders/types.ts +++ b/frontend/src/hooks/api/secretFolders/types.ts @@ -36,3 +36,13 @@ export type TDeleteFolderDTO = { folderId: string; path?: string; }; + +export type TUpdateFolderBatchDTO = { + projectId: string; + folders: { + name: string; + environment: string; + id: string; + path?: string; + }[]; +}; diff --git a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx index e7517b37e..af1ef5d7d 100644 --- a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx +++ b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx @@ -60,9 +60,10 @@ import { useGetImportedSecretsAllEnvs, useGetProjectSecretsAllEnv, useGetUserWsKey, - useUpdateFolder, useUpdateSecretV3 } from "@app/hooks/api"; +import { useUpdateFolderBatch } from "@app/hooks/api/secretFolders/queries"; +import { TUpdateFolderBatchDTO } from "@app/hooks/api/secretFolders/types"; import { TSecretFolder } from "@app/hooks/api/types"; import { ProjectVersion } from "@app/hooks/api/workspace/types"; @@ -205,7 +206,7 @@ export const SecretOverviewPage = () => { const { mutateAsync: updateSecretV3 } = useUpdateSecretV3(); const { mutateAsync: deleteSecretV3 } = useDeleteSecretV3(); const { mutateAsync: createFolder } = useCreateFolder(); - const { mutateAsync: updateFolder } = useUpdateFolder(); + const { mutateAsync: updateFolderBatch } = useUpdateFolderBatch(); const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([ "addSecretsInAllEnvs", @@ -244,60 +245,54 @@ export const SecretOverviewPage = () => { const handleFolderUpdate = async (newFolderName: string) => { const { name: oldFolderName } = popUp.updateFolder.data as TSecretFolder; - let processedUpdates = 0; - let totalFolderMatchCount = 0; - - await Promise.allSettled( - userAvailableEnvs.map(async (env) => { - if ( - permission.cannot( - ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath }) - ) - ) { - return; - } + const updatedFolders: TUpdateFolderBatchDTO["folders"] = []; + userAvailableEnvs.forEach((env) => { + if ( + permission.can( + ProjectPermissionActions.Edit, + subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath }) + ) + ) { const folder = getFolderByNameAndEnv(oldFolderName, env.slug); if (folder) { - totalFolderMatchCount += 1; - - await updateFolder({ - folderId: folder.id, - name: newFolderName, - path: secretPath, + updatedFolders.push({ environment: env.slug, - projectId: workspaceId + name: newFolderName, + id: folder.id, + path: secretPath }); - - processedUpdates += 1; } - }) - ); + } + }); - if (totalFolderMatchCount === 0) { + if (updatedFolders.length === 0) { createNotification({ type: "info", text: "You don't have access to rename selected folder" }); - } else if (processedUpdates === totalFolderMatchCount) { + + handlePopUpClose("updateFolder"); + return; + } + + try { + await updateFolderBatch({ + projectId: workspaceId, + folders: updatedFolders + }); createNotification({ type: "success", text: "Successfully renamed folder across environments" }); - } else if (processedUpdates > 0) { - createNotification({ - type: "warning", - text: "Partially renamed folder across environments" - }); - } else { + } catch (err) { createNotification({ type: "error", text: "Failed to rename folder across environments" }); + } finally { + handlePopUpClose("updateFolder"); } - - handlePopUpClose("updateFolder"); }; const handleSecretCreate = async (env: string, key: string, value: string) => {