mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Store original JSON in DashboardModel (#73881)
* Dashboard: Save original JSON to state * Dashboards: Update tests * Dashboards: Fix original json access * Dashboard: Save original to the DashboardModel * Dashboard: Cleanup tests * Remove original db setter
This commit is contained in:
parent
c761f2e4d9
commit
c9323e303d
@ -22,12 +22,6 @@ jest.mock('@grafana/runtime', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('app/core/services/backend_srv', () => ({
|
||||
backendSrv: {
|
||||
getDashboardByUid: jest.fn().mockResolvedValue({ dashboard: {} }),
|
||||
},
|
||||
}));
|
||||
|
||||
const store = configureStore();
|
||||
const mockPost = jest.fn();
|
||||
const buildMocks = () => ({
|
||||
@ -76,7 +70,7 @@ describe('SaveDashboardDrawer', () => {
|
||||
expect(screen.getByRole('button', { name: /overwrite/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render corresponding save modal once the errror is handled', async () => {
|
||||
it('should render corresponding save modal once the error is handled', async () => {
|
||||
const { onDismiss, dashboard, error } = buildMocks();
|
||||
mockPost.mockRejectedValueOnce(error);
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { config, isFetchError } from '@grafana/runtime';
|
||||
import { Drawer, Tab, TabsBar } from '@grafana/ui';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
|
||||
import { jsonDiff } from '../VersionHistory/utils';
|
||||
|
||||
@ -18,33 +16,24 @@ import { useDashboardSave } from './useDashboardSave';
|
||||
|
||||
export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCopy }: SaveDashboardModalProps) => {
|
||||
const [options, setOptions] = useState<SaveDashboardOptions>({});
|
||||
|
||||
const previous = dashboard.getOriginalDashboard();
|
||||
const isProvisioned = dashboard.meta.provisioned;
|
||||
const isNew = dashboard.version === 0;
|
||||
|
||||
const previous = useAsync(async () => {
|
||||
if (isNew) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = await backendSrv.getDashboardByUid(dashboard.uid);
|
||||
return result.dashboard;
|
||||
}, [dashboard, isNew]);
|
||||
|
||||
const data = useMemo<SaveDashboardData>(() => {
|
||||
const clone = dashboard.getSaveModelClone({
|
||||
saveTimerange: Boolean(options.saveTimerange),
|
||||
saveVariables: Boolean(options.saveVariables),
|
||||
});
|
||||
|
||||
if (!previous.value) {
|
||||
if (!previous) {
|
||||
return { clone, diff: {}, diffCount: 0, hasChanges: false };
|
||||
}
|
||||
|
||||
const cloneJSON = JSON.stringify(clone, null, 2);
|
||||
const cloneSafe = JSON.parse(cloneJSON); // avoids undefined issues
|
||||
|
||||
const diff = jsonDiff(previous.value, cloneSafe);
|
||||
const diff = jsonDiff(previous, cloneSafe);
|
||||
let diffCount = 0;
|
||||
for (const d of Object.values(diff)) {
|
||||
diffCount += d.length;
|
||||
@ -56,7 +45,7 @@ export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCop
|
||||
diffCount,
|
||||
hasChanges: diffCount > 0 && !isNew,
|
||||
};
|
||||
}, [dashboard, previous.value, options, isNew]);
|
||||
}, [dashboard, previous, options, isNew]);
|
||||
|
||||
const [showDiff, setShowDiff] = useState(false);
|
||||
const { state, onDashboardSave } = useDashboardSave(dashboard, isCopy);
|
||||
@ -69,7 +58,7 @@ export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCop
|
||||
|
||||
const renderSaveBody = () => {
|
||||
if (showDiff) {
|
||||
return <SaveDashboardDiff diff={data.diff} oldValue={previous.value} newValue={data.clone} />;
|
||||
return <SaveDashboardDiff diff={data.diff} oldValue={previous} newValue={data.clone} />;
|
||||
}
|
||||
|
||||
if (isNew || isCopy) {
|
||||
|
@ -124,6 +124,13 @@ const getTestDashboard = (overrides?: Partial<Dashboard>, metaOverrides?: Partia
|
||||
return new DashboardModel(data, metaOverrides);
|
||||
};
|
||||
|
||||
const dashboardBase = {
|
||||
getModel: getTestDashboard,
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
};
|
||||
|
||||
describe('PublicDashboardPage', () => {
|
||||
beforeEach(() => {
|
||||
config.featureToggles.publicDashboards = true;
|
||||
@ -144,12 +151,7 @@ describe('PublicDashboardPage', () => {
|
||||
|
||||
describe('Given a simple public dashboard', () => {
|
||||
const newState = {
|
||||
dashboard: {
|
||||
getModel: getTestDashboard,
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
dashboard: dashboardBase,
|
||||
};
|
||||
|
||||
it('Should render panels', async () => {
|
||||
@ -220,10 +222,8 @@ describe('PublicDashboardPage', () => {
|
||||
|
||||
const newState = {
|
||||
dashboard: {
|
||||
...dashboardBase,
|
||||
getModel: () => getTestDashboard({ panels }),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
};
|
||||
setup(undefined, newState);
|
||||
@ -247,13 +247,11 @@ describe('PublicDashboardPage', () => {
|
||||
it('Should render time range and refresh picker buttons', async () => {
|
||||
setup(undefined, {
|
||||
dashboard: {
|
||||
...dashboardBase,
|
||||
getModel: () =>
|
||||
getTestDashboard({
|
||||
timepicker: { hidden: false, collapse: false, refresh_intervals: [], time_options: [] },
|
||||
}),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
});
|
||||
expect(await screen.findByTestId(selectors.TimePicker.openButton)).toBeInTheDocument();
|
||||
@ -266,10 +264,8 @@ describe('PublicDashboardPage', () => {
|
||||
it('Should render public dashboard paused screen', async () => {
|
||||
setup(undefined, {
|
||||
dashboard: {
|
||||
...dashboardBase,
|
||||
getModel: () => getTestDashboard(undefined, { publicDashboardEnabled: false, dashboardNotFound: false }),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
});
|
||||
|
||||
@ -285,10 +281,8 @@ describe('PublicDashboardPage', () => {
|
||||
it('Should render public dashboard deleted screen', async () => {
|
||||
setup(undefined, {
|
||||
dashboard: {
|
||||
...dashboardBase,
|
||||
getModel: () => getTestDashboard(undefined, { dashboardNotFound: true }),
|
||||
initError: null,
|
||||
initPhase: DashboardInitPhase.Completed,
|
||||
permissions: [],
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -104,6 +104,7 @@ export class DashboardModel implements TimeModel {
|
||||
private appEventsSubscription: Subscription;
|
||||
private lastRefresh: number;
|
||||
private timeRangeUpdatedDuringEdit = false;
|
||||
private originalDashboard: Dashboard | null = null;
|
||||
|
||||
// ------------------
|
||||
// not persisted
|
||||
@ -130,6 +131,7 @@ export class DashboardModel implements TimeModel {
|
||||
panelsAffectedByVariableChange: true,
|
||||
lastRefresh: true,
|
||||
timeRangeUpdatedDuringEdit: true,
|
||||
originalDashboard: true,
|
||||
};
|
||||
|
||||
constructor(
|
||||
@ -169,6 +171,7 @@ export class DashboardModel implements TimeModel {
|
||||
this.links = data.links ?? [];
|
||||
this.gnetId = data.gnetId || null;
|
||||
this.panels = map(data.panels ?? [], (panelData: any) => new PanelModel(panelData));
|
||||
this.originalDashboard = data;
|
||||
this.ensurePanelsHaveUniqueIds();
|
||||
this.formatDate = this.formatDate.bind(this);
|
||||
|
||||
@ -1296,6 +1299,10 @@ export class DashboardModel implements TimeModel {
|
||||
this.templateVariableValueUpdated();
|
||||
this.startRefresh(event.payload);
|
||||
}
|
||||
|
||||
getOriginalDashboard() {
|
||||
return this.originalDashboard;
|
||||
}
|
||||
}
|
||||
|
||||
function isPanelWithLegend(panel: PanelModel): panel is PanelModel & Pick<Required<PanelModel>, 'legend'> {
|
||||
|
Loading…
Reference in New Issue
Block a user