Skip to content

[Embeddable] Clientside migration system #162986

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

Merged
merged 24 commits into from
Aug 22, 2023

Conversation

ThomThomson
Copy link
Contributor

@ThomThomson ThomThomson commented Aug 2, 2023

Closes #158677
Closes #161816

Summary

This PR changes the versioning scheme used by Dashboard Panels and by value Embeddables, and introduces a new clientside system that can migrate Embeddable Inputs to their latest versions.

Embeddable Migration System

Note
This PR does not affect the existing serverside Embeddable migrations in any way - it only adds a secondary system on top that can be used without adding an additional saved object migration to Dashboard. Older Embeddable migrations will continue to run against older inputs.

How are versions stored?

After this PR Embeddable versions and the migrations between are no longer tied to the Kibana version. Instead, each Embeddable type maintains its own versioning information. This is stored as follows:

  • the Latest version is stored in the Embeddable Factory. If a latest version is not present, no migrations will be run against this embeddable type.
  • the Current version is stored in the Embeddable's Input.

When do migrations get run?

The migrations get run as part of the embeddableFactory.create process. This means that every Embeddable that gets rendered by any application will receive migrations. Persisting an Embeddable in a saved object no longer means worrying about keeping them up to date or writing any migrations. Just save the input as-is, and pass it to an Embeddable Renderer, which will do the rest.

On Dashboards... there are some extra steps.

Dashboard Embeddable Migrations

Usually the process of just rendering an Embeddable and having the migrations happen naturally is enough. For Dashboard this is not the case because:

  • Dashboards need to know if a migration has happened in order to prompt the user to save.
  • Dashboards remember their last saved state to allow for resetting and diff checking. Resetting a Dashboard should not undo migrations!

For these reasons, Dashboards run a function clientside on saved object load. This function migrates all panels and Controls on the Dashboard before the Embeddables are even created. If this function detects any changes a save prompt will be shown

Save Prompt

Saving back-propagates any changes from the migrations back to the saved object, and can make loading faster next time because migrations can be skipped. To encourage users to do this if changes have been detected and the Dashboard is in edit mode a prompt will be shown next to the unsaved changes badge. Here is the prompt in context:

Screenshot 2023-08-11 at 6 04 02 PM

Old Version Key

There is a version key in the Dashboard panel type which has historically stored the Kibana version the Dashboard was last saved on.

In this PR that version key was removed and version information was moved inside the Input key so that all Embeddables can be versioned, not just the ones stored in Dashboards. As part of that change, the two usages of the old version key needed to be updated to reflect that the older version key can be missing.

The two usages were:

  1. To check if the panel was saved in a version before 7.3.0 in order to use a legacy version of the extract references function. This PR removes that legacy version of the extract method, because the migration that required it would never be run on a version after 8.0 because upgrading to 8.x requires a stop at 7.17 first.
  2. To check if a panel injected via the URL is older than 7.3.0 in order to abort loading it. This has been updated to also check directly for the hallmarks of an older style panel, like the lack of a gridData key.

How to test

src/plugins/image_embeddable/public/image_embeddable/image_embeddable_factory.tsx

public latestVersion = '1.0.1';
public migrations = {
  '1.0.1': (input: ImageEmbeddableInput) => {
    input.imageConfig.altText = `Neat migration! Previous alt text was "${input.imageConfig.altText}"`;
    return input;
  },
};

The new save prompt should show, but the unsaved changes badge should not show. Resetting the input, switching back and forth between view & edit mode and any action done on the Dashboard should not undo the migration, or run the migration again on already migrated input.

If the migration runs against the input more than once, you'll see the alt text become: Neat migration! Previous alt text was "Neat migration! Previous alt text was "Whatever your alt text was originally"".

What is not covered?

