Folders: Migrate move folder to new API (#110550)

This commit is contained in:
Andrej Ocenas 2025-09-04 10:23:56 +02:00 committed by GitHub
parent c9707a7463
commit 2958126c87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 75 additions and 42 deletions

View File

@ -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<typeof useGetFolderQuery>,
resultParents: ReturnType<typeof useGetFolderParentsQuery>,

View File

@ -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<void, { folder: FolderDTO; destinationUID: string }>({
moveFolder: builder.mutation<void, MoveFolderArgs>({
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,

View File

@ -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,