From c18944f156a7f990a2348230e6d8d14289a094ef Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 1 Jun 2021 17:23:48 +0300 Subject: [PATCH] [Lens] By Value Migrations for 7.13 (#100622) (#101031) * quick fix for 7.13 lens migration not being run on by value panels Co-authored-by: Joe Reuter Co-authored-by: Devon Thomson Co-authored-by: Joe Reuter Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../embeddable/lens_embeddable_factory.ts | 29 +++++ .../saved_object_migrations.test.ts.snap} | 0 .../server/migrations/common_migrations.ts | 46 +++++++ .../saved_object_migrations.test.ts} | 30 ++++- .../saved_object_migrations.ts} | 120 ++---------------- .../plugins/lens/server/migrations/types.ts | 89 +++++++++++++ x-pack/plugins/lens/server/plugin.tsx | 4 + x-pack/plugins/lens/server/saved_objects.ts | 2 +- 8 files changed, 201 insertions(+), 119 deletions(-) create mode 100644 x-pack/plugins/lens/server/embeddable/lens_embeddable_factory.ts rename x-pack/plugins/lens/server/{__snapshots__/migrations.test.ts.snap => migrations/__snapshots__/saved_object_migrations.test.ts.snap} (100%) create mode 100644 x-pack/plugins/lens/server/migrations/common_migrations.ts rename x-pack/plugins/lens/server/{migrations.test.ts => migrations/saved_object_migrations.test.ts} (98%) rename x-pack/plugins/lens/server/{migrations.ts => migrations/saved_object_migrations.ts} (80%) create mode 100644 x-pack/plugins/lens/server/migrations/types.ts diff --git a/x-pack/plugins/lens/server/embeddable/lens_embeddable_factory.ts b/x-pack/plugins/lens/server/embeddable/lens_embeddable_factory.ts new file mode 100644 index 000000000000..ddc822f37b95 --- /dev/null +++ b/x-pack/plugins/lens/server/embeddable/lens_embeddable_factory.ts @@ -0,0 +1,29 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EmbeddableRegistryDefinition } from 'src/plugins/embeddable/server'; +import { SerializableState } from '../../../../../src/plugins/kibana_utils/common'; +import { DOC_TYPE } from '../../common'; +import { commonRenameOperationsForFormula } from '../migrations/common_migrations'; +import { LensDocShapePre712 } from '../migrations/types'; + +export const lensEmbeddableFactory = (): EmbeddableRegistryDefinition => { + return { + id: DOC_TYPE, + migrations: { + // This migration is run in 7.13.1 for `by value` panels because the 7.13 release window was missed. + '7.13.1': (state) => { + const lensState = (state as unknown) as { attributes: LensDocShapePre712 }; + const migratedLensState = commonRenameOperationsForFormula(lensState.attributes); + return ({ + ...lensState, + attributes: migratedLensState, + } as unknown) as SerializableState; + }, + }, + }; +}; diff --git a/x-pack/plugins/lens/server/__snapshots__/migrations.test.ts.snap b/x-pack/plugins/lens/server/migrations/__snapshots__/saved_object_migrations.test.ts.snap similarity index 100% rename from x-pack/plugins/lens/server/__snapshots__/migrations.test.ts.snap rename to x-pack/plugins/lens/server/migrations/__snapshots__/saved_object_migrations.test.ts.snap diff --git a/x-pack/plugins/lens/server/migrations/common_migrations.ts b/x-pack/plugins/lens/server/migrations/common_migrations.ts new file mode 100644 index 000000000000..85055e471bac --- /dev/null +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -0,0 +1,46 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { cloneDeep } from 'lodash'; +import { LensDocShapePre712, OperationTypePre712, LensDocShapePost712 } from './types'; + +export const commonRenameOperationsForFormula = ( + attributes: LensDocShapePre712 +): LensDocShapePost712 => { + const renameMapping = { + avg: 'average', + cardinality: 'unique_count', + derivative: 'differences', + } as const; + function shouldBeRenamed(op: OperationTypePre712): op is keyof typeof renameMapping { + return op in renameMapping; + } + const newAttributes = cloneDeep(attributes); + const datasourceLayers = newAttributes.state.datasourceStates.indexpattern.layers || {}; + (newAttributes as LensDocShapePost712).state.datasourceStates.indexpattern.layers = Object.fromEntries( + Object.entries(datasourceLayers).map(([layerId, layer]) => { + return [ + layerId, + { + ...layer, + columns: Object.fromEntries( + Object.entries(layer.columns).map(([columnId, column]) => { + const copy = { + ...column, + operationType: shouldBeRenamed(column.operationType) + ? renameMapping[column.operationType] + : column.operationType, + }; + return [columnId, copy]; + }) + ), + }, + ]; + }) + ); + return newAttributes as LensDocShapePost712; +}; diff --git a/x-pack/plugins/lens/server/migrations.test.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts similarity index 98% rename from x-pack/plugins/lens/server/migrations.test.ts rename to x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts index bed19942e52b..5478d86e9b14 100644 --- a/x-pack/plugins/lens/server/migrations.test.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts @@ -5,8 +5,12 @@ * 2.0. */ -import { migrations, LensDocShape } from './migrations'; -import { SavedObjectMigrationContext, SavedObjectMigrationFn } from 'src/core/server'; +import { migrations, LensDocShape } from './saved_object_migrations'; +import { + SavedObjectMigrationContext, + SavedObjectMigrationFn, + SavedObjectUnsanitizedDoc, +} from 'src/core/server'; describe('Lens migrations', () => { describe('7.7.0 missing dimensions in XY', () => { @@ -767,10 +771,7 @@ describe('Lens migrations', () => { }, }; - it('should rename only specific operation types', () => { - const result = migrations['7.13.0'](example, context) as ReturnType< - SavedObjectMigrationFn - >; + const validate = (result: SavedObjectUnsanitizedDoc>) => { const layers = result.attributes.state.datasourceStates.indexpattern.layers; expect(layers).toEqual({ '5ab74ddc-93ca-44e2-9857-ecf85c86b53e': { @@ -832,6 +833,23 @@ describe('Lens migrations', () => { expect(result.attributes.state.query).toEqual(example.attributes.state.query); expect(result.attributes.state.filters).toEqual(example.attributes.state.filters); expect(result.attributes.title).toEqual(example.attributes.title); + }; + + it('should rename only specific operation types', () => { + const result = migrations['7.13.0'](example, context) as ReturnType< + SavedObjectMigrationFn + >; + validate(result); + }); + + it('can be applied multiple times', () => { + const result1 = migrations['7.13.0'](example, context) as ReturnType< + SavedObjectMigrationFn + >; + const result2 = migrations['7.13.1'](result1, context) as ReturnType< + SavedObjectMigrationFn + >; + validate(result2); }); }); }); diff --git a/x-pack/plugins/lens/server/migrations.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts similarity index 80% rename from x-pack/plugins/lens/server/migrations.ts rename to x-pack/plugins/lens/server/migrations/saved_object_migrations.ts index 430c1a6caa66..ba7004ba67a9 100644 --- a/x-pack/plugins/lens/server/migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -14,7 +14,9 @@ import { SavedObjectUnsanitizedDoc, } from 'src/core/server'; import { Query, Filter } from 'src/plugins/data/public'; -import { PersistableFilter } from '../common'; +import { PersistableFilter } from '../../common'; +import { LensDocShapePost712, LensDocShapePre712 } from './types'; +import { commonRenameOperationsForFormula } from './common_migrations'; interface LensDocShapePre710 { visualizationType: string | null; @@ -106,86 +108,6 @@ interface DatatableStatePost711 { }; } -type OperationTypePre712 = - | 'avg' - | 'cardinality' - | 'derivative' - | 'filters' - | 'terms' - | 'date_histogram' - | 'min' - | 'max' - | 'sum' - | 'median' - | 'percentile' - | 'last_value' - | 'count' - | 'range' - | 'cumulative_sum' - | 'counter_rate' - | 'moving_average'; -type OperationTypePost712 = Exclude< - OperationTypePre712 | 'average' | 'unique_count' | 'differences', - 'avg' | 'cardinality' | 'derivative' ->; -interface LensDocShapePre712 { - visualizationType: string | null; - title: string; - expression: string | null; - state: { - datasourceStates: { - // This is hardcoded as our only datasource - indexpattern: { - layers: Record< - string, - { - columns: Record< - string, - { - operationType: OperationTypePre712; - } - >; - } - >; - }; - }; - visualization: VisualizationState; - query: Query; - filters: Filter[]; - }; -} - -interface LensDocShapePost712 { - visualizationType: string | null; - title: string; - expression: string | null; - state: { - datasourceMetaData: { - filterableIndexPatterns: Array<{ id: string; title: string }>; - }; - datasourceStates: { - // This is hardcoded as our only datasource - indexpattern: { - currentIndexPatternId: string; - layers: Record< - string, - { - columns: Record< - string, - { - operationType: OperationTypePost712; - } - >; - } - >; - }; - }; - visualization: VisualizationState; - query: Query; - filters: Filter[]; - }; -} - /** * Removes the `lens_auto_date` subexpression from a stored expression * string. For example: aggConfigs={lens_auto_date aggConfigs="JSON string"} @@ -471,38 +393,11 @@ const renameOperationsForFormula: SavedObjectMigrationFn< LensDocShapePre712, LensDocShapePost712 > = (doc) => { - const renameMapping = { - avg: 'average', - cardinality: 'unique_count', - derivative: 'differences', - } as const; - function shouldBeRenamed(op: OperationTypePre712): op is keyof typeof renameMapping { - return op in renameMapping; - } const newDoc = cloneDeep(doc); - const datasourceLayers = newDoc.attributes.state.datasourceStates.indexpattern.layers || {}; - (newDoc.attributes as LensDocShapePost712).state.datasourceStates.indexpattern.layers = Object.fromEntries( - Object.entries(datasourceLayers).map(([layerId, layer]) => { - return [ - layerId, - { - ...layer, - columns: Object.fromEntries( - Object.entries(layer.columns).map(([columnId, column]) => { - const copy = { - ...column, - operationType: shouldBeRenamed(column.operationType) - ? renameMapping[column.operationType] - : column.operationType, - }; - return [columnId, copy]; - }) - ), - }, - ]; - }) - ); - return newDoc as SavedObjectUnsanitizedDoc; + return { + ...newDoc, + attributes: commonRenameOperationsForFormula(newDoc.attributes), + }; }; export const migrations: SavedObjectMigrationMap = { @@ -514,4 +409,5 @@ export const migrations: SavedObjectMigrationMap = { '7.11.0': removeSuggestedPriority, '7.12.0': transformTableState, '7.13.0': renameOperationsForFormula, + '7.13.1': renameOperationsForFormula, // duplicate this migration in case a broken by value panel is added to the library }; diff --git a/x-pack/plugins/lens/server/migrations/types.ts b/x-pack/plugins/lens/server/migrations/types.ts new file mode 100644 index 000000000000..38e079ff3805 --- /dev/null +++ b/x-pack/plugins/lens/server/migrations/types.ts @@ -0,0 +1,89 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Query, Filter } from 'src/plugins/data/public'; + +export type OperationTypePre712 = + | 'avg' + | 'cardinality' + | 'derivative' + | 'filters' + | 'terms' + | 'date_histogram' + | 'min' + | 'max' + | 'sum' + | 'median' + | 'percentile' + | 'last_value' + | 'count' + | 'range' + | 'cumulative_sum' + | 'counter_rate' + | 'moving_average'; +export type OperationTypePost712 = Exclude< + OperationTypePre712 | 'average' | 'unique_count' | 'differences', + 'avg' | 'cardinality' | 'derivative' +>; + +export interface LensDocShapePre712 { + visualizationType: string | null; + title: string; + expression: string | null; + state: { + datasourceStates: { + // This is hardcoded as our only datasource + indexpattern: { + layers: Record< + string, + { + columns: Record< + string, + { + operationType: OperationTypePre712; + } + >; + } + >; + }; + }; + query: Query; + visualization: VisualizationState; + filters: Filter[]; + }; +} + +export interface LensDocShapePost712 { + visualizationType: string | null; + title: string; + expression: string | null; + state: { + datasourceMetaData: { + filterableIndexPatterns: Array<{ id: string; title: string }>; + }; + datasourceStates: { + // This is hardcoded as our only datasource + indexpattern: { + currentIndexPatternId: string; + layers: Record< + string, + { + columns: Record< + string, + { + operationType: OperationTypePost712; + } + >; + } + >; + }; + }; + visualization: VisualizationState; + query: Query; + filters: Filter[]; + }; +} diff --git a/x-pack/plugins/lens/server/plugin.tsx b/x-pack/plugins/lens/server/plugin.tsx index 92b14ba509ba..c23c98cd12ae 100644 --- a/x-pack/plugins/lens/server/plugin.tsx +++ b/x-pack/plugins/lens/server/plugin.tsx @@ -17,10 +17,13 @@ import { scheduleLensTelemetry, } from './usage'; import { setupSavedObjects } from './saved_objects'; +import { EmbeddableSetup } from '../../../../src/plugins/embeddable/server'; +import { lensEmbeddableFactory } from './embeddable/lens_embeddable_factory'; export interface PluginSetupContract { usageCollection?: UsageCollectionSetup; taskManager?: TaskManagerSetupContract; + embeddable: EmbeddableSetup; } export interface PluginStartContract { @@ -53,6 +56,7 @@ export class LensServerPlugin implements Plugin<{}, {}, {}, {}> { plugins.taskManager ); } + plugins.embeddable.registerEmbeddableFactory(lensEmbeddableFactory()); return {}; } diff --git a/x-pack/plugins/lens/server/saved_objects.ts b/x-pack/plugins/lens/server/saved_objects.ts index 202439abf037..0266378981fd 100644 --- a/x-pack/plugins/lens/server/saved_objects.ts +++ b/x-pack/plugins/lens/server/saved_objects.ts @@ -7,7 +7,7 @@ import { CoreSetup } from 'kibana/server'; import { getEditPath } from '../common'; -import { migrations } from './migrations'; +import { migrations } from './migrations/saved_object_migrations'; export function setupSavedObjects(core: CoreSetup) { core.savedObjects.registerType({