This PR is a very basic and simple version of the clientside migration system. More functionality and features can be added to it as they are needed. Because of this, some pieces are missing:

  • Right now, the Dashboard does not run clientside Control Group Migrations. This is because in order to do so, the Dashboard Saved Object would need to optionally store a Control Group version, which would require changes in the saved object schema, and the content management. Before we can implement any migrations for the Control Group itself we will need to make these changes. Fortunately, migrations are quite rare for the Control Group itself.

  • Drilldown Migrations are not covered at the moment because in order for them to be implemented we would need to extend the Embeddable Enhancements system to be able to store version information independent from the actual Embeddable version. There are currently no migrations registered for drilldowns, but before we add one, we would need to ensure that a system for properly versioning them is in place.

  • Propagation from child Embeddables back to Container
    The function that runs on Dashboard saved object load means that most of the time, when the embeddable create function is called on Dashboards there are no more migrations to be run. If for any reason there are migrations to be run at this point, the embeddable inheritance structure means they get immediately undone because the Container's child input is considered the source of truth. This could happen when:

    • The Dashboard has state in the session storage. If a user has unsaved panels in their session storage, they refresh the Dashboard, and Kibana has updated, the Embeddable create method will detect if any new migrations have been registered and will run them, but the container will pick up the changes and immediately undo them.
    • There are panels from an older version injected via the URL from a snapshot link. Again the migrations will get run, but will immediately get undone.

    Implementing this back-propagation on the existing Embeddables framework would require ugly workarounds and changes that would affect the stability of the Embeddable platform. Once we move to a less prescriptive inheritance structure, this will not be a problem.

Checklist

Delete any items that are not applicable to this PR.

@cla-checker-service
Copy link

cla-checker-service bot commented Aug 2, 2023

💚 CLA has been signed

@ThomThomson ThomThomson force-pushed the embeddable/migrationSystem branch from dcc92cd to 6936bee Compare August 2, 2023 15:14
@@ -51,190 +51,6 @@ const commonAttributes: DashboardAttributes = {
title: '',
};

