Plugins: Allow async panel migrations (#73782)

* Plugins: Allow async panel migrations

* comment
This commit is contained in:
Josh Hunt 2023-08-28 10:06:55 +00:00 committed by GitHub
parent 7543a28ae2
commit f95405d3c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 54 additions and 12 deletions

View File

@ -135,9 +135,12 @@ export interface PanelEditorProps<T = any> {
}
/**
* Called when a panel is first loaded with current panel model
* Called when a panel is first loaded with current panel model to migrate panel options if needed.
* Can return panel options, or a Promise that resolves to panel options for async migrations
*/
export type PanelMigrationHandler<TOptions = any> = (panel: PanelModel<TOptions>) => Partial<TOptions>;
export type PanelMigrationHandler<TOptions = any> = (
panel: PanelModel<TOptions>
) => Partial<TOptions> | Promise<Partial<TOptions>>;
/**
* Called before a panel is initialized. Allows panel inspection for any updates before changing the panel type.

View File

@ -132,14 +132,14 @@ describe('DashboardPrompt', () => {
});
});
it('Should ignore panel schema migrations', () => {
it('Should ignore panel schema migrations', async () => {
const { original, dash } = getTestContext();
const plugin = getPanelPlugin({}).setMigrationHandler((panel) => {
delete (panel as any).legend;
return { option1: 'Aasd' };
});
dash.panels[0].pluginLoaded(plugin);
await dash.panels[0].pluginLoaded(plugin);
expect(hasChanges(dash, original)).toBe(false);
});

View File

@ -8,6 +8,7 @@ import {
standardFieldConfigEditorRegistry,
dateTime,
TimeRange,
PanelMigrationHandler,
} from '@grafana/data';
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
import { mockStandardFieldConfigOptions } from '@grafana/data/test/helpers/fieldConfig';
@ -80,7 +81,7 @@ describe('PanelModel', () => {
},
});
beforeEach(() => {
beforeEach(async () => {
persistedOptionsMock = {
fieldOptions: {
thresholds: [
@ -141,7 +142,44 @@ describe('PanelModel', () => {
};
model = new PanelModel(modelJson);
model.pluginLoaded(tablePlugin);
await model.pluginLoaded(tablePlugin);
});
describe('migrations', () => {
let initialMigrator: PanelMigrationHandler<(typeof model)['options']> | undefined = undefined;
beforeEach(() => {
initialMigrator = tablePlugin.onPanelMigration;
});
afterEach(() => {
tablePlugin.onPanelMigration = initialMigrator;
});
it('should run sync migrations', async () => {
model.options.valueToMigrate = 'old-legacy';
tablePlugin.onPanelMigration = (p) => ({ ...p.options, valueToMigrate: 'new-version' });
tablePlugin.onPanelMigration = (p) => {
p.options.valueToMigrate = 'new-version';
return p.options;
};
await model.pluginLoaded(tablePlugin);
expect(model.options).toMatchObject({ valueToMigrate: 'new-version' });
});
it('should run async migrations', async () => {
model.options.valueToMigrate = 'old-legacy';
tablePlugin.onPanelMigration = async (p) =>
new Promise((resolve) => {
setTimeout(() => resolve({ ...p.options, valueToMigrate: 'new-version' }), 10);
});
await model.pluginLoaded(tablePlugin);
expect(model.options).toMatchObject({ valueToMigrate: 'new-version' });
});
});
it('should apply defaults', () => {

View File

@ -429,7 +429,7 @@ export class PanelModel implements DataConfigSource, IPanelModel {
this.options = options.options;
}
pluginLoaded(plugin: PanelPlugin) {
async pluginLoaded(plugin: PanelPlugin) {
this.plugin = plugin;
const version = getPluginVersion(plugin);
@ -451,7 +451,8 @@ export class PanelModel implements DataConfigSource, IPanelModel {
if (plugin.onPanelMigration) {
if (version !== this.pluginVersion) {
this.options = plugin.onPanelMigration(this);
const newPanelOptions = plugin.onPanelMigration(this);
this.options = await newPanelOptions;
this.pluginVersion = version;
}
}

View File

@ -83,9 +83,9 @@ describe('applyPanelTimeOverrides', () => {
expect(height).toBe(82);
});
it('Calculate panel height with panel plugin zeroChromePadding', () => {
it('Calculate panel height with panel plugin zeroChromePadding', async () => {
const panelModel = new PanelModel({});
panelModel.pluginLoaded(
await panelModel.pluginLoaded(
getPanelPlugin({ id: 'table' }, null as unknown as ComponentClass<PanelProps>, null).setNoPadding()
);

View File

@ -30,7 +30,7 @@ export function initPanelState(panel: PanelModel): ThunkResult<Promise<void>> {
}
if (!panel.plugin) {
panel.pluginLoaded(plugin);
await panel.pluginLoaded(plugin);
}
dispatch(panelModelAndPluginReady({ key: panel.key, plugin }));
@ -120,7 +120,7 @@ export function changeToLibraryPanel(panel: PanelModel, libraryPanel: LibraryEle
plugin = await dispatch(loadPanelPlugin(newPluginId));
}
panel.pluginLoaded(plugin);
await panel.pluginLoaded(plugin);
panel.generateNewKey();
await dispatch(panelModelAndPluginReady({ key: panel.key, plugin }));