Compare commits

...

6 Commits

Author SHA1 Message Date
Ivan Ortega bf46319d28 Undo rename for scene serializer 2025-12-17 20:29:37 +01:00
Ivan Ortega 7ec202cced Rename serializerVersion 2025-12-17 20:26:39 +01:00
Ivan Ortega a23873e88f type error 2025-12-17 19:01:37 +01:00
Ivan Ortega 10be5fdb1e Fix linting errors 2025-12-17 18:09:52 +01:00
Ivan Ortega d60f98950e Rename option 2025-12-17 18:01:00 +01:00
Ivan Ortega c88c29fadf Use v2 serializer to transform v1 to v2 2025-12-16 23:26:54 +01:00
4 changed files with 34 additions and 7 deletions
@@ -85,6 +85,7 @@ export interface LoadDashboardOptions {
slug?: string;
type?: string;
urlFolderUid?: string;
targetVersion?: 'v1' | 'v2';
}
export type HomeDashboardDTO = DashboardDTO & {
@@ -266,7 +266,8 @@ export function createDashboardSceneFromDashboardModel(
let alertStatesLayer: AlertStatesDataLayer | undefined;
const uid = oldModel.uid;
const isReport = options?.route === DashboardRoutes.Report;
const serializerVersion = shouldForceV2API() && !oldModel.meta.isSnapshot && !isReport ? 'v2' : 'v1';
const targetVersion =
options?.targetVersion ?? (shouldForceV2API() && !oldModel.meta.isSnapshot && !isReport ? 'v2' : 'v1');
if (oldModel.meta.isSnapshot) {
variables = createVariablesForSnapshot(oldModel);
@@ -354,7 +355,7 @@ export function createDashboardSceneFromDashboardModel(
let body: DashboardLayoutManager;
if (serializerVersion === 'v2' && oldModel.panels.some((p) => p.type === 'row')) {
if (targetVersion === 'v2' && oldModel.panels.some((p) => p.type === 'row')) {
body = createRowsFromPanels(oldModel.panels);
} else {
body = new DefaultGridLayoutManager({
@@ -404,7 +405,7 @@ export function createDashboardSceneFromDashboardModel(
hideTimeControls: oldModel.timepicker.hidden,
}),
},
serializerVersion
targetVersion
);
// Enable panel profiling for this dashboard using the composed SceneRenderProfiler
@@ -1,5 +1,5 @@
import { config } from '@grafana/runtime';
import { SceneTimeRange } from '@grafana/scenes';
import { SceneGridLayout, SceneTimeRange } from '@grafana/scenes';
import { Dashboard } from '@grafana/schema';
import {
Spec as DashboardV2Spec,
@@ -13,6 +13,7 @@ import { DashboardDataDTO } from 'app/types/dashboard';
import { DashboardScene } from '../scene/DashboardScene';
import * as exporters from '../scene/export/exporters';
import { DefaultGridLayoutManager } from '../scene/layout-default/DefaultGridLayoutManager';
import * as v1ToScene from '../serialization/transformSaveModelToScene';
import * as sceneToV1 from '../serialization/transformSceneToSaveModel';
import * as sceneToV2 from '../serialization/transformSceneToSaveModelSchemaV2';
@@ -25,7 +26,6 @@ describe('ShareExportTab', () => {
let makeExportableV1Spy: jest.SpyInstance;
let transformSceneToV1Spy: jest.SpyInstance;
let transformSceneToV2Spy: jest.SpyInstance;
beforeEach(() => {
config.featureToggles.kubernetesDashboards = true;
config.featureToggles.dashboardNewLayouts = false;
@@ -65,6 +65,16 @@ describe('ShareExportTab', () => {
templating: { list: [] },
} as Dashboard);
// Mock transformSaveModelToScene to return a mock scene (used for v1->v2 export with rows)
jest.spyOn(v1ToScene, 'transformSaveModelToScene').mockImplementation(() => {
return new DashboardScene({
title: 'Mock Scene for V2 Export',
uid: 'mock-scene-uid',
body: new DefaultGridLayoutManager({ grid: new SceneGridLayout({ children: [] }) }),
$timeRange: new SceneTimeRange({ from: 'now-6h', to: 'now' }),
});
});
transformSceneToV2Spy = jest.spyOn(sceneToV2, 'transformSceneToSaveModelSchemaV2').mockReturnValue({
title: 'Scene V2',
annotations: [],
@@ -19,10 +19,11 @@ import { K8S_V2_DASHBOARD_API_CONFIG } from 'app/features/dashboard/api/v2';
import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
import { DashboardJson } from 'app/features/manage-dashboards/types';
import { DashboardDataDTO } from 'app/types/dashboard';
import { DashboardDataDTO, DashboardRoutes } from 'app/types/dashboard';
import { DashboardScene } from '../scene/DashboardScene';
import { makeExportableV1, makeExportableV2 } from '../scene/export/exporters';
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel';
import { transformSceneToSaveModelSchemaV2 } from '../serialization/transformSceneToSaveModelSchemaV2';
import { getVariablesCompatibility } from '../utils/getVariablesCompatibility';
@@ -216,7 +217,21 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> impleme
}
if (exportMode === ExportMode.V2Resource) {
const spec = transformSceneToSaveModelSchemaV2(scene);
// When the initial save model was v1, we need to recreate the scene with v2 serializer
// to properly handle rows (convert SceneGridRow to RowsLayout instead of losing them)
let sceneForExport = scene;
if (initialSaveModelVersion === 'v1' && !isDashboardV2Spec(origDashboard)) {
const v1SaveModel = transformSceneToSaveModel(scene);
sceneForExport = transformSaveModelToScene(
{
dashboard: { ...v1SaveModel, title: v1SaveModel.title ?? '', uid: v1SaveModel.uid ?? '' },
meta: scene.state.meta,
},
{ uid: scene.state.uid ?? '', route: DashboardRoutes.Normal, targetVersion: 'v2' }
);
}
const spec = transformSceneToSaveModelSchemaV2(sceneForExport);
const specCopy = JSON.parse(JSON.stringify(spec));
const statelessSpec = await makeExportableV2(specCopy, isSharingExternally);
const exportableV2 = isSharingExternally ? statelessSpec : spec;