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
pull/73941/head^2
Alex Khomenko 2 years ago committed by GitHub
parent c761f2e4d9
commit c9323e303d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      public/app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer.test.tsx
  2. 21
      public/app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer.tsx
  3. 30
      public/app/features/dashboard/containers/PublicDashboardPage.test.tsx
  4. 7
      public/app/features/dashboard/state/DashboardModel.ts

@ -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…
Cancel
Save