Skip to content

Commit

Permalink
Ignore expressions that are already migrated
Browse files Browse the repository at this point in the history
  • Loading branch information
wylieconlon committed Apr 29, 2020
1 parent cbf7ecc commit 93e6956
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 80 deletions.
28 changes: 28 additions & 0 deletions x-pack/plugins/lens/server/migrations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,5 +261,33 @@ describe('Lens migrations', () => {
const result = migrations['7.8.0'](example, context);
expect(result.attributes.expression).toContain(`timeField=\"products.created_on\"`);
});

it('should handle pre-migrated expression', () => {
const input = {
type: 'lens',
attributes: {
...example.attributes,
expression: `kibana
| kibana_context query="{\\"query\\":\\"\\",\\"language\\":\\"kuery\\"}" filters="[]"
| lens_merge_tables layerIds="bd09dc71-a7e2-42d0-83bd-85df8291f03c"
tables={esaggs
index="ff959d40-b880-11e8-a6d9-e546fe2bba5f"
metricsAtAllLevels=false
partialRows=false
includeFormatHints=true
aggConfigs="[{\\"id\\":\\"1d9cc16c-1460-41de-88f8-471932ecbc97\\",\\"enabled\\":true,\\"type\\":\\"date_histogram\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"products.created_on\\",\\"useNormalizedEsInterval\\":true,\\"interval\\":\\"auto\\",\\"drop_partials\\":false,\\"min_doc_count\\":0,\\"extended_bounds\\":{}}},{\\"id\\":\\"66115819-8481-4917-a6dc-8ffb10dd02df\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}}]"
timeField=\\"products.created_on\\"
}
| lens_xy_chart
xTitle="products.created_on"
yTitle="Count of records"
legend={lens_xy_legendConfig isVisible=true position="right"}
layers={}
`,
},
};
const result = migrations['7.8.0'](input, context);
expect(result).toEqual(input);
});
});
});
183 changes: 103 additions & 80 deletions x-pack/plugins/lens/server/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { cloneDeep, flow } from 'lodash';
import { fromExpression, toExpression, Ast, ExpressionFunctionAST } from '@kbn/interpreter/common';
import { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'src/core/server';
import { SavedObjectMigrationFn } from 'src/core/server';

interface XYLayerPre77 {
layerId: string;
Expand All @@ -15,98 +15,121 @@ interface XYLayerPre77 {
accessors: string[];
}

function removeLensAutoDate(doc: SavedObjectUnsanitizedDoc) {
/**
* Removes the `lens_auto_date` subexpression from a stored expression
* string. For example: aggConfigs={lens_auto_date aggConfigs="JSON string"}
*/
const removeLensAutoDate: SavedObjectMigrationFn = (doc, context) => {
const expression: string = doc.attributes?.expression;
const ast = fromExpression(expression);
try {
const ast = fromExpression(expression);
const newChain: ExpressionFunctionAST[] = ast.chain.map(topNode => {
if (topNode.function !== 'lens_merge_tables') {
return topNode;
}
return {
...topNode,
arguments: {
...topNode.arguments,
tables: (topNode.arguments.tables as Ast[]).map(middleNode => {
return {
type: 'expression',
chain: middleNode.chain.map(node => {
// Check for sub-expression in aggConfigs
if (
node.function === 'esaggs' &&
typeof node.arguments.aggConfigs[0] !== 'string'
) {
return {
...node,
arguments: {
...node.arguments,
aggConfigs: (node.arguments.aggConfigs[0] as Ast).chain[0].arguments
.aggConfigs,
},
};
}
return node;
}),
};
}),
},
};
});

const newChain: ExpressionFunctionAST[] = ast.chain.map(topNode => {
if (topNode.function !== 'lens_merge_tables') {
return topNode;
}
return {
...topNode,
arguments: {
...topNode.arguments,
tables: (topNode.arguments.tables as Ast[]).map(middleNode => {
return {
type: 'expression',
chain: middleNode.chain.map(node => {
if (node.function === 'esaggs') {
...doc,
attributes: {
...doc.attributes,
expression: toExpression({ ...ast, chain: newChain }),
},
};
} catch (e) {
context.log.warning(e.message);
return { ...doc };
}
};

/**
* Adds missing timeField arguments to esaggs in the Lens expression
*/
const addTimeFieldToEsaggs: SavedObjectMigrationFn = (doc, context) => {
const expression: string = doc.attributes?.expression;

try {
const ast = fromExpression(expression);
const newChain: ExpressionFunctionAST[] = ast.chain.map(topNode => {
if (topNode.function !== 'lens_merge_tables') {
return topNode;
}
return {
...topNode,
arguments: {
...topNode.arguments,
tables: (topNode.arguments.tables as Ast[]).map(middleNode => {
return {
type: 'expression',
chain: middleNode.chain.map(node => {
// Skip if there are any timeField arguments already, because that indicates
// the fix is already applied
if (node.function !== 'esaggs' || node.arguments.timeField) {
return node;
}
const timeField: string[] = [];
JSON.parse(node.arguments.aggConfigs[0] as string).forEach(
(agg: { type: string; params: { field: string } }) => {
if (agg.type !== 'date_histogram') {
return;
}
timeField.push(agg.params.field);
}
);
return {
...node,
arguments: {
...node.arguments,
aggConfigs: (node.arguments.aggConfigs[0] as Ast).chain[0].arguments.aggConfigs,
timeField,
},
};
}
return node;
}),
};
}),
},
};
});

return {
...doc,
attributes: {
...doc.attributes,
expression: toExpression({ ...ast, chain: newChain }),
},
};
}
}),
};
}),
},
};
});

function addTimeFieldToEsaggs(doc: SavedObjectUnsanitizedDoc) {
const expression: string = doc.attributes?.expression;
const ast = fromExpression(expression);

const newChain: ExpressionFunctionAST[] = ast.chain.map(topNode => {
if (topNode.function !== 'lens_merge_tables') {
return topNode;
}
return {
...topNode,
arguments: {
...topNode.arguments,
tables: (topNode.arguments.tables as Ast[]).map(middleNode => {
return {
type: 'expression',
chain: middleNode.chain.map(node => {
if (node.function !== 'esaggs') {
return node;
}
const timeField: string[] = [];
JSON.parse(node.arguments.aggConfigs[0] as string).forEach(
(agg: { type: string; params: { field: string } }) => {
if (agg.type !== 'date_histogram') {
return;
}
timeField.push(agg.params.field);
}
);
return {
...node,
arguments: {
...node.arguments,
timeField,
},
};
}),
};
}),
...doc,
attributes: {
...doc.attributes,
expression: toExpression({ ...ast, chain: newChain }),
},
};
});

return {
...doc,
attributes: {
...doc.attributes,
expression: toExpression({ ...ast, chain: newChain }),
},
};
}
} catch (e) {
context.log.warning(e.message);
return { ...doc };
}
};

export const migrations: Record<string, SavedObjectMigrationFn> = {
'7.7.0': doc => {
Expand Down

0 comments on commit 93e6956

Please sign in to comment.