mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard Schema V2: Remove type assertions and anys from schemav2 code (#99222)
* Remove type assertions and anys from schemav2 code * Handle type conversion * fix test * Filter undefined
This commit is contained in:
parent
29d9d8cf51
commit
1c8a7d8872
@ -3504,12 +3504,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "8"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[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, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/serialization/transformToV1TypesUtils.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
|
@ -38,6 +38,9 @@ import {
|
||||
LibraryPanelKind,
|
||||
Element,
|
||||
RepeatOptions,
|
||||
DashboardCursorSync,
|
||||
FieldConfig,
|
||||
FieldColor,
|
||||
} from '../../../../../packages/grafana-schema/src/schema/dashboard/v2alpha0';
|
||||
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
|
||||
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
|
||||
@ -55,7 +58,7 @@ import {
|
||||
} from '../utils/utils';
|
||||
|
||||
import { sceneVariablesSetToSchemaV2Variables } from './sceneVariablesSetToVariables';
|
||||
import { transformCursorSynctoEnum } from './transformToV2TypesUtils';
|
||||
import { colorIdEnumToColorIdV2, transformCursorSynctoEnum } from './transformToV2TypesUtils';
|
||||
|
||||
// FIXME: This is temporary to avoid creating partial types for all the new schema, it has some performance implications, but it's fine for now
|
||||
type DeepPartial<T> = T extends object
|
||||
@ -238,7 +241,8 @@ export function gridItemToGridLayoutItemKind(gridItem: DashboardGridItem, isSnap
|
||||
|
||||
function getElements(state: DashboardSceneState) {
|
||||
const panels = state.body.getVizPanels() ?? [];
|
||||
const panelsArray = panels.reduce((acc: Element[], vizPanel: VizPanel) => {
|
||||
|
||||
const panelsArray = panels.map((vizPanel: VizPanel) => {
|
||||
if (isLibraryPanel(vizPanel)) {
|
||||
const behavior = getLibraryPanelBehavior(vizPanel)!;
|
||||
const elementSpec: LibraryPanelKind = {
|
||||
@ -248,8 +252,43 @@ function getElements(state: DashboardSceneState) {
|
||||
uid: behavior.state.uid,
|
||||
},
|
||||
};
|
||||
acc.push(elementSpec);
|
||||
return elementSpec;
|
||||
} else {
|
||||
// Handle type conversion for color mode
|
||||
const rawColor = vizPanel.state.fieldConfig.defaults.color;
|
||||
let color: FieldColor | undefined;
|
||||
|
||||
if (rawColor) {
|
||||
const convertedMode = colorIdEnumToColorIdV2(rawColor.mode);
|
||||
|
||||
if (convertedMode) {
|
||||
color = {
|
||||
...rawColor,
|
||||
mode: convertedMode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Remove null from the defaults because schema V2 doesn't support null for these fields
|
||||
const decimals = vizPanel.state.fieldConfig.defaults.decimals ?? undefined;
|
||||
const min = vizPanel.state.fieldConfig.defaults.min ?? undefined;
|
||||
const max = vizPanel.state.fieldConfig.defaults.max ?? undefined;
|
||||
|
||||
const defaults: FieldConfig = Object.fromEntries(
|
||||
Object.entries({
|
||||
...vizPanel.state.fieldConfig.defaults,
|
||||
decimals,
|
||||
min,
|
||||
max,
|
||||
color,
|
||||
}).filter(([_, value]) => value !== undefined)
|
||||
);
|
||||
|
||||
const vizFieldConfig: FieldConfigSource = {
|
||||
...vizPanel.state.fieldConfig,
|
||||
defaults,
|
||||
};
|
||||
|
||||
const elementSpec: PanelKind = {
|
||||
kind: 'Panel',
|
||||
spec: {
|
||||
@ -270,20 +309,15 @@ function getElements(state: DashboardSceneState) {
|
||||
spec: {
|
||||
pluginVersion: vizPanel.state.pluginVersion ?? '',
|
||||
options: vizPanel.state.options,
|
||||
fieldConfig: (vizPanel.state.fieldConfig as FieldConfigSource) ?? defaultFieldConfigSource(),
|
||||
fieldConfig: vizFieldConfig ?? defaultFieldConfigSource(),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
acc.push(elementSpec);
|
||||
return elementSpec;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
// create elements
|
||||
|
||||
const elements = createElements(panelsArray);
|
||||
return elements;
|
||||
});
|
||||
return createElements(panelsArray);
|
||||
}
|
||||
|
||||
function getPanelLinks(panel: VizPanel): DataLink[] {
|
||||
@ -326,12 +360,8 @@ export function getDataQueryKind(query: SceneDataQuery): string {
|
||||
return query.datasource?.type ?? getDefaultDataSourceRef()?.type ?? '';
|
||||
}
|
||||
|
||||
export function getDataQuerySpec(query: SceneDataQuery): Record<string, any> {
|
||||
const dataQuerySpec = {
|
||||
kind: getDataQueryKind(query),
|
||||
spec: query,
|
||||
};
|
||||
return dataQuerySpec;
|
||||
export function getDataQuerySpec(query: SceneDataQuery): DataQueryKind['spec'] {
|
||||
return query;
|
||||
}
|
||||
|
||||
function getVizPanelTransformations(vizPanel: VizPanel): TransformationKind[] {
|
||||
@ -339,30 +369,35 @@ function getVizPanelTransformations(vizPanel: VizPanel): TransformationKind[] {
|
||||
const dataProvider = vizPanel.state.$data;
|
||||
if (dataProvider instanceof SceneDataTransformer) {
|
||||
const transformationList = dataProvider.state.transformations;
|
||||
|
||||
if (transformationList.length === 0) {
|
||||
return [];
|
||||
}
|
||||
transformationList.forEach((transformationItem) => {
|
||||
const transformation = transformationItem as DataTransformerConfig;
|
||||
const transformationSpec: DataTransformerConfig = {
|
||||
id: transformation.id,
|
||||
disabled: transformation.disabled,
|
||||
filter: {
|
||||
id: transformation.filter?.id ?? '',
|
||||
options: transformation.filter?.options ?? {},
|
||||
},
|
||||
options: transformation.options,
|
||||
};
|
||||
|
||||
if (transformation.topic !== undefined) {
|
||||
transformationSpec.topic = transformation.topic;
|
||||
for (const transformationItem of transformationList) {
|
||||
const transformation = transformationItem;
|
||||
|
||||
if ('id' in transformation) {
|
||||
// Transformation is a DataTransformerConfig
|
||||
const transformationSpec: DataTransformerConfig = {
|
||||
id: transformation.id,
|
||||
disabled: transformation.disabled,
|
||||
filter: {
|
||||
id: transformation.filter?.id ?? '',
|
||||
options: transformation.filter?.options ?? {},
|
||||
},
|
||||
...(transformation.topic && { topic: transformation.topic }),
|
||||
options: transformation.options,
|
||||
};
|
||||
|
||||
transformations.push({
|
||||
kind: transformation.id,
|
||||
spec: transformationSpec,
|
||||
});
|
||||
} else {
|
||||
throw new Error('Unsupported transformation type');
|
||||
}
|
||||
|
||||
transformations.push({
|
||||
kind: transformation.id,
|
||||
spec: transformationSpec,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
return transformations;
|
||||
}
|
||||
@ -397,14 +432,14 @@ function getVizPanelQueryOptions(vizPanel: VizPanel): QueryOptionsSpec {
|
||||
}
|
||||
|
||||
function createElements(panels: Element[]): Record<string, Element> {
|
||||
return panels.reduce(
|
||||
(acc, panel) => {
|
||||
const key = panel.kind === 'Panel' ? getVizPanelKeyForPanelId(panel.spec.id) : panel.spec.uid;
|
||||
acc[key] = panel;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, Element>
|
||||
);
|
||||
const elements: Record<string, Element> = {};
|
||||
|
||||
for (const panel of panels) {
|
||||
const key = panel.kind === 'Panel' ? getVizPanelKeyForPanelId(panel.spec.id) : panel.spec.uid;
|
||||
elements[key] = panel;
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
function repeaterToLayoutItems(repeater: DashboardGridItem, isSnapshot = false): GridLayoutItemKind[] {
|
||||
@ -553,97 +588,111 @@ export function getDefaultDataSourceRef(): DataSourceRef | undefined {
|
||||
}
|
||||
|
||||
// Function to know if the dashboard transformed is a valid DashboardV2Spec
|
||||
function validateDashboardSchemaV2(dash: any): dash is DashboardV2Spec {
|
||||
function validateDashboardSchemaV2(dash: unknown): dash is DashboardV2Spec {
|
||||
if (typeof dash !== 'object' || dash === null) {
|
||||
throw new Error('Dashboard is not an object or is null');
|
||||
}
|
||||
|
||||
if (typeof dash.title !== 'string') {
|
||||
if ('title' in dash && typeof dash.title !== 'string') {
|
||||
throw new Error('Title is not a string');
|
||||
}
|
||||
if (typeof dash.description !== 'string') {
|
||||
if ('description' in dash && typeof dash.description !== 'string') {
|
||||
throw new Error('Description is not a string');
|
||||
}
|
||||
if (typeof dash.cursorSync !== 'string') {
|
||||
throw new Error('CursorSync is not a string');
|
||||
if ('cursorSync' in dash && typeof dash.cursorSync !== 'string') {
|
||||
const validCursorSyncValues = ((): string[] => {
|
||||
const typeValues: DashboardCursorSync[] = ['Off', 'Crosshair', 'Tooltip'];
|
||||
return typeValues;
|
||||
})();
|
||||
|
||||
if (
|
||||
'cursorSync' in dash &&
|
||||
(typeof dash.cursorSync !== 'string' || !validCursorSyncValues.includes(dash.cursorSync))
|
||||
) {
|
||||
throw new Error('CursorSync is not a string');
|
||||
}
|
||||
}
|
||||
if (typeof dash.liveNow !== 'boolean') {
|
||||
if ('liveNow' in dash && typeof dash.liveNow !== 'boolean') {
|
||||
throw new Error('LiveNow is not a boolean');
|
||||
}
|
||||
if (typeof dash.preload !== 'boolean') {
|
||||
if ('preload' in dash && typeof dash.preload !== 'boolean') {
|
||||
throw new Error('Preload is not a boolean');
|
||||
}
|
||||
if (typeof dash.editable !== 'boolean') {
|
||||
if ('editable' in dash && typeof dash.editable !== 'boolean') {
|
||||
throw new Error('Editable is not a boolean');
|
||||
}
|
||||
if (!Array.isArray(dash.links)) {
|
||||
if ('links' in dash && !Array.isArray(dash.links)) {
|
||||
throw new Error('Links is not an array');
|
||||
}
|
||||
if (!Array.isArray(dash.tags)) {
|
||||
if ('tags' in dash && !Array.isArray(dash.tags)) {
|
||||
throw new Error('Tags is not an array');
|
||||
}
|
||||
|
||||
if (dash.id !== undefined && typeof dash.id !== 'number') {
|
||||
if ('id' in dash && dash.id !== undefined && typeof dash.id !== 'number') {
|
||||
throw new Error('ID is not a number');
|
||||
}
|
||||
|
||||
// Time settings
|
||||
if (typeof dash.timeSettings !== 'object' || dash.timeSettings === null) {
|
||||
if (!('timeSettings' in dash) || typeof dash.timeSettings !== 'object' || dash.timeSettings === null) {
|
||||
throw new Error('TimeSettings is not an object or is null');
|
||||
}
|
||||
if (typeof dash.timeSettings.timezone !== 'string') {
|
||||
if (!('timezone' in dash.timeSettings) || typeof dash.timeSettings.timezone !== 'string') {
|
||||
throw new Error('Timezone is not a string');
|
||||
}
|
||||
if (typeof dash.timeSettings.from !== 'string') {
|
||||
if (!('from' in dash.timeSettings) || typeof dash.timeSettings.from !== 'string') {
|
||||
throw new Error('From is not a string');
|
||||
}
|
||||
if (typeof dash.timeSettings.to !== 'string') {
|
||||
if (!('to' in dash.timeSettings) || typeof dash.timeSettings.to !== 'string') {
|
||||
throw new Error('To is not a string');
|
||||
}
|
||||
if (typeof dash.timeSettings.autoRefresh !== 'string') {
|
||||
if (!('autoRefresh' in dash.timeSettings) || typeof dash.timeSettings.autoRefresh !== 'string') {
|
||||
throw new Error('AutoRefresh is not a string');
|
||||
}
|
||||
if (!Array.isArray(dash.timeSettings.autoRefreshIntervals)) {
|
||||
if (!('autoRefreshIntervals' in dash.timeSettings) || !Array.isArray(dash.timeSettings.autoRefreshIntervals)) {
|
||||
throw new Error('AutoRefreshIntervals is not an array');
|
||||
}
|
||||
if (!Array.isArray(dash.timeSettings.quickRanges)) {
|
||||
if (!('quickRanges' in dash.timeSettings) || !Array.isArray(dash.timeSettings.quickRanges)) {
|
||||
throw new Error('QuickRanges is not an array');
|
||||
}
|
||||
if (typeof dash.timeSettings.hideTimepicker !== 'boolean') {
|
||||
if (!('hideTimepicker' in dash.timeSettings) || typeof dash.timeSettings.hideTimepicker !== 'boolean') {
|
||||
throw new Error('HideTimepicker is not a boolean');
|
||||
}
|
||||
if (typeof dash.timeSettings.weekStart !== 'string') {
|
||||
if (!('weekStart' in dash.timeSettings) || typeof dash.timeSettings.weekStart !== 'string') {
|
||||
throw new Error('WeekStart is not a string');
|
||||
}
|
||||
if (typeof dash.timeSettings.fiscalYearStartMonth !== 'number') {
|
||||
if (!('fiscalYearStartMonth' in dash.timeSettings) || typeof dash.timeSettings.fiscalYearStartMonth !== 'number') {
|
||||
throw new Error('FiscalYearStartMonth is not a number');
|
||||
}
|
||||
if (dash.timeSettings.nowDelay !== undefined && typeof dash.timeSettings.nowDelay !== 'string') {
|
||||
if (
|
||||
'nowDelay' in dash.timeSettings &&
|
||||
dash.timeSettings.nowDelay !== undefined &&
|
||||
typeof dash.timeSettings.nowDelay !== 'string'
|
||||
) {
|
||||
throw new Error('NowDelay is not a string');
|
||||
}
|
||||
|
||||
// Other sections
|
||||
if (!Array.isArray(dash.variables)) {
|
||||
if (!('variables' in dash) || !Array.isArray(dash.variables)) {
|
||||
throw new Error('Variables is not an array');
|
||||
}
|
||||
if (typeof dash.elements !== 'object' || dash.elements === null) {
|
||||
if (!('elements' in dash) || typeof dash.elements !== 'object' || dash.elements === null) {
|
||||
throw new Error('Elements is not an object or is null');
|
||||
}
|
||||
if (!Array.isArray(dash.annotations)) {
|
||||
if (!('annotations' in dash) || !Array.isArray(dash.annotations)) {
|
||||
throw new Error('Annotations is not an array');
|
||||
}
|
||||
|
||||
// Layout
|
||||
if (typeof dash.layout !== 'object' || dash.layout === null) {
|
||||
if (!('layout' in dash) || typeof dash.layout !== 'object' || dash.layout === null) {
|
||||
throw new Error('Layout is not an object or is null');
|
||||
}
|
||||
if (dash.layout.kind !== 'GridLayout') {
|
||||
if (!('kind' in dash.layout) || dash.layout.kind !== 'GridLayout') {
|
||||
throw new Error('Layout kind is not GridLayout');
|
||||
}
|
||||
if (typeof dash.layout.spec !== 'object' || dash.layout.spec === null) {
|
||||
if (!('spec' in dash.layout) || typeof dash.layout.spec !== 'object' || dash.layout.spec === null) {
|
||||
throw new Error('Layout spec is not an object or is null');
|
||||
}
|
||||
if (!Array.isArray(dash.layout.spec.items)) {
|
||||
if (!('items' in dash.layout.spec) || !Array.isArray(dash.layout.spec.items)) {
|
||||
throw new Error('Layout spec items is not an array');
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
VariableRefresh as VariableRefreshV1,
|
||||
VariableSort as VariableSortV1,
|
||||
DashboardCursorSync as DashboardCursorSyncV1,
|
||||
FieldColorModeId as FieldColorModeIdV1,
|
||||
DataTopic,
|
||||
} from '@grafana/schema';
|
||||
import { DataTransformerConfig } from '@grafana/schema/dist/esm/raw/dashboard/x/dashboard_types.gen';
|
||||
@ -15,6 +16,7 @@ import {
|
||||
VariableHide,
|
||||
VariableRefresh,
|
||||
VariableSort,
|
||||
FieldColorModeId as FieldColorModeIdV2,
|
||||
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
|
||||
|
||||
export function transformCursorSynctoEnum(cursorSync?: DashboardCursorSyncV1): DashboardCursorSync {
|
||||
@ -83,3 +85,40 @@ export function transformDataTopic(topic: DataTransformerConfig['topic']): DataT
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function colorIdEnumToColorIdV2(colorId: FieldColorModeIdV1 | string): FieldColorModeIdV2 | undefined {
|
||||
switch (colorId) {
|
||||
case FieldColorModeIdV1.Thresholds:
|
||||
return 'thresholds';
|
||||
case FieldColorModeIdV1.PaletteClassic:
|
||||
return 'palette-classic';
|
||||
case FieldColorModeIdV1.PaletteClassicByName:
|
||||
return 'palette-classic-by-name';
|
||||
case FieldColorModeIdV1.ContinuousGrYlRd:
|
||||
return 'continuous-GrYlRd';
|
||||
case FieldColorModeIdV1.ContinuousRdYlGr:
|
||||
return 'continuous-RdYlGr';
|
||||
case FieldColorModeIdV1.ContinuousBlYlRd:
|
||||
return 'continuous-BlYlRd';
|
||||
case FieldColorModeIdV1.ContinuousYlRd:
|
||||
return 'continuous-YlRd';
|
||||
case FieldColorModeIdV1.ContinuousBlPu:
|
||||
return 'continuous-BlPu';
|
||||
case FieldColorModeIdV1.ContinuousYlBl:
|
||||
return 'continuous-YlBl';
|
||||
case FieldColorModeIdV1.ContinuousBlues:
|
||||
return 'continuous-blues';
|
||||
case FieldColorModeIdV1.ContinuousReds:
|
||||
return 'continuous-reds';
|
||||
case FieldColorModeIdV1.ContinuousGreens:
|
||||
return 'continuous-greens';
|
||||
case FieldColorModeIdV1.ContinuousPurples:
|
||||
return 'continuous-purples';
|
||||
case FieldColorModeIdV1.Fixed:
|
||||
return 'fixed';
|
||||
case FieldColorModeIdV1.Shades:
|
||||
return 'shades';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user