Skip to content

Commit

Permalink
persistable state migrations (#103680) (#104764)
Browse files Browse the repository at this point in the history
  • Loading branch information
ppisljar authored Jul 7, 2021
1 parent cba02df commit 026a7b4
Show file tree
Hide file tree
Showing 26 changed files with 470 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) &gt; [EmbeddableSetup](./kibana-plugin-plugins-embeddable-server.embeddablesetup.md) &gt; [getMigrationVersions](./kibana-plugin-plugins-embeddable-server.embeddablesetup.getmigrationversions.md)
[Home](./index.md) &gt; [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) &gt; [EmbeddableSetup](./kibana-plugin-plugins-embeddable-server.embeddablesetup.md) &gt; [getAllMigrations](./kibana-plugin-plugins-embeddable-server.embeddablesetup.getallmigrations.md)

## EmbeddableSetup.getMigrationVersions property
## EmbeddableSetup.getAllMigrations property

<b>Signature:</b>

```typescript
getMigrationVersions: () => string[];
getAllMigrations: () => MigrateFunctionsObject;
```
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface EmbeddableSetup extends PersistableStateService<EmbeddableState
| Property | Type | Description |
| --- | --- | --- |
| [getMigrationVersions](./kibana-plugin-plugins-embeddable-server.embeddablesetup.getmigrationversions.md) | <code>() =&gt; string[]</code> | |
| [getAllMigrations](./kibana-plugin-plugins-embeddable-server.embeddablesetup.getallmigrations.md) | <code>() =&gt; MigrateFunctionsObject</code> | |
| [registerEmbeddableFactory](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md) | <code>(factory: EmbeddableRegistryDefinition) =&gt; void</code> | |
| [registerEnhancement](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md) | <code>(enhancement: EnhancementRegistryDefinition) =&gt; void</code> | |
2 changes: 2 additions & 0 deletions examples/embeddable_examples/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export { TODO_EMBEDDABLE, TodoEmbeddableFactory } from './todo';

export { BOOK_EMBEDDABLE } from './book';

export { SIMPLE_EMBEDDABLE } from './migrations';

import { EmbeddableExamplesPlugin } from './plugin';

