diff --git a/public/app/features/dashboard-scene/settings/JsonModelEditView.tsx b/public/app/features/dashboard-scene/settings/JsonModelEditView.tsx index fd80438d267..9a1f5977628 100644 --- a/public/app/features/dashboard-scene/settings/JsonModelEditView.tsx +++ b/public/app/features/dashboard-scene/settings/JsonModelEditView.tsx @@ -4,11 +4,13 @@ import { useState } from 'react'; import { GrafanaTheme2, PageLayoutType } from '@grafana/data'; import { SceneComponentProps, SceneObjectBase, sceneUtils } from '@grafana/scenes'; import { Dashboard } from '@grafana/schema'; +import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen'; import { Alert, Box, Button, CodeEditor, Stack, useStyles2 } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { Trans, t } from 'app/core/internationalization'; +import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api'; import { getPrettyJSON } from 'app/features/inspector/utils/utils'; -import { DashboardDTO, SaveDashboardResponseDTO } from 'app/types'; +import { DashboardDataDTO, SaveDashboardResponseDTO } from 'app/types'; import { NameAlreadyExistsError, @@ -17,10 +19,10 @@ import { isVersionMismatchError, } from '../saving/shared'; import { useSaveDashboard } from '../saving/useSaveDashboard'; -import { DashboardScene } from '../scene/DashboardScene'; +import { DashboardScene, isV2Dashboard } from '../scene/DashboardScene'; import { NavToolbarActions } from '../scene/NavToolbarActions'; +import { transformSaveModelSchemaV2ToScene } from '../serialization/transformSaveModelSchemaV2ToScene'; import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene'; -import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel'; import { getDashboardSceneFor } from '../utils/utils'; import { DashboardEditView, DashboardEditViewState, useDashboardEditPageNav } from './utils'; @@ -46,9 +48,8 @@ export class JsonModelEditView extends SceneObjectBase i return getDashboardSceneFor(this); } - public getSaveModel(): Dashboard { - const dashboard = this.getDashboard(); - return transformSceneToSaveModel(dashboard); + public getSaveModel(): Dashboard | DashboardV2Spec { + return this.getDashboard().getSaveModel(); } public getJsonText(): string { @@ -60,21 +61,34 @@ export class JsonModelEditView extends SceneObjectBase i this.setState({ jsonText: value }); }; - public onSaveSuccess = (result: SaveDashboardResponseDTO) => { - const jsonModel = JSON.parse(this.state.jsonText); + public onSaveSuccess = async (result: SaveDashboardResponseDTO) => { + const jsonModel: DashboardDataDTO | DashboardV2Spec = JSON.parse(this.state.jsonText); const dashboard = this.getDashboard(); - jsonModel.version = result.version; - const rsp: DashboardDTO = { - dashboard: jsonModel, - meta: dashboard.state.meta, - }; - const newDashboardScene = transformSaveModelToScene(rsp); - const newState = sceneUtils.cloneSceneObjectState(newDashboardScene.state); + const isV2 = isV2Dashboard(jsonModel); + let newDashboardScene: DashboardScene; - dashboard.pauseTrackingChanges(); - dashboard.setInitialSaveModel(rsp.dashboard); - dashboard.setState(newState); + if (isV2) { + // FIXME: We could avoid this call by storing the entire dashboard DTO as initial dashboard scene instead of only the spec and metadata + const dto = await getDashboardAPI('v2').getDashboardDTO(result.uid); + newDashboardScene = transformSaveModelSchemaV2ToScene(dto); + const newState = sceneUtils.cloneSceneObjectState(newDashboardScene.state); + + dashboard.pauseTrackingChanges(); + dashboard.setInitialSaveModel(dto.spec, dto.metadata); + dashboard.setState(newState); + } else { + jsonModel.version = result.version; + newDashboardScene = transformSaveModelToScene({ + dashboard: jsonModel, + meta: dashboard.state.meta, + }); + const newState = sceneUtils.cloneSceneObjectState(newDashboardScene.state); + + dashboard.pauseTrackingChanges(); + dashboard.setInitialSaveModel(jsonModel, dashboard.state.meta); + dashboard.setState(newState); + } this.setState({ jsonText: this.getJsonText() }); }; @@ -94,11 +108,12 @@ export class JsonModelEditView extends SceneObjectBase i folderUid: dashboard.state.meta.folderUid, overwrite, rawDashboardJSON: JSON.parse(model.state.jsonText), + k8s: dashboard.state.meta.k8s, }); setIsSaving(true); if (result.status === 'success') { - model.onSaveSuccess(result); + await model.onSaveSuccess(result); setIsSaving(false); } else { setIsSaving(true); diff --git a/public/app/features/dashboard/api/v2.test.ts b/public/app/features/dashboard/api/v2.test.ts index 97e4b2527a5..9c2ca0470c8 100644 --- a/public/app/features/dashboard/api/v2.test.ts +++ b/public/app/features/dashboard/api/v2.test.ts @@ -8,6 +8,8 @@ import { AnnoKeyFolderId, AnnoKeyFolderTitle, AnnoKeyFolderUrl, + AnnoKeyMessage, + AnnoKeySavedFromUI, DeprecatedInternalId, } from 'app/features/apiserver/types'; @@ -58,6 +60,10 @@ jest.mock('@grafana/runtime', () => ({ }), config: { ...jest.requireActual('@grafana/runtime').config, + buildInfo: { + ...jest.requireActual('@grafana/runtime').config.buildInfo, + versionString: '10.0.0', + }, }, })); @@ -138,6 +144,7 @@ describe('v2 dashboard API', () => { annotations: { [AnnoKeyFolder]: 'new-folder', + [AnnoKeyMessage]: 'test save', }, }, }; @@ -205,6 +212,7 @@ describe('v2 dashboard API', () => { name: 'existing-dash', annotations: { [AnnoKeyFolder]: 'folderUidXyz', + [AnnoKeySavedFromUI]: '10.0.0', }, }, spec: { diff --git a/public/app/features/dashboard/services/DashboardLoaderSrv.ts b/public/app/features/dashboard/services/DashboardLoaderSrv.ts index 97d47bbd055..d788365f4d5 100644 --- a/public/app/features/dashboard/services/DashboardLoaderSrv.ts +++ b/public/app/features/dashboard/services/DashboardLoaderSrv.ts @@ -148,8 +148,8 @@ export class DashboardLoaderSrv extends DashboardLoaderSrvBase { return result; }) .catch((e) => { - console.error('Failed to load dashboard', e); if (isFetchError(e) && !(e instanceof DashboardVersionError)) { + console.error('Failed to load dashboard', e); e.isHandled = true; if (e.status === 404) { appEvents.emit(AppEvents.alertError, ['Dashboard not found']); @@ -216,8 +216,8 @@ export class DashboardLoaderSrvV2 extends DashboardLoaderSrvBase { - console.error('Failed to load dashboard', e); if (isFetchError(e) && !(e instanceof DashboardVersionError)) { + console.error('Failed to load dashboard', e); e.isHandled = true; if (e.status === 404) { appEvents.emit(AppEvents.alertError, ['Dashboard not found']); diff --git a/public/app/features/dashboard/services/DashboardSrv.ts b/public/app/features/dashboard/services/DashboardSrv.ts index d42c49d1109..084f2af33f5 100644 --- a/public/app/features/dashboard/services/DashboardSrv.ts +++ b/public/app/features/dashboard/services/DashboardSrv.ts @@ -56,6 +56,8 @@ export class DashboardSrv { return getDashboardAPI().saveDashboard({ dashboard: parsedJson, folderUid: this.dashboard?.meta.folderUid || parsedJson.folderUid, + message: 'Edit Dashboard JSON', + k8s: this.dashboard?.meta.k8s, }); }