From 25221f76756eda7a48ea0e4d863eb13b397ec054 Mon Sep 17 00:00:00 2001
From: Marta Bondyra
Date: Thu, 26 Mar 2020 19:32:24 +0100
Subject: [PATCH 001/127] [Lens] Fix display single bar in XYChart Bar Vis
(#61452)
---
.../lens/public/xy_visualization/xy_expression.test.tsx | 8 +++++++-
.../lens/public/xy_visualization/xy_expression.tsx | 4 ++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx
index d6abee101db31..cdc5fc2ff1c17 100644
--- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx
+++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx
@@ -39,7 +39,12 @@ function sampleArgs() {
formatHint: { id: 'number', params: { pattern: '0,0.000' } },
},
{ id: 'b', name: 'b', formatHint: { id: 'number', params: { pattern: '000,0' } } },
- { id: 'c', name: 'c', formatHint: { id: 'string' } },
+ {
+ id: 'c',
+ name: 'c',
+ formatHint: { id: 'string' },
+ meta: { type: 'date-histogram', aggConfigParams: { interval: '10s' } },
+ },
{ id: 'd', name: 'ColD', formatHint: { id: 'string' } },
],
rows: [
@@ -179,6 +184,7 @@ describe('xy_expression', () => {
Object {
"max": 1546491600000,
"min": 1546405200000,
+ "minInterval": 10000,
}
`);
});
diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx
index eaf3acf7bb2a7..e059cb2ab5ad5 100644
--- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx
+++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx
@@ -35,6 +35,7 @@ import { XYArgs, SeriesType, visualizationTypes } from './types';
import { VisualizationContainer } from '../visualization_container';
import { isHorizontalChart } from './state_helpers';
import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public';
+import { parseInterval } from '../../../../../../src/plugins/data/common';
import { getExecuteTriggerActions } from './services';
type InferPropType = T extends React.FunctionComponent ? P : T;
@@ -210,11 +211,14 @@ export function XYChart({
const shouldRotate = isHorizontalChart(layers);
const xTitle = (xAxisColumn && xAxisColumn.name) || args.xTitle;
+ const interval = parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval);
+
const xDomain =
data.dateRange && layers.every(l => l.xScaleType === 'time')
? {
min: data.dateRange.fromDate.getTime(),
max: data.dateRange.toDate.getTime(),
+ minInterval: interval?.asMilliseconds(),
}
: undefined;
return (
From 0fcaf0b8bec387406fec98137e4dc14566837944 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez
Date: Thu, 26 Mar 2020 14:41:51 -0400
Subject: [PATCH 002/127] [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 15393535eb8e9..f2ea8f8c6dd0c 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 56dad3286c4ce..0dd584e32a248 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": "(所有影响因素的作业分数)",
From 7fe06f7bea44bf88db014169b5fb0819d8846309 Mon Sep 17 00:00:00 2001
From: Joe Portner <5295965+jportner@users.noreply.github.com>
Date: Thu, 26 Mar 2020 15:02:37 -0400
Subject: [PATCH 003/127] Upgrade vega-util (#61125)
---
.../vis_type_vega/public/data_model/vega_parser.js | 3 ++-
.../vis_type_vega/public/vega_view/vega_base_view.js | 3 ++-
yarn.lock | 6 +++---
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.js b/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.js
index 7c2638d1f5165..735ce60f76d47 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.js
+++ b/src/legacy/core_plugins/vis_type_vega/public/data_model/vega_parser.js
@@ -116,7 +116,8 @@ export class VegaParser {
*/
_compileVegaLite() {
this.vlspec = this.spec;
- const logger = vega.logger(vega.Warn);
+ // eslint-disable-next-line import/namespace
+ const logger = vega.logger(vega.Warn); // note: eslint has a false positive here
logger.warn = this._onWarning.bind(this);
this.spec = vegaLite.compile(this.vlspec, logger).spec;
diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js
index 76a2e672e0bd0..c90f059ff7c94 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js
+++ b/src/legacy/core_plugins/vis_type_vega/public/vega_view/vega_base_view.js
@@ -132,7 +132,8 @@ export class VegaBaseView {
createViewConfig() {
const config = {
- logLevel: vega.Warn,
+ // eslint-disable-next-line import/namespace
+ logLevel: vega.Warn, // note: eslint has a false positive here
renderer: this._parser.renderer,
};
diff --git a/yarn.lock b/yarn.lock
index d6ffd77e9a296..d483db8e27de1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -30416,9 +30416,9 @@ vega-typings@*, vega-typings@^0.3.17:
vega-util "^1.7.0"
vega-util@^1.7.0:
- version "1.7.1"
- resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.7.1.tgz#0b95bbe6058895c332596c215247507caa68ab61"
- integrity sha512-jdzigLdaXH0rClqkr/qHY//xvmLyxQyZL4Wxb3mew29QpITrMk/USV6v/399h29xVt1+hJuw1vpLoJqAq6WerA==
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.13.1.tgz#3eae51043184c6b873c17b148755c21b01274a0e"
+ integrity sha512-TmvZSMKqhGlS7eAXphqJUhq+NZVYbvXX2ahargTRkVckGWjEUpWhMC7T13vYihrU2Lf/OevKbrruSXKOBxke2w==
vega-view-transforms@^2.0.3:
version "2.0.3"
From 36a4944f0ced11a644222edbdf997eb58784016a Mon Sep 17 00:00:00 2001
From: Angela Chuang <6295984+angorayc@users.noreply.github.com>
Date: Thu, 26 Mar 2020 19:54:33 +0000
Subject: [PATCH 004/127] [SIEM] Allow Import timeline for authorised users
(#61438)
* allow users importing data if they are authorized
* rename props
* rename types
* hide import timeline btn if unauthorized
---
.../components/import_data_modal/index.tsx | 8 +--
.../public/components/open_timeline/index.tsx | 10 +--
.../open_timeline/open_timeline.tsx | 18 ++---
.../timelines_table/actions_columns.test.tsx | 68 +++++++++++++++++++
.../timelines_table/actions_columns.tsx | 1 +
.../public/components/open_timeline/types.ts | 4 +-
.../containers/detection_engine/rules/api.ts | 8 +--
.../detection_engine/rules/types.ts | 4 +-
.../public/containers/timeline/all/api.ts | 6 +-
.../public/pages/timelines/timelines_page.tsx | 24 ++++---
.../apis/siem/saved_objects/timeline.ts | 2 +-
11 files changed, 115 insertions(+), 38 deletions(-)
diff --git a/x-pack/legacy/plugins/siem/public/components/import_data_modal/index.tsx b/x-pack/legacy/plugins/siem/public/components/import_data_modal/index.tsx
index 503710f1ee8aa..c827411a41e2e 100644
--- a/x-pack/legacy/plugins/siem/public/components/import_data_modal/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/import_data_modal/index.tsx
@@ -21,7 +21,7 @@ import {
} from '@elastic/eui';
import React, { useCallback, useState } from 'react';
-import { ImportRulesResponse, ImportRulesProps } from '../../containers/detection_engine/rules';
+import { ImportDataResponse, ImportDataProps } from '../../containers/detection_engine/rules';
import {
displayErrorToast,
displaySuccessToast,
@@ -37,7 +37,7 @@ interface ImportDataModalProps {
errorMessage: string;
failedDetailed: (id: string, statusCode: number, message: string) => string;
importComplete: () => void;
- importData: (arg: ImportRulesProps) => Promise;
+ importData: (arg: ImportDataProps) => Promise;
showCheckBox: boolean;
showModal: boolean;
submitBtnText: string;
@@ -75,7 +75,7 @@ export const ImportDataModalComponent = ({
closeModal();
}, [setIsImporting, setSelectedFiles, closeModal]);
- const importRulesCallback = useCallback(async () => {
+ const importDataCallback = useCallback(async () => {
if (selectedFiles != null) {
setIsImporting(true);
const abortCtrl = new AbortController();
@@ -152,7 +152,7 @@ export const ImportDataModalComponent = ({
{i18n.CANCEL_BUTTON}
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx
index 6c2cd21d808b7..c27a6039da29d 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx
@@ -54,7 +54,7 @@ interface OwnProps {
export type OpenTimelineOwnProps = OwnProps &
Pick<
OpenTimelineProps,
- 'defaultPageSize' | 'title' | 'importCompleteToggle' | 'setImportCompleteToggle'
+ 'defaultPageSize' | 'title' | 'importDataModalToggle' | 'setImportDataModalToggle'
> &
PropsFromRedux;
@@ -77,9 +77,9 @@ export const StatefulOpenTimelineComponent = React.memo(
defaultPageSize,
hideActions = [],
isModal = false,
- importCompleteToggle,
+ importDataModalToggle,
onOpenTimeline,
- setImportCompleteToggle,
+ setImportDataModalToggle,
timeline,
title,
updateTimeline,
@@ -269,7 +269,7 @@ export const StatefulOpenTimelineComponent = React.memo(
defaultPageSize={defaultPageSize}
isLoading={loading}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
- importCompleteToggle={importCompleteToggle}
+ importDataModalToggle={importDataModalToggle}
onAddTimelinesToFavorites={undefined}
onDeleteSelected={onDeleteSelected}
onlyFavorites={onlyFavorites}
@@ -284,7 +284,7 @@ export const StatefulOpenTimelineComponent = React.memo(
query={search}
refetch={refetch}
searchResults={timelines}
- setImportCompleteToggle={setImportCompleteToggle}
+ setImportDataModalToggle={setImportDataModalToggle}
selectedItems={selectedItems}
sortDirection={sortDirection}
sortField={sortField}
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/open_timeline.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/open_timeline.tsx
index 8b3da4427a362..6b2f953b82de4 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/open_timeline.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/open_timeline.tsx
@@ -33,7 +33,7 @@ export const OpenTimeline = React.memo(
defaultPageSize,
isLoading,
itemIdToExpandedNotesRowMap,
- importCompleteToggle,
+ importDataModalToggle,
onAddTimelinesToFavorites,
onDeleteSelected,
onlyFavorites,
@@ -50,7 +50,7 @@ export const OpenTimeline = React.memo(
searchResults,
selectedItems,
sortDirection,
- setImportCompleteToggle,
+ setImportDataModalToggle,
sortField,
title,
totalSearchResultsCount,
@@ -103,18 +103,18 @@ export const OpenTimeline = React.memo(
}, [refetch]);
const handleCloseModal = useCallback(() => {
- if (setImportCompleteToggle != null) {
- setImportCompleteToggle(false);
+ if (setImportDataModalToggle != null) {
+ setImportDataModalToggle(false);
}
- }, [setImportCompleteToggle]);
+ }, [setImportDataModalToggle]);
const handleComplete = useCallback(() => {
- if (setImportCompleteToggle != null) {
- setImportCompleteToggle(false);
+ if (setImportDataModalToggle != null) {
+ setImportDataModalToggle(false);
}
if (refetch != null) {
refetch();
}
- }, [setImportCompleteToggle, refetch]);
+ }, [setImportDataModalToggle, refetch]);
return (
<>
@@ -136,7 +136,7 @@ export const OpenTimeline = React.memo(
importData={importTimelines}
successMessage={i18n.SUCCESSFULLY_IMPORTED_TIMELINES}
showCheckBox={false}
- showModal={importCompleteToggle ?? false}
+ showModal={importDataModalToggle ?? false}
submitBtnText={i18n.IMPORT_TIMELINE_BTN_TITLE}
subtitle={i18n.INITIAL_PROMPT_TEXT}
title={i18n.IMPORT_TIMELINE}
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.test.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.test.tsx
index ca82e30798d82..8805037ecc4ca 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.test.tsx
@@ -156,4 +156,72 @@ describe('#getActionsColumns', () => {
expect(onOpenTimeline).toBeCalledWith({ duplicate: true, timelineId: 'saved-timeline-11' });
});
+
+ test('it renders the export icon when enableExportTimelineDownloader is including the action export', () => {
+ const testProps: TimelinesTableProps = {
+ ...getMockTimelinesTableProps(mockResults),
+ actionTimelineToShow: ['export'],
+ };
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.find('[data-test-subj="export-timeline"]').exists()).toBe(true);
+ });
+
+ test('it renders No export icon when export is not included in the action ', () => {
+ const testProps: TimelinesTableProps = {
+ ...getMockTimelinesTableProps(mockResults),
+ };
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.find('[data-test-subj="export-timeline"]').exists()).toBe(false);
+ });
+
+ test('it renders a disabled the export button if the timeline does not have a saved object id', () => {
+ const missingSavedObjectId: OpenTimelineResult[] = [
+ omit('savedObjectId', { ...mockResults[0] }),
+ ];
+
+ const testProps: TimelinesTableProps = {
+ ...getMockTimelinesTableProps(missingSavedObjectId),
+ actionTimelineToShow: ['export'],
+ };
+ const wrapper = mountWithIntl(
+
+
+
+ );
+
+ const props = wrapper
+ .find('[data-test-subj="export-timeline"]')
+ .first()
+ .props() as EuiButtonIconProps;
+ expect(props.isDisabled).toBe(true);
+ });
+
+ test('it invokes enableExportTimelineDownloader with the expected params when the button is clicked', () => {
+ const enableExportTimelineDownloader = jest.fn();
+ const testProps: TimelinesTableProps = {
+ ...getMockTimelinesTableProps(mockResults),
+ actionTimelineToShow: ['export'],
+ enableExportTimelineDownloader,
+ };
+ const wrapper = mountWithIntl(
+
+
+
+ );
+
+ wrapper
+ .find('[data-test-subj="export-timeline"]')
+ .first()
+ .simulate('click');
+
+ expect(enableExportTimelineDownloader).toBeCalledWith(mockResults[0]);
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.tsx
index 4bbf98dafe38d..8588beed64b79 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/timelines_table/actions_columns.tsx
@@ -55,6 +55,7 @@ export const getActionsColumns = ({
},
enabled: ({ savedObjectId }: OpenTimelineResult) => savedObjectId != null,
description: i18n.EXPORT_SELECTED,
+ 'data-test-subj': 'export-timeline',
};
const deleteTimelineColumn = {
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts
index 1265c056ec506..51c72681c0863 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts
@@ -121,7 +121,7 @@ export interface OpenTimelineProps {
/** Required by EuiTable for expandable rows: a map of `TimelineResult.savedObjectId` to rendered notes */
itemIdToExpandedNotesRowMap: Record;
/** Display import timelines modal*/
- importCompleteToggle?: boolean;
+ importDataModalToggle?: boolean;
/** If this callback is specified, a "Favorite Selected" button will be displayed, and this callback will be invoked when the button is clicked */
onAddTimelinesToFavorites?: OnAddTimelinesToFavorites;
/** If this callback is specified, a "Delete Selected" button will be displayed, and this callback will be invoked when the button is clicked */
@@ -153,7 +153,7 @@ export interface OpenTimelineProps {
/** the currently-selected timelines in the table */
selectedItems: OpenTimelineResult[];
/** Toggle export timelines modal*/
- setImportCompleteToggle?: React.Dispatch>;
+ setImportDataModalToggle?: React.Dispatch>;
/** the requested sort direction of the query results */
sortDirection: 'asc' | 'desc';
/** the requested field to sort on */
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
index 4b0e0030be53d..2dd6955581eff 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
@@ -15,10 +15,10 @@ import {
Rule,
FetchRuleProps,
BasicFetchProps,
- ImportRulesProps,
+ ImportDataProps,
ExportDocumentsProps,
RuleStatusResponse,
- ImportRulesResponse,
+ ImportDataResponse,
PrePackagedRulesStatusResponse,
BulkRuleResponse,
} from './types';
@@ -204,11 +204,11 @@ export const importRules = async ({
fileToImport,
overwrite = false,
signal,
-}: ImportRulesProps): Promise => {
+}: ImportDataProps): Promise => {
const formData = new FormData();
formData.append('file', fileToImport);
- return KibanaServices.get().http.fetch(
+ return KibanaServices.get().http.fetch(
`${DETECTION_ENGINE_RULES_URL}/_import`,
{
method: 'POST',
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
index 53a1c0770028c..f676ab944fce4 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
@@ -194,7 +194,7 @@ export interface BasicFetchProps {
signal: AbortSignal;
}
-export interface ImportRulesProps {
+export interface ImportDataProps {
fileToImport: File;
overwrite?: boolean;
signal: AbortSignal;
@@ -208,7 +208,7 @@ export interface ImportRulesResponseError {
};
}
-export interface ImportRulesResponse {
+export interface ImportDataResponse {
success: boolean;
success_count: number;
errors: ImportRulesResponseError[];
diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/all/api.ts b/x-pack/legacy/plugins/siem/public/containers/timeline/all/api.ts
index 0479851fc5b55..4c8e2384de585 100644
--- a/x-pack/legacy/plugins/siem/public/containers/timeline/all/api.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/timeline/all/api.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { ImportRulesProps, ImportRulesResponse } from '../../detection_engine/rules';
+import { ImportDataProps, ImportDataResponse } from '../../detection_engine/rules';
import { KibanaServices } from '../../../lib/kibana';
import { TIMELINE_IMPORT_URL, TIMELINE_EXPORT_URL } from '../../../../common/constants';
import { ExportSelectedData } from '../../../components/generic_downloader';
@@ -13,11 +13,11 @@ export const importTimelines = async ({
fileToImport,
overwrite = false,
signal,
-}: ImportRulesProps): Promise => {
+}: ImportDataProps): Promise => {
const formData = new FormData();
formData.append('file', fileToImport);
- return KibanaServices.get().http.fetch(`${TIMELINE_IMPORT_URL}`, {
+ return KibanaServices.get().http.fetch(`${TIMELINE_IMPORT_URL}`, {
method: 'POST',
headers: { 'Content-Type': undefined },
query: { overwrite },
diff --git a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx
index 38462e6526454..75bef7a04a4c9 100644
--- a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx
@@ -14,6 +14,7 @@ import { StatefulOpenTimeline } from '../../components/open_timeline';
import { WrapperPage } from '../../components/wrapper_page';
import { SpyRoute } from '../../utils/route/spy_routes';
import * as i18n from './translations';
+import { useKibana } from '../../lib/kibana';
const TimelinesContainer = styled.div`
width: 100%;
@@ -28,17 +29,24 @@ type OwnProps = TimelinesProps;
export const DEFAULT_SEARCH_RESULTS_PER_PAGE = 10;
const TimelinesPageComponent: React.FC = ({ apolloClient }) => {
- const [importCompleteToggle, setImportCompleteToggle] = useState(false);
+ const [importDataModalToggle, setImportDataModalToggle] = useState(false);
const onImportTimelineBtnClick = useCallback(() => {
- setImportCompleteToggle(true);
- }, [setImportCompleteToggle]);
+ setImportDataModalToggle(true);
+ }, [setImportDataModalToggle]);
+
+ const uiCapabilities = useKibana().services.application.capabilities;
+ const capabilitiesCanUserCRUD: boolean =
+ typeof uiCapabilities.siem.crud === 'boolean' ? uiCapabilities.siem.crud : false;
+
return (
<>
-
- {i18n.ALL_TIMELINES_IMPORT_TIMELINE_TITLE}
-
+ {capabilitiesCanUserCRUD && (
+
+ {i18n.ALL_TIMELINES_IMPORT_TIMELINE_TITLE}
+
+ )}
@@ -46,8 +54,8 @@ const TimelinesPageComponent: React.FC = ({ apolloClient }) => {
apolloClient={apolloClient}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isModal={false}
- importCompleteToggle={importCompleteToggle}
- setImportCompleteToggle={setImportCompleteToggle}
+ importDataModalToggle={importDataModalToggle && capabilitiesCanUserCRUD}
+ setImportDataModalToggle={setImportDataModalToggle}
title={i18n.ALL_TIMELINES_PANEL_TITLE}
/>
diff --git a/x-pack/test/api_integration/apis/siem/saved_objects/timeline.ts b/x-pack/test/api_integration/apis/siem/saved_objects/timeline.ts
index a7e7cf4476f3f..6fe11bc294795 100644
--- a/x-pack/test/api_integration/apis/siem/saved_objects/timeline.ts
+++ b/x-pack/test/api_integration/apis/siem/saved_objects/timeline.ts
@@ -175,7 +175,7 @@ export default function({ getService }: FtrProviderContext) {
expect(version).to.not.be.empty();
});
- it.skip('Update a timeline with a new title', async () => {
+ it('Update a timeline with a new title', async () => {
const titleToSaved = 'hello title';
const response = await createBasicTimeline(client, titleToSaved);
const { savedObjectId, version } = response.data && response.data.persistTimeline.timeline;
From c4df77e6047b6414692e7380c51dd95625ecbefd Mon Sep 17 00:00:00 2001
From: Rashmi Kulkarni
Date: Thu, 26 Mar 2020 13:08:20 -0700
Subject: [PATCH 005/127] configurable test users for xpack -homepage tests.
(#60808)
* configurable test users for xpack
* removed exclusive tests
* added data-test-subj for the access denied page
* updated the JEST snapshot, cleaned up the test
* changes to the test_api_keys role
* more changes to consolidate the page object function
Co-authored-by: Elastic Machine
---
.../api_keys_grid_page.test.tsx.snap | 5 ++++-
.../permission_denied/permission_denied.tsx | 2 +-
.../test/functional/apps/api_keys/home_page.ts | 17 +++++++++++++++--
x-pack/test/functional/config.js | 15 +++++++++++++++
.../functional/page_objects/api_keys_page.ts | 7 ++++++-
5 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap
index ceb0fe751c2c7..6d1e0054078bd 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap
@@ -135,7 +135,9 @@ exports[`APIKeysGridPage renders permission denied if user does not have require
}
iconType="securityApp"
title={
-