Skip to content

Commit 54e087d

Browse files
committed
[Lens] Migration from 7.7
1 parent eacdbcd commit 54e087d

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { migrations, RawLensSavedXYObject770 } from './migrations';
8+
import { SimpleSavedObject } from 'src/core/public';
9+
10+
describe('Lens migrations', () => {
11+
describe('7.7.0 missing dimensions in XY', () => {
12+
const migrate = (doc: SimpleSavedObject | RawLensSavedXYObject770) => migrations['7.7.0'](doc);
13+
14+
const example: RawLensSavedXYObject770 = {
15+
type: 'lens',
16+
attributes: {
17+
expression:
18+
'kibana\n| kibana_context query="{\\"language\\":\\"kuery\\",\\"query\\":\\"\\"}" \n| lens_merge_tables layerIds="c61a8afb-a185-4fae-a064-fb3846f6c451" \n tables={esaggs index="logstash-*" metricsAtAllLevels=false partialRows=false includeFormatHints=true aggConfigs="[{\\"id\\":\\"2cd09808-3915-49f4-b3b0-82767eba23f7\\",\\"enabled\\":true,\\"type\\":\\"max\\",\\"schema\\":\\"metric\\",\\"params\\":{\\"field\\":\\"bytes\\"}}]" | lens_rename_columns idMap="{\\"col-0-2cd09808-3915-49f4-b3b0-82767eba23f7\\":\\"2cd09808-3915-49f4-b3b0-82767eba23f7\\"}"}\n| lens_metric_chart title="Maximum of bytes" accessor="2cd09808-3915-49f4-b3b0-82767eba23f7"',
19+
state: {
20+
datasourceMetaData: {
21+
filterableIndexPatterns: [
22+
{
23+
id: 'logstash-*',
24+
title: 'logstash-*',
25+
},
26+
],
27+
},
28+
datasourceStates: {
29+
indexpattern: {
30+
currentIndexPatternId: 'logstash-*',
31+
layers: {
32+
'c61a8afb-a185-4fae-a064-fb3846f6c451': {
33+
columnOrder: ['2cd09808-3915-49f4-b3b0-82767eba23f7'],
34+
columns: {
35+
'2cd09808-3915-49f4-b3b0-82767eba23f7': {
36+
dataType: 'number',
37+
isBucketed: false,
38+
label: 'Maximum of bytes',
39+
operationType: 'max',
40+
scale: 'ratio',
41+
sourceField: 'bytes',
42+
},
43+
'd3e62a7a-c259-4fff-a2fc-eebf20b7008a': {
44+
dataType: 'number',
45+
isBucketed: false,
46+
label: 'Minimum of bytes',
47+
operationType: 'min',
48+
scale: 'ratio',
49+
sourceField: 'bytes',
50+
},
51+
'd6e40cea-6299-43b4-9c9d-b4ee305a2ce8': {
52+
dataType: 'date',
53+
isBucketed: true,
54+
label: 'Date Histogram of @timestamp',
55+
operationType: 'date_histogram',
56+
params: {
57+
interval: 'auto',
58+
},
59+
scale: 'interval',
60+
sourceField: '@timestamp',
61+
},
62+
},
63+
indexPatternId: 'logstash-*',
64+
},
65+
},
66+
},
67+
},
68+
filters: [],
69+
query: {
70+
language: 'kuery',
71+
query: '',
72+
},
73+
visualization: {
74+
accessor: '2cd09808-3915-49f4-b3b0-82767eba23f7',
75+
isHorizontal: false,
76+
layerId: 'c61a8afb-a185-4fae-a064-fb3846f6c451',
77+
layers: [
78+
{
79+
accessors: [
80+
'd3e62a7a-c259-4fff-a2fc-eebf20b7008a',
81+
'26ef70a9-c837-444c-886e-6bd905ee7335',
82+
],
83+
layerId: 'c61a8afb-a185-4fae-a064-fb3846f6c451',
84+
seriesType: 'area',
85+
splitAccessor: '54cd64ed-2a44-4591-af84-b2624504569a',
86+
xAccessor: 'd6e40cea-6299-43b4-9c9d-b4ee305a2ce8',
87+
},
88+
],
89+
legend: {
90+
isVisible: true,
91+
position: 'right',
92+
},
93+
preferredSeriesType: 'area',
94+
},
95+
},
96+
title: 'Artistpreviouslyknownaslens',
97+
visualizationType: 'lnsXY',
98+
},
99+
};
100+
101+
it('should not change anything by XY visualizations', () => {
102+
const target = {
103+
...example,
104+
attributes: {
105+
...example.attributes,
106+
visualizationType: 'lnsMetric',
107+
},
108+
};
109+
const result = migrate(target as SimpleSavedObject);
110+
expect(result).toEqual(target);
111+
});
112+
113+
it('should handle missing layers', () => {
114+
const result = migrate({
115+
...example,
116+
attributes: {
117+
...example.attributes,
118+
state: {
119+
...example.attributes.state,
120+
datasourceStates: {
121+
indexpattern: {
122+
layers: [],
123+
},
124+
},
125+
},
126+
},
127+
} as SimpleSavedObject) as RawLensSavedXYObject770;
128+
129+
expect(result.attributes.state.visualization.layers).toEqual([
130+
{
131+
layerId: 'c61a8afb-a185-4fae-a064-fb3846f6c451',
132+
seriesType: 'area',
133+
// Removed split accessor
134+
splitAccessor: undefined,
135+
xAccessor: undefined,
136+
// Removed a yAcccessor
137+
accessors: [],
138+
},
139+
]);
140+
});
141+
142+
it('should remove only missing accessors', () => {
143+
const result = migrate(example) as RawLensSavedXYObject770;
144+
145+
expect(result.attributes.state.visualization.layers).toEqual([
146+
{
147+
layerId: 'c61a8afb-a185-4fae-a064-fb3846f6c451',
148+
seriesType: 'area',
149+
xAccessor: 'd6e40cea-6299-43b4-9c9d-b4ee305a2ce8',
150+
// Removed split accessor
151+
splitAccessor: undefined,
152+
// Removed a yAcccessor
153+
accessors: ['d3e62a7a-c259-4fff-a2fc-eebf20b7008a'],
154+
},
155+
]);
156+
});
157+
});
158+
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { cloneDeep } from 'lodash';
8+
import { SimpleSavedObject } from 'src/core/server';
9+
10+
export interface RawLensSavedXYObject770 {
11+
type: 'lens';
12+
attributes: Record<string, unknown> & {
13+
visualizationType: string;
14+
state: Record<string, unknown> & {
15+
datasourceStates?: Record<string, unknown> & {
16+
indexpattern?: Record<string, unknown> & {
17+
layers: Record<string, Record<string, unknown> & { columns: Record<string, unknown> }>;
18+
};
19+
};
20+
visualization: Record<string, unknown> & {
21+
layers: Array<
22+
Record<string, unknown> & {
23+
layerId: string;
24+
accessors: string[];
25+
xAccessor: string;
26+
splitAccessor: string;
27+
}
28+
>;
29+
};
30+
};
31+
};
32+
}
33+
34+
type LensSavedXYObjectPost770 = RawLensSavedXYObject770;
35+
36+
function isLensSavedXY770(
37+
doc: SimpleSavedObject | RawLensSavedXYObject770
38+
): doc is RawLensSavedXYObject770 {
39+
return (
40+
doc.type === 'lens' &&
41+
doc.attributes &&
42+
(doc.attributes as Record<string, string>).visualizationType === 'lnsXY'
43+
);
44+
}
45+
46+
export const migrations = {
47+
'7.7.0': (
48+
doc: SimpleSavedObject | RawLensSavedXYObject770
49+
): SimpleSavedObject | LensSavedXYObjectPost770 => {
50+
const newDoc = cloneDeep(doc);
51+
if (!isLensSavedXY770(newDoc)) {
52+
return newDoc;
53+
}
54+
const datasourceState = newDoc.attributes.state?.datasourceStates?.indexpattern;
55+
const datasourceLayers = datasourceState?.layers ?? {};
56+
const xyState = newDoc.attributes.state?.visualization;
57+
newDoc.attributes.state.visualization.layers = xyState.layers.map(layer => {
58+
const layerId = layer.layerId;
59+
const datasource = datasourceLayers[layerId];
60+
return {
61+
...layer,
62+
xAccessor: datasource?.columns[layer.xAccessor] ? layer.xAccessor : undefined,
63+
splitAccessor: datasource?.columns[layer.splitAccessor] ? layer.splitAccessor : undefined,
64+
accessors: layer.accessors.filter(accessor => !!datasource?.columns[accessor]),
65+
};
66+
}) as typeof xyState.layers;
67+
return newDoc;
68+
},
69+
};

x-pack/plugins/lens/server/saved_objects.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { CoreSetup } from 'kibana/server';
88
import { getEditPath } from '../common';
9+
import { migrations } from './migrations';
910

1011
export function setupSavedObjects(core: CoreSetup) {
1112
core.savedObjects.registerType({
@@ -22,6 +23,7 @@ export function setupSavedObjects(core: CoreSetup) {
2223
uiCapabilitiesPath: 'visualize.show',
2324
}),
2425
},
26+
migrations,
2527
mappings: {
2628
properties: {
2729
title: {

0 commit comments

Comments
 (0)