diff --git a/public/app/api/clients/folder/v1beta1/hooks.ts b/public/app/api/clients/folder/v1beta1/hooks.ts index 9c7cc1aae0f..e9b3214daa0 100644 --- a/public/app/api/clients/folder/v1beta1/hooks.ts +++ b/public/app/api/clients/folder/v1beta1/hooks.ts @@ -12,10 +12,11 @@ import { useNewFolderMutation as useLegacyNewFolderMutation, useMoveFoldersMutation as useMoveFoldersMutationLegacy, useSaveFolderMutation as useLegacySaveFolderMutation, + useMoveFolderMutation as useMoveFolderMutationLegacy, MoveFoldersArgs, DeleteFoldersArgs, + MoveFolderArgs, } from 'app/features/browse-dashboards/api/browseDashboardsAPI'; -import { dispatch } from 'app/store/store'; import { FolderDTO, NewFolder } from 'app/types/folders'; import kbn from '../../../../core/utils/kbn'; @@ -49,16 +50,6 @@ import { ReplaceFolderApiArg, } from './index'; -/** Trigger necessary actions to ensure legacy folder stores are updated */ -function dispatchRefetchChildren(parentUID?: string) { - dispatch( - refetchChildren({ - parentUID: parentUID || GENERAL_FOLDER_UID, - pageSize: PAGE_SIZE, - }) - ); -} - function getFolderUrl(uid: string, title: string): string { // mimics https://github.com/grafana/grafana/blob/79fe8a9902335c7a28af30e467b904a4ccfac503/pkg/services/dashboards/models.go#L188 // Not the same slugify as on the backend https://github.com/grafana/grafana/blob/aac66e91198004bc044754105e18bfff8fbfd383/pkg/infra/slugify/slugify.go#L86 @@ -179,16 +170,16 @@ export function useGetFolderQueryFacade(uid?: string) { export function useDeleteFolderMutationFacade() { const [deleteFolder] = useDeleteFolderMutation(); const [deleteFolderLegacy] = useDeleteFolderMutationLegacy(); + const refresh = useRefreshFolders(); const notify = useAppNotification(); return async (folder: FolderDTO) => { if (config.featureToggles.foldersAppPlatformAPI) { const result = await deleteFolder({ name: folder.uid }); if (!result.error) { - // We need to update a legacy version of the folder storage for now until all is in the new API. - // we could do it in the enhanceEndpoint method but we would also need to change the args as we need parentUID + // we could do this in the enhanceEndpoint method, but we would also need to change the args as we need parentUID // here and so it seemed easier to do it here. - dispatchRefetchChildren(folder.parentUid); + refresh({ childrenOf: folder.parentUid }); // Before this was done in backend srv automatically because the old API sent a message wiht 200 request. see // public/app/core/services/backend_srv.ts#L341-L361. New API does not do that so we do it here. notify.success(t('folders.api.folder-deleted-success', 'Folder deleted')); @@ -204,6 +195,7 @@ export function useDeleteMultipleFoldersMutationFacade() { const [deleteFolders] = useDeleteFoldersMutationLegacy(); const [deleteFolder] = useDeleteFolderMutation(); const dispatch = useDispatch(); + const refresh = useRefreshFolders(); if (!config.featureToggles.foldersAppPlatformAPI) { return deleteFolders; @@ -231,7 +223,7 @@ export function useDeleteMultipleFoldersMutationFacade() { } } - dispatch(refreshParents(folderUIDs)); + refresh({ parentsOf: folderUIDs }); return { data: undefined }; }; } @@ -240,6 +232,7 @@ export function useMoveMultipleFoldersMutationFacade() { const moveFoldersLegacyResult = useMoveFoldersMutationLegacy(); const [updateFolder, updateFolderData] = useUpdateFolderMutation(); const dispatch = useDispatch(); + const refetch = useRefreshFolders(); if (!config.featureToggles.foldersAppPlatformAPI) { return moveFoldersLegacyResult; @@ -271,16 +264,7 @@ export function useMoveMultipleFoldersMutationFacade() { } } } - - // Refresh the state of the parent folders to update the UI after folders are moved - dispatch( - refetchChildren({ - parentUID: destinationUID, - pageSize: PAGE_SIZE, - }) - ); - dispatch(refreshParents(folderUIDs)); - + refetch({ childrenOf: destinationUID, parentsOf: folderUIDs }); return { data: undefined }; } @@ -290,6 +274,7 @@ export function useMoveMultipleFoldersMutationFacade() { export function useCreateFolder() { const [createFolder, result] = useCreateFolderMutation(); const legacyHook = useLegacyNewFolderMutation(); + const refresh = useRefreshFolders(); if (!config.featureToggles.foldersAppPlatformAPI) { return legacyHook; @@ -312,7 +297,7 @@ export function useCreateFolder() { }; const result = await createFolder(payload); - dispatchRefetchChildren(folder.parentUid); + refresh({ childrenOf: folder.parentUid }); return { ...result, @@ -326,6 +311,7 @@ export function useCreateFolder() { export function useUpdateFolder() { const [updateFolder, result] = useReplaceFolderMutation(); const legacyHook = useLegacySaveFolderMutation(); + const refresh = useRefreshFolders(); if (!config.featureToggles.foldersAppPlatformAPI) { return legacyHook; @@ -344,7 +330,7 @@ export function useUpdateFolder() { }; const result = await updateFolder(payload); - dispatchRefetchChildren(folder.parentUid); + refresh({ childrenOf: folder.parentUid }); return { ...result, @@ -355,6 +341,55 @@ export function useUpdateFolder() { return [updateFolderAppPlatform, result] as const; } +export function useMoveFolderMutationFacade() { + const [updateFolder, updateFolderData] = useUpdateFolderMutation(); + const moveFolderResult = useMoveFolderMutationLegacy(); + const refresh = useRefreshFolders(); + const notify = useAppNotification(); + + if (!config.featureToggles.foldersAppPlatformAPI) { + return moveFolderResult; + } + + async function moveFolder({ folderUID, destinationUID }: MoveFolderArgs) { + const result = await updateFolder({ + name: folderUID, + patch: { metadata: { annotations: { [AnnoKeyFolder]: destinationUID } } }, + }); + if (!result.error) { + refresh({ parentsOf: [folderUID], childrenOf: destinationUID }); + // Before this was done in backend srv automatically because the old API sent a message with 200 request. see + // public/app/core/services/backend_srv.ts#L341-L361. New API does not do that so we do it here. + notify.success(t('folders.api.folder-moved-success', 'Folder moved')); + } + return result; + } + + return [moveFolder, updateFolderData] as const; +} + +/** + * Refresh the state of the folders to update the UI after folders are updated. This refreshes legacy storage + * of the folder structure outside the RTK query. Once all is migrated to new API this should not be needed. + */ +function useRefreshFolders() { + const dispatch = useDispatch(); + + return (options: { parentsOf?: string[]; childrenOf?: string }) => { + if (options.parentsOf) { + dispatch(refreshParents(options.parentsOf)); + } + if (options.childrenOf) { + dispatch( + refetchChildren({ + parentUID: options.childrenOf, + pageSize: PAGE_SIZE, + }) + ); + } + }; +} + function combinedState( result: ReturnType, resultParents: ReturnType, diff --git a/public/app/features/browse-dashboards/api/browseDashboardsAPI.ts b/public/app/features/browse-dashboards/api/browseDashboardsAPI.ts index ed03d636017..c5f8b5a5e90 100644 --- a/public/app/features/browse-dashboards/api/browseDashboardsAPI.ts +++ b/public/app/features/browse-dashboards/api/browseDashboardsAPI.ts @@ -44,6 +44,11 @@ export interface MoveFoldersArgs { folderUIDs: string[]; } +export interface MoveFolderArgs { + folderUID: string; + destinationUID: string; +} + export interface ImportInputs { name: string; type: string; @@ -141,22 +146,16 @@ export const browseDashboardsAPI = createApi({ }), // move an *individual* folder. used in the folder actions menu. - moveFolder: builder.mutation({ + moveFolder: builder.mutation({ invalidatesTags: ['getFolder'], - query: ({ folder, destinationUID }) => ({ - url: `/folders/${folder.uid}/move`, + query: ({ folderUID, destinationUID }) => ({ + url: `/folders/${folderUID}/move`, method: 'POST', body: { parentUID: destinationUID }, }), - onQueryStarted: ({ folder, destinationUID }, { queryFulfilled, dispatch }) => { - const { parentUid } = folder; + onQueryStarted: ({ folderUID, destinationUID }, { queryFulfilled, dispatch }) => { queryFulfilled.then(() => { - dispatch( - refetchChildren({ - parentUID: parentUid, - pageSize: PAGE_SIZE, - }) - ); + dispatch(refreshParents([folderUID])); dispatch( refetchChildren({ parentUID: destinationUID, diff --git a/public/app/features/browse-dashboards/components/FolderActionsButton.tsx b/public/app/features/browse-dashboards/components/FolderActionsButton.tsx index 41d877a77c3..3faa6d4b686 100644 --- a/public/app/features/browse-dashboards/components/FolderActionsButton.tsx +++ b/public/app/features/browse-dashboards/components/FolderActionsButton.tsx @@ -12,9 +12,8 @@ import { getReadOnlyTooltipText } from 'app/features/provisioning/utils/reposito import { ShowModalReactEvent } from 'app/types/events'; import { FolderDTO } from 'app/types/folders'; -import { useDeleteFolderMutationFacade } from '../../../api/clients/folder/v1beta1/hooks'; +import { useDeleteFolderMutationFacade, useMoveFolderMutationFacade } from '../../../api/clients/folder/v1beta1/hooks'; import { ManagerKind } from '../../apiserver/types'; -import { useMoveFolderMutation } from '../api/browseDashboardsAPI'; import { getFolderPermissions } from '../permissions'; import { DeleteModal } from './BrowseActions/DeleteModal'; @@ -30,7 +29,7 @@ export function FolderActionsButton({ folder, repoType, isReadOnlyRepo }: Props) const [isOpen, setIsOpen] = useState(false); const [showPermissionsDrawer, setShowPermissionsDrawer] = useState(false); const [showDeleteProvisionedFolderDrawer, setShowDeleteProvisionedFolderDrawer] = useState(false); - const [moveFolder] = useMoveFolderMutation(); + const [moveFolder] = useMoveFolderMutationFacade(); const deleteFolder = useDeleteFolderMutationFacade(); @@ -40,7 +39,7 @@ export function FolderActionsButton({ folder, repoType, isReadOnlyRepo }: Props) const canMoveFolder = canEditFolders && !isProvisionedFolder; const onMove = async (destinationUID: string) => { - await moveFolder({ folder, destinationUID }); + await moveFolder({ folderUID: folder.uid, destinationUID: destinationUID }); reportInteraction('grafana_manage_dashboards_item_moved', { item_counts: { folder: 1,