Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ export function getDashboardApi({
initialPanelsRuntimeState ?? {},
trackPanel,
getPanelReferences,
pushPanelReferences
pushPanelReferences,
savedObjectId
);
const dataLoadingManager = initializeDataLoadingManager(panelsManager.api.children$);
const dataViewsManager = initializeDataViewsManager(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
apiPublishesUnsavedChanges,
apiHasSerializableState,
getTitle,
SerializedPanelState,
} from '@kbn/presentation-publishing';
import { i18n } from '@kbn/i18n';
import { coreServices, usageCollectionService } from '../services/kibana_services';
Expand All @@ -41,14 +42,16 @@ import { arePanelLayoutsEqual } from './are_panel_layouts_equal';
import { dashboardClonePanelActionStrings } from '../dashboard_actions/_dashboard_actions_strings';
import { placeClonePanel } from '../panel_placement/place_clone_panel_strategy';
import { PanelPlacementStrategy } from '../plugin_constants';
import { getDashboardBackupService } from '../services/dashboard_backup_service';

export function initializePanelsManager(
incomingEmbeddable: EmbeddablePackageState | undefined,
initialPanels: DashboardPanelMap,
initialPanelsRuntimeState: UnsavedPanelState,
trackPanel: ReturnType<typeof initializeTrackPanel>,
getReferencesForPanelId: (id: string) => Reference[],
pushReferences: (references: Reference[]) => void
pushReferences: (references: Reference[]) => void,
dashboardId?: string
) {
const children$ = new BehaviorSubject<{
[key: string]: unknown;
Expand Down Expand Up @@ -382,11 +385,28 @@ export function initializePanelsManager(
} => {
const references: Reference[] = [];

const getApiMissingFallback = (id: string): SerializedPanelState<object> => {
// If the Dashboard has been saved with this panel before, we should be able to grab it from the current value of panels$
if (Object.keys(panels$.value[id].explicitInput ?? {}).length > 0) {
return {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a panel is not new so explicitInput is populated but has unsaved changes - Shouldn't unsaved changes version of explicitInput be used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think you're right. We could confirm this by making a change to a panel, minimizing it with a collapsible section and then saving the Dashboard.

That change would be overwritten. It seems to me that we should only use the serializedPanelBackup here. I'll make that change.

rawState: panels$.value[id].explicitInput,
references: getReferencesForPanelId(id),
};
} else {
// otherwise, this panel may be new, and therefore will have its unsaved changes backed up in the dashboard backup service
return (
getDashboardBackupService().getSerializedPanelBackup(id, dashboardId) ?? {
rawState: {},
}
);
}
};

const panels = Object.keys(panels$.value).reduce((acc, id) => {
const childApi = children$.value[id];
const serializeResult = apiHasSerializableState(childApi)
? childApi.serializeState()
: { rawState: {} };
: getApiMissingFallback(id);
acc[id] = { ...panels$.value[id], explicitInput: { ...serializeResult.rawState, id } };

references.push(...prefixReferencesFromPanel(id, serializeResult.references ?? []));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { childrenUnsavedChanges$, initializeUnsavedChanges } from '@kbn/presenta
import {
PublishesSavedObjectId,
PublishingSubject,
SerializedPanelState,
StateComparators,
apiHasSerializableState,
} from '@kbn/presentation-publishing';
import { omit } from 'lodash';
import { BehaviorSubject, Subject, combineLatest, debounceTime, skipWhile, switchMap } from 'rxjs';
Expand Down Expand Up @@ -93,17 +95,34 @@ export function initializeUnsavedChangesManager({

// backup unsaved changes if configured to do so
if (creationOptions?.useSessionStorageIntegration) {
const dashboardBackupService = getDashboardBackupService();

// Current behaviour expects time range not to be backed up. Revisit this?
const dashboardStateToBackup = omit(dashboardChanges ?? {}, [
'timeRange',
'refreshInterval',
]);

// TEMPORARY - back up serialized state for all panels with changes
if (unsavedPanelState) {
const serializedPanelBackup: { [key: string]: SerializedPanelState<object> } = {};
for (const uuid of Object.keys(unsavedPanelState)) {
const childApi = panelsManager.api.children$.value[uuid];
if (!apiHasSerializableState(childApi)) continue;
serializedPanelBackup[uuid] = childApi.serializeState();
}
dashboardBackupService.setSerializedPanelsBackups(
serializedPanelBackup,
savedObjectId$.value
);
}

const reactEmbeddableChanges = unsavedPanelState ? { ...unsavedPanelState } : {};
if (controlGroupChanges) {
reactEmbeddableChanges[PANELS_CONTROL_GROUP_KEY] = controlGroupChanges;
}

getDashboardBackupService().setState(
dashboardBackupService.setState(
savedObjectId$.value,
dashboardStateToBackup,
reactEmbeddableChanges
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { i18n } from '@kbn/i18n';
import { Storage } from '@kbn/kibana-utils-plugin/public';
import { set } from '@kbn/safer-lodash-set';

import { ViewMode } from '@kbn/presentation-publishing';
import { SerializedPanelState, ViewMode } from '@kbn/presentation-publishing';
import { coreServices, spacesService } from './kibana_services';
import { DashboardState, UnsavedPanelState } from '../dashboard_api/types';
import { DEFAULT_DASHBOARD_STATE } from '../dashboard_api/default_dashboard_state';
Expand All @@ -27,6 +27,9 @@ const DASHBOARD_VIEWMODE_LOCAL_KEY = 'dashboardViewMode';
// this key is named `panels` for BWC reasons, but actually contains the entire dashboard state
const DASHBOARD_STATE_SESSION_KEY = 'dashboardStateManagerPanels';

// Temporary key for Dashboard to back up serialized state of all its panels
const DASHBOARD_SERIALIZED_PANEL_BACKUP_KEY = 'dashboardSerializedPanelBackup';

const getPanelsGetError = (message: string) =>
i18n.translate('dashboard.panelStorageError.getError', {
defaultMessage: 'Error encountered while fetching unsaved changes: {message}',
Expand Down Expand Up @@ -119,6 +122,32 @@ class DashboardBackupService implements DashboardBackupServiceType {
}
}

public getSerializedPanelBackup(panelId: string, dashboardId = DASHBOARD_PANELS_UNSAVED_ID) {
return this.sessionStorage.get(DASHBOARD_SERIALIZED_PANEL_BACKUP_KEY)?.[this.activeSpaceId]?.[
dashboardId
]?.[panelId] as SerializedPanelState<object> | undefined;
}

public setSerializedPanelsBackups(
serializedPanels: { [key: string]: SerializedPanelState<object> },
dashboardId = DASHBOARD_PANELS_UNSAVED_ID
) {
try {
const serializedPanelsBackup =
this.sessionStorage.get(DASHBOARD_SERIALIZED_PANEL_BACKUP_KEY) ?? {};
set(serializedPanelsBackup, [this.activeSpaceId, dashboardId], serializedPanels);
this.sessionStorage.set(DASHBOARD_SERIALIZED_PANEL_BACKUP_KEY, serializedPanelsBackup);
} catch (e) {
coreServices.notifications.toasts.addDanger({
title: i18n.translate('dashboard.panelStorageError.setError', {
defaultMessage: 'Error encountered while setting unsaved changes: {message}',
values: { message: e.message },
}),
'data-test-subj': 'dashboardPanelsSetFailure',
});
}
}

public getState(id = DASHBOARD_PANELS_UNSAVED_ID) {
try {
const dashboardState = this.sessionStorage.get(DASHBOARD_STATE_SESSION_KEY)?.[
Expand Down