describe('legacy extract references', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of these tests & the legacy extract references function have been removed because they are no longer used. It is not possible to upgrade to 8.10 without first upgrading to 7.17, and URLs from before 7.3 are unsupported as of 8.7.0 with this PR

Copy link
Contributor

Choose a reason for hiding this comment

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

What if a user imports 7.3 saved objects from saved object import UI into 8.11 instance? Will these migrations be needed in that case? Or will saved object import fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

7.3 used JSON exporting which is incompatible with versions over 8.0 which require NDJSON. 7.17 was the latest version that supported JSON, so the user would have to import them to 7.17 first which would run all the required migrations.

@ThomThomson ThomThomson added Feature:Dashboard Dashboard related features Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas loe:x-large Extra Large Level of Effort impact:critical This issue should be addressed immediately due to a critical level of impact on the product. Project:Serverless Work as part of the Serverless project for its initial release labels Aug 11, 2023
@ThomThomson
Copy link
Contributor Author

@gchaps @amyjtechwriter here is the current copy for the save prompt:

Screenshot 2023-08-11 at 6 06 11 PM

I'm not sure what to say here exactly, but I'd like to get across that we've run some extra code this time because of a Kibana version upgrade, and if you save your dashboard we won't have to run that extra code anymore!

@ThomThomson
Copy link
Contributor Author

@elasticmachine merge upstream

@ThomThomson
Copy link
Contributor Author

@elasticmachine merge upstream

@ThomThomson
Copy link
Contributor Author

@gchaps thank you for the suggestion! I've updated the copy to:

One or more panels on this dashboard have been updated to a new version. Save the dashboard so it loads faster next time.

@andreadelrio - do you think having a second badge as per the current design is the right way to notify a Dashboard author that an upgrade has happened and saving their Dashboard is a good idea?

@ThomThomson ThomThomson force-pushed the embeddable/migrationSystem branch from 0a94fe9 to b8eace5 Compare August 16, 2023 16:45
@ThomThomson ThomThomson marked this pull request as ready for review August 16, 2023 18:08
@ThomThomson ThomThomson requested review from a team as code owners August 16, 2023 18:08
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-presentation (Team:Presentation)

@andreadelrio
Copy link
Contributor

andreadelrio commented Aug 16, 2023

@andreadelrio - do you think having a second badge as per the current design is the right way to notify a Dashboard author that an upgrade has happened and saving their Dashboard is a good idea?

@ThomThomson I think the second badge would work however I'm not sure users will know that Unsaved changes and Save recommended are trying to communicate something different. Will they be curious to go and hover and see the tooltip? Maybe it's just a matter of a different label.

If we wanted to bring even more attention to the recommendation we could add either a EuiBeacon + EuiTourStep on the Save button or use a EuiToast.

Base 1 Base

@ThomThomson
Copy link
Contributor Author

Oh! I really like the EuiBeacon / EuiTourStep combination. Do you think this should replace the badge entirely? Or do you think we should keep both?

@andreadelrio
Copy link
Contributor

Oh! I really like the EuiBeacon / EuiTourStep combination. Do you think this should replace the badge entirely? Or do you think we should keep both?

I would keep both.

@nreese nreese self-requested a review August 17, 2023 15:55
/**
* This version key was used to store Kibana version information from versions 7.3.0 -> 8.11.0.
* As of version 8.11.0, the versioning information is now per-embeddable-type and is stored on the
* embeddable's input. (embeddableConfig in this type).
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to clarify, this means that version will no longer be populated for SavedDashboardPanel and will be undefined for newly created dashboards?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes exactly. The version key will no longer exist in the Dashboard panels themselves. It will exist in the input and only if the Embeddable factory has a latestVersion defined.

@@ -51,190 +51,6 @@ const commonAttributes: DashboardAttributes = {
title: '',
};

describe('legacy extract references', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

What if a user imports 7.3 saved objects from saved object import UI into 8.11 instance? Will these migrations be needed in that case? Or will saved object import fail?

@botelastic botelastic bot added the Feature:Embedding Embedding content via iFrame label Aug 17, 2023
Copy link
Contributor

@nreese nreese left a comment

Choose a reason for hiding this comment

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

LGTM
code review, tested in chrome

Copy link
Member

@ppisljar ppisljar left a comment

Choose a reason for hiding this comment

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

persistable state changes LGTM

@TinaHeiligers
Copy link
Contributor

I've had 👀 on behalf of the Core team. The things this PR doesn't cover make sense in terms of priorities and will need to be done at some point. If there are issues, please link back to the initial work here. If not, they need to be created a.s.a.p

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
dashboard 386 388 +2
embeddable 108 116 +8
total +10

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
embeddable 439 442 +3
kibanaUtils 416 417 +1
total +4

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
dashboard 358.9KB 368.6KB +9.7KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
dashboard 41.8KB 33.5KB -8.3KB
embeddable 69.9KB 78.6KB +8.7KB
kibanaUtils 73.6KB 73.6KB +4.0B
total +403.0B
Unknown metric groups

API count

id before after diff
dashboard 100 101 +1
embeddable 537 542 +5
kibanaUtils 609 610 +1
total +7

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@ThomThomson
Copy link
Contributor Author

@TinaHeiligers I've created issues for the followups identified in this PR:

#164499, #164500 & #164501

@ThomThomson
Copy link
Contributor Author

@andreadelrio I looked into the Toast, and the EuiTour options for drawing more attention to the save recommended badge when clientside migrations have run, and both of those options would require more effort than I anticipated. I'm considering delaying that until we are certain that folks aren't saving their dashboards in these cases, and / or the lack of back-propagation starts to cause problems.

if (panelMigrationRun) anyMigrationRun = true;
panel.explicitInput = newInput as DashboardPanelState['explicitInput'];
} else if (factory.latestVersion) {
// by reference panels are always considered to be of the latest version
Copy link
Contributor

Choose a reason for hiding this comment

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

So a by reference panel that needs a migration would not trigger a "Save recommended"? If a user does edit and save the dashboard would we save the migrated by reference panels too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the time being, by reference Embeddables should never need a migration via this system. Any embeddables that can be both by reference or by value will need to implement the same migration function in both the SO migrations, and the clientside Embeddable migrations.

So currently, no it won't trigger a "Save recommended", but if the user saves, we will save the latestVersion key into the Embeddable's input.

A by reference embeddable may need migration from this system when we implement drilldown migrations, but won't until then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting Feature:Dashboard Dashboard related features Feature:Embedding Embedding content via iFrame impact:critical This issue should be addressed immediately due to a critical level of impact on the product. loe:x-large Extra Large Level of Effort Project:Serverless Work as part of the Serverless project for its initial release release_note:skip Skip the PR/issue when compiling release notes Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas v8.11.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Dashboard] Prompt Authors to save after client side migrations are run [Embeddable] Clientside Migration System
10 participants