Dashboard: Select the last used data source by default when adding a panel to a dashboard (#71777)

Co-authored-by: Juan Cabanas <juan.cabanas@grafana.com>
This commit is contained in:
Alexa V 2023-08-16 17:17:44 +02:00 committed by GitHub
parent 6f0d3830b0
commit 3fda30194c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 178 additions and 1 deletions

View File

@ -4,9 +4,16 @@ import { DataQuery, getDataSourceRef } from '@grafana/data';
import { locationService } from '@grafana/runtime';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { QueryGroup } from 'app/features/query/components/QueryGroup';
import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource';
import { QueryGroupDataSource, QueryGroupOptions } from 'app/types';
import { getDashboardSrv } from '../../services/DashboardSrv';
import { PanelModel } from '../../state';
import {
getLastUsedDatasourceFromStorage,
initLastUsedDatasourceKeyForDashboard,
setLastUsedDatasourceKeyForDashboard,
} from '../../utils/dashboard';
interface Props {
/** Current panel */
@ -20,12 +27,28 @@ export class PanelEditorQueries extends PureComponent<Props> {
super(props);
}
// store last used datasource in local storage
updateLastUsedDatasource = (datasource: QueryGroupDataSource) => {
if (!datasource.uid) {
return;
}
const dashboardUid = getDashboardSrv().getCurrent()?.uid ?? '';
// if datasource is MIXED reset datasource uid in storage, because Mixed datasource can contain multiple ds
if (datasource.uid === MIXED_DATASOURCE_NAME) {
return initLastUsedDatasourceKeyForDashboard(dashboardUid!);
}
setLastUsedDatasourceKeyForDashboard(dashboardUid, datasource.uid);
};
buildQueryOptions(panel: PanelModel): QueryGroupOptions {
const dataSource: QueryGroupDataSource = panel.datasource ?? {
default: true,
};
const datasourceSettings = getDatasourceSrv().getInstanceSettings(dataSource);
// store last datasource used in local storage
this.updateLastUsedDatasource(dataSource);
return {
cacheTimeout: datasourceSettings?.meta.queryOptions?.cacheTimeout ? panel.cacheTimeout : undefined,
dataSource: {
@ -51,7 +74,20 @@ export class PanelEditorQueries extends PureComponent<Props> {
// If the panel model has no datasource property load the default data source property and update the persisted model
// Because this part of the panel model is not in redux yet we do a forceUpdate.
if (!panel.datasource) {
const ds = getDatasourceSrv().getInstanceSettings(null);
let ds;
// check if we have last used datasource from local storage
// get dashboard uid
const dashboardUid = getDashboardSrv().getCurrent()?.uid ?? '';
const lastUsedDatasource = getLastUsedDatasourceFromStorage(dashboardUid!);
// do we have a last used datasource for this dashboard
if (lastUsedDatasource?.datasourceUid !== null) {
// get datasource from uid
ds = getDatasourceSrv().getInstanceSettings(lastUsedDatasource?.datasourceUid);
}
// else load default datasource
if (!ds) {
ds = getDatasourceSrv().getInstanceSettings(null);
}
panel.datasource = getDataSourceRef(ds!);
this.forceUpdate();
}

View File

@ -12,6 +12,8 @@ import { saveDashboard as saveDashboardApiCall } from 'app/features/manage-dashb
import { useDispatch } from 'app/types';
import { DashboardSavedEvent } from 'app/types/events';
import { updateDashboardUidLastUsedDatasource } from '../../utils/dashboard';
import { SaveDashboardOptions } from './types';
const saveDashboard = async (
@ -60,6 +62,10 @@ export const useDashboardSave = (dashboard: DashboardModel, isCopy = false) => {
// important that these happen before location redirect below
appEvents.publish(new DashboardSavedEvent());
notifyApp.success('Dashboard saved');
//Update local storage dashboard to handle things like last used datasource
updateDashboardUidLastUsedDatasource(result.uid);
if (isCopy) {
reportInteraction('grafana_dashboard_copied', {
name: dashboard.title,

View File

@ -0,0 +1,78 @@
import {
updateDashboardUidLastUsedDatasource,
getLastUsedDatasourceFromStorage,
initLastUsedDatasourceKeyForDashboard,
setLastUsedDatasourceKeyForDashboard,
} from './dashboard'; // Replace with the path to your actual module
// Mock the store module
jest.mock('app/core/store', () => ({
exists: jest.fn(),
getObject: jest.fn(),
setObject: jest.fn(),
}));
const store = jest.requireMock('app/core/store');
describe('Last Used Datasource Local Storage', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should retrieve the last used datasource', () => {
store.exists.mockReturnValue(true);
store.getObject.mockReturnValue({
dashboardUid: '123',
datasourceUid: 'datasource1',
});
const result = getLastUsedDatasourceFromStorage('123');
expect(result).toEqual({ dashboardUid: '123', datasourceUid: 'datasource1' });
});
it('should update only the dashboard UID', () => {
store.exists.mockReturnValue(true);
store.getObject.mockReturnValue({
dashboardUid: '456',
datasourceUid: 'datasource2',
});
updateDashboardUidLastUsedDatasource('789');
expect(store.setObject).toHaveBeenCalledWith('grafana.dashboards.panelEdit.lastUsedDatasource', {
dashboardUid: '789',
datasourceUid: 'datasource2',
});
});
it('should initialize local storage for a dashboard with empty datasource UID', () => {
initLastUsedDatasourceKeyForDashboard('999');
expect(store.setObject).toHaveBeenCalledWith('grafana.dashboards.panelEdit.lastUsedDatasource', {
dashboardUid: '999',
datasourceUid: '',
});
});
it('should set a new datasource UID and dashboard UID for a dashboard', () => {
store.exists.mockReturnValue(false);
setLastUsedDatasourceKeyForDashboard('111', 'datasource3');
expect(store.setObject).toHaveBeenCalledWith('grafana.dashboards.panelEdit.lastUsedDatasource', {
dashboardUid: '111',
datasourceUid: 'datasource3',
});
});
it('should update the datasource UID while keeping the existing dashboard UID', () => {
store.exists.mockReturnValue(true);
store.getObject.mockReturnValue({
dashboardUid: '222',
datasourceUid: 'datasource4',
});
setLastUsedDatasourceKeyForDashboard('222', 'datasource5');
expect(store.setObject).toHaveBeenCalledWith('grafana.dashboards.panelEdit.lastUsedDatasource', {
dashboardUid: '222',
datasourceUid: 'datasource5',
});
});
});

View File

@ -104,3 +104,60 @@ export function getCopiedPanelPlugin(): (PanelPluginMeta & PanelPluginInfo) | un
return undefined;
}
type LastUsedDatasource =
| {
dashboardUid: string;
datasourceUid: string;
}
| undefined;
const PANEL_EDIT_LAST_USED_DATASOURCE = 'grafana.dashboards.panelEdit.lastUsedDatasource';
// Function that returns last used datasource from local storage
export function getLastUsedDatasourceFromStorage(dashboardUid: string): LastUsedDatasource {
// Check if user has any local storage associated with this dashboard
if (store.exists(PANEL_EDIT_LAST_USED_DATASOURCE)) {
const lastUsedDatasource: LastUsedDatasource = store.getObject(PANEL_EDIT_LAST_USED_DATASOURCE);
if (lastUsedDatasource?.dashboardUid === dashboardUid) {
return lastUsedDatasource;
}
}
return undefined;
}
// Function that updates local storage with new dashboard uid and keeps existing datasource
export function updateDashboardUidLastUsedDatasource(dashUid: string) {
// Check if user has any datasource uid in local storage
if (!store.exists(PANEL_EDIT_LAST_USED_DATASOURCE)) {
return;
}
const oldRegistryLastUsedDatasource: LastUsedDatasource = store.getObject(PANEL_EDIT_LAST_USED_DATASOURCE);
//keep existing datasource uid
const datasourceUid = oldRegistryLastUsedDatasource?.datasourceUid ?? '';
updatePropsLastUsedDatasourceKey(dashUid, datasourceUid);
}
// Function that updates local storage with new dashboard uid and resets datasource to empty
export function initLastUsedDatasourceKeyForDashboard(dashboardUid: string | undefined) {
store.setObject(PANEL_EDIT_LAST_USED_DATASOURCE, { dashboardUid: dashboardUid, datasourceUid: '' });
}
// Function that updates local storage with new datasource uid and keeps existing dashboard when there is dash uid key in local storage
// or sets new local storage with new dashboard uid and existing datasource
export function setLastUsedDatasourceKeyForDashboard(dashUid: string, dsUid: string) {
// Check if user has any datasource uid in local storage
const lastUsedDatasource = getLastUsedDatasourceFromStorage(dashUid);
if (!lastUsedDatasource) {
updatePropsLastUsedDatasourceKey(dashUid, dsUid);
} else {
// set new local storage with new dashboard uid and existing datasource
const dashboardUid = lastUsedDatasource?.dashboardUid ?? '';
updatePropsLastUsedDatasourceKey(dashboardUid, dsUid);
}
}
// Function that updates local storage with new dashboard uid and datasource uid
function updatePropsLastUsedDatasourceKey(dashboardUid: string | undefined, datasourceUid: string) {
store.setObject(PANEL_EDIT_LAST_USED_DATASOURCE, { dashboardUid: dashboardUid, datasourceUid: datasourceUid });
}