[TSVB] By Value Migrations for 7.13 (#100746) (#101033)

* Created common TSVB migrations. Registered them in serverside embeddable factory so that by value panels receive them

Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>

Co-authored-by: Devon Thomson <devon.thomson@elastic.co>
This commit is contained in:
Stratoula Kalafateli 2021-06-01 13:53:49 +03:00 committed by GitHub
parent 157e2cf140
commit 550d110244
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 193 additions and 54 deletions

View file

@ -147,7 +147,10 @@ function createExtractPanelReferencesMigration(
};
}
type ValueOrReferenceInput = SavedObjectEmbeddableInput & { attributes?: SerializableValue };
type ValueOrReferenceInput = SavedObjectEmbeddableInput & {
attributes?: SerializableValue;
savedVis?: SerializableValue;
};
// Runs the embeddable migrations on each panel
const migrateByValuePanels = (
@ -158,19 +161,21 @@ const migrateByValuePanels = (
// Skip if panelsJSON is missing otherwise this will cause saved object import to fail when
// importing objects without panelsJSON. At development time of this, there is no guarantee each saved
// object has panelsJSON in all previous versions of kibana.
if (typeof attributes.panelsJSON !== 'string') {
return attributes;
if (typeof attributes?.panelsJSON !== 'string') {
return doc;
}
const panels = JSON.parse(attributes.panelsJSON) as SavedDashboardPanel[];
// Same here, prevent failing saved object import if ever panels aren't an array.
if (!Array.isArray(panels)) {
return attributes;
return doc;
}
const newPanels: SavedDashboardPanel[] = [];
panels.forEach((panel) => {
// Convert each panel into a state that can be passed to EmbeddablesSetup.migrate
const originalPanelState = convertSavedDashboardPanelToPanelState<ValueOrReferenceInput>(panel);
if (originalPanelState.explicitInput.attributes) {
// saved vis is used to store by value input for Visualize. This should eventually be renamed to `attributes` to align with Lens and Maps
if (originalPanelState.explicitInput.attributes || originalPanelState.explicitInput.savedVis) {
// If this panel is by value, migrate the state using embeddable migrations
const migratedInput = deps.embeddable.migrate(
{

View file

@ -0,0 +1,52 @@
/*
* 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 { flow } from 'lodash';
import { EmbeddableRegistryDefinition } from 'src/plugins/embeddable/server';
import { SerializableState } from '../../../kibana_utils/common';
import {
commonAddSupportOfDualIndexSelectionModeInTSVB,
commonHideTSVBLastValueIndicator,
commonRemoveDefaultIndexPatternAndTimeFieldFromTSVBModel,
} from '../migrations/visualization_common_migrations';
const byValueAddSupportOfDualIndexSelectionModeInTSVB = (state: SerializableState) => {
return {
...state,
savedVis: commonAddSupportOfDualIndexSelectionModeInTSVB(state.savedVis),
};
};
const byValueHideTSVBLastValueIndicator = (state: SerializableState) => {
return {
...state,
savedVis: commonHideTSVBLastValueIndicator(state.savedVis),
};
};
const byValueRemoveDefaultIndexPatternAndTimeFieldFromTSVBModel = (state: SerializableState) => {
return {
...state,
savedVis: commonRemoveDefaultIndexPatternAndTimeFieldFromTSVBModel(state.savedVis),
};
};
export const visualizeEmbeddableFactory = (): EmbeddableRegistryDefinition => {
return {
id: 'visualization',
migrations: {
// These migrations are run in 7.13.1 for `by value` panels because the 7.13 release window was missed.
'7.13.1': (state) =>
flow(
byValueAddSupportOfDualIndexSelectionModeInTSVB,
byValueHideTSVBLastValueIndicator,
byValueRemoveDefaultIndexPatternAndTimeFieldFromTSVBModel
)(state),
},
};
};

View file

@ -0,0 +1,44 @@
/*
* 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 const commonAddSupportOfDualIndexSelectionModeInTSVB = (visState: any) => {
if (visState && visState.type === 'metrics') {
const { params } = visState;
if (typeof params?.index_pattern === 'string') {
params.use_kibana_indexes = false;
}
}
return visState;
};
export const commonHideTSVBLastValueIndicator = (visState: any) => {
if (visState && visState.type === 'metrics' && visState.params.type !== 'timeseries') {
return {
...visState,
params: {
...visState.params,
hide_last_value_indicator: true,
},
};
}
return visState;
};
export const commonRemoveDefaultIndexPatternAndTimeFieldFromTSVBModel = (visState: any) => {
if (visState && visState.type === 'metrics') {
const { params } = visState;
delete params.default_index_pattern;
delete params.default_timefield;
return visState;
}
return visState;
};

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { visualizationSavedObjectTypeMigrations } from './visualization_migrations';
import { visualizationSavedObjectTypeMigrations } from './visualization_saved_object_migrations';
import { SavedObjectMigrationContext, SavedObjectMigrationFn } from 'kibana/server';
const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext;
@ -1977,4 +1977,44 @@ describe('migration visualization', () => {
expect(params).not.toHaveProperty('default_timefield');
});
});
describe('7.13.0 and 7.13.1 tsvb migrations can run twice', () => {
const migrate = (doc: any) =>
visualizationSavedObjectTypeMigrations['7.13.0'](
doc as Parameters<SavedObjectMigrationFn>[0],
savedObjectMigrationContext
);
const migrateAgain = (doc: any) =>
visualizationSavedObjectTypeMigrations['7.13.1'](
doc as Parameters<SavedObjectMigrationFn>[0],
savedObjectMigrationContext
);
const createTestDocWithType = (type: string) => ({
attributes: {
title: 'My Vis',
description: 'This is my super cool vis.',
visState: `{"type":"metrics","params":{"type":"${type}","default_index_pattern":"test", "default_timefield":"test", "index_pattern":"testme"}}`,
},
});
it('the migrations can be applied twice without breaking anything', () => {
const migratedTestDoc = migrate(createTestDocWithType('markdown'));
const { params } = JSON.parse(migratedTestDoc.attributes.visState);
expect(params.hide_last_value_indicator).toBeTruthy();
expect(params).not.toHaveProperty('default_index_pattern');
expect(params).not.toHaveProperty('default_timefield');
expect(params.use_kibana_indexes).toBeFalsy();
const migratedTestDocNew = migrateAgain(migratedTestDoc);
const visState = JSON.parse(migratedTestDocNew.attributes.visState);
expect(visState.params.hide_last_value_indicator).toBeTruthy();
expect(visState.params).not.toHaveProperty('default_index_pattern');
expect(visState.params).not.toHaveProperty('default_timefield');
expect(params.use_kibana_indexes).toBeFalsy();
});
});
});

View file

@ -11,6 +11,11 @@ import { cloneDeep, get, omit, has, flow, forOwn } from 'lodash';
import { SavedObjectMigrationFn } from 'kibana/server';
import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common';
import {
commonAddSupportOfDualIndexSelectionModeInTSVB,
commonHideTSVBLastValueIndicator,
commonRemoveDefaultIndexPatternAndTimeFieldFromTSVBModel,
} from './visualization_common_migrations';
const migrateIndexPattern: SavedObjectMigrationFn<any, any> = (doc) => {
const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON');
@ -799,22 +804,16 @@ const addSupportOfDualIndexSelectionModeInTSVB: SavedObjectMigrationFn<any, any>
visState = JSON.parse(visStateJSON);
} catch (e) {
// Let it go, the data is invalid and we'll leave it as is
return doc;
}
if (visState && visState.type === 'metrics') {
const { params } = visState;
if (typeof params?.index_pattern === 'string') {
params.use_kibana_indexes = false;
}
return {
...doc,
attributes: {
...doc.attributes,
visState: JSON.stringify(visState),
},
};
}
const newVisState = commonAddSupportOfDualIndexSelectionModeInTSVB(visState);
return {
...doc,
attributes: {
...doc.attributes,
visState: JSON.stringify(newVisState),
},
};
}
return doc;
};
@ -929,25 +928,17 @@ const migrateVislibAreaLineBarTypes: SavedObjectMigrationFn<any, any> = (doc) =>
const hideTSVBLastValueIndicator: SavedObjectMigrationFn<any, any> = (doc) => {
try {
const visState = JSON.parse(doc.attributes.visState);
if (visState && visState.type === 'metrics' && visState.params.type !== 'timeseries')
return {
...doc,
attributes: {
...doc.attributes,
visState: JSON.stringify({
...visState,
params: {
...visState.params,
hide_last_value_indicator: true,
},
}),
},
};
const newVisState = commonHideTSVBLastValueIndicator(visState);
return {
...doc,
attributes: {
...doc.attributes,
visState: JSON.stringify(newVisState),
},
};
} catch (e) {
// Let it go, the data is invalid and we'll leave it as is
}
return doc;
};
@ -962,23 +953,17 @@ const removeDefaultIndexPatternAndTimeFieldFromTSVBModel: SavedObjectMigrationFn
visState = JSON.parse(visStateJSON);
} catch (e) {
// Let it go, the data is invalid and we'll leave it as is
}
if (visState && visState.type === 'metrics') {
const { params } = visState;
delete params.default_index_pattern;
delete params.default_timefield;
return {
...doc,
attributes: {
...doc.attributes,
visState: JSON.stringify(visState),
},
};
return doc;
}
}
return doc;
const newVisState = commonRemoveDefaultIndexPatternAndTimeFieldFromTSVBModel(visState);
return {
...doc,
attributes: {
...doc.attributes,
visState: JSON.stringify(newVisState),
},
};
};
export const visualizationSavedObjectTypeMigrations = {
@ -1021,4 +1006,10 @@ export const visualizationSavedObjectTypeMigrations = {
hideTSVBLastValueIndicator,
removeDefaultIndexPatternAndTimeFieldFromTSVBModel
),
'7.13.1': flow(
// duplicate these migrations in case a broken by value panel is added to the library
addSupportOfDualIndexSelectionModeInTSVB,
hideTSVBLastValueIndicator,
removeDefaultIndexPatternAndTimeFieldFromTSVBModel
),
};

View file

@ -24,6 +24,8 @@ import { visualizationSavedObjectType } from './saved_objects';
import { VisualizationsPluginSetup, VisualizationsPluginStart } from './types';
import { registerVisualizationsCollector } from './usage_collector';
import { EmbeddableSetup } from '../../embeddable/server';
import { visualizeEmbeddableFactory } from './embeddable/visualize_embeddable_factory';
export class VisualizationsPlugin
implements Plugin<VisualizationsPluginSetup, VisualizationsPluginStart> {
@ -35,7 +37,10 @@ export class VisualizationsPlugin
this.config = initializerContext.config.legacy.globalConfig$;
}
public setup(core: CoreSetup, plugins: { usageCollection?: UsageCollectionSetup }) {
public setup(
core: CoreSetup,
plugins: { usageCollection?: UsageCollectionSetup; embeddable: EmbeddableSetup }
) {
this.logger.debug('visualizations: Setup');
core.savedObjects.registerType(visualizationSavedObjectType);
@ -59,6 +64,8 @@ export class VisualizationsPlugin
registerVisualizationsCollector(plugins.usageCollection, this.config);
}
plugins.embeddable.registerEmbeddableFactory(visualizeEmbeddableFactory());
return {};
}

View file

@ -7,7 +7,7 @@
*/
import { SavedObjectsType } from 'kibana/server';
import { visualizationSavedObjectTypeMigrations } from './visualization_migrations';
import { visualizationSavedObjectTypeMigrations } from '../migrations/visualization_saved_object_migrations';
export const visualizationSavedObjectType: SavedObjectsType = {
name: 'visualization',