Timeseries: Time regions migration (#66998)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
Adela Almasan
2023-04-27 05:28:58 +03:00
committed by GitHub
co-authored by Ryan McKinley
parent 5c4ecf7a86
commit 2beee35567
9 changed files with 234 additions and 15 deletions
@@ -9,7 +9,7 @@
Migrate
</button>
</p>
<p>Some features like colored time regions and negative transforms are not supported in the new panel yet.</p>
<p>Some features are not supported in the new panel yet.</p>
</div>
<gf-form-switch
@@ -521,6 +521,37 @@ exports[`Graph Migrations stepped line 1`] = `
}
`;
exports[`Graph Migrations time regions should migrate 1`] = `
{
"alert": undefined,
"datasource": {
"type": "datasource",
"uid": "gdev-testdata",
},
"fieldConfig": {
"defaults": {
"custom": {
"drawStyle": "points",
"spanNulls": false,
},
},
"overrides": [],
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true,
},
"tooltip": {
"mode": "single",
"sort": "none",
},
},
}
`;
exports[`Graph Migrations transforms should preserve "constant" transform 1`] = `
{
"defaults": {
@@ -2,17 +2,32 @@ import { cloneDeep } from 'lodash';
import { PanelModel, FieldConfigSource, FieldMatcherID, ReducerID } from '@grafana/data';
import { TooltipDisplayMode, SortOrder } from '@grafana/schema';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { DashboardModel, PanelModel as PanelModelState } from 'app/features/dashboard/state';
import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures';
import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types';
import { graphPanelChangedHandler } from './migrations';
describe('Graph Migrations', () => {
let prevFieldConfig: FieldConfigSource;
let dashboard: DashboardModel;
beforeEach(() => {
prevFieldConfig = {
defaults: {},
overrides: [],
};
dashboard = createDashboardModelFixture({
id: 74,
version: 7,
annotations: {},
links: [],
panels: [],
});
getDashboardSrv().setCurrent(dashboard);
});
it('simple bars', () => {
@@ -82,6 +97,36 @@ describe('Graph Migrations', () => {
expect(panel.fieldConfig.overrides[1].matcher.id).toBe(FieldMatcherID.byRegexp);
});
describe('time regions', () => {
test('should migrate', () => {
const old = {
angular: {
timeRegions: [
{
colorMode: 'red',
fill: true,
fillColor: 'rgba(234, 112, 112, 0.12)',
fromDayOfWeek: 1,
line: true,
lineColor: 'rgba(237, 46, 24, 0.60)',
op: 'time',
},
],
},
};
const panel = { datasource: { type: 'datasource', uid: 'gdev-testdata' } } as PanelModel;
dashboard.panels.push(new PanelModelState(panel));
panel.options = graphPanelChangedHandler(panel, 'graph', old, prevFieldConfig);
expect(dashboard.panels).toHaveLength(1);
expect(dashboard.annotations.list).toHaveLength(2); // built-in + time region
expect(
dashboard.annotations.list.filter((annotation) => annotation.target?.queryType === GrafanaQueryType.TimeRegions)
).toHaveLength(1);
expect(panel).toMatchSnapshot();
});
});
describe('legend', () => {
test('without values', () => {
const old = {
@@ -31,12 +31,19 @@ import {
StackingMode,
SortOrder,
GraphTransform,
AnnotationQuery,
ComparisonOperation,
} from '@grafana/schema';
import { TimeRegionConfig } from 'app/core/utils/timeRegions';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { GrafanaQuery, GrafanaQueryType } from 'app/plugins/datasource/grafana/types';
import { defaultGraphConfig } from './config';
import { PanelOptions } from './panelcfg.gen';
let dashboardRefreshDebouncer: ReturnType<typeof setTimeout> | null = null;
/**
* This is called when the panel changes from another panel
*/
@@ -48,10 +55,25 @@ export const graphPanelChangedHandler: PanelTypeChangedHandler = (
) => {
// Changing from angular/flot panel to react/uPlot
if (prevPluginId === 'graph' && prevOptions.angular) {
const { fieldConfig, options } = graphToTimeseriesOptions({
const { fieldConfig, options, annotations } = graphToTimeseriesOptions({
...prevOptions.angular,
fieldConfig: prevFieldConfig,
panel: panel,
});
const dashboard = getDashboardSrv().getCurrent();
if (dashboard && annotations?.length > 0) {
dashboard.annotations.list = [...dashboard.annotations.list, ...annotations];
// Trigger a full dashboard refresh when annotations change
if (dashboardRefreshDebouncer == null) {
dashboardRefreshDebouncer = setTimeout(() => {
dashboardRefreshDebouncer = null;
getTimeSrv().refreshTimeModel();
});
}
}
panel.fieldConfig = fieldConfig; // Mutates the incoming panel
panel.alert = prevOptions.angular.alert;
return options;
@@ -63,7 +85,13 @@ export const graphPanelChangedHandler: PanelTypeChangedHandler = (
return {};
};
export function graphToTimeseriesOptions(angular: any): { fieldConfig: FieldConfigSource; options: PanelOptions } {
export function graphToTimeseriesOptions(angular: any): {
fieldConfig: FieldConfigSource;
options: PanelOptions;
annotations: AnnotationQuery[];
} {
let annotations: AnnotationQuery[] = [];
const overrides: ConfigOverrideRule[] = angular.fieldConfig?.overrides ?? [];
const yaxes = angular.yaxes ?? [];
let y1 = getFieldConfigFromOldAxis(yaxes[0]);
@@ -362,6 +390,55 @@ export function graphToTimeseriesOptions(angular: any): { fieldConfig: FieldConf
}
}
// timeRegions migration
if (angular.timeRegions?.length) {
let regions: any[] = angular.timeRegions.map((old: GraphTimeRegionConfig, idx: number) => ({
name: `T${idx + 1}`,
color: old.colorMode !== 'custom' ? old.colorMode : old.fillColor,
line: old.line,
fill: old.fill,
fromDayOfWeek: old.fromDayOfWeek,
toDayOfWeek: old.toDayOfWeek,
from: old.from,
to: old.to,
}));
regions.forEach((region: GraphTimeRegionConfig, idx: number) => {
const anno: AnnotationQuery<GrafanaQuery> = {
datasource: {
type: 'datasource',
uid: 'grafana',
},
enable: true,
hide: true, // don't show the toggle at the top of the dashboard
filter: {
exclude: false,
ids: [angular.panel.id],
},
iconColor: region.fillColor ?? (region as any).color,
name: `T${idx + 1}`,
target: {
queryType: GrafanaQueryType.TimeRegions,
refId: 'Anno',
timeRegion: {
fromDayOfWeek: region.fromDayOfWeek,
toDayOfWeek: region.toDayOfWeek,
from: region.from,
to: region.to,
timezone: 'utc', // graph panel was always UTC
},
},
};
if (region.fill) {
annotations.push(anno);
} else if (region.line) {
anno.iconColor = region.lineColor ?? 'white';
annotations.push(anno);
}
});
}
const tooltipConfig = angular.tooltip;
if (tooltipConfig) {
if (tooltipConfig.shared !== undefined) {
@@ -479,9 +556,18 @@ export function graphToTimeseriesOptions(angular: any): { fieldConfig: FieldConf
overrides,
},
options,
annotations,
};
}
interface GraphTimeRegionConfig extends TimeRegionConfig {
colorMode: string;
fill: boolean;
fillColor: string;
line: boolean;
lineColor: string;
}
function getThresholdColor(threshold: AngularThreshold): string {
if (threshold.colorMode === 'critical') {
return 'red';