LibraryPanels: Prevents duplicate repeated panels from being created (#38804)

* LibraryPanels: Prevents duplicate repeated panels from being created

* Tests: adds tests

* Chore: updates after PR feedback
This commit is contained in:
Hugo Häggmark 2021-09-06 05:57:01 +02:00 committed by GitHub
parent d913e46e37
commit 3b0910e470
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 100 additions and 28 deletions

View File

@ -1,6 +1,6 @@
import { thunkTester } from '../../../../../../test/core/thunk/thunkTester';
import { closeEditor, initialState, PanelEditorState } from './reducers';
import { initPanelEditor, exitPanelEditor } from './actions';
import { exitPanelEditor, initPanelEditor, skipPanelUpdate } from './actions';
import { cleanUpEditPanel, panelModelAndPluginReady } from '../../../state/reducers';
import { DashboardModel, PanelModel } from '../../../state';
import { getPanelPlugin } from 'app/features/plugins/__mocks__/pluginMocks';
@ -125,4 +125,46 @@ describe('panelEditor actions', () => {
expect(sourcePanel.getOptions()).toEqual({});
});
});
describe('skipPanelUpdate', () => {
describe('when called with panel with an library uid different from the modified panel', () => {
it('then it should return true', () => {
const meta: any = {};
const modified: any = { libraryPanel: { uid: '123', name: 'Name', meta, version: 1 } };
const panel: any = { libraryPanel: { uid: '456', name: 'Name', meta, version: 1 } };
expect(skipPanelUpdate(modified, panel)).toEqual(true);
});
});
describe('when called with a panel that is the same as the modified panel', () => {
it('then it should return true', () => {
const meta: any = {};
const modified: any = { editSourceId: 14, libraryPanel: { uid: '123', name: 'Name', meta, version: 1 } };
const panel: any = { id: 14, libraryPanel: { uid: '123', name: 'Name', meta, version: 1 } };
expect(skipPanelUpdate(modified, panel)).toEqual(true);
});
});
describe('when called with a panel that is repeated', () => {
it('then it should return true', () => {
const meta: any = {};
const modified: any = { libraryPanel: { uid: '123', name: 'Name', meta, version: 1 } };
const panel: any = { repeatPanelId: 14, libraryPanel: { uid: '123', name: 'Name', meta, version: 1 } };
expect(skipPanelUpdate(modified, panel)).toEqual(true);
});
});
describe('when called with a panel that is a duplicate of the modified panel', () => {
it('then it should return false', () => {
const meta: any = {};
const modified: any = { libraryPanel: { uid: '123', name: 'Name', meta, version: 1 } };
const panel: any = { libraryPanel: { uid: '123', name: 'Name', meta, version: 1 } };
expect(skipPanelUpdate(modified, panel)).toEqual(false);
});
});
});
});

View File

@ -4,9 +4,9 @@ import {
closeEditor,
PANEL_EDITOR_UI_STATE_STORAGE_KEY,
PanelEditorUIState,
setDiscardChanges,
setPanelEditorUIState,
updateEditorInitState,
setDiscardChanges,
} from './reducers';
import { cleanUpEditPanel, panelModelAndPluginReady } from '../../../state/reducers';
import store from 'app/core/store';
@ -33,38 +33,68 @@ export function discardPanelChanges(): ThunkResult<void> {
};
}
function updateDuplicateLibraryPanels(modifiedPanel: PanelModel, dashboard: DashboardModel | null, dispatch: any) {
if (modifiedPanel.libraryPanel?.uid === undefined || !dashboard) {
return;
}
const modifiedSaveModel = modifiedPanel.getSaveModel();
for (const panel of dashboard.panels) {
if (panel.libraryPanel?.uid !== modifiedPanel.libraryPanel!.uid) {
continue;
export function updateDuplicateLibraryPanels(
modifiedPanel: PanelModel,
dashboard: DashboardModel | null
): ThunkResult<void> {
return (dispatch) => {
if (modifiedPanel.libraryPanel?.uid === undefined || !dashboard) {
return;
}
panel.restoreModel({
...modifiedSaveModel,
...pick(panel, 'gridPos', 'id'),
});
const modifiedSaveModel = modifiedPanel.getSaveModel();
for (const panel of dashboard.panels) {
if (skipPanelUpdate(modifiedPanel, panel)) {
continue;
}
// Loaded plugin is not included in the persisted properties
// So is not handled by restoreModel
const pluginChanged = panel.plugin?.meta.id !== modifiedPanel.plugin?.meta.id;
panel.plugin = modifiedPanel.plugin;
panel.configRev++;
panel.restoreModel({
...modifiedSaveModel,
...pick(panel, 'gridPos', 'id'),
});
if (pluginChanged) {
dispatch(panelModelAndPluginReady({ panelId: panel.id, plugin: panel.plugin! }));
// Loaded plugin is not included in the persisted properties
// So is not handled by restoreModel
const pluginChanged = panel.plugin?.meta.id !== modifiedPanel.plugin?.meta.id;
panel.plugin = modifiedPanel.plugin;
panel.configRev++;
if (pluginChanged) {
dispatch(panelModelAndPluginReady({ panelId: panel.id, plugin: panel.plugin! }));
}
// Resend last query result on source panel query runner
// But do this after the panel edit editor exit process has completed
setTimeout(() => {
panel.getQueryRunner().useLastResultFrom(modifiedPanel.getQueryRunner());
}, 20);
}
// Resend last query result on source panel query runner
// But do this after the panel edit editor exit process has completed
setTimeout(() => {
panel.getQueryRunner().useLastResultFrom(modifiedPanel.getQueryRunner());
}, 20);
if (modifiedPanel.repeat) {
// We skip any repeated library panels so we need to update them by calling processRepeats
// But do this after the panel edit editor exit process has completed
setTimeout(() => dashboard.processRepeats(), 20);
}
};
}
export function skipPanelUpdate(modifiedPanel: PanelModel, panelToUpdate: PanelModel): boolean {
// don't update library panels that aren't of the same type
if (panelToUpdate.libraryPanel?.uid !== modifiedPanel.libraryPanel!.uid) {
return true;
}
// don't update the modifiedPanel twice
if (panelToUpdate.id && panelToUpdate.id === modifiedPanel.editSourceId) {
return true;
}
// don't update library panels that are repeated
if (panelToUpdate.repeatPanelId) {
return true;
}
return false;
}
export function exitPanelEditor(): ThunkResult<void> {
@ -78,7 +108,7 @@ export function exitPanelEditor(): ThunkResult<void> {
const sourcePanel = getSourcePanel();
const panelTypeChanged = sourcePanel.type !== panel.type;
updateDuplicateLibraryPanels(panel, dashboard, dispatch);
dispatch(updateDuplicateLibraryPanels(panel, dashboard));
// restore the source panel ID before we update source panel
modifiedSaveModel.id = sourcePanel.id;