Browse Dashboards: Imported dashboards now display immediately in the dashboard list (#81819)

* create importDashboard method in rtk query

* fix unit tests

* Revert "fix unit tests"

This reverts commit 72cd81c803.

* fix unit test
This commit is contained in:
Ashley Harrison 2024-02-06 10:46:24 +00:00 committed by GitHub
parent 8c38ebfeae
commit e2c2704296
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 92 additions and 33 deletions

View File

@ -3947,7 +3947,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Do not use any type assertions.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
@ -3955,9 +3955,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Unexpected any. Specify a different type.", "15"],
[0, 0, 0, "Unexpected any. Specify a different type.", "16"]
[0, 0, 0, "Unexpected any. Specify a different type.", "14"]
],
"public/app/features/manage-dashboards/state/reducers.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],

View File

@ -3,12 +3,20 @@ import { lastValueFrom } from 'rxjs';
import { isTruthy, locationUtil } from '@grafana/data';
import { BackendSrvRequest, getBackendSrv, locationService } from '@grafana/runtime';
import { Dashboard } from '@grafana/schema';
import { notifyApp } from 'app/core/actions';
import { createSuccessNotification } from 'app/core/copy/appNotification';
import { contextSrv } from 'app/core/core';
import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { DashboardDTO, DescendantCount, DescendantCountDTO, FolderDTO, SaveDashboardResponseDTO } from 'app/types';
import {
DashboardDTO,
DescendantCount,
DescendantCountDTO,
FolderDTO,
ImportDashboardResponseDTO,
SaveDashboardResponseDTO,
} from 'app/types';
import { refetchChildren, refreshParents } from '../state';
import { DashboardTreeSelection } from '../types';
@ -28,6 +36,20 @@ interface MoveItemsArgs extends DeleteItemsArgs {
destinationUID: string;
}
export interface ImportInputs {
name: string;
type: string;
value: string;
pluginId?: string;
}
interface ImportOptions {
dashboard: Dashboard;
overwrite: boolean;
inputs: ImportInputs[];
folderUid: string;
}
function createBackendSrvBaseQuery({ baseURL }: { baseURL: string }): BaseQueryFn<RequestOptions> {
async function backendSrvBaseQuery(requestOptions: RequestOptions) {
try {
@ -299,6 +321,30 @@ export const browseDashboardsAPI = createApi({
});
},
}),
importDashboard: builder.mutation<ImportDashboardResponseDTO, ImportOptions>({
query: ({ dashboard, overwrite, inputs, folderUid }) => ({
method: 'POST',
url: '/dashboards/import',
data: {
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);
});
},
}),
}),
});

View File

@ -1,8 +1,8 @@
import { thunkTester } from 'test/core/thunk/thunkTester';
import { DataSourceInstanceSettings, ThresholdsMode } from '@grafana/data';
import { BackendSrv, setBackendSrv } from '@grafana/runtime';
import { defaultDashboard, FieldColorModeId } from '@grafana/schema';
import { browseDashboardsAPI } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
import { getLibraryPanel } from 'app/features/library-panels/state/api';
import { PanelModel } from '../../dashboard/state';
@ -20,6 +20,15 @@ const mocks = {
describe('importDashboard', () => {
it('Should send data source uid', async () => {
// note: the actual action returned is more complicated
// but we don't really care about the return type in this test
// we're only testing that the correct data is passed to initiate
const mockAction = jest.fn().mockImplementation(() => ({
type: 'foo',
}));
const importDashboardRtkQueryMock = jest
.spyOn(browseDashboardsAPI.endpoints.importDashboard, 'initiate')
.mockImplementation(mockAction);
const form: ImportDashboardDTO = {
title: 'Asda',
uid: '12',
@ -40,17 +49,6 @@ describe('importDashboard', () => {
},
};
let postArgs: unknown;
setBackendSrv({
post: (url, args) => {
postArgs = args;
return Promise.resolve({
importedUrl: '/my/dashboard',
});
},
} as BackendSrv);
await thunkTester({
importDashboard: {
...initialImportDashboardState,
@ -70,7 +68,7 @@ describe('importDashboard', () => {
.givenThunk(importDashboard)
.whenThunkIsDispatched(form);
expect(postArgs).toEqual({
expect(importDashboardRtkQueryMock).toHaveBeenCalledWith({
dashboard: {
title: 'Asda',
uid: '12',

View File

@ -1,7 +1,8 @@
import { DataSourceInstanceSettings, locationUtil } from '@grafana/data';
import { getBackendSrv, getDataSourceSrv, isFetchError, locationService } from '@grafana/runtime';
import { DataSourceInstanceSettings } from '@grafana/data';
import { getBackendSrv, getDataSourceSrv, isFetchError } from '@grafana/runtime';
import { notifyApp } from 'app/core/actions';
import { createErrorNotification } from 'app/core/copy/appNotification';
import { browseDashboardsAPI, ImportInputs } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { FolderInfo, PermissionLevelString, SearchQueryType, ThunkResult } from 'app/types';
@ -200,7 +201,7 @@ export function importDashboard(importDashboardForm: ImportDashboardDTO): ThunkR
const dashboard = getState().importDashboard.dashboard;
const inputs = getState().importDashboard.inputs;
let inputsToPersist = [] as any[];
const inputsToPersist: ImportInputs[] = [];
importDashboardForm.dataSources?.forEach((dataSource: DataSourceInstanceSettings, index: number) => {
const input = inputs.dataSources[index];
inputsToPersist.push({
@ -221,18 +222,17 @@ export function importDashboard(importDashboardForm: ImportDashboardDTO): ThunkR
});
});
const result = await getBackendSrv().post('api/dashboards/import', {
// uid: if user changed it, take the new uid from importDashboardForm,
// else read it from original dashboard
// by default the uid input is disabled, onSubmit ignores values from disabled inputs
dashboard: { ...dashboard, title: importDashboardForm.title, uid: importDashboardForm.uid || dashboard.uid },
overwrite: true,
inputs: inputsToPersist,
folderUid: importDashboardForm.folder.uid,
});
const dashboardUrl = locationUtil.stripBaseFromUrl(result.importedUrl);
locationService.push(dashboardUrl);
dispatch(
browseDashboardsAPI.endpoints.importDashboard.initiate({
// uid: if user changed it, take the new uid from importDashboardForm,
// else read it from original dashboard
// by default the uid input is disabled, onSubmit ignores values from disabled inputs
dashboard: { ...dashboard, title: importDashboardForm.title, uid: importDashboardForm.uid || dashboard.uid },
overwrite: true,
inputs: inputsToPersist,
folderUid: importDashboardForm.folder.uid,
})
);
};
}

View File

@ -8,6 +8,23 @@ export interface DashboardDTO {
meta: DashboardMeta;
}
export interface ImportDashboardResponseDTO {
uid: string;
pluginId: string;
title: string;
imported: boolean;
importedRevision?: number;
importedUri: string;
importedUrl: string;
slug: string;
dashboardId: number;
folderId: number;
folderUid: string;
description: string;
path: string;
removed: boolean;
}
export interface SaveDashboardResponseDTO {
id: number;
slug: string;