Dashboard Schema V2: ResponseTransformers: Transform layout, panels, annotations and variables when ensuring v1 (#99050)

* wip

* Fix annotationV1

* variables

* tests

* Fix query variable

* Don't need extra chaining arg in maxPerRow

* Logic for libary panels; tests for variables and library panels

* lint
pull/99391/head
Haris Rozajac 5 months ago committed by GitHub
parent daced46d27
commit a0e7569e4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      .betterer.results
  2. 2
      public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts
  3. 363
      public/app/features/dashboard/api/ResponseTransformers.test.ts
  4. 406
      public/app/features/dashboard/api/ResponseTransformers.ts

@ -3775,7 +3775,8 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
],
"public/app/features/dashboard/api/v0.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]

@ -175,8 +175,6 @@ export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptio
} else if (sceneUtils.isAdHocVariable(variable)) {
variables.push({
...commonProperties,
name: variable.state.name,
type: 'adhoc',
datasource: variable.state.datasource,
allowCustomValue: variable.state.allowCustomValue,
// @ts-expect-error

@ -1,5 +1,6 @@
import { DataQuery, VariableModel, VariableRefresh } from '@grafana/schema';
import { DashboardV2Spec, VariableKind } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { AnnotationQuery, DataQuery, VariableModel, VariableRefresh, Panel } from '@grafana/schema';
import { DashboardV2Spec, PanelKind, VariableKind } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { handyTestingSchema } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/examples';
import {
AnnoKeyCreatedBy,
AnnoKeyDashboardGnetId,
@ -16,7 +17,12 @@ import {
} from 'app/features/dashboard-scene/serialization/transformToV2TypesUtils';
import { DashboardDataDTO, DashboardDTO } from 'app/types';
import { getDefaultDatasource, getPanelQueries, ResponseTransformers } from './ResponseTransformers';
import {
getDefaultDatasource,
getPanelQueries,
ResponseTransformers,
transformMappingsToV1,
} from './ResponseTransformers';
import { DashboardWithAccessInfo } from './types';
jest.mock('@grafana/runtime', () => ({
@ -546,15 +552,10 @@ describe('ResponseTransformers', () => {
tooltip: 'Link 1 Tooltip',
},
],
annotations: [],
variables: [],
elements: {},
layout: {
kind: 'GridLayout',
spec: {
items: [],
},
},
annotations: handyTestingSchema.annotations,
variables: handyTestingSchema.variables,
elements: handyTestingSchema.elements,
layout: handyTestingSchema.layout,
},
access: {
url: '/d/dashboard-slug',
@ -612,153 +613,237 @@ describe('ResponseTransformers', () => {
expect(dashboard.fiscalYearStartMonth).toBe(dashboardV2.spec.timeSettings.fiscalYearStartMonth);
expect(dashboard.weekStart).toBe(dashboardV2.spec.timeSettings.weekStart);
expect(dashboard.links).toEqual(dashboardV2.spec.links);
expect(dashboard.annotations).toEqual({ list: [] });
// variables
validateVariablesV1ToV2(dashboardV2.spec.variables[0], dashboard.templating?.list?.[0]);
validateVariablesV1ToV2(dashboardV2.spec.variables[1], dashboard.templating?.list?.[1]);
validateVariablesV1ToV2(dashboardV2.spec.variables[2], dashboard.templating?.list?.[2]);
validateVariablesV1ToV2(dashboardV2.spec.variables[3], dashboard.templating?.list?.[3]);
validateVariablesV1ToV2(dashboardV2.spec.variables[4], dashboard.templating?.list?.[4]);
validateVariablesV1ToV2(dashboardV2.spec.variables[5], dashboard.templating?.list?.[5]);
validateVariablesV1ToV2(dashboardV2.spec.variables[6], dashboard.templating?.list?.[6]);
validateVariablesV1ToV2(dashboardV2.spec.variables[7], dashboard.templating?.list?.[7]);
// annotations
validateAnnotation(dashboard.annotations!.list![0], dashboardV2.spec.annotations[0]);
validateAnnotation(dashboard.annotations!.list![1], dashboardV2.spec.annotations[1]);
validateAnnotation(dashboard.annotations!.list![2], dashboardV2.spec.annotations[2]);
validateAnnotation(dashboard.annotations!.list![3], dashboardV2.spec.annotations[3]);
// panel
const panelKey = 'panel-1';
const panelV2 = dashboardV2.spec.elements[panelKey] as PanelKind;
expect(panelV2.kind).toBe('Panel');
validatePanel(dashboard.panels![0], panelV2, dashboardV2.spec.layout, panelKey);
// library panel
expect(dashboard.panels![1].libraryPanel).toEqual(dashboardV2.spec.elements['library-panel-1'].spec);
});
});
describe('getPanelQueries', () => {
it('respects targets data source', () => {
const panelDs = {
type: 'theoretical-ds',
uid: 'theoretical-uid',
};
const targets: DataQuery[] = [
{
refId: 'A',
datasource: {
type: 'theoretical-ds',
uid: 'theoretical-uid',
describe('getPanelQueries', () => {
it('respects targets data source', () => {
const panelDs = {
type: 'theoretical-ds',
uid: 'theoretical-uid',
};
const targets: DataQuery[] = [
{
refId: 'A',
datasource: {
type: 'theoretical-ds',
uid: 'theoretical-uid',
},
},
},
{
refId: 'B',
datasource: {
type: 'theoretical-ds',
uid: 'theoretical-uid',
{
refId: 'B',
datasource: {
type: 'theoretical-ds',
uid: 'theoretical-uid',
},
},
},
];
];
const result = getPanelQueries(targets, panelDs);
const result = getPanelQueries(targets, panelDs);
expect(result).toHaveLength(targets.length);
expect(result[0].spec.refId).toBe('A');
expect(result[1].spec.refId).toBe('B');
expect(result).toHaveLength(targets.length);
expect(result[0].spec.refId).toBe('A');
expect(result[1].spec.refId).toBe('B');
result.forEach((query) => {
expect(query.kind).toBe('PanelQuery');
expect(query.spec.datasource).toEqual({
type: 'theoretical-ds',
uid: 'theoretical-uid',
result.forEach((query) => {
expect(query.kind).toBe('PanelQuery');
expect(query.spec.datasource).toEqual({
type: 'theoretical-ds',
uid: 'theoretical-uid',
});
expect(query.spec.query.kind).toBe('theoretical-ds');
});
expect(query.spec.query.kind).toBe('theoretical-ds');
});
});
it('respects panel data source', () => {
const panelDs = {
type: 'theoretical-ds',
uid: 'theoretical-uid',
};
const targets: DataQuery[] = [
{
refId: 'A',
},
{
refId: 'B',
},
];
it('respects panel data source', () => {
const panelDs = {
type: 'theoretical-ds',
uid: 'theoretical-uid',
};
const targets: DataQuery[] = [
{
refId: 'A',
},
{
refId: 'B',
},
];
const result = getPanelQueries(targets, panelDs);
const result = getPanelQueries(targets, panelDs);
expect(result).toHaveLength(targets.length);
expect(result[0].spec.refId).toBe('A');
expect(result[1].spec.refId).toBe('B');
expect(result).toHaveLength(targets.length);
expect(result[0].spec.refId).toBe('A');
expect(result[1].spec.refId).toBe('B');
result.forEach((query) => {
expect(query.kind).toBe('PanelQuery');
expect(query.spec.datasource).toEqual({
type: 'theoretical-ds',
uid: 'theoretical-uid',
result.forEach((query) => {
expect(query.kind).toBe('PanelQuery');
expect(query.spec.datasource).toEqual({
type: 'theoretical-ds',
uid: 'theoretical-uid',
});
expect(query.spec.query.kind).toBe('theoretical-ds');
});
expect(query.spec.query.kind).toBe('theoretical-ds');
});
});
});
});
function validateVariablesV1ToV2(v2: VariableKind, v1: VariableModel | undefined) {
if (!v1) {
return expect(v1).toBeDefined();
}
const v1Common = {
name: v1.name,
label: v1.label,
description: v1.description,
hide: transformVariableHideToEnum(v1.hide),
skipUrlSync: v1.skipUrlSync,
};
const v2Common = {
name: v2.spec.name,
label: v2.spec.label,
description: v2.spec.description,
hide: v2.spec.hide,
skipUrlSync: v2.spec.skipUrlSync,
};
expect(v2Common).toEqual(v1Common);
if (v2.kind === 'QueryVariable') {
expect(v2.spec.datasource).toEqual(v1.datasource);
expect(v2.spec.query).toEqual({
kind: v1.datasource?.type,
spec: {
...(typeof v1.query === 'object' ? v1.query : {}),
},
});
}
if (v2.kind === 'DatasourceVariable') {
expect(v2.spec.pluginId).toBe(v1.query);
expect(v2.spec.refresh).toBe(transformVariableRefreshToEnum(v1.refresh));
}
if (v2.kind === 'CustomVariable') {
expect(v2.spec.query).toBe(v1.query);
expect(v2.spec.options).toEqual(v1.options);
}
if (v2.kind === 'AdhocVariable') {
expect(v2.spec.datasource).toEqual(v1.datasource);
expect(v2.spec.filters).toEqual([]);
// @ts-expect-error
expect(v2.spec.baseFilters).toEqual(v1.baseFilters);
function validateAnnotation(v1: AnnotationQuery, v2: DashboardV2Spec['annotations'][0]) {
const { spec: v2Spec } = v2;
expect(v1.name).toBe(v2Spec.name);
expect(v1.datasource).toBe(v2Spec.datasource);
expect(v1.enable).toBe(v2Spec.enable);
expect(v1.hide).toBe(v2Spec.hide);
expect(v1.iconColor).toBe(v2Spec.iconColor);
expect(v1.builtIn).toBe(v2Spec.builtIn ? 1 : 0);
expect(v1.target).toBe(v2Spec.query?.spec);
expect(v1.filter).toEqual(v2Spec.filter);
}
if (v2.kind === 'ConstantVariable') {
expect(v2.spec.query).toBe(v1.query);
function validatePanel(v1: Panel, v2: PanelKind, layoutV2: DashboardV2Spec['layout'], panelKey: string) {
const { spec: v2Spec } = v2;
expect(v1.id).toBe(v2Spec.id);
expect(v1.id).toBe(v2Spec.id);
expect(v1.type).toBe(v2Spec.vizConfig.kind);
expect(v1.title).toBe(v2Spec.title);
expect(v1.description).toBe(v2Spec.description);
expect(v1.fieldConfig).toEqual(transformMappingsToV1(v2Spec.vizConfig.spec.fieldConfig));
expect(v1.options).toBe(v2Spec.vizConfig.spec.options);
expect(v1.pluginVersion).toBe(v2Spec.vizConfig.spec.pluginVersion);
expect(v1.links).toEqual(v2Spec.links);
expect(v1.targets).toEqual(
v2Spec.data.spec.queries.map((q) => {
return {
refId: q.spec.refId,
hide: q.spec.hidden,
datasource: q.spec.datasource,
...q.spec.query.spec,
};
})
);
expect(v1.transformations).toEqual(v2Spec.data.spec.transformations.map((t) => t.spec));
const layoutElement = layoutV2.spec.items.find(
(item) => item.kind === 'GridLayoutItem' && item.spec.element.name === panelKey
);
expect(v1.gridPos?.x).toEqual(layoutElement?.spec.x);
expect(v1.gridPos?.y).toEqual(layoutElement?.spec.y);
expect(v1.gridPos?.w).toEqual(layoutElement?.spec.width);
expect(v1.gridPos?.h).toEqual(layoutElement?.spec.height);
expect(v1.repeat).toEqual(layoutElement?.spec.repeat?.value);
expect(v1.repeatDirection).toEqual(layoutElement?.spec.repeat?.direction);
expect(v1.maxPerRow).toEqual(layoutElement?.spec.repeat?.maxPerRow);
expect(v1.cacheTimeout).toBe(v2Spec.data.spec.queryOptions.cacheTimeout);
expect(v1.maxDataPoints).toBe(v2Spec.data.spec.queryOptions.maxDataPoints);
expect(v1.interval).toBe(v2Spec.data.spec.queryOptions.interval);
expect(v1.hideTimeOverride).toBe(v2Spec.data.spec.queryOptions.hideTimeOverride);
expect(v1.queryCachingTTL).toBe(v2Spec.data.spec.queryOptions.queryCachingTTL);
expect(v1.timeFrom).toBe(v2Spec.data.spec.queryOptions.timeFrom);
expect(v1.timeShift).toBe(v2Spec.data.spec.queryOptions.timeShift);
expect(v1.transparent).toBe(v2Spec.transparent);
}
if (v2.kind === 'IntervalVariable') {
expect(v2.spec.query).toBe(v1.query);
expect(v2.spec.options).toEqual(v1.options);
expect(v2.spec.current).toEqual(v1.current);
// @ts-expect-error
expect(v2.spec.auto).toBe(v1.auto);
// @ts-expect-error
expect(v2.spec.auto_min).toBe(v1.auto_min);
// @ts-expect-error
expect(v2.spec.auto_count).toBe(v1.auto_count);
}
if (v2.kind === 'TextVariable') {
expect(v2.spec.query).toBe(v1.query);
expect(v2.spec.current).toEqual(v1.current);
}
if (v2.kind === 'GroupByVariable') {
expect(v2.spec.datasource).toEqual(v1.datasource);
expect(v2.spec.options).toEqual(v1.options);
function validateVariablesV1ToV2(v2: VariableKind, v1: VariableModel | undefined) {
if (!v1) {
return expect(v1).toBeDefined();
}
const v1Common = {
name: v1.name,
label: v1.label,
description: v1.description,
hide: transformVariableHideToEnum(v1.hide),
skipUrlSync: v1.skipUrlSync,
};
const v2Common = {
name: v2.spec.name,
label: v2.spec.label,
description: v2.spec.description,
hide: v2.spec.hide,
skipUrlSync: v2.spec.skipUrlSync,
};
expect(v2Common).toEqual(v1Common);
if (v2.kind === 'QueryVariable') {
expect(v2.spec.datasource).toEqual(v1.datasource);
if (typeof v1.query === 'string') {
expect(v2.spec.query).toEqual(v1.query);
} else {
expect(v2.spec.query).toEqual({
kind: v1.datasource?.type,
spec: {
...(typeof v1.query === 'object' ? v1.query : {}),
},
});
}
}
if (v2.kind === 'DatasourceVariable') {
expect(v2.spec.pluginId).toBe(v1.query);
expect(v2.spec.refresh).toBe(transformVariableRefreshToEnum(v1.refresh));
}
if (v2.kind === 'CustomVariable') {
expect(v2.spec.query).toBe(v1.query);
expect(v2.spec.options).toEqual(v1.options);
}
if (v2.kind === 'AdhocVariable') {
expect(v2.spec.datasource).toEqual(v1.datasource);
// @ts-expect-error
expect(v2.spec.filters).toEqual(v1.filters);
// @ts-expect-error
expect(v2.spec.baseFilters).toEqual(v1.baseFilters);
}
if (v2.kind === 'ConstantVariable') {
expect(v2.spec.query).toBe(v1.query);
}
if (v2.kind === 'IntervalVariable') {
expect(v2.spec.query).toBe(v1.query);
expect(v2.spec.options).toEqual(v1.options);
expect(v2.spec.current).toEqual(v1.current);
// @ts-expect-error
expect(v2.spec.auto).toBe(v1.auto);
// @ts-expect-error
expect(v2.spec.auto_min).toBe(v1.auto_min);
// @ts-expect-error
expect(v2.spec.auto_count).toBe(v1.auto_count);
}
if (v2.kind === 'TextVariable') {
expect(v2.spec.query).toBe(v1.query);
expect(v2.spec.current).toEqual(v1.current);
}
if (v2.kind === 'GroupByVariable') {
expect(v2.spec.datasource).toEqual(v1.datasource);
expect(v2.spec.options).toEqual(v1.options);
}
}
}
});

@ -1,6 +1,18 @@
import { TypedVariableModel } from '@grafana/data';
import { config } from '@grafana/runtime';
import { AnnotationQuery, DataQuery, DataSourceRef, Panel } from '@grafana/schema';
import {
AnnotationQuery,
DataQuery,
DataSourceRef,
Panel,
VariableModel,
VariableType,
FieldConfigSource as FieldConfigSourceV1,
FieldColorModeId as FieldColorModeIdV1,
ThresholdsMode as ThresholdsModeV1,
MappingType as MappingTypeV1,
SpecialValueMatch as SpecialValueMatchV1,
} from '@grafana/schema';
import {
AnnotationQueryKind,
DashboardV2Spec,
@ -12,6 +24,10 @@ import {
PanelQueryKind,
QueryVariableKind,
TransformationKind,
FieldColorModeId,
FieldConfigSource,
ThresholdsMode,
SpecialValueMatch,
AdhocVariableKind,
CustomVariableKind,
ConstantVariableKind,
@ -19,7 +35,7 @@ import {
TextVariableKind,
GroupByVariableKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { DataTransformerConfig } from '@grafana/schema/src/raw/dashboard/x/dashboard_types.gen';
import { DashboardLink, DataTransformerConfig } from '@grafana/schema/src/raw/dashboard/x/dashboard_types.gen';
import {
AnnoKeyCreatedBy,
AnnoKeyDashboardGnetId,
@ -31,8 +47,14 @@ import {
AnnoKeyUpdatedBy,
AnnoKeyUpdatedTimestamp,
} from 'app/features/apiserver/types';
import { TypedVariableModelV2 } from 'app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene';
import { getDefaultDataSourceRef } from 'app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2';
import { transformCursorSyncV2ToV1 } from 'app/features/dashboard-scene/serialization/transformToV1TypesUtils';
import {
transformCursorSyncV2ToV1,
transformSortVariableToEnumV1,
transformVariableHideToEnumV1,
transformVariableRefreshToEnumV1,
} from 'app/features/dashboard-scene/serialization/transformToV1TypesUtils';
import {
transformCursorSynctoEnum,
transformDataTopic,
@ -179,6 +201,9 @@ export function ensureV1Response(
};
} else {
// if dashboard is on v2 schema convert to v1 schema
const annotations = getAnnotationsV1(spec.annotations);
const variables = getVariablesV1(spec.variables);
const panels = getPanelsV1(spec.elements, spec.layout);
return {
meta: {
created: dashboard.metadata.creationTimestamp,
@ -224,9 +249,9 @@ export function ensureV1Response(
weekStart: spec.timeSettings.weekStart,
version: parseInt(dashboard.metadata.resourceVersion, 10),
links: spec.links,
annotations: { list: [] }, // TODO
panels: [], // TODO
templating: { list: [] }, // TODO
annotations: { list: annotations },
panels,
templating: { list: variables },
},
};
}
@ -593,3 +618,372 @@ function getAnnotations(annotations: AnnotationQuery[]): DashboardV2Spec['annota
return aq;
});
}
function getVariablesV1(vars: DashboardV2Spec['variables']): VariableModel[] {
const variables: VariableModel[] = [];
for (const v of vars) {
const commonProperties = {
name: v.spec.name,
label: v.spec.label,
...(v.spec.description && { description: v.spec.description }),
skipUrlSync: v.spec.skipUrlSync,
hide: transformVariableHideToEnumV1(v.spec.hide),
type: transformToV1VariableTypes(v),
};
switch (v.kind) {
case 'QueryVariable':
const qv: VariableModel = {
...commonProperties,
current: v.spec.current,
options: v.spec.options,
query: typeof v.spec.query === 'string' ? v.spec.query : v.spec.query.spec,
datasource: v.spec.datasource,
sort: transformSortVariableToEnumV1(v.spec.sort),
refresh: transformVariableRefreshToEnumV1(v.spec.refresh),
regex: v.spec.regex,
allValue: v.spec.allValue,
includeAll: v.spec.includeAll,
multi: v.spec.multi,
// @ts-expect-error - definition is not part of v1 VariableModel
definition: v.spec.definition,
};
variables.push(qv);
break;
case 'DatasourceVariable':
const dv: VariableModel = {
...commonProperties,
current: v.spec.current,
options: [],
regex: v.spec.regex,
refresh: transformVariableRefreshToEnumV1(v.spec.refresh),
query: v.spec.pluginId,
multi: v.spec.multi,
allValue: v.spec.allValue,
includeAll: v.spec.includeAll,
};
variables.push(dv);
break;
case 'CustomVariable':
const cv: VariableModel = {
...commonProperties,
current: {
text: v.spec.current.value,
value: v.spec.current.value,
},
options: v.spec.options,
query: v.spec.query,
multi: v.spec.multi,
allValue: v.spec.allValue,
includeAll: v.spec.includeAll,
};
variables.push(cv);
break;
case 'ConstantVariable':
const constant: VariableModel = {
...commonProperties,
current: {
text: v.spec.current.value,
value: v.spec.current.value,
},
hide: transformVariableHideToEnumV1(v.spec.hide),
// @ts-expect-error
query: v.spec.current.value,
};
variables.push(constant);
break;
case 'IntervalVariable':
const iv: VariableModel = {
...commonProperties,
current: {
text: v.spec.current.value,
value: v.spec.current.value,
},
hide: transformVariableHideToEnumV1(v.spec.hide),
query: v.spec.query,
refresh: transformVariableRefreshToEnumV1(v.spec.refresh),
options: v.spec.options,
// @ts-expect-error
auto: v.spec.auto,
auto_min: v.spec.auto_min,
auto_count: v.spec.auto_count,
};
variables.push(iv);
break;
case 'TextVariable':
const current = {
text: v.spec.current.value,
value: v.spec.current.value,
};
const tv: VariableModel = {
...commonProperties,
current: {
text: v.spec.current.value,
value: v.spec.current.value,
},
options: [{ ...current, selected: true }],
query: v.spec.query,
};
variables.push(tv);
break;
case 'GroupByVariable':
const gv: VariableModel = {
...commonProperties,
datasource: v.spec.datasource,
current: v.spec.current,
options: v.spec.options,
};
variables.push(gv);
break;
case 'AdhocVariable':
const av: VariableModel = {
...commonProperties,
datasource: v.spec.datasource,
// @ts-expect-error
baseFilters: v.spec.baseFilters,
filters: v.spec.filters,
defaultKeys: v.spec.defaultKeys,
};
variables.push(av);
break;
default:
// do not throw error, just log it
console.error(`Variable transformation not implemented: ${v}`);
}
}
return variables;
}
function getAnnotationsV1(annotations: DashboardV2Spec['annotations']): AnnotationQuery[] {
// @ts-expect-error - target v2 query is not compatible with v1 target
return annotations.map((a) => {
return {
name: a.spec.name,
datasource: a.spec.datasource,
enable: a.spec.enable,
hide: a.spec.hide,
iconColor: a.spec.iconColor,
builtIn: a.spec.builtIn ? 1 : 0,
target: a.spec.query?.spec,
filter: a.spec.filter,
};
});
}
interface LibraryPanelDTO extends Pick<Panel, 'libraryPanel' | 'id' | 'title' | 'gridPos'> {}
function getPanelsV1(
panels: DashboardV2Spec['elements'],
layout: DashboardV2Spec['layout']
): Array<Panel | LibraryPanelDTO> {
return Object.entries(panels).map(([key, p]) => {
const layoutElement = layout.spec.items.find(
(item) => item.kind === 'GridLayoutItem' && item.spec.element.name === key
);
const { x, y, width, height, repeat } = layoutElement?.spec || { x: 0, y: 0, width: 0, height: 0 };
const gridPos = { x, y, w: width, h: height };
if (p.kind === 'Panel') {
const panel = p.spec;
return {
id: panel.id,
type: panel.vizConfig.kind,
title: panel.title,
description: panel.description,
fieldConfig: transformMappingsToV1(panel.vizConfig.spec.fieldConfig),
options: panel.vizConfig.spec.options,
pluginVersion: panel.vizConfig.spec.pluginVersion,
links:
// @ts-expect-error - Panel link is wrongly typed as DashboardLink
panel.links?.map<DashboardLink>((l) => ({
title: l.title,
url: l.url,
...(l.targetBlank && { targetBlank: l.targetBlank }),
})) || [],
targets: panel.data.spec.queries.map((q) => {
return {
refId: q.spec.refId,
hide: q.spec.hidden,
datasource: q.spec.datasource,
...q.spec.query.spec,
};
}),
transformations: panel.data.spec.transformations.map((t) => t.spec),
gridPos,
cacheTimeout: panel.data.spec.queryOptions.cacheTimeout,
maxDataPoints: panel.data.spec.queryOptions.maxDataPoints,
interval: panel.data.spec.queryOptions.interval,
hideTimeOverride: panel.data.spec.queryOptions.hideTimeOverride,
queryCachingTTL: panel.data.spec.queryOptions.queryCachingTTL,
timeFrom: panel.data.spec.queryOptions.timeFrom,
timeShift: panel.data.spec.queryOptions.timeShift,
transparent: panel.transparent,
...(repeat?.value && { repeat: repeat.value }),
...(repeat?.direction && { repeatDirection: repeat.direction }),
...(repeat?.maxPerRow && { maxPerRow: repeat.maxPerRow }),
};
} else if (p.kind === 'LibraryPanel') {
const panel = p.spec;
return {
id: 0, //TODO: LibraryPanelSpec.id will be available after https://github.com/grafana/grafana/pull/99281/ is merged
title: panel.name,
gridPos,
libraryPanel: {
uid: panel.uid,
name: panel.name,
},
};
} else {
throw new Error(`Unknown element kind: ${p}`);
}
});
}
export function transformMappingsToV1(fieldConfig: FieldConfigSource): FieldConfigSourceV1 {
const getThresholdsMode = (mode: ThresholdsMode): ThresholdsModeV1 => {
switch (mode) {
case 'absolute':
return ThresholdsModeV1.Absolute;
case 'percentage':
return ThresholdsModeV1.Percentage;
default:
return ThresholdsModeV1.Absolute;
}
};
const transformedDefaults: any = {
...fieldConfig.defaults,
};
if (fieldConfig.defaults.mappings) {
transformedDefaults.mappings = fieldConfig.defaults.mappings.map((mapping) => {
switch (mapping.type) {
case 'value':
return {
...mapping,
type: MappingTypeV1.ValueToText,
};
case 'range':
return {
...mapping,
type: MappingTypeV1.RangeToText,
};
case 'regex':
return {
...mapping,
type: MappingTypeV1.RegexToText,
};
case 'special':
return {
...mapping,
options: {
...mapping.options,
match: transformSpecialValueMatchToV1(mapping.options.match),
},
type: MappingTypeV1.SpecialValue,
};
default:
return mapping;
}
});
}
if (fieldConfig.defaults.thresholds) {
transformedDefaults.thresholds = {
...fieldConfig.defaults.thresholds,
mode: getThresholdsMode(fieldConfig.defaults.thresholds.mode),
};
}
if (fieldConfig.defaults.color?.mode) {
transformedDefaults.color = {
...fieldConfig.defaults.color,
mode: colorIdToEnumv1(fieldConfig.defaults.color.mode),
};
}
return {
...fieldConfig,
defaults: transformedDefaults,
};
}
function colorIdToEnumv1(colorId: FieldColorModeId): FieldColorModeIdV1 {
switch (colorId) {
case 'thresholds':
return FieldColorModeIdV1.Thresholds;
case 'palette-classic':
return FieldColorModeIdV1.PaletteClassic;
case 'palette-classic-by-name':
return FieldColorModeIdV1.PaletteClassicByName;
case 'continuous-GrYlRd':
return FieldColorModeIdV1.ContinuousGrYlRd;
case 'continuous-RdYlGr':
return FieldColorModeIdV1.ContinuousRdYlGr;
case 'continuous-BlYlRd':
return FieldColorModeIdV1.ContinuousBlYlRd;
case 'continuous-YlRd':
return FieldColorModeIdV1.ContinuousYlRd;
case 'continuous-BlPu':
return FieldColorModeIdV1.ContinuousBlPu;
case 'continuous-YlBl':
return FieldColorModeIdV1.ContinuousYlBl;
case 'continuous-blues':
return FieldColorModeIdV1.ContinuousBlues;
case 'continuous-reds':
return FieldColorModeIdV1.ContinuousReds;
case 'continuous-greens':
return FieldColorModeIdV1.ContinuousGreens;
case 'continuous-purples':
return FieldColorModeIdV1.ContinuousPurples;
case 'fixed':
return FieldColorModeIdV1.Fixed;
case 'shades':
return FieldColorModeIdV1.Shades;
default:
return FieldColorModeIdV1.Thresholds;
}
}
function transformSpecialValueMatchToV1(match: SpecialValueMatch): SpecialValueMatchV1 {
switch (match) {
case 'true':
return SpecialValueMatchV1.True;
case 'false':
return SpecialValueMatchV1.False;
case 'null':
return SpecialValueMatchV1.Null;
case 'nan':
return SpecialValueMatchV1.NaN;
case 'null+nan':
return SpecialValueMatchV1.NullAndNan;
case 'empty':
return SpecialValueMatchV1.Empty;
default:
throw new Error(`Unknown match type: ${match}`);
}
}
function transformToV1VariableTypes(variable: TypedVariableModelV2): VariableType {
switch (variable.kind) {
case 'QueryVariable':
return 'query';
case 'DatasourceVariable':
return 'datasource';
case 'CustomVariable':
return 'custom';
case 'ConstantVariable':
return 'constant';
case 'IntervalVariable':
return 'interval';
case 'TextVariable':
return 'textbox';
case 'GroupByVariable':
return 'groupby';
case 'AdhocVariable':
return 'adhoc';
default:
throw new Error(`Unknown variable type: ${variable}`);
}
}

Loading…
Cancel
Save