[Canvas] Feat: make an empty pipeline expression legal (#28796)

This commit is contained in:
Robert Monfera 2019-04-03 00:57:03 +02:00 committed by GitHub
parent 9036aa49f4
commit 2e37f0d62f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 32 additions and 20 deletions

View file

@ -22,17 +22,21 @@ import { getType } from './get_type';
describe('ast fromExpression', () => {
describe('invalid expression', () => {
it('throws when empty', () => {
const check = () => fromExpression('');
expect(check).toThrowError(/Unable to parse expression/i);
});
it('throws with invalid expression', () => {
const check = () => fromExpression('wat!');
expect(check).toThrowError(/Unable to parse expression/i);
});
});
describe('zero-item expression', () => {
it('yields a zero-length chain when empty', () => {
const expression = '';
const astObject = fromExpression(expression);
expect(astObject).toHaveProperty('chain');
expect(astObject.chain).toEqual([]);
});
});
describe('single item expression', () => {
it('is a chain', () => {
const expression = 'whatever';

View file

@ -147,7 +147,7 @@ function peg$parse(input, options) {
peg$c3 = function(first, rest) {
return addMeta({
type: 'expression',
chain: [first].concat(rest)
chain: first ? [first].concat(rest) : []
}, text(), location());
},
peg$c4 = peg$otherExpectation("function"),
@ -364,6 +364,9 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
s2 = peg$parsefunction();
if (s2 === peg$FAILED) {
s2 = null;
}
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$currPos;

View file

@ -5,6 +5,9 @@
* Yes, technically you can load this and build the parser in real time, but this makes it annoying
* to share the grammar between the front end and back, so, you know, just generate the parser.
* You shouldn't be futzing around in the grammar very often anyway.
*
* Instructions for generating `grammar.json`: https://github.com/elastic/kibana/issues/28776#issue-399489673
*
*/
{
@ -20,10 +23,10 @@ start
= expression
expression
= space? first:function rest:('|' space? fn:function { return fn; })* {
= space? first:function? rest:('|' space? fn:function { return fn; })* {
return addMeta({
type: 'expression',
chain: [first].concat(rest)
chain: first ? [first].concat(rest) : []
}, text(), location());
}

View file

@ -145,7 +145,7 @@ export function interpreterProvider(config) {
const argAstsWithDefaults = reduce(
argDefs,
(argAsts, argDef, argName) => {
if (typeof argAsts[argName] === 'undefined' && typeof argDef.default !== 'undefined') {
if (typeof argAsts[argName] === 'undefined' && typeof argDef.default !== 'undefined') {
argAsts[argName] = [fromExpression(argDef.default, 'argument')];
}

View file

@ -16,16 +16,18 @@ export const timeFilter = () => ({
help: 'Set a time window',
reuseDomNode: true, // must be true, otherwise filters get reset when re-rendered
render(domNode, config, handlers) {
const ast = fromExpression(handlers.getFilter());
// Check if the current column is what we expect it to be. If the user changes column this will be called again,
// but we don't want to run setFilter() unless we have to because it will cause a data refresh
const column = get(ast, 'chain[0].arguments.column[0]');
if (column !== config.column) {
set(ast, 'chain[0].arguments.column[0]', config.column);
handlers.setFilter(toExpression(ast));
const filterExpression = handlers.getFilter();
const filterExists = filterExpression !== '';
const ast = fromExpression(filterExpression);
if (filterExists) {
// Check if the current column is what we expect it to be. If the user changes column this will be called again,
// but we don't want to run setFilter() unless we have to because it will cause a data refresh
const column = get(ast, 'chain[0].arguments.column[0]');
if (column !== config.column) {
set(ast, 'chain[0].arguments.column[0]', config.column);
handlers.setFilter(toExpression(ast));
}
}
ReactDOM.render(
<TimeFilter
compact={config.compact}

View file

@ -14,5 +14,5 @@ export const Debug = ({ payload }) => (
);
Debug.propTypes = {
payload: PropTypes.object.isRequired,
payload: PropTypes.object,
};

View file

@ -25,7 +25,7 @@ export class RenderWithFn extends React.Component {
destroy: PropTypes.func.isRequired,
onDestroy: PropTypes.func.isRequired,
}),
config: PropTypes.object.isRequired,
config: PropTypes.object,
size: PropTypes.object.isRequired,
onError: PropTypes.func.isRequired,
};