-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Embeddable] Reset dashboard breaks panel state when ran after editor edits #201627
Comments
Pinging @elastic/kibana-presentation (Team:Presentation) |
regression caused by #188039. Explicit input is getting overridden.
Instead, only |
…tor edits (elastic#201687) Closes elastic#201627 ### The problem `ReactEmbeddableRender` uses `lastSavedRuntimeState` as last saved state baseline. Resetting reverts to this state. ``` // Code sample from src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx const serializedState = parentApi.getSerializedStateForChild(uuid); const lastSavedRuntimeState = serializedState ? await factory.deserializeState(serializedState) : ({} as RuntimeState); ... const unsavedChanges = initializeUnsavedChanges<RuntimeState>( lastSavedRuntimeState, parentApi, comparators ); ``` `DashboardContainer` getSerializedStateForChild implemenation ``` // Code sample from src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx public getSerializedStateForChild = (childId: string) => { const rawState = this.getInput().panels[childId].explicitInput; const { id, ...serializedState } = rawState; if (!rawState || Object.keys(serializedState).length === 0) return; const references = getReferencesForPanelId(childId, this.savedObjectReferences); return { rawState, // references from old installations may not be prefixed with panel id // fall back to passing all references in these cases to preserve backwards compatability references: references.length > 0 ? references : this.savedObjectReferences, }; }; ``` The problem is that `create_dashboard` clears `this.getInput().panels[childId].explicitInput` for a panel with embeddable transfer state. This causes `lastSavedRuntimeState` to be an empty object, which causes reset to use `undefined` for the previous value when reseting an embeddables keys. ``` // Code sample from src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts const incomingEmbeddable = creationOptions?.getIncomingEmbeddable?.(); if ( incomingEmbeddable.embeddableId && Boolean(initialDashboardInput.panels[incomingEmbeddable.embeddableId]) ) { // this embeddable already exists, we will update the explicit input. const panelToUpdate = initialDashboardInput.panels[incomingEmbeddable.embeddableId]; const sameType = panelToUpdate.type === incomingEmbeddable.type; panelToUpdate.type = incomingEmbeddable.type; const nextRuntimeState = { // if the incoming panel is the same type as what was there before we can safely spread the old panel's explicit input ...(sameType ? panelToUpdate.explicitInput : {}), ...incomingEmbeddable.input, id: incomingEmbeddable.embeddableId, // maintain hide panel titles setting. hidePanelTitles: panelToUpdate.explicitInput.hidePanelTitles, }; if (embeddableService.reactEmbeddableRegistryHasKey(incomingEmbeddable.type)) { panelToUpdate.explicitInput = { id: panelToUpdate.explicitInput.id }; runtimePanelsToRestore[incomingEmbeddable.embeddableId] = nextRuntimeState; } else { panelToUpdate.explicitInput = nextRuntimeState; } } ``` ### The fix The solution is to retain `panelToUpdate.explicitInput` original value so that `lastSavedRuntimeState` is set to the original runtime state and reset will revert to this value instead of `{}`. ### test instructions 1) install web logs sample data 2) create new dashboard 3) click "Add panel" and select "Legacy => Agg based" 4) create pie chart with terms split on "machine.os", click "Save and return" 5) save dashboard 6) edit pie chart, Under "options", unselect "donut". Click "Save and return" 7) click reset in dashboard. Verify that visualization returns to original state. Note, the same will not happen for map embeddables. There is a bug in map embeddables where resetting "attributes" state does not update the current map. Co-authored-by: Elastic Machine <[email protected]> (cherry picked from commit 289bb16)
…tor edits (elastic#201687) Closes elastic#201627 ### The problem `ReactEmbeddableRender` uses `lastSavedRuntimeState` as last saved state baseline. Resetting reverts to this state. ``` // Code sample from src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx const serializedState = parentApi.getSerializedStateForChild(uuid); const lastSavedRuntimeState = serializedState ? await factory.deserializeState(serializedState) : ({} as RuntimeState); ... const unsavedChanges = initializeUnsavedChanges<RuntimeState>( lastSavedRuntimeState, parentApi, comparators ); ``` `DashboardContainer` getSerializedStateForChild implemenation ``` // Code sample from src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx public getSerializedStateForChild = (childId: string) => { const rawState = this.getInput().panels[childId].explicitInput; const { id, ...serializedState } = rawState; if (!rawState || Object.keys(serializedState).length === 0) return; const references = getReferencesForPanelId(childId, this.savedObjectReferences); return { rawState, // references from old installations may not be prefixed with panel id // fall back to passing all references in these cases to preserve backwards compatability references: references.length > 0 ? references : this.savedObjectReferences, }; }; ``` The problem is that `create_dashboard` clears `this.getInput().panels[childId].explicitInput` for a panel with embeddable transfer state. This causes `lastSavedRuntimeState` to be an empty object, which causes reset to use `undefined` for the previous value when reseting an embeddables keys. ``` // Code sample from src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts const incomingEmbeddable = creationOptions?.getIncomingEmbeddable?.(); if ( incomingEmbeddable.embeddableId && Boolean(initialDashboardInput.panels[incomingEmbeddable.embeddableId]) ) { // this embeddable already exists, we will update the explicit input. const panelToUpdate = initialDashboardInput.panels[incomingEmbeddable.embeddableId]; const sameType = panelToUpdate.type === incomingEmbeddable.type; panelToUpdate.type = incomingEmbeddable.type; const nextRuntimeState = { // if the incoming panel is the same type as what was there before we can safely spread the old panel's explicit input ...(sameType ? panelToUpdate.explicitInput : {}), ...incomingEmbeddable.input, id: incomingEmbeddable.embeddableId, // maintain hide panel titles setting. hidePanelTitles: panelToUpdate.explicitInput.hidePanelTitles, }; if (embeddableService.reactEmbeddableRegistryHasKey(incomingEmbeddable.type)) { panelToUpdate.explicitInput = { id: panelToUpdate.explicitInput.id }; runtimePanelsToRestore[incomingEmbeddable.embeddableId] = nextRuntimeState; } else { panelToUpdate.explicitInput = nextRuntimeState; } } ``` ### The fix The solution is to retain `panelToUpdate.explicitInput` original value so that `lastSavedRuntimeState` is set to the original runtime state and reset will revert to this value instead of `{}`. ### test instructions 1) install web logs sample data 2) create new dashboard 3) click "Add panel" and select "Legacy => Agg based" 4) create pie chart with terms split on "machine.os", click "Save and return" 5) save dashboard 6) edit pie chart, Under "options", unselect "donut". Click "Save and return" 7) click reset in dashboard. Verify that visualization returns to original state. Note, the same will not happen for map embeddables. There is a bug in map embeddables where resetting "attributes" state does not update the current map. Co-authored-by: Elastic Machine <[email protected]> (cherry picked from commit 289bb16)
#201902 reverted so issue is still open |
Why was revert required?
Why did tests start failingWithout the fix, dashboard panel state is getting cleared. This is the source of the original issue; that resetting causes the embeddable to reset itself against empty state. Lens embeddable is only getting state from embeddable transfer service that looks like
With this state, the panel title gets set to 'My TSVB to Lens viz 2 (converted)' from the by-ref lens saved object. With the fix, dashboard panel state is not getting cleared. This means that lens embeddable is getting original dashboard panel state plus state from embeddable transfer and state looks like
With this state, the panel title gets set to 'My TSVB to Lens viz 2' from the previous dashboard panel state. What is the problemThe root of the problem is that there are 2 versions of serialize state
In most cases these are the same values, but for panels with incoming state from embeddable service, these are separate values. The current logic of |
…#203158) Part of #201627 This is a short term fix for serverless and 8.x branches (long term fix is an architectural change that will only be merged into 9.0 and 8.18 branches). Fix prevents users from reseting a panel edited via embeddable transfer service. This prevents panel from getting into an invalid state. --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Elastic Machine <[email protected]>
…elastic#203158) Part of elastic#201627 This is a short term fix for serverless and 8.x branches (long term fix is an architectural change that will only be merged into 9.0 and 8.18 branches). Fix prevents users from reseting a panel edited via embeddable transfer service. This prevents panel from getting into an invalid state. --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Elastic Machine <[email protected]> (cherry picked from commit e103a25)
…elastic#203158) Part of elastic#201627 This is a short term fix for serverless and 8.x branches (long term fix is an architectural change that will only be merged into 9.0 and 8.18 branches). Fix prevents users from reseting a panel edited via embeddable transfer service. This prevents panel from getting into an invalid state. --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Elastic Machine <[email protected]> (cherry picked from commit e103a25)
…elastic#203158) Part of elastic#201627 This is a short term fix for serverless and 8.x branches (long term fix is an architectural change that will only be merged into 9.0 and 8.18 branches). Fix prevents users from reseting a panel edited via embeddable transfer service. This prevents panel from getting into an invalid state. --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Elastic Machine <[email protected]> (cherry picked from commit e103a25)
…tor edits (elastic#201687) Closes elastic#201627 ### The problem `ReactEmbeddableRender` uses `lastSavedRuntimeState` as last saved state baseline. Resetting reverts to this state. ``` // Code sample from src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx const serializedState = parentApi.getSerializedStateForChild(uuid); const lastSavedRuntimeState = serializedState ? await factory.deserializeState(serializedState) : ({} as RuntimeState); ... const unsavedChanges = initializeUnsavedChanges<RuntimeState>( lastSavedRuntimeState, parentApi, comparators ); ``` `DashboardContainer` getSerializedStateForChild implemenation ``` // Code sample from src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx public getSerializedStateForChild = (childId: string) => { const rawState = this.getInput().panels[childId].explicitInput; const { id, ...serializedState } = rawState; if (!rawState || Object.keys(serializedState).length === 0) return; const references = getReferencesForPanelId(childId, this.savedObjectReferences); return { rawState, // references from old installations may not be prefixed with panel id // fall back to passing all references in these cases to preserve backwards compatability references: references.length > 0 ? references : this.savedObjectReferences, }; }; ``` The problem is that `create_dashboard` clears `this.getInput().panels[childId].explicitInput` for a panel with embeddable transfer state. This causes `lastSavedRuntimeState` to be an empty object, which causes reset to use `undefined` for the previous value when reseting an embeddables keys. ``` // Code sample from src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts const incomingEmbeddable = creationOptions?.getIncomingEmbeddable?.(); if ( incomingEmbeddable.embeddableId && Boolean(initialDashboardInput.panels[incomingEmbeddable.embeddableId]) ) { // this embeddable already exists, we will update the explicit input. const panelToUpdate = initialDashboardInput.panels[incomingEmbeddable.embeddableId]; const sameType = panelToUpdate.type === incomingEmbeddable.type; panelToUpdate.type = incomingEmbeddable.type; const nextRuntimeState = { // if the incoming panel is the same type as what was there before we can safely spread the old panel's explicit input ...(sameType ? panelToUpdate.explicitInput : {}), ...incomingEmbeddable.input, id: incomingEmbeddable.embeddableId, // maintain hide panel titles setting. hidePanelTitles: panelToUpdate.explicitInput.hidePanelTitles, }; if (embeddableService.reactEmbeddableRegistryHasKey(incomingEmbeddable.type)) { panelToUpdate.explicitInput = { id: panelToUpdate.explicitInput.id }; runtimePanelsToRestore[incomingEmbeddable.embeddableId] = nextRuntimeState; } else { panelToUpdate.explicitInput = nextRuntimeState; } } ``` ### The fix The solution is to retain `panelToUpdate.explicitInput` original value so that `lastSavedRuntimeState` is set to the original runtime state and reset will revert to this value instead of `{}`. ### test instructions 1) install web logs sample data 2) create new dashboard 3) click "Add panel" and select "Legacy => Agg based" 4) create pie chart with terms split on "machine.os", click "Save and return" 5) save dashboard 6) edit pie chart, Under "options", unselect "donut". Click "Save and return" 7) click reset in dashboard. Verify that visualization returns to original state. Note, the same will not happen for map embeddables. There is a bug in map embeddables where resetting "attributes" state does not update the current map. Co-authored-by: Elastic Machine <[email protected]>
…elastic#203158) Part of elastic#201627 This is a short term fix for serverless and 8.x branches (long term fix is an architectural change that will only be merged into 9.0 and 8.18 branches). Fix prevents users from reseting a panel edited via embeddable transfer service. This prevents panel from getting into an invalid state. --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Elastic Machine <[email protected]>
Describe the bug:
The bug seems to affect any embeddable who not rely on inline editing state.
The main problem is relying on the fact that the initial state necessarily starts from a
deserializedState
in a dashboard, which is not true, as a dashboard can be loaded AFTER passing thru an editor edit session.Saving this broken state wipes out entirely the panel state and breaks the dashboard.
Steps to reproduce:
Save and return
Save and return
againReset
nowdev
console to see some error about initialization of the panel (specific per viz)Any additional context:
The problem seems related to the fact that navigating back from an editor doesn't pass thru the
deserializeState
function, but assign an empty object:kibana/src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx
Lines 110 to 112 in d0f0809
This
lastSavedRuntimeState
is then passed to theinitializeUnsavedChanges
function who is in charge to enrich the specific embeddable API with additional methods to manage the dashboardReset
feature:kibana/src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx
Lines 176 to 180 in d0f0809
Given the empty object passed when coming from a full editor any state within a panel will get wiped out.
Also it is not possible to workaround it as specific embeddable api will be overriden by the
initializeUnsavedChanges
anyway.The text was updated successfully, but these errors were encountered: