2025-01-31 20:25:16 +08:00
|
|
|
import { createApi } from '@reduxjs/toolkit/query/react';
|
2023-04-12 17:44:01 +08:00
|
|
|
|
2024-07-17 20:00:46 +08:00
|
|
|
import { AppEvents, isTruthy, locationUtil } from '@grafana/data';
|
2025-01-31 20:25:16 +08:00
|
|
|
import { config, getBackendSrv, locationService } from '@grafana/runtime';
|
2024-02-06 18:46:24 +08:00
|
|
|
import { Dashboard } from '@grafana/schema';
|
2025-01-27 21:14:19 +08:00
|
|
|
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
|
2025-01-31 20:25:16 +08:00
|
|
|
import { createBaseQuery, handleRequestError } from 'app/api/createBaseQuery';
|
2024-07-17 20:00:46 +08:00
|
|
|
import appEvents from 'app/core/app_events';
|
2023-06-22 16:44:19 +08:00
|
|
|
import { contextSrv } from 'app/core/core';
|
2024-06-05 22:34:23 +08:00
|
|
|
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
|
2025-01-27 21:14:19 +08:00
|
|
|
import { isV1DashboardCommand, isV2DashboardCommand } from 'app/features/dashboard/api/utils';
|
2023-06-22 16:44:19 +08:00
|
|
|
import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types';
|
|
|
|
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
2024-02-06 18:46:24 +08:00
|
|
|
import {
|
|
|
|
DashboardDTO,
|
|
|
|
DescendantCount,
|
|
|
|
DescendantCountDTO,
|
|
|
|
FolderDTO,
|
2024-02-22 02:02:37 +08:00
|
|
|
FolderListItemDTO,
|
2024-02-06 18:46:24 +08:00
|
|
|
ImportDashboardResponseDTO,
|
2024-03-19 19:44:52 +08:00
|
|
|
PermissionLevelString,
|
2024-02-06 18:46:24 +08:00
|
|
|
SaveDashboardResponseDTO,
|
|
|
|
} from 'app/types';
|
2023-04-12 17:44:01 +08:00
|
|
|
|
2024-07-17 20:00:46 +08:00
|
|
|
import { t } from '../../../core/internationalization';
|
2023-06-22 16:44:19 +08:00
|
|
|
import { refetchChildren, refreshParents } from '../state';
|
2023-04-26 18:20:10 +08:00
|
|
|
import { DashboardTreeSelection } from '../types';
|
|
|
|
|
2023-06-27 18:38:33 +08:00
|
|
|
import { PAGE_SIZE } from './services';
|
2023-06-22 16:44:19 +08:00
|
|
|
|
|
|
|
interface DeleteItemsArgs {
|
|
|
|
selectedItems: Omit<DashboardTreeSelection, 'panel' | '$all'>;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface MoveItemsArgs extends DeleteItemsArgs {
|
|
|
|
destinationUID: string;
|
|
|
|
}
|
|
|
|
|
2024-02-06 18:46:24 +08:00
|
|
|
export interface ImportInputs {
|
|
|
|
name: string;
|
|
|
|
type: string;
|
|
|
|
value: string;
|
|
|
|
pluginId?: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ImportOptions {
|
|
|
|
dashboard: Dashboard;
|
|
|
|
overwrite: boolean;
|
|
|
|
inputs: ImportInputs[];
|
|
|
|
folderUid: string;
|
|
|
|
}
|
|
|
|
|
2024-06-13 15:12:10 +08:00
|
|
|
interface RestoreDashboardArgs {
|
|
|
|
dashboardUID: string;
|
2024-08-20 15:08:18 +08:00
|
|
|
targetFolderUID: string;
|
2024-06-13 15:12:10 +08:00
|
|
|
}
|
|
|
|
|
2024-06-21 00:18:11 +08:00
|
|
|
interface HardDeleteDashboardArgs {
|
|
|
|
dashboardUID: string;
|
|
|
|
}
|
|
|
|
|
2024-02-22 02:02:37 +08:00
|
|
|
export interface ListFolderQueryArgs {
|
|
|
|
page: number;
|
|
|
|
parentUid: string | undefined;
|
|
|
|
limit: number;
|
2024-03-19 19:44:52 +08:00
|
|
|
permission?: PermissionLevelString;
|
2024-02-22 02:02:37 +08:00
|
|
|
}
|
|
|
|
|
2023-04-12 17:44:01 +08:00
|
|
|
export const browseDashboardsAPI = createApi({
|
2023-05-24 17:41:03 +08:00
|
|
|
tagTypes: ['getFolder'],
|
2023-04-20 23:37:00 +08:00
|
|
|
reducerPath: 'browseDashboardsAPI',
|
2025-01-31 20:25:16 +08:00
|
|
|
baseQuery: createBaseQuery({ baseURL: '/api' }),
|
2023-04-12 17:44:01 +08:00
|
|
|
endpoints: (builder) => ({
|
2024-02-22 02:02:37 +08:00
|
|
|
listFolders: builder.query<FolderListItemDTO[], ListFolderQueryArgs>({
|
2025-02-12 14:16:01 +08:00
|
|
|
providesTags: (result) =>
|
|
|
|
result && result.length > 0
|
|
|
|
? result.map((folder) => ({ type: 'getFolder', id: folder.uid }))
|
|
|
|
: [{ type: 'getFolder', id: 'EMPTY_RESULT' }],
|
2024-03-19 19:44:52 +08:00
|
|
|
query: ({ parentUid, limit, page, permission }) => ({
|
|
|
|
url: '/folders',
|
|
|
|
params: { parentUid, limit, page, permission },
|
|
|
|
}),
|
2024-02-22 02:02:37 +08:00
|
|
|
}),
|
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// get folder info (e.g. title, parents) but *not* children
|
2023-04-12 17:44:01 +08:00
|
|
|
getFolder: builder.query<FolderDTO, string>({
|
2023-06-22 16:44:19 +08:00
|
|
|
providesTags: (_result, _error, folderUID) => [{ type: 'getFolder', id: folderUID }],
|
2023-05-04 17:32:37 +08:00
|
|
|
query: (folderUID) => ({ url: `/folders/${folderUID}`, params: { accesscontrol: true } }),
|
2023-04-12 17:44:01 +08:00
|
|
|
}),
|
2024-01-15 18:56:31 +08:00
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// create a new folder
|
|
|
|
newFolder: builder.mutation<FolderDTO, { title: string; parentUid?: string }>({
|
2025-02-12 14:16:01 +08:00
|
|
|
invalidatesTags: ['getFolder'],
|
2023-06-22 16:44:19 +08:00
|
|
|
query: ({ title, parentUid }) => ({
|
|
|
|
method: 'POST',
|
|
|
|
url: '/folders',
|
2025-01-31 20:25:16 +08:00
|
|
|
body: {
|
2023-06-22 16:44:19 +08:00
|
|
|
title,
|
|
|
|
parentUid,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
onQueryStarted: ({ parentUid }, { queryFulfilled, dispatch }) => {
|
|
|
|
queryFulfilled.then(async ({ data: folder }) => {
|
|
|
|
await contextSrv.fetchUserPermissions();
|
|
|
|
dispatch(
|
|
|
|
refetchChildren({
|
|
|
|
parentUID: parentUid,
|
2023-06-27 18:38:33 +08:00
|
|
|
pageSize: PAGE_SIZE,
|
2023-06-22 16:44:19 +08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}),
|
2024-01-15 18:56:31 +08:00
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// save an existing folder (e.g. rename)
|
2023-06-01 00:03:54 +08:00
|
|
|
saveFolder: builder.mutation<FolderDTO, FolderDTO>({
|
2023-06-22 16:44:19 +08:00
|
|
|
// because the getFolder calls contain the parents, renaming a parent/grandparent/etc needs to invalidate all child folders
|
|
|
|
// we could do something smart and recursively invalidate these child folders but it doesn't seem worth it
|
|
|
|
// instead let's just invalidate all the getFolder calls
|
|
|
|
invalidatesTags: ['getFolder'],
|
|
|
|
query: ({ uid, title, version }) => ({
|
2023-06-01 00:03:54 +08:00
|
|
|
method: 'PUT',
|
2023-06-22 16:44:19 +08:00
|
|
|
url: `/folders/${uid}`,
|
2025-01-31 20:25:16 +08:00
|
|
|
body: {
|
2023-06-22 16:44:19 +08:00
|
|
|
title,
|
|
|
|
version,
|
2023-06-01 00:03:54 +08:00
|
|
|
},
|
|
|
|
}),
|
2023-06-22 16:44:19 +08:00
|
|
|
onQueryStarted: ({ parentUid }, { queryFulfilled, dispatch }) => {
|
|
|
|
queryFulfilled.then(() => {
|
|
|
|
dispatch(
|
|
|
|
refetchChildren({
|
|
|
|
parentUID: parentUid,
|
2023-06-27 18:38:33 +08:00
|
|
|
pageSize: PAGE_SIZE,
|
2023-06-22 16:44:19 +08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}),
|
2024-06-13 15:12:10 +08:00
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// move an *individual* folder. used in the folder actions menu.
|
|
|
|
moveFolder: builder.mutation<void, { folder: FolderDTO; destinationUID: string }>({
|
|
|
|
invalidatesTags: ['getFolder'],
|
|
|
|
query: ({ folder, destinationUID }) => ({
|
|
|
|
url: `/folders/${folder.uid}/move`,
|
|
|
|
method: 'POST',
|
2025-01-31 20:25:16 +08:00
|
|
|
body: { parentUID: destinationUID },
|
2023-06-22 16:44:19 +08:00
|
|
|
}),
|
|
|
|
onQueryStarted: ({ folder, destinationUID }, { queryFulfilled, dispatch }) => {
|
|
|
|
const { parentUid } = folder;
|
|
|
|
queryFulfilled.then(() => {
|
|
|
|
dispatch(
|
|
|
|
refetchChildren({
|
|
|
|
parentUID: parentUid,
|
2023-06-27 18:38:33 +08:00
|
|
|
pageSize: PAGE_SIZE,
|
2023-06-22 16:44:19 +08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
dispatch(
|
|
|
|
refetchChildren({
|
|
|
|
parentUID: destinationUID,
|
2023-06-27 18:38:33 +08:00
|
|
|
pageSize: PAGE_SIZE,
|
2023-06-22 16:44:19 +08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
2023-06-01 00:03:54 +08:00
|
|
|
}),
|
2024-06-13 15:12:10 +08:00
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// delete an *individual* folder. used in the folder actions menu.
|
|
|
|
deleteFolder: builder.mutation<void, FolderDTO>({
|
|
|
|
query: ({ uid }) => ({
|
|
|
|
url: `/folders/${uid}`,
|
|
|
|
method: 'DELETE',
|
|
|
|
params: {
|
|
|
|
// TODO: Once backend returns alert rule counts, set this back to true
|
|
|
|
// when this is merged https://github.com/grafana/grafana/pull/67259
|
|
|
|
forceDeleteRules: false,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
onQueryStarted: ({ parentUid }, { queryFulfilled, dispatch }) => {
|
|
|
|
queryFulfilled.then(() => {
|
|
|
|
dispatch(
|
|
|
|
refetchChildren({
|
|
|
|
parentUID: parentUid,
|
2023-06-27 18:38:33 +08:00
|
|
|
pageSize: PAGE_SIZE,
|
2023-06-22 16:44:19 +08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}),
|
2024-06-13 15:12:10 +08:00
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// gets the descendant counts for a folder. used in the move/delete modals.
|
2023-05-03 05:20:30 +08:00
|
|
|
getAffectedItems: builder.query<DescendantCount, DashboardTreeSelection>({
|
2024-06-24 20:28:44 +08:00
|
|
|
// don't cache this data for now, since library panel/alert rule creation isn't done through rtk query
|
|
|
|
keepUnusedDataFor: 0,
|
2023-04-26 18:20:10 +08:00
|
|
|
queryFn: async (selectedItems) => {
|
|
|
|
const folderUIDs = Object.keys(selectedItems.folder).filter((uid) => selectedItems.folder[uid]);
|
2023-05-03 05:20:30 +08:00
|
|
|
|
|
|
|
const promises = folderUIDs.map((folderUID) => {
|
|
|
|
return getBackendSrv().get<DescendantCountDTO>(`/api/folders/${folderUID}/counts`);
|
2023-04-26 18:20:10 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
const results = await Promise.all(promises);
|
|
|
|
|
2023-05-03 05:20:30 +08:00
|
|
|
const totalCounts = {
|
|
|
|
folder: Object.values(selectedItems.folder).filter(isTruthy).length,
|
|
|
|
dashboard: Object.values(selectedItems.dashboard).filter(isTruthy).length,
|
|
|
|
libraryPanel: 0,
|
|
|
|
alertRule: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const folderCounts of results) {
|
2023-09-07 18:41:00 +08:00
|
|
|
// TODO remove nullish coalescing once nestedFolders is toggled on
|
|
|
|
totalCounts.folder += folderCounts.folder ?? 0;
|
2023-05-03 05:20:30 +08:00
|
|
|
totalCounts.dashboard += folderCounts.dashboard;
|
2023-09-07 18:41:00 +08:00
|
|
|
totalCounts.alertRule += folderCounts.alertrule;
|
|
|
|
totalCounts.libraryPanel += folderCounts.librarypanel;
|
2023-05-03 05:20:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return { data: totalCounts };
|
2023-04-26 18:20:10 +08:00
|
|
|
},
|
|
|
|
}),
|
2024-06-13 15:12:10 +08:00
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// move *multiple* items (folders and dashboards). used in the move modal.
|
|
|
|
moveItems: builder.mutation<void, MoveItemsArgs>({
|
|
|
|
invalidatesTags: ['getFolder'],
|
|
|
|
queryFn: async ({ selectedItems, destinationUID }, _api, _extraOptions, baseQuery) => {
|
|
|
|
const selectedDashboards = Object.keys(selectedItems.dashboard).filter((uid) => selectedItems.dashboard[uid]);
|
|
|
|
const selectedFolders = Object.keys(selectedItems.folder).filter((uid) => selectedItems.folder[uid]);
|
|
|
|
|
|
|
|
// Move all the folders sequentially
|
|
|
|
// TODO error handling here
|
|
|
|
for (const folderUID of selectedFolders) {
|
|
|
|
await baseQuery({
|
|
|
|
url: `/folders/${folderUID}/move`,
|
|
|
|
method: 'POST',
|
2025-01-31 20:25:16 +08:00
|
|
|
body: { parentUID: destinationUID },
|
2023-06-22 16:44:19 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move all the dashboards sequentially
|
|
|
|
// TODO error handling here
|
|
|
|
for (const dashboardUID of selectedDashboards) {
|
2025-01-31 17:30:39 +08:00
|
|
|
if (config.featureToggles.useV2DashboardsAPI) {
|
|
|
|
const fullDash = await getDashboardAPI('v2').getDashboardDTO(dashboardUID);
|
2023-06-22 16:44:19 +08:00
|
|
|
|
2025-01-31 17:30:39 +08:00
|
|
|
await getDashboardAPI('v2').saveDashboard({
|
|
|
|
dashboard: fullDash.spec,
|
|
|
|
folderUid: destinationUID,
|
|
|
|
overwrite: false,
|
|
|
|
message: '',
|
|
|
|
k8s: fullDash.metadata,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
const fullDash: DashboardDTO = await getDashboardAPI().getDashboardDTO(dashboardUID);
|
2024-12-04 18:29:00 +08:00
|
|
|
|
2025-01-31 17:30:39 +08:00
|
|
|
await getDashboardAPI().saveDashboard({
|
|
|
|
dashboard: fullDash.dashboard,
|
|
|
|
folderUid: destinationUID,
|
|
|
|
overwrite: false,
|
|
|
|
message: '',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2023-06-22 16:44:19 +08:00
|
|
|
return { data: undefined };
|
|
|
|
},
|
|
|
|
onQueryStarted: ({ destinationUID, selectedItems }, { queryFulfilled, dispatch }) => {
|
|
|
|
const selectedDashboards = Object.keys(selectedItems.dashboard).filter((uid) => selectedItems.dashboard[uid]);
|
|
|
|
const selectedFolders = Object.keys(selectedItems.folder).filter((uid) => selectedItems.folder[uid]);
|
|
|
|
queryFulfilled.then(() => {
|
|
|
|
dispatch(
|
|
|
|
refetchChildren({
|
|
|
|
parentUID: destinationUID,
|
2023-06-27 18:38:33 +08:00
|
|
|
pageSize: PAGE_SIZE,
|
2023-06-22 16:44:19 +08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
dispatch(refreshParents([...selectedFolders, ...selectedDashboards]));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}),
|
2024-06-13 15:12:10 +08:00
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// delete *multiple* items (folders and dashboards). used in the delete modal.
|
|
|
|
deleteItems: builder.mutation<void, DeleteItemsArgs>({
|
2025-02-12 14:16:01 +08:00
|
|
|
invalidatesTags: ['getFolder'],
|
2023-06-22 16:44:19 +08:00
|
|
|
queryFn: async ({ selectedItems }, _api, _extraOptions, baseQuery) => {
|
|
|
|
const selectedDashboards = Object.keys(selectedItems.dashboard).filter((uid) => selectedItems.dashboard[uid]);
|
|
|
|
const selectedFolders = Object.keys(selectedItems.folder).filter((uid) => selectedItems.folder[uid]);
|
|
|
|
// Delete all the folders sequentially
|
|
|
|
// TODO error handling here
|
|
|
|
for (const folderUID of selectedFolders) {
|
|
|
|
await baseQuery({
|
|
|
|
url: `/folders/${folderUID}`,
|
|
|
|
method: 'DELETE',
|
|
|
|
params: {
|
|
|
|
// TODO: Once backend returns alert rule counts, set this back to true
|
|
|
|
// when this is merged https://github.com/grafana/grafana/pull/67259
|
|
|
|
forceDeleteRules: false,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// Delete all the dashboards sequentially
|
|
|
|
// TODO error handling here
|
|
|
|
for (const dashboardUID of selectedDashboards) {
|
2025-02-20 01:35:00 +08:00
|
|
|
const response = await getDashboardAPI().deleteDashboard(dashboardUID, true);
|
2024-12-04 18:29:00 +08:00
|
|
|
|
2025-02-20 01:35:00 +08:00
|
|
|
// handling success alerts for these feature toggles
|
|
|
|
// for legacy response, the success alert will be triggered by showSuccessAlert function in public/app/core/services/backend_srv.ts
|
|
|
|
if (config.featureToggles.dashboardRestore) {
|
|
|
|
const name = response?.title;
|
2024-07-17 20:00:46 +08:00
|
|
|
|
2025-02-20 01:35:00 +08:00
|
|
|
if (name) {
|
|
|
|
const payload =
|
|
|
|
config.featureToggles.useV2DashboardsAPI || config.featureToggles.kubernetesDashboards
|
|
|
|
? ['Dashboard moved to Recently deleted']
|
|
|
|
: [
|
|
|
|
t('browse-dashboards.soft-delete.success', 'Dashboard {{name}} moved to Recently deleted', {
|
|
|
|
name,
|
|
|
|
}),
|
|
|
|
];
|
|
|
|
|
|
|
|
appEvents.publish({
|
|
|
|
type: AppEvents.alertSuccess.name,
|
|
|
|
payload,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else if (config.featureToggles.useV2DashboardsAPI || config.featureToggles.kubernetesDashboards) {
|
2024-07-17 20:00:46 +08:00
|
|
|
appEvents.publish({
|
|
|
|
type: AppEvents.alertSuccess.name,
|
2025-02-20 01:35:00 +08:00
|
|
|
payload: ['Dashboard deleted'],
|
2024-07-17 20:00:46 +08:00
|
|
|
});
|
|
|
|
}
|
2023-06-22 16:44:19 +08:00
|
|
|
}
|
|
|
|
return { data: undefined };
|
|
|
|
},
|
|
|
|
onQueryStarted: ({ selectedItems }, { queryFulfilled, dispatch }) => {
|
|
|
|
const selectedDashboards = Object.keys(selectedItems.dashboard).filter((uid) => selectedItems.dashboard[uid]);
|
|
|
|
const selectedFolders = Object.keys(selectedItems.folder).filter((uid) => selectedItems.folder[uid]);
|
|
|
|
queryFulfilled.then(() => {
|
|
|
|
dispatch(refreshParents([...selectedFolders, ...selectedDashboards]));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}),
|
2024-06-13 15:12:10 +08:00
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
// save an existing dashboard
|
2025-01-27 21:14:19 +08:00
|
|
|
saveDashboard: builder.mutation<SaveDashboardResponseDTO, SaveDashboardCommand<Dashboard | DashboardV2Spec>>({
|
2024-06-26 18:35:04 +08:00
|
|
|
queryFn: async (cmd) => {
|
|
|
|
try {
|
2025-01-27 21:14:19 +08:00
|
|
|
// When we use the `useV2DashboardsAPI` flag, we can save 'v2' schema dashboards
|
|
|
|
if (config.featureToggles.useV2DashboardsAPI && isV2DashboardCommand(cmd)) {
|
|
|
|
const response = await getDashboardAPI('v2').saveDashboard(cmd);
|
|
|
|
return { data: response };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isV1DashboardCommand(cmd)) {
|
|
|
|
const rsp = await getDashboardAPI().saveDashboard(cmd);
|
|
|
|
return { data: rsp };
|
|
|
|
}
|
|
|
|
throw new Error('Invalid dashboard version');
|
2024-06-26 18:35:04 +08:00
|
|
|
} catch (error) {
|
2025-01-31 20:25:16 +08:00
|
|
|
return handleRequestError(error);
|
2024-06-26 18:35:04 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
onQueryStarted: ({ folderUid }, { queryFulfilled, dispatch }) => {
|
|
|
|
dashboardWatcher.ignoreNextSave();
|
|
|
|
queryFulfilled.then(async () => {
|
|
|
|
await contextSrv.fetchUserPermissions();
|
|
|
|
dispatch(
|
|
|
|
refetchChildren({
|
|
|
|
parentUID: folderUid,
|
2023-06-27 18:38:33 +08:00
|
|
|
pageSize: PAGE_SIZE,
|
2023-06-22 16:44:19 +08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
2023-05-24 17:41:03 +08:00
|
|
|
}),
|
2024-06-13 15:12:10 +08:00
|
|
|
|
2024-02-06 18:46:24 +08:00
|
|
|
importDashboard: builder.mutation<ImportDashboardResponseDTO, ImportOptions>({
|
|
|
|
query: ({ dashboard, overwrite, inputs, folderUid }) => ({
|
|
|
|
method: 'POST',
|
|
|
|
url: '/dashboards/import',
|
2025-01-31 20:25:16 +08:00
|
|
|
body: {
|
2024-02-06 18:46:24 +08:00
|
|
|
dashboard,
|
|
|
|
overwrite,
|
|
|
|
inputs,
|
|
|
|
folderUid,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
onQueryStarted: ({ folderUid }, { queryFulfilled, dispatch }) => {
|
|
|
|
queryFulfilled.then(async (response) => {
|
|
|
|
dispatch(
|
|
|
|
refetchChildren({
|
|
|
|
parentUID: folderUid,
|
|
|
|
pageSize: PAGE_SIZE,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
const dashboardUrl = locationUtil.stripBaseFromUrl(response.data.importedUrl);
|
|
|
|
locationService.push(dashboardUrl);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}),
|
2024-06-13 15:12:10 +08:00
|
|
|
|
|
|
|
// restore a dashboard that got soft deleted
|
|
|
|
restoreDashboard: builder.mutation<void, RestoreDashboardArgs>({
|
2024-08-20 15:08:18 +08:00
|
|
|
query: ({ dashboardUID, targetFolderUID }) => ({
|
2024-06-13 15:12:10 +08:00
|
|
|
url: `/dashboards/uid/${dashboardUID}/trash`,
|
2025-01-31 20:25:16 +08:00
|
|
|
body: {
|
2024-08-20 15:08:18 +08:00
|
|
|
folderUid: targetFolderUID,
|
|
|
|
},
|
2024-06-13 15:12:10 +08:00
|
|
|
method: 'PATCH',
|
|
|
|
}),
|
|
|
|
}),
|
2024-06-21 00:18:11 +08:00
|
|
|
|
|
|
|
// permanently delete a dashboard. used in PermanentlyDeleteModal.
|
|
|
|
hardDeleteDashboard: builder.mutation<void, HardDeleteDashboardArgs>({
|
2024-07-17 20:00:46 +08:00
|
|
|
queryFn: async ({ dashboardUID }, _api, _extraOptions, baseQuery) => {
|
|
|
|
const response = await baseQuery({
|
|
|
|
url: `/dashboards/uid/${dashboardUID}/trash`,
|
|
|
|
method: 'DELETE',
|
|
|
|
showSuccessAlert: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
// @ts-expect-error
|
|
|
|
const name = response?.data?.title;
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
appEvents.publish({
|
|
|
|
type: AppEvents.alertSuccess.name,
|
|
|
|
payload: [t('browse-dashboards.hard-delete.success', 'Dashboard {{name}} deleted', { name })],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return { data: undefined };
|
|
|
|
},
|
2024-06-21 00:18:11 +08:00
|
|
|
onQueryStarted: ({ dashboardUID }, { queryFulfilled, dispatch }) => {
|
|
|
|
queryFulfilled.then(() => {
|
|
|
|
dispatch(refreshParents([dashboardUID]));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}),
|
2023-04-12 17:44:01 +08:00
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
2023-06-22 16:44:19 +08:00
|
|
|
export const {
|
|
|
|
endpoints,
|
|
|
|
useDeleteFolderMutation,
|
|
|
|
useDeleteItemsMutation,
|
|
|
|
useGetAffectedItemsQuery,
|
|
|
|
useGetFolderQuery,
|
|
|
|
useMoveFolderMutation,
|
|
|
|
useMoveItemsMutation,
|
|
|
|
useNewFolderMutation,
|
|
|
|
useSaveDashboardMutation,
|
|
|
|
useSaveFolderMutation,
|
2024-06-13 15:12:10 +08:00
|
|
|
useRestoreDashboardMutation,
|
2024-06-21 00:18:11 +08:00
|
|
|
useHardDeleteDashboardMutation,
|
2023-06-22 16:44:19 +08:00
|
|
|
} = browseDashboardsAPI;
|
2024-02-22 02:02:37 +08:00
|
|
|
|
2023-04-12 17:44:01 +08:00
|
|
|
export { skipToken } from '@reduxjs/toolkit/query/react';
|