[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 <johannes.reuter@elastic.co>

Co-authored-by: Devon Thomson <devon.thomson@elastic.co>
Co-authored-by: Joe Reuter <johannes.reuter@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Stratoula Kalafateli 2021-06-01 17:23:48 +03:00 committed by GitHub
parent 0a7f567e6a
commit c18944f156
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 201 additions and 119 deletions

View file

@ -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;
},
},
};
};

View file

@ -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;
};

View file

@ -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<LensDocShape, LensDocShape>
>;
const validate = (result: SavedObjectUnsanitizedDoc<LensDocShape<unknown>>) => {
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<LensDocShape, LensDocShape>
>;
validate(result);
});
it('can be applied multiple times', () => {
const result1 = migrations['7.13.0'](example, context) as ReturnType<
SavedObjectMigrationFn<LensDocShape, LensDocShape>
>;
const result2 = migrations['7.13.1'](result1, context) as ReturnType<
SavedObjectMigrationFn<LensDocShape, LensDocShape>
>;
validate(result2);
});
});
});

View file

@ -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<VisualizationState = unknown> {
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<VisualizationState = unknown> {
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<VisualizationState = unknown> {
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<LensDocShapePost712>;
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
};

View file

@ -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<VisualizationState = unknown> {
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<VisualizationState = unknown> {
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[];
};
}

View file

@ -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 {};
}

View file

@ -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({