mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[8.16] [visualize] fix Save to library action from a by value panel breaks the chart panel (#210125) (#211057)
# Backport This will backport the following commits from `main` to `8.16`: - [[visualize] fix Save to library action from a by value panel breaks the chart panel (#210125)](https://github.com/elastic/kibana/pull/210125) <!--- Backport version: 9.6.4 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Nathan Reese","email":"reese.nathan@elastic.co"},"sourceCommit":{"committedDate":"2025-02-13T17:09:29Z","message":"[visualize] fix Save to library action from a by value panel breaks the chart panel (#210125)\n\nFixes https://github.com/elastic/kibana/issues/206921\r\n\r\n### Problem\r\nThe visualize embeddable is inconstant when passing runtime state to\r\n`buildEmbeddable`. Sometimes, only `{ savedObjectId }` is provided. The\r\nembeddable tried to work around this by calling `deserializeState` in\r\n`buildEmbeddable`.\r\n\r\nThere was a different bug with the `deserializeState` guard in\r\n`buildEmbeddable` where state like `{ savedObjectId, savedVis: {} }`\r\nwould not pass the guard. Dashboard adds runtime state so `savedVis` was\r\ngetting added to `initialState` and thus failing the guard\r\n\r\nThis resulted in the visualize embeddable trying to initialize with\r\nstate `{ savedObjectId }` instead of state in the shape `{\r\nsavedObjectId, serializedVis: {} }`. This resulted in error message like\r\n\"Could not read properties of undefined\" when the embeddable tried to\r\nread from `state.serializedVis.type`.\r\n\r\n### Solution\r\nThe solution is to ensure that `buildEmbeddable` is always passed\r\nruntime state containing `serializedVis`. This pattern is in line with\r\nthe lens embeddable.\r\n\r\n### Test instructions\r\n* install sample web logs\r\n* create agg based visualization\r\n* create new dashboard, add agg based visualization. Open context menu\r\nof vis and select \"Unlink from library\". (Side note, removing legacy\r\nvisualizations from add panel makes it hard to add by-value agg based\r\nvisualizations to a dashboard)\r\n* save dashboard\r\n* edit agg based vis\r\n* Click \"Save to library\" and fill out form\r\n* Verify visualization is rendered in dashboard.\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>","sha":"109dcce33864a4d8be2e5dc6ac088d8a9976afb5","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Presentation","v9.0.0","project:embeddableRebuild","backport:version","v8.18.0","v9.1.0","v8.19.0","v8.17.3","v8.16.5"],"title":"[visualize] fix Save to library action from a by value panel breaks the chart panel","number":210125,"url":"https://github.com/elastic/kibana/pull/210125","mergeCommit":{"message":"[visualize] fix Save to library action from a by value panel breaks the chart panel (#210125)\n\nFixes https://github.com/elastic/kibana/issues/206921\r\n\r\n### Problem\r\nThe visualize embeddable is inconstant when passing runtime state to\r\n`buildEmbeddable`. Sometimes, only `{ savedObjectId }` is provided. The\r\nembeddable tried to work around this by calling `deserializeState` in\r\n`buildEmbeddable`.\r\n\r\nThere was a different bug with the `deserializeState` guard in\r\n`buildEmbeddable` where state like `{ savedObjectId, savedVis: {} }`\r\nwould not pass the guard. Dashboard adds runtime state so `savedVis` was\r\ngetting added to `initialState` and thus failing the guard\r\n\r\nThis resulted in the visualize embeddable trying to initialize with\r\nstate `{ savedObjectId }` instead of state in the shape `{\r\nsavedObjectId, serializedVis: {} }`. This resulted in error message like\r\n\"Could not read properties of undefined\" when the embeddable tried to\r\nread from `state.serializedVis.type`.\r\n\r\n### Solution\r\nThe solution is to ensure that `buildEmbeddable` is always passed\r\nruntime state containing `serializedVis`. This pattern is in line with\r\nthe lens embeddable.\r\n\r\n### Test instructions\r\n* install sample web logs\r\n* create agg based visualization\r\n* create new dashboard, add agg based visualization. Open context menu\r\nof vis and select \"Unlink from library\". (Side note, removing legacy\r\nvisualizations from add panel makes it hard to add by-value agg based\r\nvisualizations to a dashboard)\r\n* save dashboard\r\n* edit agg based vis\r\n* Click \"Save to library\" and fill out form\r\n* Verify visualization is rendered in dashboard.\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>","sha":"109dcce33864a4d8be2e5dc6ac088d8a9976afb5"}},"sourceBranch":"main","suggestedTargetBranches":["8.17","8.16"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/211052","number":211052,"state":"OPEN"},{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/211050","number":211050,"state":"OPEN"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/210125","number":210125,"mergeCommit":{"message":"[visualize] fix Save to library action from a by value panel breaks the chart panel (#210125)\n\nFixes https://github.com/elastic/kibana/issues/206921\r\n\r\n### Problem\r\nThe visualize embeddable is inconstant when passing runtime state to\r\n`buildEmbeddable`. Sometimes, only `{ savedObjectId }` is provided. The\r\nembeddable tried to work around this by calling `deserializeState` in\r\n`buildEmbeddable`.\r\n\r\nThere was a different bug with the `deserializeState` guard in\r\n`buildEmbeddable` where state like `{ savedObjectId, savedVis: {} }`\r\nwould not pass the guard. Dashboard adds runtime state so `savedVis` was\r\ngetting added to `initialState` and thus failing the guard\r\n\r\nThis resulted in the visualize embeddable trying to initialize with\r\nstate `{ savedObjectId }` instead of state in the shape `{\r\nsavedObjectId, serializedVis: {} }`. This resulted in error message like\r\n\"Could not read properties of undefined\" when the embeddable tried to\r\nread from `state.serializedVis.type`.\r\n\r\n### Solution\r\nThe solution is to ensure that `buildEmbeddable` is always passed\r\nruntime state containing `serializedVis`. This pattern is in line with\r\nthe lens embeddable.\r\n\r\n### Test instructions\r\n* install sample web logs\r\n* create agg based visualization\r\n* create new dashboard, add agg based visualization. Open context menu\r\nof vis and select \"Unlink from library\". (Side note, removing legacy\r\nvisualizations from add panel makes it hard to add by-value agg based\r\nvisualizations to a dashboard)\r\n* save dashboard\r\n* edit agg based vis\r\n* Click \"Save to library\" and fill out form\r\n* Verify visualization is rendered in dashboard.\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>","sha":"109dcce33864a4d8be2e5dc6ac088d8a9976afb5"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/211051","number":211051,"state":"OPEN"},{"branch":"8.17","label":"v8.17.3","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.16","label":"v8.16.5","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
ae82382d92
commit
0f2874417e
5 changed files with 38 additions and 42 deletions
|
@ -30,13 +30,11 @@ import {
|
|||
import { getSavedVisualization } from '../utils/saved_visualize_utils';
|
||||
import type { SerializedVis } from '../vis';
|
||||
import {
|
||||
isVisualizeSavedObjectState,
|
||||
VisualizeSavedObjectInputState,
|
||||
VisualizeSerializedState,
|
||||
VisualizeRuntimeState,
|
||||
VisualizeSavedVisInputState,
|
||||
ExtraSavedObjectProperties,
|
||||
isVisualizeRuntimeState,
|
||||
} from './types';
|
||||
|
||||
export const deserializeState = async (
|
||||
|
@ -49,15 +47,24 @@ export const deserializeState = async (
|
|||
},
|
||||
} as VisualizeRuntimeState;
|
||||
let serializedState = cloneDeep(state.rawState);
|
||||
if (isVisualizeSavedObjectState(serializedState)) {
|
||||
serializedState = await deserializeSavedObjectState(serializedState);
|
||||
} else if (isVisualizeRuntimeState(serializedState)) {
|
||||
if ((serializedState as VisualizeSavedObjectInputState).savedObjectId) {
|
||||
serializedState = await deserializeSavedObjectState(
|
||||
serializedState as VisualizeSavedObjectInputState
|
||||
);
|
||||
} else if ((serializedState as VisualizeRuntimeState).serializedVis) {
|
||||
// TODO remove once embeddable only exposes SerializedState
|
||||
// Canvas passes incoming embeddable state in getSerializedStateForChild
|
||||
// without this early return, serializedVis gets replaced in deserializeSavedVisState
|
||||
// and breaks adding a new by-value embeddable in Canvas
|
||||
return serializedState as VisualizeRuntimeState;
|
||||
}
|
||||
|
||||
const references: Reference[] = state.references ?? [];
|
||||
|
||||
const deserializedSavedVis = deserializeSavedVisState(serializedState, references);
|
||||
const deserializedSavedVis = deserializeSavedVisState(
|
||||
serializedState as VisualizeSavedVisInputState,
|
||||
references
|
||||
);
|
||||
|
||||
return {
|
||||
...serializedState,
|
||||
|
|
|
@ -69,26 +69,6 @@ export type VisualizeOutputState = VisualizeSavedVisInputState &
|
|||
Required<Omit<SerializedTitles, 'hidePanelTitles'>> &
|
||||
ExtraSavedObjectProperties;
|
||||
|
||||
export const isVisualizeSavedObjectState = (
|
||||
state: unknown
|
||||
): state is VisualizeSavedObjectInputState => {
|
||||
return (
|
||||
typeof state !== 'undefined' &&
|
||||
(state as VisualizeSavedObjectInputState).savedObjectId !== undefined &&
|
||||
!!(state as VisualizeSavedObjectInputState).savedObjectId &&
|
||||
!('savedVis' in (state as VisualizeSavedObjectInputState)) &&
|
||||
!('serializedVis' in (state as VisualizeSavedObjectInputState))
|
||||
);
|
||||
};
|
||||
|
||||
export const isVisualizeRuntimeState = (state: unknown): state is VisualizeRuntimeState => {
|
||||
return (
|
||||
!isVisualizeSavedObjectState(state) &&
|
||||
!('savedVis' in (state as VisualizeRuntimeState)) &&
|
||||
(state as VisualizeRuntimeState).serializedVis !== undefined
|
||||
);
|
||||
};
|
||||
|
||||
export type VisualizeApi = Partial<HasEditCapabilities> &
|
||||
PublishesDataViews &
|
||||
PublishesDataLoading &
|
||||
|
|
|
@ -53,7 +53,6 @@ import {
|
|||
VisualizeOutputState,
|
||||
VisualizeRuntimeState,
|
||||
VisualizeSerializedState,
|
||||
isVisualizeSavedObjectState,
|
||||
} from './types';
|
||||
import { initializeEditApi } from './initialize_edit_api';
|
||||
|
||||
|
@ -66,15 +65,11 @@ export const getVisualizeEmbeddableFactory: (deps: {
|
|||
}) => ({
|
||||
type: VISUALIZE_EMBEDDABLE_TYPE,
|
||||
deserializeState,
|
||||
buildEmbeddable: async (initialState: unknown, buildApi, uuid, parentApi) => {
|
||||
// Handle state transfer from legacy visualize editor, which uses the legacy visualize embeddable and doesn't
|
||||
// produce a snapshot state. If buildEmbeddable is passed only a savedObjectId in the state, this means deserializeState
|
||||
// was never run, and it needs to be invoked manually
|
||||
const state = isVisualizeSavedObjectState(initialState)
|
||||
? await deserializeState({
|
||||
rawState: initialState,
|
||||
})
|
||||
: (initialState as VisualizeRuntimeState);
|
||||
buildEmbeddable: async (initialState, buildApi, uuid, parentApi) => {
|
||||
const state = {
|
||||
...initialState,
|
||||
linkedToLibrary: Boolean(initialState.savedObjectId),
|
||||
};
|
||||
|
||||
// Initialize dynamic actions
|
||||
const dynamicActionsApi = embeddableEnhancedStart?.initializeReactEmbeddableDynamicActions(
|
||||
|
@ -311,7 +306,13 @@ export const getVisualizeEmbeddableFactory: (deps: {
|
|||
},
|
||||
],
|
||||
savedObjectProperties: getUnchangingComparator(),
|
||||
linkedToLibrary: [linkedToLibrary$, (value) => linkedToLibrary$.next(value)],
|
||||
linkedToLibrary: [
|
||||
linkedToLibrary$,
|
||||
(value) => linkedToLibrary$.next(value),
|
||||
(a, b) => {
|
||||
return a === undefined || b === undefined ? true : a === b;
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ import {
|
|||
VisualizationSavedObjectAttributes,
|
||||
} from '../common/content_management';
|
||||
import { AddAggVisualizationPanelAction } from './actions/add_agg_vis_action';
|
||||
import type { VisualizeSerializedState } from './embeddable/types';
|
||||
import type { VisualizeRuntimeState } from './embeddable/types';
|
||||
import { getVisualizeEmbeddableFactoryLazy } from './embeddable';
|
||||
|
||||
/**
|
||||
|
@ -412,10 +412,15 @@ export class VisualizationsPlugin
|
|||
return getVisualizeEmbeddableFactory({ embeddableStart, embeddableEnhancedStart });
|
||||
});
|
||||
embeddable.registerReactEmbeddableSavedObject<VisualizationSavedObjectAttributes>({
|
||||
onAdd: (container, savedObject) => {
|
||||
container.addNewPanel<VisualizeSerializedState>({
|
||||
onAdd: async (container, savedObject) => {
|
||||
const { deserializeState } = await import('./embeddable/state');
|
||||
const initialState = await deserializeState({
|
||||
rawState: { savedObjectId: savedObject.id },
|
||||
references: savedObject.references,
|
||||
});
|
||||
container.addNewPanel<VisualizeRuntimeState>({
|
||||
panelType: VISUALIZE_EMBEDDABLE_TYPE,
|
||||
initialState: { savedObjectId: savedObject.id },
|
||||
initialState,
|
||||
});
|
||||
},
|
||||
embeddableType: VISUALIZE_EMBEDDABLE_TYPE,
|
||||
|
|
|
@ -187,7 +187,10 @@ export const getTopNavConfig = (
|
|||
stateTransfer.navigateToWithEmbeddablePackage(app, {
|
||||
state: {
|
||||
type: VISUALIZE_EMBEDDABLE_TYPE,
|
||||
input: { savedObjectId: id },
|
||||
input: {
|
||||
serializedVis: vis.serialize(),
|
||||
savedObjectId: id,
|
||||
},
|
||||
embeddableId: saveOptions.copyOnSave ? undefined : embeddableId,
|
||||
searchSessionId: data.search.session.getSessionId(),
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue