From 0c7cab16ab195379f17adad7f136f01fb11bc11e Mon Sep 17 00:00:00 2001
From: Melissa Alvarez
Date: Thu, 26 Mar 2020 14:41:51 -0400
Subject: [PATCH] [ML] Data Visualizer, Explorer, Transform wizard: Ensure
query bar syntax errors are shown (#61333)
* remove deprecated kqlFilterBar dir
* remove unused explorer code
* show syntax error message below query input
* remove unused translations
* syntax error message for datavisualizer
* add syntax error handling to transform wizard
* fix translation label
---
x-pack/plugins/ml/common/constants/search.ts | 5 +
.../components/anomalies_table/links_menu.js | 2 +-
.../__snapshots__/kql_filter_bar.test.js.snap | 15 --
.../kql_filter_bar/__tests__/utils.js | 123 ----------
.../__snapshots__/click_outside.test.js.snap | 3 -
.../click_outside/click_outside.js | 42 ----
.../click_outside/click_outside.test.js | 16 --
.../kql_filter_bar/click_outside/index.js | 7 -
.../__snapshots__/filter_bar.test.js.snap | 133 -----------
.../kql_filter_bar/filter_bar/filter_bar.js | 224 ------------------
.../filter_bar/filter_bar.test.js | 83 -------
.../kql_filter_bar/filter_bar/index.js | 7 -
.../components/kql_filter_bar/index.js | 7 -
.../kql_filter_bar/kql_filter_bar.js | 115 ---------
.../kql_filter_bar/kql_filter_bar.test.js | 63 -----
.../__snapshots__/suggestion.test.js.snap | 24 --
.../kql_filter_bar/suggestion/index.js | 7 -
.../kql_filter_bar/suggestion/suggestion.js | 117 ---------
.../suggestion/suggestion.test.js | 30 ---
.../__snapshots__/suggestions.test.js.snap | 40 ----
.../kql_filter_bar/suggestions/index.js | 7 -
.../kql_filter_bar/suggestions/suggestions.js | 81 -------
.../suggestions/suggestions.test.js | 57 -----
.../components/kql_filter_bar/utils.js | 97 --------
.../components/search_panel/search_panel.tsx | 73 +++---
.../explorer_query_bar/explorer_query_bar.tsx | 79 +++---
.../public/application/explorer/explorer.js | 7 +-
.../explorer/explorer_constants.ts | 5 -
.../explorer/explorer_dashboard_service.ts | 6 -
.../application/explorer/explorer_utils.js | 39 +++
.../reducers/explorer_reducer/reducer.ts | 7 -
.../reducers/explorer_reducer/state.ts | 10 -
.../step_define/step_define_form.tsx | 110 ++++++---
.../translations/translations/ja-JP.json | 2 -
.../translations/translations/zh-CN.json | 2 -
35 files changed, 217 insertions(+), 1428 deletions(-)
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/__snapshots__/kql_filter_bar.test.js.snap
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/__tests__/utils.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/__snapshots__/click_outside.test.js.snap
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.test.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/index.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.test.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/index.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/index.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.test.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/__snapshots__/suggestion.test.js.snap
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/index.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.test.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/__snapshots__/suggestions.test.js.snap
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/index.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.test.js
delete mode 100644 x-pack/plugins/ml/public/application/components/kql_filter_bar/utils.js
diff --git a/x-pack/plugins/ml/common/constants/search.ts b/x-pack/plugins/ml/common/constants/search.ts
index e17f6b3098421..da65748668a4f 100644
--- a/x-pack/plugins/ml/common/constants/search.ts
+++ b/x-pack/plugins/ml/common/constants/search.ts
@@ -11,3 +11,8 @@ export enum SEARCH_QUERY_LANGUAGE {
KUERY = 'kuery',
LUCENE = 'lucene',
}
+
+export interface ErrorMessage {
+ query: string;
+ message: string;
+}
diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js
index 7da49a378ec96..2a34f12330a75 100644
--- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js
+++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js
@@ -21,7 +21,7 @@ import { checkPermission } from '../../privilege/check_privilege';
import { SEARCH_QUERY_LANGUAGE } from '../../../../common/constants/search';
import { isRuleSupported } from '../../../../common/util/anomaly_utils';
import { parseInterval } from '../../../../common/util/parse_interval';
-import { escapeDoubleQuotes } from '../kql_filter_bar/utils';
+import { escapeDoubleQuotes } from '../../explorer/explorer_utils';
import { getFieldTypeFromMapping } from '../../services/mapping_service';
import { ml } from '../../services/ml_api_service';
import { mlJobService } from '../../services/job_service';
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/__snapshots__/kql_filter_bar.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/__snapshots__/kql_filter_bar.test.js.snap
deleted file mode 100644
index f2eeb00b6c643..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/__snapshots__/kql_filter_bar.test.js.snap
+++ /dev/null
@@ -1,15 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`KqlFilterBar snapshot 1`] = `
-
-
-
-`;
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/__tests__/utils.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/__tests__/utils.js
deleted file mode 100644
index 6029799ffe8b8..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/__tests__/utils.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import expect from '@kbn/expect';
-import { removeFilterFromQueryString, getQueryPattern, escapeRegExp } from '../utils';
-
-describe('ML - KqlFilterBar utils', () => {
- const fieldName = 'http.response.status_code';
- const fieldValue = '200';
- const speciaCharFieldName = 'normal(brackets)name';
- const speciaCharFieldValue = '<>:;[})';
-
- describe('removeFilterFromQueryString', () => {
- it('removes selected fieldName/fieldValue from query string with one value', () => {
- const currentQueryString = 'http.response.status_code : "200"';
- const expectedOutput = '';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it('removes selected fieldName/fieldValue of type number from existing query string with one value', () => {
- const currentQueryString = 'http.response.status_code : 200';
- const expectedOutput = '';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it('removes selected fieldName/fieldValue from query string with multiple values', () => {
- const currentQueryString = 'test_field : test_value or http.response.status_code : "200"';
- const expectedOutput = 'test_field : test_value';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it('removes selected fieldName/fieldValue of type number from existing query string with multiple values', () => {
- const currentQueryString = 'http.response.status_code : 200 or test_field : test_value';
- const expectedOutput = 'test_field : test_value';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it("removes 'and' from end of the query to ensure kuery syntax is valid", () => {
- const currentQueryString = 'http.response.status_code : "200" and';
- const expectedOutput = '';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it("removes 'or' from end of the query to ensure kuery syntax is valid", () => {
- const currentQueryString = 'http.response.status_code : "200" or';
- const expectedOutput = '';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it("removes 'and' from start of the query to ensure kuery syntax is valid", () => {
- const currentQueryString = ' and http.response.status_code : "200"';
- const expectedOutput = '';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it("removes 'or' from start of the query to ensure kuery syntax is valid", () => {
- const currentQueryString = ' or http.response.status_code : "200" ';
- const expectedOutput = '';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it('removes selected fieldName/fieldValue correctly from AND query string when it is the middle value', () => {
- const currentQueryString = `http.response.status_code : "400" and http.response.status_code : "200"
- and http.response.status_code : "300"`;
- const expectedOutput =
- 'http.response.status_code : "400" and http.response.status_code : "300"';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
-
- it('removes selected fieldName/fieldValue correctly from OR query string when it is the middle value', () => {
- const currentQueryString = `http.response.status_code : "400" or http.response.status_code : "200"
- or http.response.status_code : "300"`;
- const expectedOutput =
- 'http.response.status_code : "400" or http.response.status_code : "300"';
- const result = removeFilterFromQueryString(currentQueryString, fieldName, fieldValue);
- expect(result).to.be(expectedOutput);
- });
- });
-
- describe('getQueryPattern', () => {
- it('creates a regular expression pattern for given fieldName/fieldValue', () => {
- // The source property returns a String containing the source text of the regexp object
- // and it doesn't contain the two forward slashes on both sides and any flags.
- const expectedOutput = /(http\.response\.status_code)\s?:\s?(")?(200)(")?/i.source;
- const result = getQueryPattern(fieldName, fieldValue).source;
- expect(result).to.be(expectedOutput);
- });
-
- it('creates a regular expression pattern for given fieldName/fieldValue containing special characters', () => {
- // The source property returns a String containing the source text of the regexp object
- // and it doesn't contain the two forward slashes on both sides and any flags.
- const expectedOutput = /(normal\(brackets\)name)\s?:\s?(")?(<>:;\[\}\))(")?/i.source;
- const result = getQueryPattern(speciaCharFieldName, speciaCharFieldValue).source;
- expect(result).to.be(expectedOutput);
- });
- });
-
- describe('escapeRegExp', () => {
- it('escapes regex special characters for given fieldName/fieldValue', () => {
- // The source property returns a String containing the source text of the regexp object
- // and it doesn't contain the two forward slashes on both sides and any flags.
- const expectedFieldName = 'normal\\(brackets\\)name';
- const expectedFieldValue = '<>:;\\[\\}\\)';
- const resultFieldName = escapeRegExp(speciaCharFieldName);
- const resultFieldValue = escapeRegExp(speciaCharFieldValue);
-
- expect(resultFieldName).to.be(expectedFieldName);
- expect(resultFieldValue).to.be(expectedFieldValue);
- });
- });
-});
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/__snapshots__/click_outside.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/__snapshots__/click_outside.test.js.snap
deleted file mode 100644
index eb3e5e6005dee..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/__snapshots__/click_outside.test.js.snap
+++ /dev/null
@@ -1,3 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ClickOutside snapshot 1`] = ``;
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.js
deleted file mode 100644
index 02d6750dca965..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-
-export class ClickOutside extends Component {
- componentDidMount() {
- document.addEventListener('mousedown', this.onClick);
- }
-
- componentWillUnmount() {
- document.removeEventListener('mousedown', this.onClick);
- }
-
- setNodeRef = node => {
- this.nodeRef = node;
- };
-
- onClick = event => {
- if (this.nodeRef && !this.nodeRef.contains(event.target)) {
- this.props.onClickOutside();
- }
- };
-
- render() {
- const { style, children } = this.props;
-
- return (
-
- {children}
-
- );
- }
-}
-
-ClickOutside.propTypes = {
- onClickOutside: PropTypes.func.isRequired,
-};
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.test.js
deleted file mode 100644
index 1cd1dc6e4d715..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/click_outside.test.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { shallow } from 'enzyme';
-import { ClickOutside } from './click_outside';
-
-describe('ClickOutside', () => {
- test('snapshot', () => {
- const wrapper = shallow( {}} />);
- expect(wrapper).toMatchSnapshot();
- });
-});
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/index.js
deleted file mode 100644
index 884481f9848dd..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/click_outside/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { ClickOutside } from './click_outside';
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap
deleted file mode 100644
index f3c825a66ee2f..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap
+++ /dev/null
@@ -1,133 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`FilterBar snapshot suggestions not shown 1`] = `
-
-
-
-
- Test description for fieldValueOne
",
- "end": 1,
- "start": 0,
- "text": "fieldValueOne",
- "type": "field",
- },
- Object {
- "description": "Test description for fieldValueTwo
",
- "end": 1,
- "start": 0,
- "text": "fieldValueTwo",
- "type": "field",
- },
- ]
- }
- />
-
-`;
-
-exports[`FilterBar snapshot suggestions shown 1`] = `
-
-
-
-
- Test description for fieldValueOne",
- "end": 1,
- "start": 0,
- "text": "fieldValueOne",
- "type": "field",
- },
- Object {
- "description": "Test description for fieldValueTwo
",
- "end": 1,
- "start": 0,
- "text": "fieldValueTwo",
- "type": "field",
- },
- ]
- }
- />
-
-`;
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.js
deleted file mode 100644
index 0c1796a6e01ca..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.js
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { Suggestions } from '../suggestions';
-import { ClickOutside } from '../click_outside';
-import { EuiFieldSearch, EuiProgress, keyCodes } from '@elastic/eui';
-
-export class FilterBar extends Component {
- state = {
- isSuggestionsVisible: false,
- index: null,
- value: '',
- inputIsPristine: true,
- };
-
- static getDerivedStateFromProps(props, state) {
- if (state.inputIsPristine && props.initialValue) {
- return {
- value: props.initialValue,
- };
- }
-
- return null;
- }
-
- // Set value to filter created via filter table
- componentDidUpdate(oldProps) {
- const newProps = this.props;
- if (oldProps.valueExternal !== newProps.valueExternal) {
- this.setState({ value: newProps.valueExternal, index: null });
- }
- }
-
- incrementIndex = currentIndex => {
- let nextIndex = currentIndex + 1;
- if (currentIndex === null || nextIndex >= this.props.suggestions.length) {
- nextIndex = 0;
- }
- this.setState({ index: nextIndex });
- };
-
- decrementIndex = currentIndex => {
- let previousIndex = currentIndex - 1;
- if (previousIndex < 0) {
- previousIndex = null;
- }
- this.setState({ index: previousIndex });
- };
-
- onKeyUp = event => {
- const { selectionStart } = event.target;
- const { value } = this.state;
- switch (event.keyCode) {
- case keyCodes.LEFT:
- case keyCodes.RIGHT:
- this.setState({ isSuggestionsVisible: true });
- this.props.onChange(value, selectionStart);
- break;
- }
- };
-
- onKeyDown = event => {
- const { isSuggestionsVisible, index, value } = this.state;
- switch (event.keyCode) {
- case keyCodes.DOWN:
- event.preventDefault();
- if (isSuggestionsVisible) {
- this.incrementIndex(index);
- } else {
- this.setState({ isSuggestionsVisible: true, index: 0 });
- }
- break;
- case keyCodes.UP:
- event.preventDefault();
- if (isSuggestionsVisible) {
- this.decrementIndex(index);
- }
- break;
- case keyCodes.ENTER:
- event.preventDefault();
- if (isSuggestionsVisible && this.props.suggestions[index]) {
- this.selectSuggestion(this.props.suggestions[index]);
- } else {
- this.setState({ isSuggestionsVisible: false });
- this.props.onSubmit(value);
- }
- break;
- case keyCodes.ESC:
- event.preventDefault();
- this.setState({ isSuggestionsVisible: false });
- break;
- case keyCodes.TAB:
- this.setState({ isSuggestionsVisible: false });
- break;
- }
- };
-
- selectSuggestion = suggestion => {
- const nextInputValue =
- this.state.value.substr(0, suggestion.start) +
- suggestion.text +
- this.state.value.substr(suggestion.end);
-
- this.setState({ value: nextInputValue, index: null });
- this.props.onChange(nextInputValue, nextInputValue.length);
- };
-
- onClickOutside = () => {
- this.setState({ isSuggestionsVisible: false });
- };
-
- onChangeInputValue = event => {
- const { value, selectionStart } = event.target;
- const hasValue = Boolean(value.trim());
- this.setState({
- value,
- inputIsPristine: false,
- isSuggestionsVisible: hasValue,
- index: null,
- });
-
- if (!hasValue) {
- this.props.onSubmit(value);
- }
- this.props.onChange(value, selectionStart);
- };
-
- onClickInput = event => {
- const { selectionStart } = event.target;
- this.props.onChange(this.state.value, selectionStart);
- };
-
- onClickSuggestion = suggestion => {
- this.selectSuggestion(suggestion);
- this.inputRef.focus();
- };
-
- onMouseEnterSuggestion = index => {
- this.setState({ index });
- };
-
- onSubmit = () => {
- this.props.onSubmit(this.state.value);
- this.setState({ isSuggestionsVisible: false });
- };
-
- render() {
- const { disabled } = this.props;
- const { value } = this.state;
-
- return (
-
-
- {
- if (node) {
- this.inputRef = node;
- }
- }}
- disabled={disabled}
- value={value}
- onKeyDown={this.onKeyDown}
- onKeyUp={this.onKeyUp}
- onChange={this.onChangeInputValue}
- onClick={this.onClickInput}
- autoComplete="off"
- spellCheck={false}
- data-test-subj={this.props.testSubj}
- />
-
- {this.props.isLoading && (
-
- )}
-
-
-
-
- );
- }
-}
-
-FilterBar.propTypes = {
- initialValue: PropTypes.string,
- isLoading: PropTypes.bool,
- disabled: PropTypes.bool,
- onChange: PropTypes.func.isRequired,
- placeholder: PropTypes.string,
- onSubmit: PropTypes.func.isRequired,
- valueExternal: PropTypes.string,
- suggestions: PropTypes.array.isRequired,
- testSubj: PropTypes.string,
-};
-
-FilterBar.defaultProps = {
- isLoading: false,
- disabled: false,
- placeholder: 'tag : engineering OR tag : marketing',
- suggestions: [],
- testSubj: undefined,
-};
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.test.js
deleted file mode 100644
index 287803f9eb40a..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/filter_bar.test.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { shallow, mount } from 'enzyme';
-import { keyCodes } from '@elastic/eui';
-import { FilterBar } from './filter_bar';
-
-const defaultProps = {
- disabled: false,
- initialValue: '',
- placeholder: 'Test placeholder',
- isLoading: false,
- onChange: () => {},
- onSubmit: () => {},
- suggestions: [
- {
- description: 'Test description for fieldValueOne
',
- end: 1,
- start: 0,
- text: 'fieldValueOne',
- type: 'field',
- },
- {
- description: 'Test description for fieldValueTwo
',
- end: 1,
- start: 0,
- text: 'fieldValueTwo',
- type: 'field',
- },
- ],
-};
-
-describe('FilterBar', () => {
- test('snapshot suggestions not shown', () => {
- const wrapper = shallow();
- expect(wrapper).toMatchSnapshot();
- });
-
- test('snapshot suggestions shown', () => {
- const wrapper = shallow();
- wrapper.setState({ isSuggestionsVisible: true });
- expect(wrapper).toMatchSnapshot();
- });
-
- test('index updated in state when suggestion is navigated to via mouse', () => {
- const wrapper = mount();
- wrapper.setState({ isSuggestionsVisible: true });
-
- expect(wrapper.state('index')).toEqual(null);
-
- const firstSuggestion = wrapper.find('li').first();
- firstSuggestion.simulate('mouseenter');
- expect(wrapper.state('index')).toEqual(0);
- });
-
- test('index updated and suggestions set to visible when input added', () => {
- const wrapper = shallow();
- // default values
- expect(wrapper.state('index')).toEqual(null);
- expect(wrapper.state('isSuggestionsVisible')).toBe(false);
-
- const searchBar = wrapper.find('EuiFieldSearch');
- searchBar.simulate('keydown', { preventDefault: () => {}, keyCode: keyCodes.DOWN });
- wrapper.update();
- // updated values
- expect(wrapper.state('index')).toEqual(0);
- expect(wrapper.state('isSuggestionsVisible')).toBe(true);
- });
-
- test('index updated in state when suggestion is navigated to via keyboard', () => {
- const wrapper = shallow();
- wrapper.setState({ isSuggestionsVisible: true, value: 'f', index: 0 });
- const searchBar = wrapper.find('EuiFieldSearch');
- searchBar.simulate('keydown', { preventDefault: () => {}, keyCode: keyCodes.DOWN });
- wrapper.update();
-
- expect(wrapper.state('index')).toEqual(1);
- });
-});
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/index.js
deleted file mode 100644
index e8153037e0c10..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/filter_bar/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { FilterBar } from './filter_bar';
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/index.js
deleted file mode 100644
index d229943f6afe7..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { KqlFilterBar } from './kql_filter_bar';
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.js
deleted file mode 100644
index 0f3c6d25fe641..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React, { Component, Fragment } from 'react';
-import PropTypes from 'prop-types';
-import { uniqueId } from 'lodash';
-import { FilterBar } from './filter_bar';
-import { EuiCallOut, EuiLink, EuiText } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { getSuggestions, getKqlQueryValues } from './utils';
-import { getDocLinks } from '../../util/dependency_cache';
-
-function getErrorWithLink(errorMessage) {
- const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = getDocLinks();
- return (
-
- {`${errorMessage} Input must be valid `}
-
- {'Kibana Query Language'}
-
- {' (KQL) syntax.'}
-
- );
-}
-
-export class KqlFilterBar extends Component {
- state = {
- error: null,
- suggestions: [],
- isLoadingSuggestions: false,
- isLoadingIndexPattern: true,
- };
-
- onChange = async (inputValue, selectionStart) => {
- const { indexPattern } = this.props;
-
- this.setState({ error: null, suggestions: [], isLoadingSuggestions: true });
-
- const currentRequest = uniqueId();
- this.currentRequest = currentRequest;
- const boolFilter = [];
-
- try {
- const suggestions =
- (await getSuggestions(inputValue, selectionStart, indexPattern, boolFilter)) || [];
-
- if (currentRequest !== this.currentRequest) {
- return;
- }
-
- this.setState({ suggestions, isLoadingSuggestions: false });
- } catch (e) {
- console.error('Error while fetching suggestions', e);
- const errorMessage = i18n.translate('xpack.ml.explorer.fetchingSuggestionsErrorMessage', {
- defaultMessage: 'Error while fetching suggestions',
- });
- this.setState({ isLoadingSuggestions: false, error: e.message ? e.message : errorMessage });
- }
- };
-
- onSubmit = inputValue => {
- const { indexPattern } = this.props;
- const { onSubmit } = this.props;
-
- try {
- // returns object with properties: { influencersFilterQuery, filteredFields, queryString, isAndOperator }
- const kqlQueryValues = getKqlQueryValues(inputValue, indexPattern);
- onSubmit(kqlQueryValues);
- } catch (e) {
- console.log('Invalid kuery syntax', e); // eslint-disable-line no-console
- const errorWithLink = getErrorWithLink(e.message);
- const errorMessage = i18n.translate('xpack.ml.explorer.invalidKuerySyntaxErrorMessage', {
- defaultMessage: 'Invalid kuery syntax',
- });
- this.setState({ error: e.message ? errorWithLink : errorMessage });
- }
- };
-
- render() {
- const { error } = this.state;
- const { initialValue, placeholder, valueExternal, testSubj } = this.props;
-
- return (
-
-
- {error && {error}}
-
- );
- }
-}
-
-KqlFilterBar.propTypes = {
- indexPattern: PropTypes.object.isRequired,
- initialValue: PropTypes.string,
- onSubmit: PropTypes.func.isRequired,
- placeholder: PropTypes.string,
- valueExternal: PropTypes.string,
- testSubj: PropTypes.string,
-};
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.test.js
deleted file mode 100644
index 610d924651406..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/kql_filter_bar.test.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { shallow } from 'enzyme';
-import { KqlFilterBar } from './kql_filter_bar';
-
-const defaultProps = {
- indexPattern: {
- title: '.ml-anomalies-*',
- fields: [
- {
- name: 'nginx.access.geoip.country_iso_code',
- type: 'string',
- aggregatable: true,
- searchable: true,
- },
- {
- name: 'nginx.access.url',
- type: 'string',
- aggregatable: true,
- searchable: true,
- },
- ],
- },
- initialValue: '',
- onSubmit: () => {},
- placeholder: undefined,
-};
-
-jest.mock('../../util/dependency_cache', () => ({
- getAutocomplete: () => ({
- getQuerySuggestions: () => {},
- }),
-}));
-
-describe('KqlFilterBar', () => {
- test('snapshot', () => {
- const wrapper = shallow();
- expect(wrapper).toMatchSnapshot();
- });
-
- test('error message callout when error is present', () => {
- const wrapper = shallow();
- wrapper.setState({ error: 'Invalid syntax' });
- wrapper.update();
- const callout = wrapper.find('EuiCallOut');
-
- expect(callout.contains('Invalid syntax')).toBe(true);
- });
-
- test('suggestions loading when typing into search bar', () => {
- const wrapper = shallow();
- expect(wrapper.state('isLoadingSuggestions')).toBe(false);
- // Simulate typing in by triggering change with inputValue and selectionStart
- const filterBar = wrapper.find('FilterBar');
- filterBar.simulate('change', 'n', 1);
- expect(wrapper.state('isLoadingSuggestions')).toBe(true);
- });
-});
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/__snapshots__/suggestion.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/__snapshots__/suggestion.test.js.snap
deleted file mode 100644
index 4eb236f50be05..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/__snapshots__/suggestion.test.js.snap
+++ /dev/null
@@ -1,24 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Suggestion snapshot 1`] = `
-
-
-
-
-
- fieldValue
-
-
- <p>Test description for fieldValue</p>
-
-
-`;
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/index.js
deleted file mode 100644
index 98aedf068a987..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { Suggestion } from './suggestion';
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.js
deleted file mode 100644
index 121082a776c80..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import PropTypes from 'prop-types';
-import styled from 'styled-components';
-import { EuiIcon } from '@elastic/eui';
-import { tint } from 'polished';
-import theme from '@elastic/eui/dist/eui_theme_light.json';
-
-function getIconColor(type) {
- switch (type) {
- case 'field':
- return theme.euiColorVis7;
- case 'value':
- return theme.euiColorVis0;
- case 'operator':
- return theme.euiColorVis1;
- case 'conjunction':
- return theme.euiColorVis3;
- case 'recentSearch':
- return theme.euiColorMediumShade;
- }
-}
-
-const Description = styled.div`
- color: ${theme.euiColorDarkShade};
-
- p {
- display: inline;
-
- span {
- font-family: ${theme.euiFontFamily};
- color: ${theme.euiColorFullShade};
- padding: 0 ${theme.euiSizeXS};
- display: inline-block;
- }
- }
-`;
-
-const ListItem = styled.li`
- font-size: ${theme.euiFontSizeXS};
- height: ${theme.euiSizeXL};
- align-items: center;
- display: flex;
- background: ${props => (props.selected ? theme.euiColorLightestShade : 'initial')};
- cursor: pointer;
- border-radius: ${theme.euiSizeXS};
-
- ${Description} {
- p span {
- background: ${props =>
- props.selected ? theme.euiColorEmptyShade : theme.euiColorLightestShade};
- }
- }
-`;
-
-const Icon = styled.div`
- flex: 0 0 ${theme.euiSizeXL};
- background: ${props => tint(0.1, getIconColor(props.type))};
- color: ${props => getIconColor(props.type)};
- width: 100%;
- height: 100%;
- text-align: center;
- line-height: ${theme.euiSizeXL};
-`;
-
-const TextValue = styled.div`
- flex: 0 0 ${theme.euiSize * 16}px;
- color: ${theme.euiColorDarkestShade};
- padding: 0 ${theme.euiSizeS};
-`;
-
-function getEuiIconType(type) {
- switch (type) {
- case 'field':
- return 'kqlField';
- case 'value':
- return 'kqlValue';
- case 'recentSearch':
- return 'search';
- case 'conjunction':
- return 'kqlSelector';
- case 'operator':
- return 'kqlOperand';
- default:
- throw new Error('Unknown type', type);
- }
-}
-
-export const Suggestion = props => {
- return (
- props.onClick(props.suggestion)}
- onMouseEnter={props.onMouseEnter}
- >
-
-
-
- {props.suggestion.text}
- {props.suggestion.description}
-
- );
-};
-
-Suggestion.propTypes = {
- onClick: PropTypes.func.isRequired,
- onMouseEnter: PropTypes.func.isRequired,
- selected: PropTypes.bool,
- suggestion: PropTypes.object.isRequired,
- innerRef: PropTypes.func.isRequired,
-};
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.test.js
deleted file mode 100644
index d60f2004db445..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestion/suggestion.test.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { shallow } from 'enzyme';
-import { Suggestion } from './suggestion';
-
-const defaultProps = {
- innerRef: () => {},
- onClick: () => {},
- onMouseEnter: () => {},
- selected: true,
- suggestion: {
- description: 'Test description for fieldValue
',
- end: 1,
- start: 0,
- text: 'fieldValue',
- type: 'field',
- },
-};
-
-describe('Suggestion', () => {
- test('snapshot', () => {
- const wrapper = shallow();
- expect(wrapper).toMatchSnapshot();
- });
-});
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/__snapshots__/suggestions.test.js.snap b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/__snapshots__/suggestions.test.js.snap
deleted file mode 100644
index 869e321d4a7af..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/__snapshots__/suggestions.test.js.snap
+++ /dev/null
@@ -1,40 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Suggestions snapshot 1`] = `
-
- Test description for fieldValueOne",
- "end": 1,
- "start": 0,
- "text": "fieldValueOne",
- "type": "field",
- }
- }
- />
- Test description for fieldValueTwo",
- "end": 1,
- "start": 0,
- "text": "fieldValueTwo",
- "type": "field",
- }
- }
- />
-
-`;
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/index.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/index.js
deleted file mode 100644
index 70fb46e06bfa0..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { Suggestions } from './suggestions';
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.js
deleted file mode 100644
index 94960e1fcc865..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import styled from 'styled-components';
-import { Suggestion } from '../suggestion';
-import { rgba } from 'polished';
-import theme from '@elastic/eui/dist/eui_theme_light.json';
-
-const List = styled.ul`
- width: 100%;
- border: 1px solid ${theme.euiColorLightShade};
- border-radius: ${theme.euiSizeXS};
- box-shadow: 0px ${theme.euiSizeXS} ${theme.euiSizeXL} ${rgba(theme.euiTextColor, 0.1)};
- position: absolute;
- background: #fff;
- z-index: 10;
- left: 0;
- max-height: ${theme.euiSize * 20}px;
- overflow: scroll;
-`;
-
-export class Suggestions extends Component {
- childNodes = [];
-
- scrollIntoView = () => {
- const parent = this.parentNode;
- const child = this.childNodes[this.props.index];
-
- if (this.props.index == null || !parent || !child) {
- return;
- }
-
- const scrollTop = Math.max(
- Math.min(parent.scrollTop, child.offsetTop),
- child.offsetTop + child.offsetHeight - parent.offsetHeight
- );
-
- parent.scrollTop = scrollTop;
- };
-
- componentDidUpdate(prevProps) {
- if (prevProps.index !== this.props.index) {
- this.scrollIntoView();
- }
- }
-
- render() {
- if (!this.props.show || this.props.suggestions.length === 0) {
- return null;
- }
-
- const suggestions = this.props.suggestions.map((suggestion, index) => {
- const key = `${suggestion}_${index}`;
- return (
- (this.childNodes[index] = node)}
- selected={index === this.props.index}
- suggestion={suggestion}
- onClick={this.props.onClick}
- onMouseEnter={() => this.props.onMouseEnter(index)}
- key={key}
- />
- );
- });
-
- return (this.parentNode = node)}>{suggestions}
;
- }
-}
-
-Suggestions.propTypes = {
- index: PropTypes.number,
- onClick: PropTypes.func.isRequired,
- onMouseEnter: PropTypes.func.isRequired,
- show: PropTypes.bool,
- suggestions: PropTypes.array.isRequired,
-};
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.test.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.test.js
deleted file mode 100644
index 666bfa4cfa1fa..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/suggestions/suggestions.test.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { shallow, mount } from 'enzyme';
-import { Suggestions } from './suggestions';
-
-const defaultProps = {
- index: 0,
- onClick: () => {},
- onMouseEnter: () => {},
- show: true,
- suggestions: [
- {
- description: 'Test description for fieldValueOne
',
- end: 1,
- start: 0,
- text: 'fieldValueOne',
- type: 'field',
- },
- {
- description: 'Test description for fieldValueTwo
',
- end: 1,
- start: 0,
- text: 'fieldValueTwo',
- type: 'field',
- },
- ],
-};
-
-describe('Suggestions', () => {
- test('snapshot', () => {
- const wrapper = shallow();
- expect(wrapper).toMatchSnapshot();
- });
-
- test('is null when show is false', () => {
- const noShowProps = { ...defaultProps, show: false };
- const wrapper = shallow();
- expect(wrapper.isEmptyRender()).toBeTruthy();
- });
-
- test('is null when no suggestions are available', () => {
- const noSuggestions = { ...defaultProps, suggestions: [] };
- const wrapper = shallow();
- expect(wrapper.isEmptyRender()).toBeTruthy();
- });
-
- test('creates suggestion list item for each suggestion passed in via props', () => {
- const wrapper = mount();
- const suggestions = wrapper.find('li');
- expect(suggestions.length).toEqual(2);
- });
-});
diff --git a/x-pack/plugins/ml/public/application/components/kql_filter_bar/utils.js b/x-pack/plugins/ml/public/application/components/kql_filter_bar/utils.js
deleted file mode 100644
index d632f4079e5b9..0000000000000
--- a/x-pack/plugins/ml/public/application/components/kql_filter_bar/utils.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { esKuery } from '../../../../../../../src/plugins/data/public';
-import { getAutocomplete } from '../../util/dependency_cache';
-
-export function getSuggestions(query, selectionStart, indexPattern, boolFilter) {
- const autocomplete = getAutocomplete();
- return autocomplete.getQuerySuggestions({
- language: 'kuery',
- indexPatterns: [indexPattern],
- boolFilter,
- query,
- selectionStart,
- selectionEnd: selectionStart,
- });
-}
-
-function convertKueryToEsQuery(kuery, indexPattern) {
- const ast = esKuery.fromKueryExpression(kuery);
- return esKuery.toElasticsearchQuery(ast, indexPattern);
-}
-// Recommended by MDN for escaping user input to be treated as a literal string within a regular expression
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
-export function escapeRegExp(string) {
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
-}
-
-export function escapeParens(string) {
- return string.replace(/[()]/g, '\\$&');
-}
-
-export function escapeDoubleQuotes(string) {
- return string.replace(/\"/g, '\\$&');
-}
-
-export function getKqlQueryValues(inputValue, indexPattern) {
- const ast = esKuery.fromKueryExpression(inputValue);
- const isAndOperator = ast.function === 'and';
- const query = convertKueryToEsQuery(inputValue, indexPattern);
- const filteredFields = [];
-
- if (!query) {
- return;
- }
-
- // if ast.type == 'function' then layout of ast.arguments:
- // [{ arguments: [ { type: 'literal', value: 'AAL' } ] },{ arguments: [ { type: 'literal', value: 'AAL' } ] }]
- if (ast && Array.isArray(ast.arguments)) {
- ast.arguments.forEach(arg => {
- if (arg.arguments !== undefined) {
- arg.arguments.forEach(nestedArg => {
- if (typeof nestedArg.value === 'string') {
- filteredFields.push(nestedArg.value);
- }
- });
- } else if (typeof arg.value === 'string') {
- filteredFields.push(arg.value);
- }
- });
- }
-
- return {
- filterQuery: query,
- filteredFields,
- queryString: inputValue,
- isAndOperator,
- tableQueryString: inputValue,
- };
-}
-
-export function getQueryPattern(fieldName, fieldValue) {
- const sanitizedFieldName = escapeRegExp(fieldName);
- const sanitizedFieldValue = escapeRegExp(fieldValue);
-
- return new RegExp(`(${sanitizedFieldName})\\s?:\\s?(")?(${sanitizedFieldValue})(")?`, 'i');
-}
-
-export function removeFilterFromQueryString(currentQueryString, fieldName, fieldValue) {
- let newQueryString = '';
- // Remove the passed in fieldName and value from the existing filter
- const queryPattern = getQueryPattern(fieldName, fieldValue);
- newQueryString = currentQueryString.replace(queryPattern, '');
- // match 'and' or 'or' at the start/end of the string
- const endPattern = /\s(and|or)\s*$/gi;
- const startPattern = /^\s*(and|or)\s/gi;
- // If string has a double operator (e.g. tag:thing or or tag:other) remove and replace with the first occurring operator
- const invalidOperatorPattern = /\s+(and|or)\s+(and|or)\s+/gi;
- newQueryString = newQueryString.replace(invalidOperatorPattern, ' $1 ');
- // If string starts/ends with 'and' or 'or' remove that as that is illegal kuery syntax
- newQueryString = newQueryString.replace(endPattern, '');
- newQueryString = newQueryString.replace(startPattern, '');
-
- return newQueryString;
-}
diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx
index ef13fec3589fb..16004475eb44f 100644
--- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx
+++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx
@@ -6,14 +6,22 @@
import React, { FC, useState } from 'react';
-import { EuiFlexItem, EuiFlexGroup, EuiIconTip, EuiSuperSelect, EuiText } from '@elastic/eui';
+import {
+ EuiCode,
+ EuiFlexItem,
+ EuiFlexGroup,
+ EuiIconTip,
+ EuiInputPopover,
+ EuiSuperSelect,
+ EuiText,
+} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { IndexPattern } from '../../../../../../../../../src/plugins/data/public';
-import { SEARCH_QUERY_LANGUAGE } from '../../../../../../common/constants/search';
+import { SEARCH_QUERY_LANGUAGE, ErrorMessage } from '../../../../../../common/constants/search';
import {
esKuery,
@@ -22,8 +30,6 @@ import {
QueryStringInput,
} from '../../../../../../../../../src/plugins/data/public';
-import { getToastNotifications } from '../../../../util/dependency_cache';
-
interface Props {
indexPattern: IndexPattern;
searchString: Query['query'];
@@ -73,6 +79,7 @@ export const SearchPanel: FC = ({
query: searchString || '',
language: searchQueryLanguage,
});
+ const [errorMessage, setErrorMessage] = useState(undefined);
const searchHandler = (query: Query) => {
let filterQuery;
@@ -93,13 +100,7 @@ export const SearchPanel: FC = ({
setSearchQueryLanguage(query.language);
} catch (e) {
console.log('Invalid syntax', JSON.stringify(e, null, 2)); // eslint-disable-line no-console
- const toastNotifications = getToastNotifications();
- toastNotifications.addError(e, {
- title: i18n.translate('xpack.ml.datavisualizer.invalidSyntaxErrorMessage', {
- defaultMessage: 'Invalid syntax in search bar',
- }),
- toastMessage: e.message ? e.message : e,
- });
+ setErrorMessage({ query: query.query as string, message: e.message });
}
};
const searchChangeHandler = (query: Query) => setSearchInput(query);
@@ -107,22 +108,40 @@ export const SearchPanel: FC = ({
return (
-
+ setErrorMessage(undefined)}
+ input={
+
+ }
+ isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''}
+ >
+
+ {i18n.translate(
+ 'xpack.ml.datavisualizer.searchPanel.invalidKuerySyntaxErrorMessageQueryBar',
+ {
+ defaultMessage: 'Invalid query',
+ }
+ )}
+ {': '}
+ {errorMessage?.message.split('\n')[0]}
+
+
diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx
index 0789a7f8ef5ff..0263ad08b03cf 100644
--- a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx
+++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx
@@ -5,6 +5,8 @@
*/
import React, { FC, useState, useEffect } from 'react';
+import { EuiCode, EuiInputPopover } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
import {
Query,
esKuery,
@@ -12,10 +14,10 @@ import {
QueryStringInput,
} from '../../../../../../../../src/plugins/data/public';
import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns';
-import { QUERY_LANGUAGE_KUERY, QUERY_LANGUAGE_LUCENE } from '../../explorer_constants';
+import { SEARCH_QUERY_LANGUAGE, ErrorMessage } from '../../../../../common/constants/search';
import { explorerService } from '../../explorer_dashboard_service';
-export const DEFAULT_QUERY_LANG = QUERY_LANGUAGE_KUERY;
+export const DEFAULT_QUERY_LANG = SEARCH_QUERY_LANGUAGE.KUERY;
export function getKqlQueryValues({
inputString,
@@ -25,11 +27,11 @@ export function getKqlQueryValues({
inputString: string | { [key: string]: any };
queryLanguage: string;
indexPattern: IIndexPattern;
-}) {
+}): { clearSettings: boolean; settings: any } {
let influencersFilterQuery: any = {};
- const ast = esKuery.fromKueryExpression(inputString);
- const isAndOperator = ast.function === 'and';
const filteredFields: string[] = [];
+ const ast = esKuery.fromKueryExpression(inputString);
+ const isAndOperator = ast && ast.function === 'and';
// if ast.type == 'function' then layout of ast.arguments:
// [{ arguments: [ { type: 'literal', value: 'AAL' } ] },{ arguments: [ { type: 'literal', value: 'AAL' } ] }]
if (ast && Array.isArray(ast.arguments)) {
@@ -45,12 +47,12 @@ export function getKqlQueryValues({
}
});
}
- if (queryLanguage === QUERY_LANGUAGE_KUERY) {
+ if (queryLanguage === SEARCH_QUERY_LANGUAGE.KUERY) {
influencersFilterQuery = esKuery.toElasticsearchQuery(
esKuery.fromKueryExpression(inputString),
indexPattern
);
- } else if (queryLanguage === QUERY_LANGUAGE_LUCENE) {
+ } else if (queryLanguage === SEARCH_QUERY_LANGUAGE.LUCENE) {
influencersFilterQuery = esQuery.luceneStringToDsl(inputString);
}
@@ -78,7 +80,7 @@ function getInitSearchInputState({
}) {
if (queryString !== undefined && filterActive === true) {
return {
- language: QUERY_LANGUAGE_KUERY,
+ language: SEARCH_QUERY_LANGUAGE.KUERY,
query: queryString,
};
} else {
@@ -110,6 +112,7 @@ export const ExplorerQueryBar: FC = ({
const [searchInput, setSearchInput] = useState(
getInitSearchInputState({ filterActive, queryString })
);
+ const [errorMessage, setErrorMessage] = useState(undefined);
useEffect(() => {
if (filterIconTriggeredQuery !== undefined) {
@@ -127,30 +130,50 @@ export const ExplorerQueryBar: FC = ({
setSearchInput(query);
};
const applyInfluencersFilterQuery = (query: Query) => {
- const { clearSettings, settings } = getKqlQueryValues({
- inputString: query.query,
- queryLanguage: query.language,
- indexPattern,
- });
+ try {
+ const { clearSettings, settings } = getKqlQueryValues({
+ inputString: query.query,
+ queryLanguage: query.language,
+ indexPattern,
+ });
- if (clearSettings === true) {
- explorerService.clearInfluencerFilterSettings();
- } else {
- explorerService.setInfluencerFilterSettings(settings);
+ if (clearSettings === true) {
+ explorerService.clearInfluencerFilterSettings();
+ } else {
+ explorerService.setInfluencerFilterSettings(settings);
+ }
+ } catch (e) {
+ console.log('Invalid query syntax in search bar', e); // eslint-disable-line no-console
+ setErrorMessage({ query: query.query as string, message: e.message });
}
};
return (
-
+ setErrorMessage(undefined)}
+ input={
+
+ }
+ isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''}
+ >
+
+ {i18n.translate('xpack.ml.explorer.invalidKuerySyntaxErrorMessageQueryBar', {
+ defaultMessage: 'Invalid query',
+ })}
+ {': '}
+ {errorMessage?.message.split('\n')[0]}
+
+
);
};
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer.js b/x-pack/plugins/ml/public/application/explorer/explorer.js
index 9b02150bae9bb..d61d56d07b644 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer.js
+++ b/x-pack/plugins/ml/public/application/explorer/explorer.js
@@ -58,13 +58,12 @@ import {
DEFAULT_QUERY_LANG,
} from './components/explorer_query_bar/explorer_query_bar';
import {
+ getDateFormatTz,
removeFilterFromQueryString,
getQueryPattern,
escapeParens,
escapeDoubleQuotes,
-} from '../components/kql_filter_bar/utils';
-
-import { getDateFormatTz } from './explorer_utils';
+} from './explorer_utils';
import { getSwimlaneContainerWidth } from './legacy_utils';
import {
@@ -266,7 +265,7 @@ export class Explorer extends React.Component {
explorerService.setInfluencerFilterSettings(settings);
}
} catch (e) {
- console.log('Invalid kuery syntax', e); // eslint-disable-line no-console
+ console.log('Invalid query syntax from table', e); // eslint-disable-line no-console
const toastNotifications = getToastNotifications();
toastNotifications.addDanger(
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_constants.ts b/x-pack/plugins/ml/public/application/explorer/explorer_constants.ts
index afec50eae06aa..b084f503272cc 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_constants.ts
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_constants.ts
@@ -25,7 +25,6 @@ export const EXPLORER_ACTION = {
SET_EXPLORER_DATA: 'setExplorerData',
SET_FILTER_DATA: 'setFilterData',
SET_INFLUENCER_FILTER_SETTINGS: 'setInfluencerFilterSettings',
- SET_SEARCH_INPUT: 'setSearchInput',
SET_SELECTED_CELLS: 'setSelectedCells',
SET_SWIMLANE_CONTAINER_WIDTH: 'setSwimlaneContainerWidth',
SET_SWIMLANE_LIMIT: 'setSwimlaneLimit',
@@ -56,7 +55,3 @@ export const MAX_INFLUENCER_FIELD_NAMES = 50;
export const VIEW_BY_JOB_LABEL = i18n.translate('xpack.ml.explorer.jobIdLabel', {
defaultMessage: 'job ID',
});
-
-export const QUERY_LANGUAGE_KUERY = 'kuery';
-export const QUERY_LANGUAGE_LUCENE = 'lucene';
-export type QUERY_LANGUAGE = 'kuery' | 'lucene';
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts b/x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts
index 277c1aa6f4566..89e1a908b1ecc 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_dashboard_service.ts
@@ -132,12 +132,6 @@ export const explorerService = {
payload,
});
},
- setSearchInput: (query: any) => {
- explorerAction$.next({
- type: EXPLORER_ACTION.SET_SEARCH_INPUT,
- payload: query,
- });
- },
setSelectedCells: (payload: AppStateSelectedCells | undefined) => {
explorerAction$.next({
type: EXPLORER_ACTION.SET_SELECTED_CELLS,
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js
index 0b41f789bb571..852b16ec581bb 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js
@@ -883,3 +883,42 @@ export async function loadTopInfluencers(
}
});
}
+
+// Recommended by MDN for escaping user input to be treated as a literal string within a regular expression
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
+export function escapeRegExp(string) {
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
+}
+
+export function escapeParens(string) {
+ return string.replace(/[()]/g, '\\$&');
+}
+
+export function escapeDoubleQuotes(string) {
+ return string.replace(/\"/g, '\\$&');
+}
+
+export function getQueryPattern(fieldName, fieldValue) {
+ const sanitizedFieldName = escapeRegExp(fieldName);
+ const sanitizedFieldValue = escapeRegExp(fieldValue);
+
+ return new RegExp(`(${sanitizedFieldName})\\s?:\\s?(")?(${sanitizedFieldValue})(")?`, 'i');
+}
+
+export function removeFilterFromQueryString(currentQueryString, fieldName, fieldValue) {
+ let newQueryString = '';
+ // Remove the passed in fieldName and value from the existing filter
+ const queryPattern = getQueryPattern(fieldName, fieldValue);
+ newQueryString = currentQueryString.replace(queryPattern, '');
+ // match 'and' or 'or' at the start/end of the string
+ const endPattern = /\s(and|or)\s*$/gi;
+ const startPattern = /^\s*(and|or)\s/gi;
+ // If string has a double operator (e.g. tag:thing or or tag:other) remove and replace with the first occurring operator
+ const invalidOperatorPattern = /\s+(and|or)\s+(and|or)\s+/gi;
+ newQueryString = newQueryString.replace(invalidOperatorPattern, ' $1 ');
+ // If string starts/ends with 'and' or 'or' remove that as that is illegal kuery syntax
+ newQueryString = newQueryString.replace(endPattern, '');
+ newQueryString = newQueryString.replace(startPattern, '');
+
+ return newQueryString;
+}
diff --git a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts
index ff659029e38d7..c31b26b7adb7b 100644
--- a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts
+++ b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts
@@ -68,13 +68,6 @@ export const explorerReducer = (state: ExplorerState, nextAction: Action): Explo
nextState = setInfluencerFilterSettings(state, payload);
break;
- case EXPLORER_ACTION.SET_SEARCH_INPUT:
- nextState = {
- ...state,
- searchInput: payload,
- };
- break;
-
case EXPLORER_ACTION.SET_SELECTED_CELLS:
const selectedCells = payload;
nextState = {
diff --git a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts
index 44e1486508ea3..0a2dbf5bcff35 100644
--- a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts
+++ b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts
@@ -20,8 +20,6 @@ import {
TimeRangeBounds,
} from '../../explorer_utils';
-import { QUERY_LANGUAGE_KUERY, QUERY_LANGUAGE } from '../../explorer_constants'; // QUERY_LANGUAGE_LUCENE
-
export interface ExplorerState {
annotationsData: any[];
bounds: TimeRangeBounds | undefined;
@@ -39,10 +37,6 @@ export interface ExplorerState {
noInfluencersConfigured: boolean;
overallSwimlaneData: SwimlaneData;
queryString: string;
- searchInput: {
- query: string;
- language: QUERY_LANGUAGE;
- };
selectedCells: AppStateSelectedCells | undefined;
selectedJobs: ExplorerJob[] | null;
swimlaneBucketInterval: any;
@@ -79,10 +73,6 @@ export function getExplorerDefaultState(): ExplorerState {
noInfluencersConfigured: true,
overallSwimlaneData: getDefaultSwimlaneData(),
queryString: '',
- searchInput: {
- query: '',
- language: QUERY_LANGUAGE_KUERY,
- },
selectedCells: undefined,
selectedJobs: null,
swimlaneBucketInterval: undefined,
diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
index 1f9a60fa869b7..df22c3f3eb2e2 100644
--- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
+++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
@@ -12,6 +12,8 @@ import { i18n } from '@kbn/i18n';
import {
EuiButton,
EuiCodeEditor,
+ EuiCode,
+ EuiInputPopover,
EuiFlexGroup,
EuiFlexItem,
EuiForm,
@@ -76,6 +78,11 @@ export interface StepDefineExposedState {
valid: boolean;
}
+interface ErrorMessage {
+ query: string;
+ message: string;
+}
+
const defaultSearch = '*';
const QUERY_LANGUAGE_KUERY = 'kuery';
@@ -256,6 +263,7 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange,
query: defaults.searchString || '',
language: defaults.searchLanguage,
});
+ const [errorMessage, setErrorMessage] = useState(undefined);
// The state of the input query bar updated on every submit and to be exposed.
const [searchLanguage, setSearchLanguage] = useState(
@@ -272,18 +280,23 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange,
const searchSubmitHandler = (query: Query) => {
setSearchLanguage(query.language as QUERY_LANGUAGE);
setSearchString(query.query !== '' ? (query.query as string) : undefined);
- switch (query.language) {
- case QUERY_LANGUAGE_KUERY:
- setSearchQuery(
- esKuery.toElasticsearchQuery(
- esKuery.fromKueryExpression(query.query as string),
- indexPattern
- )
- );
- return;
- case QUERY_LANGUAGE_LUCENE:
- setSearchQuery(esQuery.luceneStringToDsl(query.query as string));
- return;
+ try {
+ switch (query.language) {
+ case QUERY_LANGUAGE_KUERY:
+ setSearchQuery(
+ esKuery.toElasticsearchQuery(
+ esKuery.fromKueryExpression(query.query as string),
+ indexPattern
+ )
+ );
+ return;
+ case QUERY_LANGUAGE_LUCENE:
+ setSearchQuery(esQuery.luceneStringToDsl(query.query as string));
+ return;
+ }
+ } catch (e) {
+ console.log('Invalid syntax', JSON.stringify(e, null, 2)); // eslint-disable-line no-console
+ setErrorMessage({ query: query.query as string, message: e.message });
}
};
@@ -620,33 +633,54 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange,
defaultMessage: 'Use a query to filter the source data (optional).',
})}
>
- setErrorMessage(undefined)}
+ input={
+
+ }
+ isOpen={
+ errorMessage?.query === searchInput.query &&
+ errorMessage?.message !== ''
}
- disableAutoFocus={true}
- dataTestSubj="transformQueryInput"
- languageSwitcherPopoverAnchorPosition="rightDown"
- />
+ >
+
+ {i18n.translate(
+ 'xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar',
+ {
+ defaultMessage: 'Invalid query',
+ }
+ )}
+ {': '}
+ {errorMessage?.message.split('\n')[0]}
+
+
)}
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 5b96a93b1ed0d..ac48a3f0efab0 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -7638,9 +7638,7 @@
"xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel": "{ numberOfCauses, plural, one {# 個の異常な {byFieldName} 値 } other {#{plusSign}異常な{byFieldName}値}}",
"xpack.ml.explorer.distributionChart.valueLabel": "値",
"xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel": "値",
- "xpack.ml.explorer.fetchingSuggestionsErrorMessage": "提案の取得中にエラーが発生しました",
"xpack.ml.explorer.intervalLabel": "間隔",
- "xpack.ml.explorer.invalidKuerySyntaxErrorMessage": "無効な Kuery 構文",
"xpack.ml.explorer.invalidKuerySyntaxErrorMessageFromTable": "クエリバーに無効な構文。インプットは有効な Kibana クエリ言語 (KQL) でなければなりません",
"xpack.ml.explorer.jobIdLabel": "ジョブ ID",
"xpack.ml.explorer.jobScoreAcrossAllInfluencersLabel": "(すべての影響因子のジョブスコア)",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 03a65921d669a..e93e9f60e9d6e 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -7638,9 +7638,7 @@
"xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel": "{ numberOfCauses, plural, one {# 个异常 {byFieldName} 值} other {#{plusSign} 个异常 {byFieldName} 值}}",
"xpack.ml.explorer.distributionChart.valueLabel": "值",
"xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel": "值",
- "xpack.ml.explorer.fetchingSuggestionsErrorMessage": "获取建议时出错",
"xpack.ml.explorer.intervalLabel": "时间间隔",
- "xpack.ml.explorer.invalidKuerySyntaxErrorMessage": "kuery 语法无效",
"xpack.ml.explorer.invalidKuerySyntaxErrorMessageFromTable": "查询栏中的语法无效。输入必须是有效的 Kibana 查询语言 (KQL)",
"xpack.ml.explorer.jobIdLabel": "作业 ID",
"xpack.ml.explorer.jobScoreAcrossAllInfluencersLabel": "(所有影响因素的作业分数)",