export {
Expand Down
10 changes: 10 additions & 0 deletions examples/embeddable_examples/public/migrations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export * from './migrations_embeddable';
export * from './migrations_embeddable_factory';
30 changes: 30 additions & 0 deletions examples/embeddable_examples/public/migrations/migration.7.3.0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { MigrateFunction } from '../../../../src/plugins/kibana_utils/common/persistable_state';
import { SimpleEmbeddableInput } from './migrations_embeddable_factory';
import { EmbeddableInput } from '../../../../src/plugins/embeddable/common';

// before 7.3.0 this embeddable received a very simple input with a variable named `number`
// eslint-disable-next-line @typescript-eslint/naming-convention
type SimpleEmbeddableInput_pre7_3_0 = EmbeddableInput & {
number: number;
};

type SimpleEmbeddable730MigrateFn = MigrateFunction<
SimpleEmbeddableInput_pre7_3_0,
SimpleEmbeddableInput
>;

// when migrating old state we'll need to set a default title, or we should make title optional in the new state
const defaultTitle = 'no title';

export const migration730: SimpleEmbeddable730MigrateFn = (state) => {
const newState: SimpleEmbeddableInput = { ...state, title: defaultTitle, value: state.number };
return newState;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { SIMPLE_EMBEDDABLE, SimpleEmbeddableInput } from '.';
import { Embeddable, IContainer } from '../../../../src/plugins/embeddable/public';

export class SimpleEmbeddable extends Embeddable<SimpleEmbeddableInput> {
// The type of this embeddable. This will be used to find the appropriate factory
// to instantiate this kind of embeddable.
public readonly type = SIMPLE_EMBEDDABLE;

constructor(initialInput: SimpleEmbeddableInput, parent?: IContainer) {
super(
// Input state is irrelevant to this embeddable, just pass it along.
initialInput,
// Initial output state - this embeddable does not do anything with output, so just
// pass along an empty object.
{},
// Optional parent component, this embeddable can optionally be rendered inside a container.
parent
);
}

/**
* Render yourself at the dom node using whatever framework you like, angular, react, or just plain
* vanilla js.
* @param node
*/
public render(node: HTMLElement) {
const input = this.getInput();
// eslint-disable-next-line no-unsanitized/property
node.innerHTML = `<div data-test-subj="simpleEmbeddable">${input.title} ${input.value}</div>`;
}

/**
* This is mostly relevant for time based embeddables which need to update data
* even if EmbeddableInput has not changed at all.
*/
public reload() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { i18n } from '@kbn/i18n';
import {
IContainer,
EmbeddableInput,
EmbeddableFactoryDefinition,
EmbeddableFactory,
} from '../../../../src/plugins/embeddable/public';
import { SimpleEmbeddable } from './migrations_embeddable';
import { migration730 } from './migration.7.3.0';

export const SIMPLE_EMBEDDABLE = 'SIMPLE_EMBEDDABLE';

// in 7.3.0 we added `title` to the input and renamed the `number` variable to `value`
export type SimpleEmbeddableInput = EmbeddableInput & {
title: string;
value: number;
};

export type SimpleEmbeddableFactory = EmbeddableFactory;
export class SimpleEmbeddableFactoryDefinition
implements EmbeddableFactoryDefinition<SimpleEmbeddableInput> {
public readonly type = SIMPLE_EMBEDDABLE;

// we need to provide migration function every time we change the interface of our state
public readonly migrations = {
'7.3.0': migration730,
};

/**
* In our simple example, we let everyone have permissions to edit this. Most
* embeddables should check the UI Capabilities service to be sure of
* the right permissions.
*/
public async isEditable() {
return true;
}

public async create(initialInput: SimpleEmbeddableInput, parent?: IContainer) {
return new SimpleEmbeddable(initialInput, parent);
}

public getDisplayName() {
return i18n.translate('embeddableExamples.migrations.displayName', {
defaultMessage: 'hello world',
});
}
}
11 changes: 11 additions & 0 deletions examples/embeddable_examples/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ import {
import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
import { createAddBookToLibraryAction } from './book/add_book_to_library_action';
import { createUnlinkBookFromLibraryAction } from './book/unlink_book_from_library_action';
import {
SIMPLE_EMBEDDABLE,
SimpleEmbeddableFactory,
SimpleEmbeddableFactoryDefinition,
} from './migrations';

export interface EmbeddableExamplesSetupDependencies {
embeddable: EmbeddableSetup;
Expand All @@ -68,6 +73,7 @@ interface ExampleEmbeddableFactories {
getTodoEmbeddableFactory: () => TodoEmbeddableFactory;
getTodoRefEmbeddableFactory: () => TodoRefEmbeddableFactory;
getBookEmbeddableFactory: () => BookEmbeddableFactory;
getMigrationsEmbeddableFactory: () => SimpleEmbeddableFactory;
}

export interface EmbeddableExamplesStart {
Expand All @@ -94,6 +100,11 @@ export class EmbeddableExamplesPlugin
new HelloWorldEmbeddableFactoryDefinition()
);

this.exampleEmbeddableFactories.getMigrationsEmbeddableFactory = deps.embeddable.registerEmbeddableFactory(
SIMPLE_EMBEDDABLE,
new SimpleEmbeddableFactoryDefinition()
);

this.exampleEmbeddableFactories.getMultiTaskTodoEmbeddableFactory = deps.embeddable.registerEmbeddableFactory(
MULTI_TASK_TODO_EMBEDDABLE,
new MultiTaskTodoEmbeddableFactoryDefinition()
Expand Down
12 changes: 10 additions & 2 deletions examples/embeddable_examples/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@
import { Plugin, CoreSetup, CoreStart } from 'kibana/server';
import { todoSavedObject } from './todo_saved_object';
import { bookSavedObject } from './book_saved_object';
import { searchableListSavedObject } from './searchable_list_saved_object';
import { EmbeddableSetup } from '../../../src/plugins/embeddable/server';

export class EmbeddableExamplesPlugin implements Plugin {
public setup(core: CoreSetup) {
export interface EmbeddableExamplesSetupDependencies {
embeddable: EmbeddableSetup;
}

export class EmbeddableExamplesPlugin
implements Plugin<void, void, EmbeddableExamplesSetupDependencies> {
public setup(core: CoreSetup, { embeddable }: EmbeddableExamplesSetupDependencies) {
core.savedObjects.registerType(todoSavedObject);
core.savedObjects.registerType(bookSavedObject);
core.savedObjects.registerType(searchableListSavedObject(embeddable));
}

public start(core: CoreStart) {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { mapValues } from 'lodash';
import { SavedObjectsType, SavedObjectUnsanitizedDoc } from 'kibana/server';
import { EmbeddableSetup } from '../../../src/plugins/embeddable/server';

export const searchableListSavedObject = (embeddable: EmbeddableSetup) => {
return {
name: 'searchableList',
hidden: false,
namespaceType: 'single',
management: {
icon: 'visualizeApp',
defaultSearchField: 'title',
importableAndExportable: true,
getTitle(obj: any) {
return obj.attributes.title;
},
},
mappings: {
properties: {
title: { type: 'text' },
version: { type: 'integer' },
},
},
migrations: () => {
// we assume all the migration will be done by embeddables service and that saved object holds no extra state besides that of searchable list embeddable input\
// if saved object would hold additional information we would need to merge the response from embeddables.getAllMigrations with our custom migrations.
return mapValues(embeddable.getAllMigrations(), (migrate) => {
return (state: SavedObjectUnsanitizedDoc) => ({
...state,
attributes: migrate(state.attributes),
});
});
},
} as SavedObjectsType;
};
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const injectImplementation = (
};
embeddableSetupMock.extract.mockImplementation(extractImplementation);
embeddableSetupMock.inject.mockImplementation(injectImplementation);
embeddableSetupMock.getMigrationVersions.mockImplementation(() => []);
embeddableSetupMock.getAllMigrations.mockImplementation(() => ({}));

const migrations = createDashboardSavedObjectTypeMigrations({
embeddable: embeddableSetupMock,
Expand Down Expand Up @@ -586,28 +586,14 @@ describe('dashboard', () => {
type: 'dashboard',
};

it('should add all embeddable migrations for versions above 7.12.0 to dashboard saved object migrations', () => {
const newEmbeddableSetupMock = createEmbeddableSetupMock();
newEmbeddableSetupMock.getMigrationVersions.mockImplementation(() => [
'7.10.100',
'7.13.0',
'8.0.0',
]);
const migrationsList = createDashboardSavedObjectTypeMigrations({
embeddable: newEmbeddableSetupMock,
});
expect(Object.keys(migrationsList).indexOf('8.0.0')).not.toBe(-1);
expect(Object.keys(migrationsList).indexOf('7.13.0')).not.toBe(-1);
expect(Object.keys(migrationsList).indexOf('7.10.100')).toBe(-1);
});

it('runs migrations on by value panels only', () => {
const newEmbeddableSetupMock = createEmbeddableSetupMock();
newEmbeddableSetupMock.getMigrationVersions.mockImplementation(() => ['7.13.0']);
newEmbeddableSetupMock.migrate.mockImplementation((state: SerializableState) => {
state.superCoolKey = 'ONLY 4 BY VALUE EMBEDDABLES THANK YOU VERY MUCH';
return state;
});
newEmbeddableSetupMock.getAllMigrations.mockImplementation(() => ({
'7.13.0': (state: SerializableState) => {
state.superCoolKey = 'ONLY 4 BY VALUE EMBEDDABLES THANK YOU VERY MUCH';
return state;
},
}));
const migrationsList = createDashboardSavedObjectTypeMigrations({
embeddable: newEmbeddableSetupMock,
});
Expand Down
Loading

0 comments on commit 026a7b4

Please sign in to comment.