Skip to content

Commit 9b01cae

Browse files
committed
persistable state migrations (#103680)
1 parent 8cb83a1 commit 9b01cae

26 files changed

+470
-103
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
22

3-
[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)
3+
[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)
44

5-
## EmbeddableSetup.getMigrationVersions property
5+
## EmbeddableSetup.getAllMigrations property
66

77
<b>Signature:</b>
88

99
```typescript
10-
getMigrationVersions: () => string[];
10+
getAllMigrations: () => MigrateFunctionsObject;
1111
```

docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface EmbeddableSetup extends PersistableStateService<EmbeddableState
1414
1515
| Property | Type | Description |
1616
| --- | --- | --- |
17-
| [getMigrationVersions](./kibana-plugin-plugins-embeddable-server.embeddablesetup.getmigrationversions.md) | <code>() =&gt; string[]</code> | |
17+
| [getAllMigrations](./kibana-plugin-plugins-embeddable-server.embeddablesetup.getallmigrations.md) | <code>() =&gt; MigrateFunctionsObject</code> | |
1818
| [registerEmbeddableFactory](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md) | <code>(factory: EmbeddableRegistryDefinition) =&gt; void</code> | |
1919
| [registerEnhancement](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md) | <code>(enhancement: EnhancementRegistryDefinition) =&gt; void</code> | |
2020

examples/embeddable_examples/public/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export { TODO_EMBEDDABLE, TodoEmbeddableFactory } from './todo';
1717

1818
export { BOOK_EMBEDDABLE } from './book';
1919

20+
export { SIMPLE_EMBEDDABLE } from './migrations';
21+
2022
import { EmbeddableExamplesPlugin } from './plugin';
2123

2224
export {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
export * from './migrations_embeddable';
10+
export * from './migrations_embeddable_factory';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import { MigrateFunction } from '../../../../src/plugins/kibana_utils/common/persistable_state';
10+
import { SimpleEmbeddableInput } from './migrations_embeddable_factory';
11+
import { EmbeddableInput } from '../../../../src/plugins/embeddable/common';
12+
13+
// before 7.3.0 this embeddable received a very simple input with a variable named `number`
14+
// eslint-disable-next-line @typescript-eslint/naming-convention
15+
type SimpleEmbeddableInput_pre7_3_0 = EmbeddableInput & {
16+
number: number;
17+
};
18+
19+
type SimpleEmbeddable730MigrateFn = MigrateFunction<
20+
SimpleEmbeddableInput_pre7_3_0,
21+
SimpleEmbeddableInput
22+
>;
23+
24+
// when migrating old state we'll need to set a default title, or we should make title optional in the new state
25+
const defaultTitle = 'no title';
26+
27+
export const migration730: SimpleEmbeddable730MigrateFn = (state) => {
28+
const newState: SimpleEmbeddableInput = { ...state, title: defaultTitle, value: state.number };
29+
return newState;
30+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import { SIMPLE_EMBEDDABLE, SimpleEmbeddableInput } from '.';
10+
import { Embeddable, IContainer } from '../../../../src/plugins/embeddable/public';
11+
12+
export class SimpleEmbeddable extends Embeddable<SimpleEmbeddableInput> {
13+
// The type of this embeddable. This will be used to find the appropriate factory
14+
// to instantiate this kind of embeddable.
15+
public readonly type = SIMPLE_EMBEDDABLE;
16+
17+
constructor(initialInput: SimpleEmbeddableInput, parent?: IContainer) {
18+
super(
19+
// Input state is irrelevant to this embeddable, just pass it along.
20+
initialInput,
21+
// Initial output state - this embeddable does not do anything with output, so just
22+
// pass along an empty object.
23+
{},
24+
// Optional parent component, this embeddable can optionally be rendered inside a container.
25+
parent
26+
);
27+
}
28+
29+
/**
30+
* Render yourself at the dom node using whatever framework you like, angular, react, or just plain
31+
* vanilla js.
32+
* @param node
33+
*/
34+
public render(node: HTMLElement) {
35+
const input = this.getInput();
36+
// eslint-disable-next-line no-unsanitized/property
37+
node.innerHTML = `<div data-test-subj="simpleEmbeddable">${input.title} ${input.value}</div>`;
38+
}
39+
40+
/**
41+
* This is mostly relevant for time based embeddables which need to update data
42+
* even if EmbeddableInput has not changed at all.
43+
*/
44+
public reload() {}
45+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import { i18n } from '@kbn/i18n';
10+
import {
11+
IContainer,
12+
EmbeddableInput,
13+
EmbeddableFactoryDefinition,
14+
EmbeddableFactory,
15+
} from '../../../../src/plugins/embeddable/public';
16+
import { SimpleEmbeddable } from './migrations_embeddable';
17+
import { migration730 } from './migration.7.3.0';
18+
19+
export const SIMPLE_EMBEDDABLE = 'SIMPLE_EMBEDDABLE';
20+
21+
// in 7.3.0 we added `title` to the input and renamed the `number` variable to `value`
22+
export type SimpleEmbeddableInput = EmbeddableInput & {
23+
title: string;
24+
value: number;
25+
};
26+
27+
export type SimpleEmbeddableFactory = EmbeddableFactory;
28+
export class SimpleEmbeddableFactoryDefinition
29+
implements EmbeddableFactoryDefinition<SimpleEmbeddableInput> {
30+
public readonly type = SIMPLE_EMBEDDABLE;
31+
32+
// we need to provide migration function every time we change the interface of our state
33+
public readonly migrations = {
34+
'7.3.0': migration730,
35+
};
36+
37+
/**
38+
* In our simple example, we let everyone have permissions to edit this. Most
39+
* embeddables should check the UI Capabilities service to be sure of
40+
* the right permissions.
41+
*/
42+
public async isEditable() {
43+
return true;
44+
}
45+
46+
public async create(initialInput: SimpleEmbeddableInput, parent?: IContainer) {
47+
return new SimpleEmbeddable(initialInput, parent);
48+
}
49+
50+
public getDisplayName() {
51+
return i18n.translate('embeddableExamples.migrations.displayName', {
52+
defaultMessage: 'hello world',
53+
});
54+
}
55+
}

examples/embeddable_examples/public/plugin.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ import {
4949
import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
5050
import { createAddBookToLibraryAction } from './book/add_book_to_library_action';
5151
import { createUnlinkBookFromLibraryAction } from './book/unlink_book_from_library_action';
52+
import {
53+
SIMPLE_EMBEDDABLE,
54+
SimpleEmbeddableFactory,
55+
SimpleEmbeddableFactoryDefinition,
56+
} from './migrations';
5257

5358
export interface EmbeddableExamplesSetupDependencies {
5459
embeddable: EmbeddableSetup;
@@ -68,6 +73,7 @@ interface ExampleEmbeddableFactories {
6873
getTodoEmbeddableFactory: () => TodoEmbeddableFactory;
6974
getTodoRefEmbeddableFactory: () => TodoRefEmbeddableFactory;
7075
getBookEmbeddableFactory: () => BookEmbeddableFactory;
76+
getMigrationsEmbeddableFactory: () => SimpleEmbeddableFactory;
7177
}
7278

7379
export interface EmbeddableExamplesStart {
@@ -94,6 +100,11 @@ export class EmbeddableExamplesPlugin
94100
new HelloWorldEmbeddableFactoryDefinition()
95101
);
96102

103+
this.exampleEmbeddableFactories.getMigrationsEmbeddableFactory = deps.embeddable.registerEmbeddableFactory(
104+
SIMPLE_EMBEDDABLE,
105+
new SimpleEmbeddableFactoryDefinition()
106+
);
107+
97108
this.exampleEmbeddableFactories.getMultiTaskTodoEmbeddableFactory = deps.embeddable.registerEmbeddableFactory(
98109
MULTI_TASK_TODO_EMBEDDABLE,
99110
new MultiTaskTodoEmbeddableFactoryDefinition()

examples/embeddable_examples/server/plugin.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@
99
import { Plugin, CoreSetup, CoreStart } from 'kibana/server';
1010
import { todoSavedObject } from './todo_saved_object';
1111
import { bookSavedObject } from './book_saved_object';
12+
import { searchableListSavedObject } from './searchable_list_saved_object';
13+
import { EmbeddableSetup } from '../../../src/plugins/embeddable/server';
1214

13-
export class EmbeddableExamplesPlugin implements Plugin {
14-
public setup(core: CoreSetup) {
15+
export interface EmbeddableExamplesSetupDependencies {
16+
embeddable: EmbeddableSetup;
17+
}
18+
19+
export class EmbeddableExamplesPlugin
20+
implements Plugin<void, void, EmbeddableExamplesSetupDependencies> {
21+
public setup(core: CoreSetup, { embeddable }: EmbeddableExamplesSetupDependencies) {
1522
core.savedObjects.registerType(todoSavedObject);
1623
core.savedObjects.registerType(bookSavedObject);
24+
core.savedObjects.registerType(searchableListSavedObject(embeddable));
1725
}
1826

1927
public start(core: CoreStart) {}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import { mapValues } from 'lodash';
10+
import { SavedObjectsType, SavedObjectUnsanitizedDoc } from 'kibana/server';
11+
import { EmbeddableSetup } from '../../../src/plugins/embeddable/server';
12+
13+
export const searchableListSavedObject = (embeddable: EmbeddableSetup) => {
14+
return {
15+
name: 'searchableList',
16+
hidden: false,
17+
namespaceType: 'single',
18+
management: {
19+
icon: 'visualizeApp',
20+
defaultSearchField: 'title',
21+
importableAndExportable: true,
22+
getTitle(obj: any) {
23+
return obj.attributes.title;
24+
},
25+
},
26+
mappings: {
27+
properties: {
28+
title: { type: 'text' },
29+
version: { type: 'integer' },
30+
},
31+
},
32+
migrations: () => {
33+
// 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\
34+
// if saved object would hold additional information we would need to merge the response from embeddables.getAllMigrations with our custom migrations.
35+
return mapValues(embeddable.getAllMigrations(), (migrate) => {
36+
return (state: SavedObjectUnsanitizedDoc) => ({
37+
...state,
38+
attributes: migrate(state.attributes),
39+
});
40+
});
41+
},
42+
} as SavedObjectsType;
43+
};

0 commit comments

Comments
 (0)