mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Table: migrate old-table config to new table config (#30142)
* feat(tablepanel): add migration button to old table panel config * feat(tablepanel): migrate old table transformations * feat(tablepanel): migrate old styles to config overrides * feat(tablepanel): migrate catch all style override to panel defaults * refactor(tablepanel): clean up typings * refactor(tablepanel): base threshold as -Infinity * feat(tablepanel): migrate align to new table config overrides * feat(tablepanel): migrate links to new table overrides * refactor(tabelpanel): clean up threshold migrations * feat(tablepanel): introduce table transform to merge * feat(tablepanel): add note informing user to manually update links with cell values
This commit is contained in:
@@ -1,4 +1,16 @@
|
||||
import { PanelModel } from '@grafana/data';
|
||||
import {
|
||||
PanelModel,
|
||||
FieldMatcherID,
|
||||
ConfigOverrideRule,
|
||||
ThresholdsMode,
|
||||
ThresholdsConfig,
|
||||
FieldConfig,
|
||||
} from '@grafana/data';
|
||||
import { ReduceTransformerOptions } from '@grafana/data/src/transformations/transformers/reduce';
|
||||
import omitBy from 'lodash/omitBy';
|
||||
import isNil from 'lodash/isNil';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import defaultTo from 'lodash/defaultTo';
|
||||
import { Options } from './types';
|
||||
|
||||
/**
|
||||
@@ -16,6 +28,195 @@ export const tableMigrationHandler = (panel: PanelModel<Options>): Partial<Optio
|
||||
return panel.options;
|
||||
};
|
||||
|
||||
const transformsMap = {
|
||||
timeseries_to_rows: 'seriesToRows',
|
||||
timeseries_to_columns: 'seriesToColumns',
|
||||
timeseries_aggregations: 'reduce',
|
||||
table: 'merge',
|
||||
};
|
||||
|
||||
const columnsMap = {
|
||||
avg: 'mean',
|
||||
min: 'min',
|
||||
max: 'max',
|
||||
total: 'sum',
|
||||
current: 'last',
|
||||
count: 'count',
|
||||
};
|
||||
|
||||
const colorModeMap = {
|
||||
cell: 'color-background',
|
||||
row: 'color-background',
|
||||
value: 'color-text',
|
||||
};
|
||||
|
||||
type Transformations = keyof typeof transformsMap;
|
||||
|
||||
type Transformation = {
|
||||
id: string;
|
||||
options: ReduceTransformerOptions;
|
||||
};
|
||||
|
||||
type Columns = keyof typeof columnsMap;
|
||||
|
||||
type Column = {
|
||||
value: Columns;
|
||||
text: string;
|
||||
};
|
||||
|
||||
type ColorModes = keyof typeof colorModeMap;
|
||||
|
||||
const generateThresholds = (thresholds: string[], colors: string[]) => {
|
||||
return [-Infinity, ...thresholds].map((threshold, idx) => ({
|
||||
color: colors[idx],
|
||||
value: isNumber(threshold) ? threshold : parseInt(threshold, 10),
|
||||
}));
|
||||
};
|
||||
|
||||
const migrateTransformations = (
|
||||
panel: PanelModel<Partial<Options>> | any,
|
||||
oldOpts: { columns: any; transform: Transformations }
|
||||
) => {
|
||||
const transformations: Transformation[] = panel.transformations ?? [];
|
||||
if (Object.keys(transformsMap).includes(oldOpts.transform)) {
|
||||
const opts: ReduceTransformerOptions = {
|
||||
reducers: [],
|
||||
};
|
||||
if (oldOpts.transform === 'timeseries_aggregations') {
|
||||
opts.includeTimeField = false;
|
||||
opts.reducers = oldOpts.columns.map((column: Column) => columnsMap[column.value]);
|
||||
}
|
||||
transformations.push({
|
||||
id: transformsMap[oldOpts.transform],
|
||||
options: opts,
|
||||
});
|
||||
}
|
||||
return transformations;
|
||||
};
|
||||
|
||||
type Style = {
|
||||
unit: string;
|
||||
type: string;
|
||||
alias: string;
|
||||
decimals: number;
|
||||
colors: string[];
|
||||
colorMode: ColorModes;
|
||||
pattern: string;
|
||||
thresholds: string[];
|
||||
align?: string;
|
||||
dateFormat: string;
|
||||
link: boolean;
|
||||
linkTargetBlank?: boolean;
|
||||
linkTooltip?: string;
|
||||
linkUrl?: string;
|
||||
};
|
||||
|
||||
const migrateTableStyleToOverride = (style: Style) => {
|
||||
const fieldMatcherId = /^\/.*\/$/.test(style.pattern) ? FieldMatcherID.byRegexp : FieldMatcherID.byName;
|
||||
const override: ConfigOverrideRule = {
|
||||
matcher: {
|
||||
id: fieldMatcherId,
|
||||
options: style.pattern,
|
||||
},
|
||||
properties: [],
|
||||
};
|
||||
|
||||
if (style.alias) {
|
||||
override.properties.push({
|
||||
id: 'displayName',
|
||||
value: style.alias,
|
||||
});
|
||||
}
|
||||
|
||||
if (style.unit) {
|
||||
override.properties.push({
|
||||
id: 'unit',
|
||||
value: style.unit,
|
||||
});
|
||||
}
|
||||
|
||||
if (style.decimals) {
|
||||
override.properties.push({
|
||||
id: 'decimals',
|
||||
value: style.decimals,
|
||||
});
|
||||
}
|
||||
|
||||
if (style.type === 'date') {
|
||||
override.properties.push({
|
||||
id: 'unit',
|
||||
value: `time: ${style.dateFormat}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (style.link) {
|
||||
override.properties.push({
|
||||
id: 'links',
|
||||
value: [
|
||||
{
|
||||
title: defaultTo(style.linkTooltip, ''),
|
||||
url: defaultTo(style.linkUrl, ''),
|
||||
targetBlank: defaultTo(style.linkTargetBlank, false),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (style.colorMode) {
|
||||
override.properties.push({
|
||||
id: 'custom.displayMode',
|
||||
value: colorModeMap[style.colorMode],
|
||||
});
|
||||
}
|
||||
|
||||
if (style.align) {
|
||||
override.properties.push({
|
||||
id: 'custom.align',
|
||||
value: style.align === 'auto' ? null : style.align,
|
||||
});
|
||||
}
|
||||
|
||||
if (style.thresholds?.length) {
|
||||
override.properties.push({
|
||||
id: 'thresholds',
|
||||
value: {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: generateThresholds(style.thresholds, style.colors),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return override;
|
||||
};
|
||||
|
||||
const migrateDefaults = (prevDefaults: Style) => {
|
||||
let defaults: FieldConfig = {
|
||||
custom: {},
|
||||
};
|
||||
if (prevDefaults) {
|
||||
defaults = omitBy(
|
||||
{
|
||||
unit: prevDefaults.unit,
|
||||
decimals: prevDefaults.decimals,
|
||||
displayName: prevDefaults.alias,
|
||||
custom: {
|
||||
align: prevDefaults.align === 'auto' ? null : prevDefaults.align,
|
||||
displayMode: colorModeMap[prevDefaults.colorMode],
|
||||
},
|
||||
},
|
||||
isNil
|
||||
);
|
||||
if (prevDefaults.thresholds.length) {
|
||||
const thresholds: ThresholdsConfig = {
|
||||
mode: ThresholdsMode.Absolute,
|
||||
steps: generateThresholds(prevDefaults.thresholds, prevDefaults.colors),
|
||||
};
|
||||
defaults.thresholds = thresholds;
|
||||
}
|
||||
}
|
||||
return defaults;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is called when the panel changes from another panel
|
||||
*/
|
||||
@@ -26,7 +227,17 @@ export const tablePanelChangedHandler = (
|
||||
) => {
|
||||
// Changing from angular table panel
|
||||
if (prevPluginId === 'table-old' && prevOptions.angular) {
|
||||
// Todo write migration logic
|
||||
const oldOpts = prevOptions.angular;
|
||||
const transformations = migrateTransformations(panel, oldOpts);
|
||||
const prevDefaults = oldOpts.styles.find((style: any) => style.pattern === '/.*/');
|
||||
const defaults = migrateDefaults(prevDefaults);
|
||||
const overrides = oldOpts.styles.filter((style: any) => style.pattern !== '/.*/').map(migrateTableStyleToOverride);
|
||||
|
||||
panel.transformations = transformations;
|
||||
panel.fieldConfig = {
|
||||
defaults,
|
||||
overrides,
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
Reference in New Issue
Block a user