mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 07:32:12 +08:00
Plugins: Allow async panel migrations (#73782)
* Plugins: Allow async panel migrations * comment
This commit is contained in:
@ -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.
|
* Called before a panel is initialized. Allows panel inspection for any updates before changing the panel type.
|
||||||
|
@ -132,14 +132,14 @@ describe('DashboardPrompt', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should ignore panel schema migrations', () => {
|
it('Should ignore panel schema migrations', async () => {
|
||||||
const { original, dash } = getTestContext();
|
const { original, dash } = getTestContext();
|
||||||
const plugin = getPanelPlugin({}).setMigrationHandler((panel) => {
|
const plugin = getPanelPlugin({}).setMigrationHandler((panel) => {
|
||||||
delete (panel as any).legend;
|
delete (panel as any).legend;
|
||||||
return { option1: 'Aasd' };
|
return { option1: 'Aasd' };
|
||||||
});
|
});
|
||||||
|
|
||||||
dash.panels[0].pluginLoaded(plugin);
|
await dash.panels[0].pluginLoaded(plugin);
|
||||||
expect(hasChanges(dash, original)).toBe(false);
|
expect(hasChanges(dash, original)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
standardFieldConfigEditorRegistry,
|
standardFieldConfigEditorRegistry,
|
||||||
dateTime,
|
dateTime,
|
||||||
TimeRange,
|
TimeRange,
|
||||||
|
PanelMigrationHandler,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
|
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
|
||||||
import { mockStandardFieldConfigOptions } from '@grafana/data/test/helpers/fieldConfig';
|
import { mockStandardFieldConfigOptions } from '@grafana/data/test/helpers/fieldConfig';
|
||||||
@ -80,7 +81,7 @@ describe('PanelModel', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
persistedOptionsMock = {
|
persistedOptionsMock = {
|
||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
thresholds: [
|
thresholds: [
|
||||||
@ -141,7 +142,44 @@ describe('PanelModel', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
model = new PanelModel(modelJson);
|
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', () => {
|
it('should apply defaults', () => {
|
||||||
|
@ -429,7 +429,7 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||||||
this.options = options.options;
|
this.options = options.options;
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginLoaded(plugin: PanelPlugin) {
|
async pluginLoaded(plugin: PanelPlugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
const version = getPluginVersion(plugin);
|
const version = getPluginVersion(plugin);
|
||||||
@ -451,7 +451,8 @@ export class PanelModel implements DataConfigSource, IPanelModel {
|
|||||||
|
|
||||||
if (plugin.onPanelMigration) {
|
if (plugin.onPanelMigration) {
|
||||||
if (version !== this.pluginVersion) {
|
if (version !== this.pluginVersion) {
|
||||||
this.options = plugin.onPanelMigration(this);
|
const newPanelOptions = plugin.onPanelMigration(this);
|
||||||
|
this.options = await newPanelOptions;
|
||||||
this.pluginVersion = version;
|
this.pluginVersion = version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,9 +83,9 @@ describe('applyPanelTimeOverrides', () => {
|
|||||||
expect(height).toBe(82);
|
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({});
|
const panelModel = new PanelModel({});
|
||||||
panelModel.pluginLoaded(
|
await panelModel.pluginLoaded(
|
||||||
getPanelPlugin({ id: 'table' }, null as unknown as ComponentClass<PanelProps>, null).setNoPadding()
|
getPanelPlugin({ id: 'table' }, null as unknown as ComponentClass<PanelProps>, null).setNoPadding()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ export function initPanelState(panel: PanelModel): ThunkResult<Promise<void>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!panel.plugin) {
|
if (!panel.plugin) {
|
||||||
panel.pluginLoaded(plugin);
|
await panel.pluginLoaded(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(panelModelAndPluginReady({ key: panel.key, plugin }));
|
dispatch(panelModelAndPluginReady({ key: panel.key, plugin }));
|
||||||
@ -120,7 +120,7 @@ export function changeToLibraryPanel(panel: PanelModel, libraryPanel: LibraryEle
|
|||||||
plugin = await dispatch(loadPanelPlugin(newPluginId));
|
plugin = await dispatch(loadPanelPlugin(newPluginId));
|
||||||
}
|
}
|
||||||
|
|
||||||
panel.pluginLoaded(plugin);
|
await panel.pluginLoaded(plugin);
|
||||||
panel.generateNewKey();
|
panel.generateNewKey();
|
||||||
|
|
||||||
await dispatch(panelModelAndPluginReady({ key: panel.key, plugin }));
|
await dispatch(panelModelAndPluginReady({ key: panel.key, plugin }));
|
||||||
|
Reference in New Issue
Block a user