From a3f2ca8878c6888fe577a32136bcff35548f74de Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 7 Jun 2019 15:26:39 -0700 Subject: [PATCH 001/189] Adds saved query to data mappings --- src/legacy/core_plugins/data/index.ts | 2 ++ src/legacy/core_plugins/data/mappings.json | 28 +++++++++++++++++++ src/legacy/core_plugins/kibana/index.js | 5 ++-- .../plugin_spec/plugin_spec_options.d.ts | 2 ++ 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/legacy/core_plugins/data/mappings.json diff --git a/src/legacy/core_plugins/data/index.ts b/src/legacy/core_plugins/data/index.ts index 0557fd82edbc8..794255b41c6dc 100644 --- a/src/legacy/core_plugins/data/index.ts +++ b/src/legacy/core_plugins/data/index.ts @@ -19,6 +19,7 @@ import { resolve } from 'path'; import { Legacy } from '../../../../kibana'; +import mappings from './mappings.json'; // eslint-disable-next-line import/no-default-export export default function DataPlugin(kibana: any) { @@ -35,6 +36,7 @@ export default function DataPlugin(kibana: any) { uiExports: { injectDefaultVars: () => ({}), styleSheetPaths: resolve(__dirname, 'public/index.scss'), + mappings, }, }; diff --git a/src/legacy/core_plugins/data/mappings.json b/src/legacy/core_plugins/data/mappings.json new file mode 100644 index 0000000000000..4926a1be3b960 --- /dev/null +++ b/src/legacy/core_plugins/data/mappings.json @@ -0,0 +1,28 @@ +{ + "query": { + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + }, + "query": { + "properties": { + "language": { + "type": "text" + }, + "query": { + "type": "text" + } + } + }, + "filters": { + "type": "text" + }, + "timefilter": { + "type": "text" + } + } + } +} diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index d2bab2a5df907..7ff833ccc384a 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -64,6 +64,7 @@ export default function (kibana) { 'plugins/kibana/visualize/saved_visualizations/saved_visualization_register', 'plugins/kibana/discover/saved_searches/saved_search_register', 'plugins/kibana/dashboard/saved_dashboard/saved_dashboard_register', + // 'plugins/data/saved_queriesfilters/saved_queriesfilters_register' ], app: { id: 'kibana', @@ -164,7 +165,7 @@ export default function (kibana) { }, }, search: { - icon: 'search', + icon: 'discoverApp', defaultSearchField: 'title', isImportableAndExportable: true, getTitle(obj) { @@ -215,7 +216,7 @@ export default function (kibana) { getTitle(obj) { return `Advanced Settings [${obj.id}]`; }, - }, + } }, savedObjectSchemas: { diff --git a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts index 50c5cfd0c5579..10ec98ea5154d 100644 --- a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts +++ b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts @@ -23,6 +23,8 @@ export type InitPluginFunction = (server: Server) => void; export interface UiExports { injectDefaultVars: (server: Server) => { [key: string]: any }; styleSheetPaths?: string; + savedObjectsManagement?: any; // Not ideal + mappings?: any; // Not ideal } export interface PluginSpecOptions { From e76067c792a1d37b861fc9810bb73d02bbeb1b80 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 10 Jun 2019 16:38:48 -0500 Subject: [PATCH 002/189] Loading saved query in discover --- src/legacy/core_plugins/data/index.ts | 16 ++++++++ .../public/discover/controllers/discover.js | 41 ++++++++++++++++--- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/data/index.ts b/src/legacy/core_plugins/data/index.ts index 794255b41c6dc..877c6a5596a45 100644 --- a/src/legacy/core_plugins/data/index.ts +++ b/src/legacy/core_plugins/data/index.ts @@ -37,6 +37,22 @@ export default function DataPlugin(kibana: any) { injectDefaultVars: () => ({}), styleSheetPaths: resolve(__dirname, 'public/index.scss'), mappings, + savedObjectsManagement: { + query: { + icon: 'search', + defaultSearchField: 'title', + isImportableAndExportable: true, + getTitle(obj: any) { + return obj.attributes.title; + }, + getInAppUrl(obj: any) { + return { + path: `/app/kibana#/discover?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`, + uiCapabilitiesPath: 'discover.show', + }; + }, + }, + }, }, }; diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 17bcc4ab46c47..85ed119d90cb2 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -166,6 +166,34 @@ uiRoutes 'search': '/discover', 'index-pattern': '/management/kibana/objects/savedSearches/' + $route.current.params.id })); + }, + savedQuery: function (AppState, Private) { + const appState = new AppState(); + const savedQueryId = appState.savedQuery; + const savedObjectsClient = Private(SavedObjectsClientProvider); + + if (savedQueryId) { + return savedObjectsClient.get('query', savedQueryId).then(savedQuery => { + if (savedQuery.error) return; + + const time = JSON.parse(savedQuery.attributes.timefilter); + + timefilter.setTime({ + from: time.timeFrom, + to: time.timeTo, + }); + + if (time.refreshInterval) { + timefilter.setRefreshInterval(time.refreshInterval); + } + + return { + ...savedQuery.attributes, + filters: JSON.parse(savedQuery.attributes.filters), + timefilter: time, + }; + }); + } } } }); @@ -488,15 +516,18 @@ function discoverController( function getStateDefaults() { return { - query: $scope.searchSource.getField('query') || { - query: '', - language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') - }, + query: ($route.current.locals.savedQuery && $route.current.locals.savedQuery.query) + || $scope.searchSource.getField('query') + || { + query: '', + language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') + }, sort: getSort.array(savedSearch.sort, $scope.indexPattern, config.get('discover:sort:defaultOrder')), columns: savedSearch.columns.length > 0 ? savedSearch.columns : config.get('defaultColumns').slice(), index: $scope.indexPattern.id, interval: 'auto', - filters: _.cloneDeep($scope.searchSource.getOwnField('filter')) + filters: ($route.current.locals.savedQuery && $route.current.locals.savedQuery.filters) + || _.cloneDeep($scope.searchSource.getOwnField('filter')) }; } From 188551ed2ea325d94bde8312341231e14f04ae03 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 10 Jun 2019 17:31:09 -0500 Subject: [PATCH 003/189] Create SavedQuery type and add it as a prop to the SearchBar --- src/legacy/core_plugins/data/public/index.ts | 1 + .../core_plugins/data/public/search/index.tsx | 2 +- .../search_bar/components/search_bar.tsx | 2 ++ .../data/public/search/search_bar/index.tsx | 18 ++++++++++++++++++ .../data/public/search/search_service.ts | 2 ++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index a00e01804723c..d80b736a53ad0 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -93,3 +93,4 @@ export { ExpressionRenderer, ExpressionRendererProps, ExpressionRunner } from '. /** @public types */ export { IndexPattern, StaticIndexPattern, StaticIndexPatternField, Field } from './index_patterns'; export { Query } from './query'; +export { SavedQuery } from './search'; diff --git a/src/legacy/core_plugins/data/public/search/index.tsx b/src/legacy/core_plugins/data/public/search/index.tsx index b0687ea3c6bcd..7afad4a127cfd 100644 --- a/src/legacy/core_plugins/data/public/search/index.tsx +++ b/src/legacy/core_plugins/data/public/search/index.tsx @@ -17,4 +17,4 @@ * under the License. */ -export { SearchService, SearchSetup } from './search_service'; +export { SearchService, SearchSetup, SavedQuery } from './search_service'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 26a641b7bc153..22a468274b856 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -29,6 +29,7 @@ import { Storage } from 'ui/storage'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; +import { SavedQuery } from '../index'; interface DateRange { from: string; @@ -48,6 +49,7 @@ interface Props { indexPatterns: IndexPattern[]; store: Storage; filters: Filter[]; + savedQuery: SavedQuery; onFiltersUpdated: (filters: Filter[]) => void; showQueryBar: boolean; showFilterBar: boolean; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index b181b43e0e527..f580c1eb66343 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -17,7 +17,25 @@ * under the License. */ +import { Filter } from '@kbn/es-query'; +import { Query } from '../../query/query_bar'; + export { SearchBar } from './components'; // @ts-ignore export { setupDirective } from './directive'; + +interface RefreshInterval { + pause: boolean; + value: number; +} + +export interface SavedQuery { + query: Query; + filters: Filter[]; + timefilter: { + timeFrom: string; + timeTo: string; + refreshInterval: RefreshInterval; + }; +} diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index 2e65280d3bcff..a653fca6cd521 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -41,3 +41,5 @@ export class SearchService { /** @public */ export type SearchSetup = ReturnType; + +export { SavedQuery } from './search_bar'; From aebd5bdb40b1f5f5ab2aae390703f9031b351483 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 11 Jun 2019 08:46:18 -0700 Subject: [PATCH 004/189] adds conditional for filters and timefilters --- .../public/discover/controllers/discover.js | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 85ed119d90cb2..f572b1882bef1 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -175,21 +175,27 @@ uiRoutes if (savedQueryId) { return savedObjectsClient.get('query', savedQueryId).then(savedQuery => { if (savedQuery.error) return; + let filters = savedQuery.attributes.filters; + if (filters) { + filters = JSON.parse(filters); + } + let time = savedQuery.attributes.timefilter; + if (time) { + time = JSON.parse(savedQuery.attributes.timefilter); - const time = JSON.parse(savedQuery.attributes.timefilter); - - timefilter.setTime({ - from: time.timeFrom, - to: time.timeTo, - }); + timefilter.setTime({ + from: time.timeFrom, + to: time.timeTo, + }); - if (time.refreshInterval) { - timefilter.setRefreshInterval(time.refreshInterval); + if (time.refreshInterval) { + timefilter.setRefreshInterval(time.refreshInterval); + } } return { ...savedQuery.attributes, - filters: JSON.parse(savedQuery.attributes.filters), + filters: filters, timefilter: time, }; }); From fd297f008431241810949dfe5f6f6354bcc1eb2b Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 11 Jun 2019 09:03:47 -0700 Subject: [PATCH 005/189] Adds append to suggestions component --- .../data/public/query/query_bar/components/query_bar_input.tsx | 1 + .../query_bar/components/typeahead/suggestions_component.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 3722c3a572e10..a910c6ec39d7a 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -516,6 +516,7 @@ export class QueryBarInputUI extends Component { onClick={this.onClickSuggestion} onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} + append={
} />
diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx index f7cd249d3dbb7..6b01c77304541 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx @@ -29,6 +29,7 @@ interface Props { show: boolean; suggestions: AutocompleteSuggestion[]; loadMore: () => void; + append: React.ReactNode; } export class SuggestionsComponent extends Component { From 39ee55e0e2909bfb466a4fc2a106510f728b48b6 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 11 Jun 2019 12:57:39 -0700 Subject: [PATCH 006/189] hard codes styling and saved query title --- .../data/public/query/query_bar/components/query_bar_input.tsx | 2 +- .../query_bar/components/typeahead/suggestions_component.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index a910c6ec39d7a..2bb98a1a5dced 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -516,7 +516,7 @@ export class QueryBarInputUI extends Component { onClick={this.onClickSuggestion} onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} - append={
} + append={
Saved Query Title
} />
diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx index 6b01c77304541..3e53436ce2dae 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx @@ -68,6 +68,7 @@ export class SuggestionsComponent extends Component { > {suggestions} + {this.props.append} From e960475ee79d36453a30376e5cbf70b263fd27a2 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 11 Jun 2019 15:37:11 -0500 Subject: [PATCH 007/189] Pass SavedQuery down through the SearchBar components and add SavedQueryRow component --- .../query/query_bar/components/query_bar.tsx | 3 ++ .../query_bar/components/query_bar_input.tsx | 5 ++- .../query_bar/components/saved_query_row.tsx | 33 +++++++++++++++++++ .../typeahead/suggestion_component.tsx | 4 +-- .../search_bar/components/search_bar.tsx | 1 + .../search/search_bar/directive/index.js | 1 + .../data/public/search/search_bar/index.tsx | 2 ++ .../public/discover/controllers/discover.js | 1 + .../kibana/public/discover/index.html | 1 + 9 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index e1df6286bd120..51ec65203af52 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -41,6 +41,7 @@ import { QueryBarInput } from './query_bar_input'; import { getQueryLog } from '../lib/get_query_log'; import { Query } from '../index'; +import { SavedQuery } from '../../../search/search_bar'; const config = chrome.getUiSettingsClient(); @@ -51,6 +52,7 @@ interface DateRange { interface Props { query: Query; + savedQuery: SavedQuery; onSubmit: (payload: { dateRange: DateRange; query: Query }) => void; disableAutoFocus?: boolean; appName: string; @@ -255,6 +257,7 @@ export class QueryBarUI extends Component { onChange={this.onChange} onSubmit={this.onInputSubmit} persistedLog={this.persistedLog} + savedQuery={this.props.savedQuery} /> {this.renderUpdateButton()} diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 2bb98a1a5dced..c3af00117a51f 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -41,11 +41,14 @@ import { QueryLanguageSwitcher } from './language_switcher'; import { SuggestionsComponent } from './typeahead/suggestions_component'; import { getQueryLog } from '../lib/get_query_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; +import { SavedQueryRow } from './saved_query_row'; +import { SavedQuery } from '../../../search/search_bar'; interface Props { indexPatterns: Array; intl: InjectedIntl; query: Query; + savedQuery: SavedQuery; appName: string; disableAutoFocus?: boolean; screenTitle?: string; @@ -516,7 +519,7 @@ export class QueryBarInputUI extends Component { onClick={this.onClickSuggestion} onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} - append={
Saved Query Title
} + append={} /> diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx new file mode 100644 index 0000000000000..6cde6f5c57672 --- /dev/null +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -0,0 +1,33 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { FunctionComponent } from 'react'; +import { SavedQuery } from '../../../search/search_bar'; + +interface Props { + savedQuery: SavedQuery; +} + +export const SavedQueryRow: FunctionComponent = props => { + return ( +
+ {props.savedQuery ? props.savedQuery.title : 'Manage Saved Queries'} +
+ ); +}; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx index b92f8c6765f56..180a90c2acc95 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx @@ -19,7 +19,7 @@ import { EuiIcon } from '@elastic/eui'; import classNames from 'classnames'; -import React, { SFC } from 'react'; +import React, { FunctionComponent } from 'react'; import { AutocompleteSuggestion } from 'ui/autocomplete_providers'; function getEuiIconType(type: string) { @@ -48,7 +48,7 @@ interface Props { ariaId: string; } -export const SuggestionComponent: SFC = props => { +export const SuggestionComponent: FunctionComponent = props => { return ( // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus
{ {this.props.showQueryBar ? ( Date: Tue, 11 Jun 2019 18:18:29 -0500 Subject: [PATCH 008/189] Add ability to save current query. Needs some bug fixes and polish. --- src/legacy/core_plugins/data/index.ts | 2 +- src/legacy/core_plugins/data/mappings.json | 28 ---- src/legacy/core_plugins/data/mappings.ts | 47 ++++++ .../query/query_bar/components/query_bar.tsx | 3 + .../query_bar/components/query_bar_input.tsx | 16 +- .../query_bar/components/saved_query_row.tsx | 137 +++++++++++++++++- .../typeahead/suggestions_component.tsx | 2 +- .../query_bar/lib/saved_query_service.ts | 51 +++++++ .../search_bar/components/search_bar.tsx | 34 +++++ .../data/public/search/search_bar/index.tsx | 4 +- 10 files changed, 285 insertions(+), 39 deletions(-) delete mode 100644 src/legacy/core_plugins/data/mappings.json create mode 100644 src/legacy/core_plugins/data/mappings.ts create mode 100644 src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts diff --git a/src/legacy/core_plugins/data/index.ts b/src/legacy/core_plugins/data/index.ts index 877c6a5596a45..549649bd2ca4a 100644 --- a/src/legacy/core_plugins/data/index.ts +++ b/src/legacy/core_plugins/data/index.ts @@ -19,7 +19,7 @@ import { resolve } from 'path'; import { Legacy } from '../../../../kibana'; -import mappings from './mappings.json'; +import { mappings } from './mappings'; // eslint-disable-next-line import/no-default-export export default function DataPlugin(kibana: any) { diff --git a/src/legacy/core_plugins/data/mappings.json b/src/legacy/core_plugins/data/mappings.json deleted file mode 100644 index 4926a1be3b960..0000000000000 --- a/src/legacy/core_plugins/data/mappings.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "query": { - "properties": { - "title": { - "type": "text" - }, - "description": { - "type": "text" - }, - "query": { - "properties": { - "language": { - "type": "text" - }, - "query": { - "type": "text" - } - } - }, - "filters": { - "type": "text" - }, - "timefilter": { - "type": "text" - } - } - } -} diff --git a/src/legacy/core_plugins/data/mappings.ts b/src/legacy/core_plugins/data/mappings.ts new file mode 100644 index 0000000000000..6786ce7bab713 --- /dev/null +++ b/src/legacy/core_plugins/data/mappings.ts @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const mappings = { + query: { + properties: { + title: { + type: 'text', + }, + description: { + type: 'text', + }, + query: { + properties: { + language: { + type: 'text', + }, + query: { + type: 'text', + }, + }, + }, + filters: { + type: 'text', + }, + timefilter: { + type: 'text', + }, + }, + }, +}; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 51ec65203af52..df88f2ebec776 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -42,6 +42,7 @@ import { QueryBarInput } from './query_bar_input'; import { getQueryLog } from '../lib/get_query_log'; import { Query } from '../index'; import { SavedQuery } from '../../../search/search_bar'; +import { SavedQueryDetails } from './saved_query_row'; const config = chrome.getUiSettingsClient(); @@ -69,6 +70,7 @@ interface Props { showAutoRefreshOnly?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; customSubmitButton?: any; + onSave: (savedQueryDetails: SavedQueryDetails) => void; } interface State { @@ -258,6 +260,7 @@ export class QueryBarUI extends Component { onSubmit={this.onInputSubmit} persistedLog={this.persistedLog} savedQuery={this.props.savedQuery} + onSave={this.props.onSave} /> {this.renderUpdateButton()} diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index c3af00117a51f..2f413eb896126 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -43,12 +43,13 @@ import { getQueryLog } from '../lib/get_query_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; import { SavedQueryRow } from './saved_query_row'; import { SavedQuery } from '../../../search/search_bar'; +import { SavedQueryDetails } from './saved_query_row'; interface Props { indexPatterns: Array; intl: InjectedIntl; query: Query; - savedQuery: SavedQuery; + savedQuery?: SavedQuery; appName: string; disableAutoFocus?: boolean; screenTitle?: string; @@ -59,6 +60,7 @@ interface Props { languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; onChange?: (query: Query) => void; onSubmit?: (query: Query) => void; + onSave?: (savedQueryDetails: SavedQueryDetails) => void; } interface State { @@ -433,6 +435,7 @@ export class QueryBarInputUI extends Component { 'indexPatterns', 'intl', 'query', + 'savedQuery', 'appName', 'disableAutoFocus', 'screenTitle', @@ -443,6 +446,7 @@ export class QueryBarInputUI extends Component { 'languageSwitcherPopoverAnchorPosition', 'onChange', 'onSubmit', + 'onSave', ]); return ( @@ -519,7 +523,15 @@ export class QueryBarInputUI extends Component { onClick={this.onClickSuggestion} onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} - append={} + append={ + this.props.savedQuery && this.props.onSave ? ( + + ) : null + } />
diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 6cde6f5c57672..6c2fbbe3856f8 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -17,17 +17,144 @@ * under the License. */ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, Fragment, useState } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiOverlayMask, + EuiModal, + EuiButton, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalBody, + EuiModalFooter, + EuiForm, + EuiFormRow, + EuiFieldText, + EuiSwitch, +} from '@elastic/eui'; import { SavedQuery } from '../../../search/search_bar'; +import { Query } from '../index'; interface Props { + query: Query; savedQuery: SavedQuery; + onSave: (savedQueryDetails: SavedQueryDetails) => void; } -export const SavedQueryRow: FunctionComponent = props => { +export interface SavedQueryDetails { + title: string; + description: string; + includeFilters: boolean; + includeTimeFilter: boolean; + query: Query; +} + +export const SavedQueryRow: FunctionComponent = ({ query, savedQuery, onSave }) => { + const [showModal, setShowModal] = useState(false); + const [title, setTitle] = useState(''); + const [description, setDescription] = useState(''); + const [includeFilters, setIncludeFilters] = useState(false); + const [includeTimeFilter, setIncludeTimeFilter] = useState(false); + + const closeModal = () => setShowModal(false); + const onClickSave = () => { + onSave({ + title, + description, + includeFilters, + includeTimeFilter, + query, + }); + }; + + let rowContent; + if (savedQuery) { + rowContent = savedQuery.title; + } else if (query.query.length !== 0) { + rowContent = ( + + setShowModal(true)}> + Save this query for reuse + + + ); + } else { + rowContent = 'Manage saved queries'; + } + + const saveQueryForm = ( + + + { + setTitle(event.target.value); + }} + /> + + + + { + setDescription(event.target.value); + }} + /> + + + + { + setIncludeFilters(!includeFilters); + }} + /> + + + + { + setIncludeTimeFilter(!includeTimeFilter); + }} + /> + + + ); + + let modal; + if (showModal) { + modal = ( + + + + Save query + + + {saveQueryForm} + + + Cancel + + + Save + + + + + ); + } + return ( -
- {props.savedQuery ? props.savedQuery.title : 'Manage Saved Queries'} -
+ + {rowContent} + {modal} + ); }; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx index 3e53436ce2dae..f84d9d9d65f3c 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx @@ -29,7 +29,7 @@ interface Props { show: boolean; suggestions: AutocompleteSuggestion[]; loadMore: () => void; - append: React.ReactNode; + append?: React.ReactNode; } export class SuggestionsComponent extends Component { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts new file mode 100644 index 0000000000000..d2b80cc19f12c --- /dev/null +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import chrome from 'ui/chrome'; +import { SavedQuery } from '../../../search'; + +export const saveQuery = (savedQuery: SavedQuery) => { + const savedObjectsClient = chrome.getSavedObjectsClient(); + + const query = { + query: + typeof savedQuery.query.query === 'string' + ? savedQuery.query.query + : JSON.stringify(savedQuery.query.query), + language: savedQuery.query.language, + }; + + const queryObject = { + title: savedQuery.title, + description: savedQuery.description, + query, + filters: '', + timefilter: '', + }; + + if (savedQuery.filters) { + queryObject.filters = JSON.stringify(savedQuery.filters); + } + + if (savedQuery.timefilter) { + queryObject.timefilter = JSON.stringify(savedQuery.timefilter); + } + + savedObjectsClient.create('query', queryObject); +}; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 95db0c36b3888..6034a1bf8b1f9 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -30,6 +30,8 @@ import { Storage } from 'ui/storage'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; import { SavedQuery } from '../index'; +import { SavedQueryDetails } from '../../../query/query_bar/components/saved_query_row'; +import { saveQuery } from '../../../query/query_bar/lib/saved_query_service'; interface DateRange { from: string; @@ -101,6 +103,37 @@ class SearchBarUI extends Component { }); }; + public onSave = (savedQueryDetails: SavedQueryDetails) => { + const savedQuery: SavedQuery = { + title: savedQueryDetails.title, + description: savedQueryDetails.description, + query: savedQueryDetails.query, + }; + + if (savedQueryDetails.includeFilters) { + savedQuery.filters = this.props.filters; + } + + if ( + savedQueryDetails.includeTimeFilter && + this.props.dateRangeTo && + this.props.dateRangeFrom && + this.props.refreshInterval && + this.props.isRefreshPaused + ) { + savedQuery.timefilter = { + timeFrom: this.props.dateRangeFrom, + timeTo: this.props.dateRangeTo, + refreshInterval: { + value: this.props.refreshInterval, + pause: this.props.isRefreshPaused, + }, + }; + } + + saveQuery(savedQuery); + }; + public componentDidMount() { if (this.filterBarRef) { this.setFilterBarHeight(); @@ -167,6 +200,7 @@ class SearchBarUI extends Component { refreshInterval={this.props.refreshInterval} showAutoRefreshOnly={this.props.showAutoRefreshOnly} onRefreshChange={this.props.onRefreshChange} + onSave={this.onSave} /> ) : ( '' diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index 307e061466286..0f6eb4307eb02 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -34,8 +34,8 @@ export interface SavedQuery { title: string; description: string; query: Query; - filters: Filter[]; - timefilter: { + filters?: Filter[]; + timefilter?: { timeFrom: string; timeTo: string; refreshInterval: RefreshInterval; From 0fc7a12b0d93cf56f3a9d0295130ddb3a1affa95 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 12 Jun 2019 15:49:03 -0500 Subject: [PATCH 009/189] re-add save button --- .../data/public/query/query_bar/components/query_bar_input.tsx | 2 +- .../data/public/query/query_bar/components/saved_query_row.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 2f413eb896126..019dc5f56dd5f 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -524,7 +524,7 @@ export class QueryBarInputUI extends Component { onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} append={ - this.props.savedQuery && this.props.onSave ? ( + this.props.onSave ? ( void; } From 239c4461ffefcaa2cb15d9b01690bbc2cf01f358 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 12 Jun 2019 15:59:10 -0500 Subject: [PATCH 010/189] wrap title in flex item --- .../data/public/query/query_bar/components/saved_query_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 85d91731f4c4c..af9b7c95f8b2a 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -71,7 +71,7 @@ export const SavedQueryRow: FunctionComponent = ({ query, savedQuery, onS let rowContent; if (savedQuery) { - rowContent = savedQuery.title; + rowContent = {savedQuery.title}; } else if (query.query.length !== 0) { rowContent = ( From cab055adcf2ff92ad40d363c0d39f5e235e00763 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 12 Jun 2019 16:57:19 -0500 Subject: [PATCH 011/189] Fix apparent bug in getDerviedStateFromProps. Query should only update when the passed in props have changed from the previous props. --- .../data/public/query/query_bar/components/query_bar.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index df88f2ebec776..b27fd19484f4b 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -42,7 +42,6 @@ import { QueryBarInput } from './query_bar_input'; import { getQueryLog } from '../lib/get_query_log'; import { Query } from '../index'; import { SavedQuery } from '../../../search/search_bar'; -import { SavedQueryDetails } from './saved_query_row'; const config = chrome.getUiSettingsClient(); @@ -89,7 +88,7 @@ export class QueryBarUI extends Component { } let nextQuery = null; - if (nextProps.query.query !== prevState.query.query) { + if (nextProps.query.query !== get(prevState, 'currentProps.query.query')) { nextQuery = { query: nextProps.query.query, language: nextProps.query.language, From 3f2e95ad127b8ca02fda985ec9d4f3e59485315c Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 12 Jun 2019 17:09:57 -0500 Subject: [PATCH 012/189] Move modal into its own component and use it in the SearchBar --- .../query/query_bar/components/query_bar.tsx | 2 +- .../query_bar/components/query_bar_input.tsx | 3 +- .../query_bar/components/saved_query_row.tsx | 110 +-------------- .../search_bar/components/save_query_form.tsx | 129 ++++++++++++++++++ .../search_bar/components/search_bar.tsx | 31 +++-- 5 files changed, 158 insertions(+), 117 deletions(-) create mode 100644 src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index b27fd19484f4b..a57bc662b2f92 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -69,7 +69,7 @@ interface Props { showAutoRefreshOnly?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; customSubmitButton?: any; - onSave: (savedQueryDetails: SavedQueryDetails) => void; + onSave: () => void; } interface State { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 019dc5f56dd5f..e4172998c95cf 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -43,7 +43,6 @@ import { getQueryLog } from '../lib/get_query_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; import { SavedQueryRow } from './saved_query_row'; import { SavedQuery } from '../../../search/search_bar'; -import { SavedQueryDetails } from './saved_query_row'; interface Props { indexPatterns: Array; @@ -60,7 +59,7 @@ interface Props { languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; onChange?: (query: Query) => void; onSubmit?: (query: Query) => void; - onSave?: (savedQueryDetails: SavedQueryDetails) => void; + onSave?: () => void; } interface State { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index af9b7c95f8b2a..37d6e92c3de7b 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -17,30 +17,15 @@ * under the License. */ -import React, { FunctionComponent, Fragment, useState } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiButtonEmpty, - EuiOverlayMask, - EuiModal, - EuiButton, - EuiModalHeader, - EuiModalHeaderTitle, - EuiModalBody, - EuiModalFooter, - EuiForm, - EuiFormRow, - EuiFieldText, - EuiSwitch, -} from '@elastic/eui'; +import React, { FunctionComponent, Fragment } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { SavedQuery } from '../../../search/search_bar'; import { Query } from '../index'; interface Props { query: Query; savedQuery?: SavedQuery; - onSave: (savedQueryDetails: SavedQueryDetails) => void; + onSave: () => void; } export interface SavedQueryDetails { @@ -52,109 +37,22 @@ export interface SavedQueryDetails { } export const SavedQueryRow: FunctionComponent = ({ query, savedQuery, onSave }) => { - const [showModal, setShowModal] = useState(false); - const [title, setTitle] = useState(''); - const [description, setDescription] = useState(''); - const [includeFilters, setIncludeFilters] = useState(false); - const [includeTimeFilter, setIncludeTimeFilter] = useState(false); - - const closeModal = () => setShowModal(false); - const onClickSave = () => { - onSave({ - title, - description, - includeFilters, - includeTimeFilter, - query, - }); - }; - let rowContent; if (savedQuery) { rowContent = {savedQuery.title}; } else if (query.query.length !== 0) { rowContent = ( - setShowModal(true)}> - Save this query for reuse - + Save this query for reuse ); } else { rowContent = 'Manage saved queries'; } - const saveQueryForm = ( - - - { - setTitle(event.target.value); - }} - /> - - - - { - setDescription(event.target.value); - }} - /> - - - - { - setIncludeFilters(!includeFilters); - }} - /> - - - - { - setIncludeTimeFilter(!includeTimeFilter); - }} - /> - - - ); - - let modal; - if (showModal) { - modal = ( - - - - Save query - - - {saveQueryForm} - - - Cancel - - - Save - - - - - ); - } - return ( {rowContent} - {modal} ); }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx new file mode 100644 index 0000000000000..91a1a7c341c36 --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -0,0 +1,129 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { FunctionComponent, useState } from 'react'; +import { + EuiButtonEmpty, + EuiOverlayMask, + EuiModal, + EuiButton, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalBody, + EuiModalFooter, + EuiForm, + EuiFormRow, + EuiFieldText, + EuiSwitch, +} from '@elastic/eui'; + +interface Props { + onSave: (savedQueryMeta: SavedQueryMeta) => void; + onClose: () => void; +} + +export interface SavedQueryMeta { + title: string; + description: string; + shouldIncludeFilters: boolean; + shouldIncludeTimefilter: boolean; +} + +export const SaveQueryForm: FunctionComponent = ({ onSave, onClose }) => { + const [title, setTitle] = useState(''); + const [description, setDescription] = useState(''); + const [shouldIncludeFilters, setShouldIncludeFilters] = useState(false); + const [shouldIncludeTimefilter, setIncludeTimefilter] = useState(false); + + const saveQueryForm = ( + + + { + setTitle(event.target.value); + }} + /> + + + + { + setDescription(event.target.value); + }} + /> + + + + { + setShouldIncludeFilters(!shouldIncludeFilters); + }} + /> + + + + { + setIncludeTimefilter(!shouldIncludeTimefilter); + }} + /> + + + ); + + return ( + + + + Save query + + + {saveQueryForm} + + + Cancel + + + onSave({ + title, + description, + shouldIncludeFilters, + shouldIncludeTimefilter, + }) + } + fill + > + Save + + + + + ); +}; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 6034a1bf8b1f9..fba05c03f4020 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -30,8 +30,8 @@ import { Storage } from 'ui/storage'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; import { SavedQuery } from '../index'; -import { SavedQueryDetails } from '../../../query/query_bar/components/saved_query_row'; import { saveQuery } from '../../../query/query_bar/lib/saved_query_service'; +import { SavedQueryMeta, SaveQueryForm } from './save_query_form'; interface DateRange { from: string; @@ -67,6 +67,7 @@ interface Props { interface State { isFiltersVisible: boolean; + showSaveQueryModal: boolean; } class SearchBarUI extends Component { @@ -80,6 +81,7 @@ class SearchBarUI extends Component { public state = { isFiltersVisible: true, + showSaveQueryModal: false, }; public setFilterBarHeight = () => { @@ -103,19 +105,19 @@ class SearchBarUI extends Component { }); }; - public onSave = (savedQueryDetails: SavedQueryDetails) => { + public onSave = (savedQueryMeta: SavedQueryMeta) => { const savedQuery: SavedQuery = { - title: savedQueryDetails.title, - description: savedQueryDetails.description, - query: savedQueryDetails.query, + title: savedQueryMeta.title, + description: savedQueryMeta.description, + query: this.props.query, }; - if (savedQueryDetails.includeFilters) { + if (savedQueryMeta.shouldIncludeFilters) { savedQuery.filters = this.props.filters; } if ( - savedQueryDetails.includeTimeFilter && + savedQueryMeta.shouldIncludeTimefilter && this.props.dateRangeTo && this.props.dateRangeFrom && this.props.refreshInterval && @@ -134,6 +136,12 @@ class SearchBarUI extends Component { saveQuery(savedQuery); }; + public onInitiateSave = () => { + this.setState({ + showSaveQueryModal: true, + }); + }; + public componentDidMount() { if (this.filterBarRef) { this.setFilterBarHeight(); @@ -200,7 +208,7 @@ class SearchBarUI extends Component { refreshInterval={this.props.refreshInterval} showAutoRefreshOnly={this.props.showAutoRefreshOnly} onRefreshChange={this.props.onRefreshChange} - onSave={this.onSave} + onSave={this.onInitiateSave} /> ) : ( '' @@ -230,6 +238,13 @@ class SearchBarUI extends Component { ) : ( '' )} + + {this.state.showSaveQueryModal ? ( + this.setState({ showSaveQueryModal: false })} + /> + ) : null} ); } From 99194ca735e47a783c4ca8badaf9149ca1075ec7 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 12 Jun 2019 17:23:18 -0500 Subject: [PATCH 013/189] Check explicitly for undefined --- .../public/search/search_bar/components/search_bar.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index fba05c03f4020..650f91c2bcd4d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -118,10 +118,10 @@ class SearchBarUI extends Component { if ( savedQueryMeta.shouldIncludeTimefilter && - this.props.dateRangeTo && - this.props.dateRangeFrom && - this.props.refreshInterval && - this.props.isRefreshPaused + this.props.dateRangeTo !== undefined && + this.props.dateRangeFrom !== undefined && + this.props.refreshInterval !== undefined && + this.props.isRefreshPaused !== undefined ) { savedQuery.timefilter = { timeFrom: this.props.dateRangeFrom, From 9d1af7f3ca9ed5ddae2ef0ca774269dd4814df86 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 12 Jun 2019 18:20:33 -0500 Subject: [PATCH 014/189] Move state caching to SearchBar --- .../query/query_bar/components/query_bar.tsx | 159 +++++------------- .../search_bar/components/search_bar.tsx | 125 +++++++++++++- 2 files changed, 158 insertions(+), 126 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index a57bc662b2f92..c7cab0ce96c84 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -21,8 +21,6 @@ import { doesKueryExpressionHaveLuceneSyntaxError } from '@kbn/es-query'; import { IndexPattern } from 'ui/index_patterns'; import classNames from 'classnames'; -import _ from 'lodash'; -import { get, isEqual } from 'lodash'; import React, { Component } from 'react'; import { Storage } from 'ui/storage'; import { timeHistory } from 'ui/timefilter/time_history'; @@ -54,6 +52,7 @@ interface Props { query: Query; savedQuery: SavedQuery; onSubmit: (payload: { dateRange: DateRange; query: Query }) => void; + onChange: (payload: { dateRange: DateRange; query: Query }) => void; disableAutoFocus?: boolean; appName: string; screenTitle: string; @@ -70,81 +69,15 @@ interface Props { onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; customSubmitButton?: any; onSave: () => void; + isDirty: boolean; } interface State { - query: Query; - inputIsPristine: boolean; - currentProps?: Props; - dateRangeFrom: string; - dateRangeTo: string; isDateRangeInvalid: boolean; } export class QueryBarUI extends Component { - public static getDerivedStateFromProps(nextProps: Props, prevState: State) { - if (isEqual(prevState.currentProps, nextProps)) { - return null; - } - - let nextQuery = null; - if (nextProps.query.query !== get(prevState, 'currentProps.query.query')) { - nextQuery = { - query: nextProps.query.query, - language: nextProps.query.language, - }; - } else if (nextProps.query.language !== prevState.query.language) { - nextQuery = { - query: '', - language: nextProps.query.language, - }; - } - - let nextDateRange = null; - if ( - nextProps.dateRangeFrom !== get(prevState, 'currentProps.dateRangeFrom') || - nextProps.dateRangeTo !== get(prevState, 'currentProps.dateRangeTo') - ) { - nextDateRange = { - dateRangeFrom: nextProps.dateRangeFrom, - dateRangeTo: nextProps.dateRangeTo, - }; - } - - const nextState: any = { - currentProps: nextProps, - }; - if (nextQuery) { - nextState.query = nextQuery; - } - if (nextDateRange) { - nextState.dateRangeFrom = nextDateRange.dateRangeFrom; - nextState.dateRangeTo = nextDateRange.dateRangeTo; - } - return nextState; - } - - /* - Keep the "draft" value in local state until the user actually submits the query. There are a couple advantages: - - 1. Each app doesn't have to maintain its own "draft" value if it wants to put off updating the query in app state - until the user manually submits their changes. Most apps have watches on the query value in app state so we don't - want to trigger those on every keypress. Also, some apps (e.g. dashboard) already juggle multiple query values, - each with slightly different semantics and I'd rather not add yet another variable to the mix. - - 2. Changes to the local component state won't trigger an Angular digest cycle. Triggering digest cycles on every - keypress has been a major source of performance issues for us in previous implementations of the query bar. - See https://github.com/elastic/kibana/issues/14086 - */ public state = { - query: { - query: this.props.query.query, - language: this.props.query.language, - }, - inputIsPristine: true, - currentProps: this.props, - dateRangeFrom: _.get(this.props, 'dateRangeFrom', 'now-15m'), - dateRangeTo: _.get(this.props, 'dateRangeTo', 'now'), isDateRangeInvalid: false, }; @@ -152,29 +85,25 @@ export class QueryBarUI extends Component { private persistedLog: PersistedLog | undefined; - public isDirty = () => { - if (!this.props.showDatePicker) { - return this.state.query.query !== this.props.query.query; - } - - return ( - this.state.query.query !== this.props.query.query || - this.state.dateRangeFrom !== this.props.dateRangeFrom || - this.state.dateRangeTo !== this.props.dateRangeTo - ); - }; - public onClickSubmitButton = (event: React.MouseEvent) => { if (this.persistedLog) { - this.persistedLog.add(this.state.query.query); + this.persistedLog.add(this.props.query.query); } - this.onSubmit(() => event.preventDefault()); + event.preventDefault(); + this.onSubmit({ query: this.props.query, dateRange: this.getDateRange() }); }; - public onChange = (query: Query) => { - this.setState({ + public getDateRange() { + return { + from: this.props.dateRangeFrom || 'now-15m', + to: this.props.dateRangeTo || 'now', + }; + } + + public onQueryChange = (query: Query) => { + this.props.onChange({ query, - inputIsPristine: false, + dateRange: this.getDateRange(), }); }; @@ -191,41 +120,37 @@ export class QueryBarUI extends Component { }) => { this.setState( { - dateRangeFrom: start, - dateRangeTo: end, isDateRangeInvalid: isInvalid, }, - () => isQuickSelection && this.onSubmit() + () => { + const retVal = { + query: this.props.query, + dateRange: { + from: start, + to: end, + }, + }; + + if (isQuickSelection) { + this.props.onSubmit(retVal); + } else { + this.props.onChange(retVal); + } + } ); }; - public onSubmit = (preventDefault?: () => void) => { - if (preventDefault) { - preventDefault(); - } - + public onSubmit = ({ query, dateRange }: { query: Query; dateRange: DateRange }) => { this.handleLuceneSyntaxWarning(); + timeHistory.add(this.getDateRange()); - timeHistory.add({ - from: this.state.dateRangeFrom, - to: this.state.dateRangeTo, - }); - - this.props.onSubmit({ - query: { - query: this.state.query.query, - language: this.state.query.language, - }, - dateRange: { - from: this.state.dateRangeFrom, - to: this.state.dateRangeTo, - }, - }); + this.props.onSubmit({ query, dateRange }); }; private onInputSubmit = (query: Query) => { - this.setState({ query }, () => { - this.onSubmit(); + this.onSubmit({ + query, + dateRange: this.getDateRange(), }); }; @@ -252,10 +177,10 @@ export class QueryBarUI extends Component { disableAutoFocus={this.props.disableAutoFocus} indexPatterns={this.props.indexPatterns} prepend={this.props.prepend} - query={this.state.query} + query={this.props.query} screenTitle={this.props.screenTitle} store={this.props.store} - onChange={this.onChange} + onChange={this.onQueryChange} onSubmit={this.onInputSubmit} persistedLog={this.persistedLog} savedQuery={this.props.savedQuery} @@ -272,7 +197,7 @@ export class QueryBarUI extends Component { React.cloneElement(this.props.customSubmitButton, { onClick: this.onClickSubmitButton }) ) : ( { return ( { private handleLuceneSyntaxWarning() { const { intl, store } = this.props; - const { query, language } = this.state.query; + const { query, language } = this.props.query; if ( language === 'kuery' && typeof query === 'string' && diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 650f91c2bcd4d..3cf26a29bbdab 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -26,6 +26,7 @@ import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; import { IndexPattern } from 'ui/index_patterns'; import { Storage } from 'ui/storage'; +import { get, isEqual } from 'lodash'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; @@ -68,6 +69,10 @@ interface Props { interface State { isFiltersVisible: boolean; showSaveQueryModal: boolean; + currentProps?: Props; + query: Query; + dateRangeFrom: string; + dateRangeTo: string; } class SearchBarUI extends Component { @@ -79,9 +84,82 @@ class SearchBarUI extends Component { public filterBarRef: Element | null = null; public filterBarWrapperRef: Element | null = null; + public static getDerivedStateFromProps(nextProps: Props, prevState: State) { + if (isEqual(prevState.currentProps, nextProps)) { + return null; + } + + let nextQuery = null; + if (nextProps.query.query !== get(prevState, 'currentProps.query.query')) { + nextQuery = { + query: nextProps.query.query, + language: nextProps.query.language, + }; + } else if (nextProps.query.language !== prevState.query.language) { + nextQuery = { + query: '', + language: nextProps.query.language, + }; + } + + let nextDateRange = null; + if ( + nextProps.dateRangeFrom !== get(prevState, 'currentProps.dateRangeFrom') || + nextProps.dateRangeTo !== get(prevState, 'currentProps.dateRangeTo') + ) { + nextDateRange = { + dateRangeFrom: nextProps.dateRangeFrom, + dateRangeTo: nextProps.dateRangeTo, + }; + } + + const nextState: any = { + currentProps: nextProps, + }; + if (nextQuery) { + nextState.query = nextQuery; + } + if (nextDateRange) { + nextState.dateRangeFrom = nextDateRange.dateRangeFrom; + nextState.dateRangeTo = nextDateRange.dateRangeTo; + } + return nextState; + } + + /* + Keep the "draft" value in local state until the user actually submits the query. There are a couple advantages: + + 1. Each app doesn't have to maintain its own "draft" value if it wants to put off updating the query in app state + until the user manually submits their changes. Most apps have watches on the query value in app state so we don't + want to trigger those on every keypress. Also, some apps (e.g. dashboard) already juggle multiple query values, + each with slightly different semantics and I'd rather not add yet another variable to the mix. + + 2. Changes to the local component state won't trigger an Angular digest cycle. Triggering digest cycles on every + keypress has been a major source of performance issues for us in previous implementations of the query bar. + See https://github.com/elastic/kibana/issues/14086 + */ public state = { isFiltersVisible: true, showSaveQueryModal: false, + currentProps: this.props, + query: { + query: this.props.query.query, + language: this.props.query.language, + }, + dateRangeFrom: _.get(this.props, 'dateRangeFrom', 'now-15m'), + dateRangeTo: _.get(this.props, 'dateRangeTo', 'now'), + }; + + public isDirty = () => { + if (!this.props.showDatePicker) { + return this.state.query.query !== this.props.query.query; + } + + return ( + this.state.query.query !== this.props.query.query || + this.state.dateRangeFrom !== this.props.dateRangeFrom || + this.state.dateRangeTo !== this.props.dateRangeTo + ); }; public setFilterBarHeight = () => { @@ -109,7 +187,7 @@ class SearchBarUI extends Component { const savedQuery: SavedQuery = { title: savedQueryMeta.title, description: savedQueryMeta.description, - query: this.props.query, + query: this.state.query, }; if (savedQueryMeta.shouldIncludeFilters) { @@ -118,14 +196,14 @@ class SearchBarUI extends Component { if ( savedQueryMeta.shouldIncludeTimefilter && - this.props.dateRangeTo !== undefined && - this.props.dateRangeFrom !== undefined && + this.state.dateRangeTo !== undefined && + this.state.dateRangeFrom !== undefined && this.props.refreshInterval !== undefined && this.props.isRefreshPaused !== undefined ) { savedQuery.timefilter = { - timeFrom: this.props.dateRangeFrom, - timeTo: this.props.dateRangeTo, + timeFrom: this.state.dateRangeFrom, + timeTo: this.state.dateRangeTo, refreshInterval: { value: this.props.refreshInterval, pause: this.props.isRefreshPaused, @@ -142,6 +220,33 @@ class SearchBarUI extends Component { }); }; + public onQueryBarChange = (queryAndDateRange: { dateRange: DateRange; query: Query }) => { + this.setState({ + query: queryAndDateRange.query, + dateRangeFrom: queryAndDateRange.dateRange.from, + dateRangeTo: queryAndDateRange.dateRange.to, + }); + }; + + public onQueryBarSubmit = (queryAndDateRange: { dateRange: DateRange; query: Query }) => { + this.setState( + { + query: queryAndDateRange.query, + dateRangeFrom: queryAndDateRange.dateRange.from, + dateRangeTo: queryAndDateRange.dateRange.to, + }, + () => { + this.props.onQuerySubmit({ + query: this.state.query, + dateRange: { + from: this.state.dateRangeFrom, + to: this.state.dateRangeTo, + }, + }); + } + ); + }; + public componentDidMount() { if (this.filterBarRef) { this.setFilterBarHeight(); @@ -193,22 +298,24 @@ class SearchBarUI extends Component {
{this.props.showQueryBar ? ( ) : ( '' From 0fa0a108245a1421f8a40c520f457ece498f2442 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 12 Jun 2019 18:31:44 -0500 Subject: [PATCH 015/189] Close modal on query save success --- .../public/query/query_bar/lib/saved_query_service.ts | 4 ++-- .../public/search/search_bar/components/search_bar.tsx | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts index d2b80cc19f12c..a228f3dab39a1 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts @@ -20,7 +20,7 @@ import chrome from 'ui/chrome'; import { SavedQuery } from '../../../search'; -export const saveQuery = (savedQuery: SavedQuery) => { +export const saveQuery = async (savedQuery: SavedQuery) => { const savedObjectsClient = chrome.getSavedObjectsClient(); const query = { @@ -47,5 +47,5 @@ export const saveQuery = (savedQuery: SavedQuery) => { queryObject.timefilter = JSON.stringify(savedQuery.timefilter); } - savedObjectsClient.create('query', queryObject); + return await savedObjectsClient.create('query', queryObject); }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 3cf26a29bbdab..7e55dfa4c1060 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -183,7 +183,7 @@ class SearchBarUI extends Component { }); }; - public onSave = (savedQueryMeta: SavedQueryMeta) => { + public onSave = async (savedQueryMeta: SavedQueryMeta) => { const savedQuery: SavedQuery = { title: savedQueryMeta.title, description: savedQueryMeta.description, @@ -211,7 +211,12 @@ class SearchBarUI extends Component { }; } - saveQuery(savedQuery); + const response = await saveQuery(savedQuery); + if (response.error === undefined) { + this.setState({ + showSaveQueryModal: false, + }); + } }; public onInitiateSave = () => { From 62225dcd655d39bc2b3a63566906cd3c52566cd5 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 13 Jun 2019 10:29:26 -0700 Subject: [PATCH 016/189] changed savedQuery type to allow for undefined --- .../query_bar/lib/saved_query_service.ts | 31 ++++++++++++++++--- .../search_bar/components/search_bar.tsx | 10 +++--- .../search/search_bar/directive/index.js | 1 + .../public/discover/controllers/discover.js | 3 ++ .../kibana/public/discover/index.html | 3 +- .../service/saved_objects_client.ts | 2 +- 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts index a228f3dab39a1..2e05aa11435c6 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts @@ -19,6 +19,7 @@ import chrome from 'ui/chrome'; import { SavedQuery } from '../../../search'; +import { Query } from '../../query_service'; export const saveQuery = async (savedQuery: SavedQuery) => { const savedObjectsClient = chrome.getSavedObjectsClient(); @@ -31,12 +32,19 @@ export const saveQuery = async (savedQuery: SavedQuery) => { language: savedQuery.query.language, }; - const queryObject = { + const queryObject: { + title: string; + description: string; + query: { + query: string; + language: string; + }; + filters?: string; + timefilter?: string; + } = { title: savedQuery.title, description: savedQuery.description, query, - filters: '', - timefilter: '', }; if (savedQuery.filters) { @@ -47,5 +55,20 @@ export const saveQuery = async (savedQuery: SavedQuery) => { queryObject.timefilter = JSON.stringify(savedQuery.timefilter); } - return await savedObjectsClient.create('query', queryObject); + const rawQueryResponse = await savedObjectsClient.create('query', queryObject); + if (rawQueryResponse.error) { + throw new Error(rawQueryResponse.error.message); + } + const responseObject: SavedQuery = { + title: rawQueryResponse.attributes.title, + description: rawQueryResponse.attributes.description, + query: rawQueryResponse.attributes.query, + }; + if (rawQueryResponse.attributes.filters) { + responseObject.filters = JSON.parse(rawQueryResponse.attributes.filters); + } + if (rawQueryResponse.attributes.timefilter) { + responseObject.timefilter = JSON.parse(rawQueryResponse.attributes.timefilter); + } + return responseObject; }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 7e55dfa4c1060..a85047b2935ef 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -64,6 +64,7 @@ interface Props { refreshInterval?: number; showAutoRefreshOnly?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; + onSaved?: (newSavedQuery: SavedQuery) => void; } interface State { @@ -212,10 +213,11 @@ class SearchBarUI extends Component { } const response = await saveQuery(savedQuery); - if (response.error === undefined) { - this.setState({ - showSaveQueryModal: false, - }); + this.setState({ + showSaveQueryModal: false, + }); + if (this.props.onSaved) { + this.props.onSaved(response); } }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js b/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js index ae5db0ca0fe21..60802464b093a 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js +++ b/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js @@ -37,6 +37,7 @@ export function setupDirective() { ['onQuerySubmit', { watchDepth: 'reference' }], ['onFiltersUpdated', { watchDepth: 'reference' }], ['onRefreshChange', { watchDepth: 'reference' }], + ['onSaved', { watchDepth: 'reference' }], ['indexPatterns', { watchDepth: 'collection' }], ['filters', { watchDepth: 'collection' }], diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 4d82fba95de5f..d56ec7c0d55bb 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -930,6 +930,9 @@ function discoverController( $scope.showAllRows = function () { $scope.minimumVisibleRows = $scope.hits; }; + $scope.onQuerySaved = function (newSavedQuery) { + $scope.savedQuery = newSavedQuery; + }; async function setupVisualization() { // If no timefield has been specified we don't create a histogram of messages diff --git a/src/legacy/core_plugins/kibana/public/discover/index.html b/src/legacy/core_plugins/kibana/public/discover/index.html index 7a4c956875bc4..9ed30c358e20e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/index.html +++ b/src/legacy/core_plugins/kibana/public/discover/index.html @@ -36,7 +36,7 @@

diff --git a/src/legacy/server/saved_objects/service/saved_objects_client.ts b/src/legacy/server/saved_objects/service/saved_objects_client.ts index a0d378bfc5a97..d2dab6db0607d 100644 --- a/src/legacy/server/saved_objects/service/saved_objects_client.ts +++ b/src/legacy/server/saved_objects/service/saved_objects_client.ts @@ -99,7 +99,7 @@ export interface MigrationVersion { } export interface SavedObjectAttributes { - [key: string]: SavedObjectAttributes | string | number | boolean | null; + [key: string]: SavedObjectAttributes | string | number | boolean | null | undefined; } export interface VisualizationAttributes extends SavedObjectAttributes { From 453e8b88ee9b3ce5c9aa56ba66a325ace17af73e Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 13 Jun 2019 10:48:30 -0700 Subject: [PATCH 017/189] preserves saved query on state after submit --- .../query/query_bar/lib/saved_query_service.ts | 2 +- .../search/search_bar/components/search_bar.tsx | 13 +++++++++++-- .../kibana/public/discover/controllers/discover.js | 4 +++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts index 2e05aa11435c6..2a14e38ba97fc 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts @@ -70,5 +70,5 @@ export const saveQuery = async (savedQuery: SavedQuery) => { if (rawQueryResponse.attributes.timefilter) { responseObject.timefilter = JSON.parse(rawQueryResponse.attributes.timefilter); } - return responseObject; + return { id: rawQueryResponse.id, savedQuery: responseObject }; }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index a85047b2935ef..9cc8441b89374 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -64,7 +64,7 @@ interface Props { refreshInterval?: number; showAutoRefreshOnly?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; - onSaved?: (newSavedQuery: SavedQuery) => void; + onSaved?: (id: string, newSavedQuery: SavedQuery) => void; } interface State { @@ -217,7 +217,16 @@ class SearchBarUI extends Component { showSaveQueryModal: false, }); if (this.props.onSaved) { - this.props.onSaved(response); + this.props.onSaved(response.id, response.savedQuery); + } + if (this.props.onQuerySubmit) { + this.props.onQuerySubmit({ + query: this.state.query, + dateRange: { + from: this.state.dateRangeFrom, + to: this.state.dateRangeTo, + }, + }); } }; diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index d56ec7c0d55bb..60f093cd96926 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -930,8 +930,10 @@ function discoverController( $scope.showAllRows = function () { $scope.minimumVisibleRows = $scope.hits; }; - $scope.onQuerySaved = function (newSavedQuery) { + $scope.onQuerySaved = function (id, newSavedQuery) { $scope.savedQuery = newSavedQuery; + $state.savedQuery = id; + $state.save(); }; async function setupVisualization() { From 2c399eb2e10e539d8e2a4ec6c8f305233d99e5d3 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 13 Jun 2019 15:40:22 -0500 Subject: [PATCH 018/189] Display success and error messages on save --- .../query_bar/lib/saved_query_service.ts | 1 - .../search_bar/components/search_bar.tsx | 37 ++++++++++++------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts index 2a14e38ba97fc..4e6b364e40baa 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts @@ -19,7 +19,6 @@ import chrome from 'ui/chrome'; import { SavedQuery } from '../../../search'; -import { Query } from '../../query_service'; export const saveQuery = async (savedQuery: SavedQuery) => { const savedObjectsClient = chrome.getSavedObjectsClient(); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 9cc8441b89374..1abbbc9e85a25 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -28,6 +28,7 @@ import { IndexPattern } from 'ui/index_patterns'; import { Storage } from 'ui/storage'; import { get, isEqual } from 'lodash'; +import { toastNotifications } from 'ui/notify'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; import { SavedQuery } from '../index'; @@ -212,21 +213,29 @@ class SearchBarUI extends Component { }; } - const response = await saveQuery(savedQuery); - this.setState({ - showSaveQueryModal: false, - }); - if (this.props.onSaved) { - this.props.onSaved(response.id, response.savedQuery); - } - if (this.props.onQuerySubmit) { - this.props.onQuerySubmit({ - query: this.state.query, - dateRange: { - from: this.state.dateRangeFrom, - to: this.state.dateRangeTo, - }, + try { + const response = await saveQuery(savedQuery); + toastNotifications.addSuccess(`Your query "${response.savedQuery.title}" was saved`); + + this.setState({ + showSaveQueryModal: false, }); + + if (this.props.onSaved) { + this.props.onSaved(response.id, response.savedQuery); + } + + if (this.props.onQuerySubmit) { + this.props.onQuerySubmit({ + query: this.state.query, + dateRange: { + from: this.state.dateRangeFrom, + to: this.state.dateRangeTo, + }, + }); + } + } catch (error) { + toastNotifications.addDanger(`An error occured while saving your query: ${error.message}`); } }; From 3ce41d76fab9df92276e4b4fe05ea6b7a4799961 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 13 Jun 2019 15:58:10 -0500 Subject: [PATCH 019/189] Link to saved objects management page --- .../public/query/query_bar/components/query_bar_input.tsx | 4 +--- .../public/query/query_bar/components/saved_query_row.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index e4172998c95cf..d18eabd9f35b4 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -200,10 +200,8 @@ export class QueryBarInputUI extends Component { }; private onQueryStringChange = (value: string) => { - const hasValue = Boolean(value.trim()); - this.setState({ - isSuggestionsVisible: hasValue, + isSuggestionsVisible: true, index: null, suggestionLimit: 50, }); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 37d6e92c3de7b..99cf0448a44c3 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -18,7 +18,7 @@ */ import React, { FunctionComponent, Fragment } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiLink } from '@elastic/eui'; import { SavedQuery } from '../../../search/search_bar'; import { Query } from '../index'; @@ -47,7 +47,11 @@ export const SavedQueryRow: FunctionComponent = ({ query, savedQuery, onS
); } else { - rowContent = 'Manage saved queries'; + rowContent = ( + + Manage Saved Queries + + ); } return ( From 4be53f7f3b612ad4bd9a181c4ad1f79ecb63bb1d Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 13 Jun 2019 17:25:26 -0500 Subject: [PATCH 020/189] Add ability to update an existing saved query --- src/legacy/core_plugins/data/public/index.ts | 2 +- .../query/query_bar/components/query_bar.tsx | 4 +- .../query_bar/components/query_bar_input.tsx | 4 +- .../query_bar/components/saved_query_row.tsx | 12 ++++-- .../query_bar/lib/saved_query_service.ts | 40 ++++++++++++------- .../core_plugins/data/public/search/index.tsx | 2 +- .../search_bar/components/save_query_form.tsx | 20 ++++++---- .../search_bar/components/search_bar.tsx | 28 ++++++++----- .../data/public/search/search_bar/index.tsx | 5 +++ .../data/public/search/search_service.ts | 2 +- .../public/discover/controllers/discover.js | 19 +++++---- 11 files changed, 88 insertions(+), 50 deletions(-) diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index d80b736a53ad0..a4a9daef6a0b2 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -93,4 +93,4 @@ export { ExpressionRenderer, ExpressionRendererProps, ExpressionRunner } from '. /** @public types */ export { IndexPattern, StaticIndexPattern, StaticIndexPatternField, Field } from './index_patterns'; export { Query } from './query'; -export { SavedQuery } from './search'; +export { SavedQueryAttributes } from './search'; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index c7cab0ce96c84..1f53c2a20c65b 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -39,7 +39,7 @@ import { QueryBarInput } from './query_bar_input'; import { getQueryLog } from '../lib/get_query_log'; import { Query } from '../index'; -import { SavedQuery } from '../../../search/search_bar'; +import { SavedQueryAttributes } from '../../../search/search_bar'; const config = chrome.getUiSettingsClient(); @@ -50,7 +50,7 @@ interface DateRange { interface Props { query: Query; - savedQuery: SavedQuery; + savedQuery?: SavedQueryAttributes; onSubmit: (payload: { dateRange: DateRange; query: Query }) => void; onChange: (payload: { dateRange: DateRange; query: Query }) => void; disableAutoFocus?: boolean; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index d18eabd9f35b4..c2ca57e80a1e2 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -42,13 +42,13 @@ import { SuggestionsComponent } from './typeahead/suggestions_component'; import { getQueryLog } from '../lib/get_query_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; import { SavedQueryRow } from './saved_query_row'; -import { SavedQuery } from '../../../search/search_bar'; +import { SavedQueryAttributes } from '../../../search/search_bar'; interface Props { indexPatterns: Array; intl: InjectedIntl; query: Query; - savedQuery?: SavedQuery; + savedQuery?: SavedQueryAttributes; appName: string; disableAutoFocus?: boolean; screenTitle?: string; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 99cf0448a44c3..47f228328b2f1 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -19,12 +19,12 @@ import React, { FunctionComponent, Fragment } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiLink } from '@elastic/eui'; -import { SavedQuery } from '../../../search/search_bar'; +import { SavedQueryAttributes } from '../../../search/search_bar'; import { Query } from '../index'; interface Props { query: Query; - savedQuery?: SavedQuery; + savedQuery?: SavedQueryAttributes; onSave: () => void; } @@ -39,7 +39,13 @@ export interface SavedQueryDetails { export const SavedQueryRow: FunctionComponent = ({ query, savedQuery, onSave }) => { let rowContent; if (savedQuery) { - rowContent = {savedQuery.title}; + rowContent = ( + +

+ Save changes to {savedQuery.title} +

+
+ ); } else if (query.query.length !== 0) { rowContent = ( diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts index 4e6b364e40baa..efed9ba4a117e 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts @@ -18,17 +18,17 @@ */ import chrome from 'ui/chrome'; -import { SavedQuery } from '../../../search'; +import { SavedQueryAttributes } from '../../../search/search_bar'; -export const saveQuery = async (savedQuery: SavedQuery) => { +export const saveQuery = async (attributes: SavedQueryAttributes, id: string = '') => { const savedObjectsClient = chrome.getSavedObjectsClient(); const query = { query: - typeof savedQuery.query.query === 'string' - ? savedQuery.query.query - : JSON.stringify(savedQuery.query.query), - language: savedQuery.query.language, + typeof attributes.query.query === 'string' + ? attributes.query.query + : JSON.stringify(attributes.query.query), + language: attributes.query.language, }; const queryObject: { @@ -41,24 +41,34 @@ export const saveQuery = async (savedQuery: SavedQuery) => { filters?: string; timefilter?: string; } = { - title: savedQuery.title, - description: savedQuery.description, + title: attributes.title, + description: attributes.description, query, }; - if (savedQuery.filters) { - queryObject.filters = JSON.stringify(savedQuery.filters); + if (attributes.filters) { + queryObject.filters = JSON.stringify(attributes.filters); } - if (savedQuery.timefilter) { - queryObject.timefilter = JSON.stringify(savedQuery.timefilter); + if (attributes.timefilter) { + queryObject.timefilter = JSON.stringify(attributes.timefilter); + } + + let rawQueryResponse; + if (id === undefined) { + rawQueryResponse = await savedObjectsClient.create('query', queryObject); + } else { + rawQueryResponse = await savedObjectsClient.create('query', queryObject, { + id, + overwrite: true, + }); } - const rawQueryResponse = await savedObjectsClient.create('query', queryObject); if (rawQueryResponse.error) { throw new Error(rawQueryResponse.error.message); } - const responseObject: SavedQuery = { + + const responseObject: SavedQueryAttributes = { title: rawQueryResponse.attributes.title, description: rawQueryResponse.attributes.description, query: rawQueryResponse.attributes.query, @@ -69,5 +79,5 @@ export const saveQuery = async (savedQuery: SavedQuery) => { if (rawQueryResponse.attributes.timefilter) { responseObject.timefilter = JSON.parse(rawQueryResponse.attributes.timefilter); } - return { id: rawQueryResponse.id, savedQuery: responseObject }; + return { id: rawQueryResponse.id, attributes: responseObject }; }; diff --git a/src/legacy/core_plugins/data/public/search/index.tsx b/src/legacy/core_plugins/data/public/search/index.tsx index 7afad4a127cfd..7b7b0cfbd18b9 100644 --- a/src/legacy/core_plugins/data/public/search/index.tsx +++ b/src/legacy/core_plugins/data/public/search/index.tsx @@ -17,4 +17,4 @@ * under the License. */ -export { SearchService, SearchSetup, SavedQuery } from './search_service'; +export { SearchService, SearchSetup, SavedQueryAttributes } from './search_service'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index 91a1a7c341c36..b32f88f75a288 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -32,8 +32,10 @@ import { EuiFieldText, EuiSwitch, } from '@elastic/eui'; +import { SavedQueryAttributes } from '../index'; interface Props { + savedQuery?: SavedQueryAttributes; onSave: (savedQueryMeta: SavedQueryMeta) => void; onClose: () => void; } @@ -45,11 +47,15 @@ export interface SavedQueryMeta { shouldIncludeTimefilter: boolean; } -export const SaveQueryForm: FunctionComponent = ({ onSave, onClose }) => { - const [title, setTitle] = useState(''); - const [description, setDescription] = useState(''); - const [shouldIncludeFilters, setShouldIncludeFilters] = useState(false); - const [shouldIncludeTimefilter, setIncludeTimefilter] = useState(false); +export const SaveQueryForm: FunctionComponent = ({ savedQuery, onSave, onClose }) => { + const [title, setTitle] = useState(savedQuery ? savedQuery.title : ''); + const [description, setDescription] = useState(savedQuery ? savedQuery.description : ''); + const [shouldIncludeFilters, setShouldIncludeFilters] = useState( + !!(savedQuery && savedQuery.filters) + ); + const [shouldIncludeTimefilter, setIncludeTimefilter] = useState( + !!(savedQuery && savedQuery.timefilter) + ); const saveQueryForm = ( @@ -86,8 +92,8 @@ export const SaveQueryForm: FunctionComponent = ({ onSave, onClose }) => { setIncludeTimefilter(!shouldIncludeTimefilter); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 1abbbc9e85a25..d9f9eecf72b3d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -31,7 +31,7 @@ import { get, isEqual } from 'lodash'; import { toastNotifications } from 'ui/notify'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; -import { SavedQuery } from '../index'; +import { SavedQuery, SavedQueryAttributes } from '../index'; import { saveQuery } from '../../../query/query_bar/lib/saved_query_service'; import { SavedQueryMeta, SaveQueryForm } from './save_query_form'; @@ -53,7 +53,7 @@ interface Props { indexPatterns: IndexPattern[]; store: Storage; filters: Filter[]; - savedQuery: SavedQuery; + savedQuery?: SavedQuery; onFiltersUpdated: (filters: Filter[]) => void; showQueryBar: boolean; showFilterBar: boolean; @@ -65,7 +65,7 @@ interface Props { refreshInterval?: number; showAutoRefreshOnly?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; - onSaved?: (id: string, newSavedQuery: SavedQuery) => void; + onSaved?: (savedQuery: SavedQuery) => void; } interface State { @@ -186,14 +186,14 @@ class SearchBarUI extends Component { }; public onSave = async (savedQueryMeta: SavedQueryMeta) => { - const savedQuery: SavedQuery = { + const savedQueryAttributes: SavedQueryAttributes = { title: savedQueryMeta.title, description: savedQueryMeta.description, query: this.state.query, }; if (savedQueryMeta.shouldIncludeFilters) { - savedQuery.filters = this.props.filters; + savedQueryAttributes.filters = this.props.filters; } if ( @@ -203,7 +203,7 @@ class SearchBarUI extends Component { this.props.refreshInterval !== undefined && this.props.isRefreshPaused !== undefined ) { - savedQuery.timefilter = { + savedQueryAttributes.timefilter = { timeFrom: this.state.dateRangeFrom, timeTo: this.state.dateRangeTo, refreshInterval: { @@ -214,15 +214,21 @@ class SearchBarUI extends Component { } try { - const response = await saveQuery(savedQuery); - toastNotifications.addSuccess(`Your query "${response.savedQuery.title}" was saved`); + let response; + if (this.props.savedQuery) { + response = await saveQuery(savedQueryAttributes, this.props.savedQuery.id); + } else { + response = await saveQuery(savedQueryAttributes); + } + + toastNotifications.addSuccess(`Your query "${response.attributes.title}" was saved`); this.setState({ showSaveQueryModal: false, }); if (this.props.onSaved) { - this.props.onSaved(response.id, response.savedQuery); + this.props.onSaved(response); } if (this.props.onQuerySubmit) { @@ -236,6 +242,7 @@ class SearchBarUI extends Component { } } catch (error) { toastNotifications.addDanger(`An error occured while saving your query: ${error.message}`); + throw error; } }; @@ -324,7 +331,7 @@ class SearchBarUI extends Component { {this.props.showQueryBar ? ( { {this.state.showSaveQueryModal ? ( this.setState({ showSaveQueryModal: false })} /> diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index 0f6eb4307eb02..31fda4a33a463 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -31,6 +31,11 @@ interface RefreshInterval { } export interface SavedQuery { + id: string; + attributes: SavedQueryAttributes; +} + +export interface SavedQueryAttributes { title: string; description: string; query: Query; diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index a653fca6cd521..fe5a09a30784f 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -42,4 +42,4 @@ export class SearchService { /** @public */ export type SearchSetup = ReturnType; -export { SavedQuery } from './search_bar'; +export { SavedQueryAttributes } from './search_bar'; diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 60f093cd96926..d84a6315c70b0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -194,9 +194,12 @@ uiRoutes } return { - ...savedQuery.attributes, - filters: filters, - timefilter: time, + id: savedQuery.id, + attributes: { + ...savedQuery.attributes, + filters: filters, + timefilter: time, + } }; }); } @@ -523,7 +526,7 @@ function discoverController( function getStateDefaults() { return { - query: ($route.current.locals.savedQuery && $route.current.locals.savedQuery.query) + query: ($scope.savedQuery && $scope.savedQuery.attributes.query) || $scope.searchSource.getField('query') || { query: '', @@ -533,7 +536,7 @@ function discoverController( columns: savedSearch.columns.length > 0 ? savedSearch.columns : config.get('defaultColumns').slice(), index: $scope.indexPattern.id, interval: 'auto', - filters: ($route.current.locals.savedQuery && $route.current.locals.savedQuery.filters) + filters: ($scope.savedQuery && $scope.savedQuery.attributes.filters) || _.cloneDeep($scope.searchSource.getOwnField('filter')) }; } @@ -930,9 +933,9 @@ function discoverController( $scope.showAllRows = function () { $scope.minimumVisibleRows = $scope.hits; }; - $scope.onQuerySaved = function (id, newSavedQuery) { - $scope.savedQuery = newSavedQuery; - $state.savedQuery = id; + $scope.onQuerySaved = function (savedQuery) { + $scope.savedQuery = savedQuery; + $state.savedQuery = savedQuery.id; $state.save(); }; From 45e03f83b58b9edfcafab383b7b3d83e2d541a49 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 14 Jun 2019 11:56:05 -0700 Subject: [PATCH 021/189] Loads saved queries into suggestions --- .../query_bar/components/query_bar_input.tsx | 20 +++++-- .../typeahead/suggestion_component.tsx | 2 + .../query_bar/lib/saved_query_service.ts | 52 ++++++++++++++++++- .../service/lib/search_dsl/sorting_params.ts | 2 +- .../public/autocomplete_providers/index.d.ts | 3 +- 5 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index c2ca57e80a1e2..308054b3ef084 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -42,7 +42,8 @@ import { SuggestionsComponent } from './typeahead/suggestions_component'; import { getQueryLog } from '../lib/get_query_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; import { SavedQueryRow } from './saved_query_row'; -import { SavedQueryAttributes } from '../../../search/search_bar'; +import { SavedQueryAttributes, SavedQuery } from '../../../search/search_bar'; +import { findSavedQueries } from '../lib/saved_query_service'; interface Props { indexPatterns: Array; @@ -86,6 +87,7 @@ const KEY_CODES = { const config = chrome.getUiSettingsClient(); const recentSearchType: AutocompleteSuggestionType = 'recentSearch'; +const savedQueryType: AutocompleteSuggestionType = 'savedQuery'; export class QueryBarInputUI extends Component { public state = { @@ -131,6 +133,7 @@ export class QueryBarInputUI extends Component { const queryString = this.getQueryString(); const recentSearchSuggestions = this.getRecentSearchSuggestions(queryString); + const savedQuerySuggestions = await this.getSavedQueriesSuggestions(queryString); const autocompleteProvider = getAutocompleteProvider(language); if ( @@ -138,7 +141,7 @@ export class QueryBarInputUI extends Component { !Array.isArray(this.state.indexPatterns) || compact(this.state.indexPatterns).length === 0 ) { - return recentSearchSuggestions; + return [...recentSearchSuggestions, ...savedQuerySuggestions]; } const indexPatterns = this.state.indexPatterns; @@ -154,7 +157,8 @@ export class QueryBarInputUI extends Component { selectionStart, selectionEnd, }); - return [...suggestions, ...recentSearchSuggestions]; + + return [...suggestions, ...recentSearchSuggestions, ...savedQuerySuggestions]; }; private getRecentSearchSuggestions = (query: string) => { @@ -174,6 +178,16 @@ export class QueryBarInputUI extends Component { }); }; + private getSavedQueriesSuggestions = async (searchText: string) => { + const savedQueries = await findSavedQueries(searchText); + return savedQueries.map((savedQuery: SavedQuery) => { + const text = toUser(savedQuery.attributes.title); + const start = 0; + const end = searchText.length; + return { type: savedQueryType, text, start, end }; + }); + }; + private updateSuggestions = debounce(async () => { const suggestions = (await this.getSuggestions()) || []; if (!this.componentIsUnmounting) { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx index 180a90c2acc95..7c65bc11f17ca 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx @@ -34,6 +34,8 @@ function getEuiIconType(type: string) { return 'kqlSelector'; case 'operator': return 'kqlOperand'; + case 'savedQuery': + return 'save'; default: throw new Error(`Unknown type: ${type}`); } diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts index efed9ba4a117e..76a6163cdc710 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts @@ -18,7 +18,19 @@ */ import chrome from 'ui/chrome'; -import { SavedQueryAttributes } from '../../../search/search_bar'; +import { SavedObjectAttributes } from 'src/legacy/server/saved_objects/service/saved_objects_client'; +import { SavedQueryAttributes, SavedQuery } from '../../../search/search_bar'; + +interface SerializedSavedQueryAttributes extends SavedObjectAttributes { + title: string; + description: string; + query: { + query: string; + language: string; + }; + filters?: string; + timefilter?: string; +} export const saveQuery = async (attributes: SavedQueryAttributes, id: string = '') => { const savedObjectsClient = chrome.getSavedObjectsClient(); @@ -81,3 +93,41 @@ export const saveQuery = async (attributes: SavedQueryAttributes, id: string = ' } return { id: rawQueryResponse.id, attributes: responseObject }; }; + +export const findSavedQueries = async (searchText: string = ''): Promise => { + const savedObjectsClient = chrome.getSavedObjectsClient(); + + const response = await savedObjectsClient.find({ + type: 'query', + search: searchText, + searchFields: ['title^5', 'description'], + sortField: '_score', + }); + + return response.savedObjects.map(savedObject => { + let queryString; + try { + queryString = JSON.parse(savedObject.attributes.query.query); + } catch (error) { + queryString = savedObject.attributes.query.query; + } + const savedQuery: SavedQueryAttributes = { + title: savedObject.attributes.title || '', + description: savedObject.attributes.description || '', + query: { + query: queryString, + language: savedObject.attributes.query.language, + }, + }; + if (savedObject.attributes.filters) { + savedQuery.filters = JSON.parse(savedObject.attributes.filters); + } + if (savedObject.attributes.timefilter) { + savedQuery.timefilter = JSON.parse(savedObject.attributes.timefilter); + } + return { + id: savedObject.id, + attributes: savedQuery, + }; + }); +}; diff --git a/src/legacy/server/saved_objects/service/lib/search_dsl/sorting_params.ts b/src/legacy/server/saved_objects/service/lib/search_dsl/sorting_params.ts index 444ab0774e7eb..4644cc8daef03 100644 --- a/src/legacy/server/saved_objects/service/lib/search_dsl/sorting_params.ts +++ b/src/legacy/server/saved_objects/service/lib/search_dsl/sorting_params.ts @@ -20,7 +20,7 @@ import Boom from 'boom'; import { getProperty, IndexMapping } from '../../../../mappings'; -const TOP_LEVEL_FIELDS = ['_id']; +const TOP_LEVEL_FIELDS = ['_id', '_score']; export function getSortingParams( mappings: IndexMapping, diff --git a/src/legacy/ui/public/autocomplete_providers/index.d.ts b/src/legacy/ui/public/autocomplete_providers/index.d.ts index 9c252bb05e52b..4f4aa4b13ffda 100644 --- a/src/legacy/ui/public/autocomplete_providers/index.d.ts +++ b/src/legacy/ui/public/autocomplete_providers/index.d.ts @@ -45,7 +45,8 @@ export type AutocompleteSuggestionType = | 'value' | 'operator' | 'conjunction' - | 'recentSearch'; + | 'recentSearch' + | 'savedQuery'; export interface AutocompleteSuggestion { description?: string; From 75d240ffb2e27a33e9a69b89e0f11b04bf369d01 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Fri, 14 Jun 2019 15:59:33 -0500 Subject: [PATCH 022/189] add description to saved query suggestion --- .../data/public/query/query_bar/components/query_bar_input.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 308054b3ef084..4478b5a1563a2 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -184,7 +184,8 @@ export class QueryBarInputUI extends Component { const text = toUser(savedQuery.attributes.title); const start = 0; const end = searchText.length; - return { type: savedQueryType, text, start, end }; + const description = savedQuery.attributes.description; + return { type: savedQueryType, text, start, end, description }; }); }; From 0dc4d90bba2c0843eaf97d5948e43ceea2ac773f Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Fri, 14 Jun 2019 17:38:36 -0500 Subject: [PATCH 023/189] Add ability to load saved queries from autocomplete --- src/legacy/core_plugins/data/public/index.ts | 2 +- .../query/query_bar/components/query_bar.tsx | 4 ++- .../query_bar/components/query_bar_input.tsx | 24 ++++++++++++++-- .../core_plugins/data/public/search/index.tsx | 2 +- .../search_bar/components/search_bar.tsx | 28 +++++++++++++++++-- .../search/search_bar/directive/index.js | 1 + .../data/public/search/search_service.ts | 2 +- .../public/discover/controllers/discover.js | 24 ++++++++++++++-- .../kibana/public/discover/index.html | 1 + .../public/autocomplete_providers/index.d.ts | 2 ++ 10 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index 9fe1067eb3b9d..c45e26ff8cd8a 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -87,4 +87,4 @@ export { ExpressionRenderer, ExpressionRendererProps, ExpressionRunner } from '. /** @public types */ export { IndexPattern, StaticIndexPattern, StaticIndexPatternField, Field } from './index_patterns'; export { Query } from './query'; -export { SavedQueryAttributes } from './search'; +export { SavedQueryAttributes, SavedQuery } from './search'; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 1f53c2a20c65b..105d8e4b0e1c8 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -39,7 +39,7 @@ import { QueryBarInput } from './query_bar_input'; import { getQueryLog } from '../lib/get_query_log'; import { Query } from '../index'; -import { SavedQueryAttributes } from '../../../search/search_bar'; +import { SavedQuery, SavedQueryAttributes } from '../../../search/search_bar'; const config = chrome.getUiSettingsClient(); @@ -69,6 +69,7 @@ interface Props { onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; customSubmitButton?: any; onSave: () => void; + onLoadSavedQuery: (savedQuery: SavedQuery) => void; isDirty: boolean; } @@ -185,6 +186,7 @@ export class QueryBarUI extends Component { persistedLog={this.persistedLog} savedQuery={this.props.savedQuery} onSave={this.props.onSave} + onLoadSavedQuery={this.props.onLoadSavedQuery} /> {this.renderUpdateButton()} diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 4478b5a1563a2..c4f422c14b4f6 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -60,6 +60,7 @@ interface Props { languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; onChange?: (query: Query) => void; onSubmit?: (query: Query) => void; + onLoadSavedQuery?: (savedQuery: SavedQuery) => void; onSave?: () => void; } @@ -185,7 +186,7 @@ export class QueryBarInputUI extends Component { const start = 0; const end = searchText.length; const description = savedQuery.attributes.description; - return { type: savedQueryType, text, start, end, description }; + return { type: savedQueryType, text, start, end, description, savedQuery }; }); }; @@ -206,6 +207,14 @@ export class QueryBarInputUI extends Component { } }; + private onLoadSavedQuery = (savedQuery: SavedQuery) => { + this.updateSuggestions(); + + if (this.props.onLoadSavedQuery) { + this.props.onLoadSavedQuery(savedQuery); + } + }; + private onChange = (query: Query) => { this.updateSuggestions(); @@ -316,23 +325,31 @@ export class QueryBarInputUI extends Component { text, start, end, + savedQuery, }: { type: AutocompleteSuggestionType; text: string; start: number; end: number; + savedQuery?: SavedQuery; }) => { if (!this.inputRef) { return; } - const query = this.getQueryString(); + if (savedQuery) { + this.setState({ isSuggestionsVisible: false, index: null }); + this.onLoadSavedQuery(savedQuery); + return; + } + + const queryString = this.getQueryString(); const { selectionStart, selectionEnd } = this.inputRef; if (selectionStart === null || selectionEnd === null) { return; } - const value = query.substr(0, selectionStart) + query.substr(selectionEnd); + const value = queryString.substr(0, selectionStart) + queryString.substr(selectionEnd); const newQueryString = value.substr(0, start) + text + value.substr(end); this.onQueryStringChange(newQueryString); @@ -459,6 +476,7 @@ export class QueryBarInputUI extends Component { 'onChange', 'onSubmit', 'onSave', + 'onLoadSavedQuery', ]); return ( diff --git a/src/legacy/core_plugins/data/public/search/index.tsx b/src/legacy/core_plugins/data/public/search/index.tsx index 7b7b0cfbd18b9..6840449076061 100644 --- a/src/legacy/core_plugins/data/public/search/index.tsx +++ b/src/legacy/core_plugins/data/public/search/index.tsx @@ -17,4 +17,4 @@ * under the License. */ -export { SearchService, SearchSetup, SavedQueryAttributes } from './search_service'; +export { SearchService, SearchSetup, SavedQueryAttributes, SavedQuery } from './search_service'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index d9f9eecf72b3d..cac1c86cd3f3e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -66,6 +66,7 @@ interface Props { showAutoRefreshOnly?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; onSaved?: (savedQuery: SavedQuery) => void; + onSavedQueryUpdated: (savedQuery: SavedQuery) => void; } interface State { @@ -260,12 +261,15 @@ class SearchBarUI extends Component { }); }; - public onQueryBarSubmit = (queryAndDateRange: { dateRange: DateRange; query: Query }) => { + public onQueryBarSubmit = (queryAndDateRange: { dateRange?: DateRange; query: Query }) => { this.setState( { query: queryAndDateRange.query, - dateRangeFrom: queryAndDateRange.dateRange.from, - dateRangeTo: queryAndDateRange.dateRange.to, + dateRangeFrom: + (queryAndDateRange.dateRange && queryAndDateRange.dateRange.from) || + this.state.dateRangeFrom, + dateRangeTo: + (queryAndDateRange.dateRange && queryAndDateRange.dateRange.to) || this.state.dateRangeTo, }, () => { this.props.onQuerySubmit({ @@ -279,6 +283,23 @@ class SearchBarUI extends Component { ); }; + public onLoadSavedQuery = (savedQuery: SavedQuery) => { + const dateRangeFrom = get( + savedQuery, + 'attributes.timefilter.timeFrom', + this.state.dateRangeFrom + ); + const dateRangeTo = get(savedQuery, 'attributes.timefilter.timeTo', this.state.dateRangeTo); + + this.setState({ + query: savedQuery.attributes.query, + dateRangeFrom, + dateRangeTo, + }); + + this.props.onSavedQueryUpdated(savedQuery); + }; + public componentDidMount() { if (this.filterBarRef) { this.setFilterBarHeight(); @@ -347,6 +368,7 @@ class SearchBarUI extends Component { onRefreshChange={this.props.onRefreshChange} onSave={this.onInitiateSave} onChange={this.onQueryBarChange} + onLoadSavedQuery={this.onLoadSavedQuery} isDirty={this.isDirty()} /> ) : ( diff --git a/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js b/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js index 60802464b093a..e84233a11c464 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js +++ b/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js @@ -38,6 +38,7 @@ export function setupDirective() { ['onFiltersUpdated', { watchDepth: 'reference' }], ['onRefreshChange', { watchDepth: 'reference' }], ['onSaved', { watchDepth: 'reference' }], + ['onSavedQueryUpdated', { watchDepth: 'reference' }], ['indexPatterns', { watchDepth: 'collection' }], ['filters', { watchDepth: 'collection' }], diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index fe5a09a30784f..6f9a97fd4c0c8 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -42,4 +42,4 @@ export class SearchService { /** @public */ export type SearchSetup = ReturnType; -export { SavedQueryAttributes } from './search_bar'; +export { SavedQuery, SavedQueryAttributes } from './search_bar'; diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 3dd56dec3afbb..e9aad9fd4344c 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -935,10 +935,30 @@ function discoverController( }; $scope.onQuerySaved = function (savedQuery) { $scope.savedQuery = savedQuery; - $state.savedQuery = savedQuery.id; - $state.save(); }; + $scope.onSavedQueryUpdated = function (savedQuery) { + $scope.savedQuery = savedQuery; + }; + + $scope.$watch('savedQuery', (newSavedQuery) => { + if (!newSavedQuery) return; + + if (newSavedQuery.attributes.timefilter) { + timefilter.setTime({ + from: newSavedQuery.attributes.timefilter.timeFrom, + to: newSavedQuery.attributes.timefilter.timeTo, + }); + } + + queryFilter.setFilters(newSavedQuery.attributes.filters || []); + + $state.query = newSavedQuery.attributes.query; + $state.savedQuery = newSavedQuery.id; + $state.save(); + $scope.fetch(); + }); + async function setupVisualization() { // If no timefield has been specified we don't create a histogram of messages if (!$scope.opts.timefield) return; diff --git a/src/legacy/core_plugins/kibana/public/discover/index.html b/src/legacy/core_plugins/kibana/public/discover/index.html index 9ed30c358e20e..9e4e4afd6869b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/index.html +++ b/src/legacy/core_plugins/kibana/public/discover/index.html @@ -50,6 +50,7 @@

diff --git a/src/legacy/ui/public/autocomplete_providers/index.d.ts b/src/legacy/ui/public/autocomplete_providers/index.d.ts index 4f4aa4b13ffda..05b8c556a6709 100644 --- a/src/legacy/ui/public/autocomplete_providers/index.d.ts +++ b/src/legacy/ui/public/autocomplete_providers/index.d.ts @@ -21,6 +21,7 @@ * WARNING: these typings are incomplete */ import { StaticIndexPattern } from 'ui/index_patterns'; +import { SavedQuery } from 'plugins/data'; export type AutocompleteProvider = ( args: { @@ -54,6 +55,7 @@ export interface AutocompleteSuggestion { start: number; text: string; type: AutocompleteSuggestionType; + savedQuery?: SavedQuery; } export function addAutocompleteProvider(language: string, provider: AutocompleteProvider): void; From 32f2654d30f5741d9bdc25df81759ca7ea8e1719 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 17 Jun 2019 10:51:03 -0700 Subject: [PATCH 024/189] Adds save as new query functionality --- .../query/query_bar/components/query_bar.tsx | 5 ++- .../query_bar/components/query_bar_input.tsx | 8 +++- .../query_bar/components/saved_query_row.tsx | 41 +++++++++++++++---- .../search_bar/components/search_bar.tsx | 22 ++++++++-- 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 105d8e4b0e1c8..21eb4786b07ed 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -69,8 +69,9 @@ interface Props { onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; customSubmitButton?: any; onSave: () => void; + onSaveNew: () => void; onLoadSavedQuery: (savedQuery: SavedQuery) => void; - isDirty: boolean; + isDirty: () => boolean; } interface State { @@ -186,7 +187,9 @@ export class QueryBarUI extends Component { persistedLog={this.persistedLog} savedQuery={this.props.savedQuery} onSave={this.props.onSave} + onSaveNew={this.props.onSaveNew} onLoadSavedQuery={this.props.onLoadSavedQuery} + isDirty={this.props.isDirty} /> {this.renderUpdateButton()} diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index c4f422c14b4f6..e116b87e8edeb 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -62,6 +62,8 @@ interface Props { onSubmit?: (query: Query) => void; onLoadSavedQuery?: (savedQuery: SavedQuery) => void; onSave?: () => void; + onSaveNew?: () => void; + isDirty?: () => boolean; } interface State { @@ -476,7 +478,9 @@ export class QueryBarInputUI extends Component { 'onChange', 'onSubmit', 'onSave', + 'onSaveNew', 'onLoadSavedQuery', + 'isDirty', ]); return ( @@ -554,11 +558,13 @@ export class QueryBarInputUI extends Component { onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} append={ - this.props.onSave ? ( + this.props.onSave && this.props.onSaveNew && this.props.isDirty ? ( ) : null } diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 47f228328b2f1..aa26df3139bdb 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -26,6 +26,8 @@ interface Props { query: Query; savedQuery?: SavedQueryAttributes; onSave: () => void; + onSaveNew: () => void; + isDirty: () => boolean; } export interface SavedQueryDetails { @@ -36,16 +38,37 @@ export interface SavedQueryDetails { query: Query; } -export const SavedQueryRow: FunctionComponent = ({ query, savedQuery, onSave }) => { +export const SavedQueryRow: FunctionComponent = ({ + query, + savedQuery, + onSave, + onSaveNew, + isDirty, +}) => { let rowContent; if (savedQuery) { - rowContent = ( - -

- Save changes to {savedQuery.title} -

-
- ); + if (isDirty()) { + rowContent = ( + + +

+ Save changes to {savedQuery.title} +

+
+ +

+ Save as new +

+
+
+ ); + } else { + rowContent = ( + +

{savedQuery.title}

+
+ ); + } } else if (query.query.length !== 0) { rowContent = ( @@ -62,7 +85,7 @@ export const SavedQueryRow: FunctionComponent = ({ query, savedQuery, onS return ( - {rowContent} + {rowContent} ); }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index cac1c86cd3f3e..3ba9f6078af14 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -72,6 +72,7 @@ interface Props { interface State { isFiltersVisible: boolean; showSaveQueryModal: boolean; + showSaveNewQueryModal: boolean; currentProps?: Props; query: Query; dateRangeFrom: string; @@ -144,6 +145,7 @@ class SearchBarUI extends Component { public state = { isFiltersVisible: true, showSaveQueryModal: false, + showSaveNewQueryModal: false, currentProps: this.props, query: { query: this.props.query.query, @@ -186,7 +188,7 @@ class SearchBarUI extends Component { }); }; - public onSave = async (savedQueryMeta: SavedQueryMeta) => { + public onSave = async (savedQueryMeta: SavedQueryMeta, saveAsNew = false) => { const savedQueryAttributes: SavedQueryAttributes = { title: savedQueryMeta.title, description: savedQueryMeta.description, @@ -216,7 +218,7 @@ class SearchBarUI extends Component { try { let response; - if (this.props.savedQuery) { + if (this.props.savedQuery && !saveAsNew) { response = await saveQuery(savedQueryAttributes, this.props.savedQuery.id); } else { response = await saveQuery(savedQueryAttributes); @@ -226,6 +228,7 @@ class SearchBarUI extends Component { this.setState({ showSaveQueryModal: false, + showSaveNewQueryModal: false, }); if (this.props.onSaved) { @@ -253,6 +256,12 @@ class SearchBarUI extends Component { }); }; + public onInitiateSaveNew = () => { + this.setState({ + showSaveNewQueryModal: true, + }); + }; + public onQueryBarChange = (queryAndDateRange: { dateRange: DateRange; query: Query }) => { this.setState({ query: queryAndDateRange.query, @@ -367,9 +376,10 @@ class SearchBarUI extends Component { showAutoRefreshOnly={this.props.showAutoRefreshOnly} onRefreshChange={this.props.onRefreshChange} onSave={this.onInitiateSave} + onSaveNew={this.onInitiateSaveNew} onChange={this.onQueryBarChange} onLoadSavedQuery={this.onLoadSavedQuery} - isDirty={this.isDirty()} + isDirty={this.isDirty} /> ) : ( '' @@ -407,6 +417,12 @@ class SearchBarUI extends Component { onClose={() => this.setState({ showSaveQueryModal: false })} /> ) : null} + {this.state.showSaveNewQueryModal ? ( + this.onSave(savedQueryMeta, true)} + onClose={() => this.setState({ showSaveNewQueryModal: false })} + /> + ) : null} ); } From 3f9e88f13e87fe1e519ba5f75aaa723a8b94ed24 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 17 Jun 2019 15:48:50 -0500 Subject: [PATCH 025/189] Fix isDirty and add Save As New when not dirty --- .../query/query_bar/components/query_bar.tsx | 2 +- .../query_bar/components/query_bar_input.tsx | 4 ++-- .../query_bar/components/saved_query_row.tsx | 17 ++++++++++++----- .../search/search_bar/components/search_bar.tsx | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 21eb4786b07ed..42c92f1e4ad8b 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -71,7 +71,7 @@ interface Props { onSave: () => void; onSaveNew: () => void; onLoadSavedQuery: (savedQuery: SavedQuery) => void; - isDirty: () => boolean; + isDirty: boolean; } interface State { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index e116b87e8edeb..fb4548a1dc578 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -63,7 +63,7 @@ interface Props { onLoadSavedQuery?: (savedQuery: SavedQuery) => void; onSave?: () => void; onSaveNew?: () => void; - isDirty?: () => boolean; + isDirty?: boolean; } interface State { @@ -558,7 +558,7 @@ export class QueryBarInputUI extends Component { onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} append={ - this.props.onSave && this.props.onSaveNew && this.props.isDirty ? ( + this.props.onSave && this.props.onSaveNew && this.props.isDirty !== undefined ? ( void; onSaveNew: () => void; - isDirty: () => boolean; + isDirty: boolean; } export interface SavedQueryDetails { @@ -47,7 +47,7 @@ export const SavedQueryRow: FunctionComponent = ({ }) => { let rowContent; if (savedQuery) { - if (isDirty()) { + if (isDirty) { rowContent = ( @@ -64,9 +64,16 @@ export const SavedQueryRow: FunctionComponent = ({ ); } else { rowContent = ( - -

{savedQuery.title}

-
+ + +

{savedQuery.title}

+
+ +

+ Save as new +

+
+
); } } else if (query.query.length !== 0) { diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 3ba9f6078af14..abe553bd30b96 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -379,7 +379,7 @@ class SearchBarUI extends Component { onSaveNew={this.onInitiateSaveNew} onChange={this.onQueryBarChange} onLoadSavedQuery={this.onLoadSavedQuery} - isDirty={this.isDirty} + isDirty={this.isDirty()} /> ) : ( '' From 0f137055987f0d62221981cce46d6209ccec8f4e Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 17 Jun 2019 17:03:43 -0500 Subject: [PATCH 026/189] Add saved query management popover --- .../search_bar/components/search_bar.tsx | 90 +++++++++++++++++-- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index abe553bd30b96..60c4777a5f7d9 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -18,9 +18,15 @@ */ // @ts-ignore -import { EuiFilterButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + EuiFilterButton, + EuiPopover, + EuiPopoverTitle, + EuiButtonEmpty, + EuiButton, +} from '@elastic/eui'; import { Filter } from '@kbn/es-query'; -import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import { InjectedIntl, injectI18n, FormattedMessage } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; @@ -34,6 +40,7 @@ import { FilterBar } from '../../../filter/filter_bar'; import { SavedQuery, SavedQueryAttributes } from '../index'; import { saveQuery } from '../../../query/query_bar/lib/saved_query_service'; import { SavedQueryMeta, SaveQueryForm } from './save_query_form'; +import { EuiFlexGroup, EuiFlexItem } from '../../../../../../../../../eui/src/components/flex'; interface DateRange { from: string; @@ -73,6 +80,7 @@ interface State { isFiltersVisible: boolean; showSaveQueryModal: boolean; showSaveNewQueryModal: boolean; + showSavedQueryPopover: boolean; currentProps?: Props; query: Query; dateRangeFrom: string; @@ -146,6 +154,7 @@ class SearchBarUI extends Component { isFiltersVisible: true, showSaveQueryModal: false, showSaveNewQueryModal: false, + showSavedQueryPopover: false, currentProps: this.props, query: { query: this.props.query.query, @@ -182,9 +191,21 @@ class SearchBarUI extends Component { public ro = new ResizeObserver(this.setFilterBarHeight); /* eslint-enable */ - public toggleFiltersVisible = () => { + public showSavedQueryPopover = () => { this.setState({ - isFiltersVisible: !this.state.isFiltersVisible, + showSavedQueryPopover: true, + }); + }; + + public hideSavedQueryPopover = () => { + this.setState({ + showSavedQueryPopover: false, + }); + }; + + public toggleSavedQueryPopover = () => { + this.setState({ + showSavedQueryPopover: !this.state.showSavedQueryPopover, }); }; @@ -338,9 +359,19 @@ class SearchBarUI extends Component { defaultMessage: 'Select to show', }); + const savedQueryDescriptionText = this.props.intl.formatMessage({ + id: 'data.search.searchBar.savedQueryDescriptionText', + defaultMessage: 'Saved queries allow you to store sets of queries, filters and time filters.', + }); + + const savedQueryPopoverTitleText = this.props.intl.formatMessage({ + id: 'data.search.searchBar.savedQueryPopoverTitleText', + defaultMessage: 'Saved Queries', + }); + const filterTriggerButton = ( 0 ? this.props.filters.length : undefined} @@ -348,10 +379,55 @@ class SearchBarUI extends Component { aria-expanded={!!this.state.isFiltersVisible} title={`${this.props.filters.length} ${filtersAppliedText} ${clickToShowOrHideText}`} > - Filters + Manage ); + const savedQueryPopover = ( + + {savedQueryPopoverTitleText} + + +

{savedQueryDescriptionText}

+
+ + + + { + if (this.props.savedQuery) { + this.onInitiateSave(); + } else { + this.onInitiateSaveNew(); + } + }} + > + + + + + {}} href={`#/management/kibana/objects`}> + + + + + +
+ ); + const classes = classNames('globalFilterGroup__wrapper', { 'globalFilterGroup__wrapper-isVisible': this.state.isFiltersVisible, }); @@ -367,7 +443,7 @@ class SearchBarUI extends Component { appName={this.props.appName} indexPatterns={this.props.indexPatterns} store={this.props.store} - prepend={this.props.showFilterBar ? filterTriggerButton : ''} + prepend={this.props.showFilterBar ? savedQueryPopover : ''} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} dateRangeTo={this.state.dateRangeTo} From afcaa35d168a2b3e8f40275e7c4b7dfbcef401fe Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 19 Jun 2019 12:10:40 -0700 Subject: [PATCH 027/189] WIP adding saved queries to the dashboard app --- .../query_bar/lib/saved_query_service.ts | 66 +++++++++++-------- .../kibana/public/dashboard/dashboard_app.tsx | 45 ++++++++++++- .../dashboard/dashboard_state_manager.ts | 3 + .../kibana/public/dashboard/types.ts | 3 +- 4 files changed, 88 insertions(+), 29 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts index 76a6163cdc710..dea4eb9d118c6 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts @@ -18,7 +18,7 @@ */ import chrome from 'ui/chrome'; -import { SavedObjectAttributes } from 'src/legacy/server/saved_objects/service/saved_objects_client'; +import { SavedObjectAttributes } from 'src/core/server'; import { SavedQueryAttributes, SavedQuery } from '../../../search/search_bar'; interface SerializedSavedQueryAttributes extends SavedObjectAttributes { @@ -104,30 +104,42 @@ export const findSavedQueries = async (searchText: string = ''): Promise { - let queryString; - try { - queryString = JSON.parse(savedObject.attributes.query.query); - } catch (error) { - queryString = savedObject.attributes.query.query; - } - const savedQuery: SavedQueryAttributes = { - title: savedObject.attributes.title || '', - description: savedObject.attributes.description || '', - query: { - query: queryString, - language: savedObject.attributes.query.language, - }, - }; - if (savedObject.attributes.filters) { - savedQuery.filters = JSON.parse(savedObject.attributes.filters); - } - if (savedObject.attributes.timefilter) { - savedQuery.timefilter = JSON.parse(savedObject.attributes.timefilter); - } - return { - id: savedObject.id, - attributes: savedQuery, - }; - }); + return response.savedObjects.map(savedObject => parseSavedQueryObject(savedObject)); +}; + +export const getSavedQuery = async (id: string): Promise => { + const savedObjectsClient = chrome.getSavedObjectsClient(); + + const response = await savedObjectsClient.get('query', id); + return parseSavedQueryObject(response); +}; + +const parseSavedQueryObject = (savedQuery: { + id: string; + attributes: SerializedSavedQueryAttributes; +}) => { + let queryString; + try { + queryString = JSON.parse(savedQuery.attributes.query.query); + } catch (error) { + queryString = savedQuery.attributes.query.query; + } + const savedQueryItems: SavedQueryAttributes = { + title: savedQuery.attributes.title || '', + description: savedQuery.attributes.description || '', + query: { + query: queryString, + language: savedQuery.attributes.query.language, + }, + }; + if (savedQuery.attributes.filters) { + savedQueryItems.filters = JSON.parse(savedQuery.attributes.filters); + } + if (savedQuery.attributes.timefilter) { + savedQueryItems.timefilter = JSON.parse(savedQuery.attributes.timefilter); + } + return { + id: savedQuery.id, + attributes: savedQueryItems, + }; }; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 08ec78d8d6fb9..82056f3574f2b 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -63,7 +63,7 @@ import { Filter } from '@kbn/es-query'; import { TimeRange } from 'ui/timefilter/time_history'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; -import { StaticIndexPattern, Query } from 'src/legacy/core_plugins/data/public'; +import { StaticIndexPattern, Query, SavedQuery } from 'src/legacy/core_plugins/data/public'; import { SaveOptions } from 'ui/saved_objects/saved_object'; import moment from 'moment'; import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard'; @@ -124,6 +124,7 @@ interface DashboardAppScope extends ng.IScope { screenTitle: string; model: { query: Query | string; + savedQuery: SavedQuery; filters: Filter[]; timeRestore: boolean; title: string; @@ -264,6 +265,7 @@ class DashboardAppController { // https://github.com/angular/angular.js/wiki/Understanding-Scopes $scope.model = { query: dashboardStateManager.getQuery(), + savedQuery: dashboardStateManager.getSavedQuery(), filters: queryFilter.getFilters(), timeRestore: dashboardStateManager.getTimeRestore(), title: dashboardStateManager.getTitle(), @@ -284,6 +286,47 @@ class DashboardAppController { }); }); } + const savedQuery = dashboardStateManager.getSavedQuery(); + if (savedQuery) { + $scope.savedQuery = savedQuery; + } + // savedQuery: function (AppState, Private) { + // const appState = new AppState(); + // const savedQueryId = appState.savedQuery; + // const savedObjectsClient = Private(SavedObjectsClientProvider); + + // if (savedQueryId) { + // return savedObjectsClient.get('query', savedQueryId).then(savedQuery => { + // if (savedQuery.error) return; + // let filters = savedQuery.attributes.filters; + // if (filters) { + // filters = JSON.parse(filters); + // } + // let time = savedQuery.attributes.timefilter; + // if (time) { + // time = JSON.parse(savedQuery.attributes.timefilter); + + // timefilter.setTime({ + // from: time.timeFrom, + // to: time.timeTo, + // }); + + // if (time.refreshInterval) { + // timefilter.setRefreshInterval(time.refreshInterval); + // } + // } + + // return { + // id: savedQuery.id, + // attributes: { + // ...savedQuery.attributes, + // filters: filters, + // timefilter: time, + // } + // }; + // }); + // } + // } }; // Part of the exposed plugin API - do not remove without careful consideration. diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts index 40dfd633d5e99..8a1a656cce40c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts @@ -418,6 +418,9 @@ export class DashboardStateManager { return this.appState.query; } + public getSavedQuery() { + return this.appState.savedQuery; + } public getUseMargins() { // Existing dashboards that don't define this should default to false. return this.appState.options.useMargins === undefined diff --git a/src/legacy/core_plugins/kibana/public/dashboard/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/types.ts index c9ffd653f1f06..b854d3eb73a0f 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/types.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/types.ts @@ -21,7 +21,7 @@ import { EmbeddableFactory } from 'ui/embeddable'; import { AppState } from 'ui/state_management/app_state'; import { UIRegistry } from 'ui/registry/_registry'; import { Filter } from '@kbn/es-query'; -import { Query } from 'src/legacy/core_plugins/data/public'; +import { Query, SavedQuery } from 'src/legacy/core_plugins/data/public'; import { DashboardViewMode } from './dashboard_view_mode'; export interface EmbeddableFactoryRegistry extends UIRegistry { @@ -100,6 +100,7 @@ export interface DashboardAppStateParameters { useMargins: boolean; }; query: Query | string; + savedQuery: SavedQuery; filters: Filter[]; viewMode: DashboardViewMode; } From 2b566a189364867705515fe3cc7c4392f6ebab19 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 19 Jun 2019 19:01:26 -0500 Subject: [PATCH 028/189] Support saved queries in Dashboard --- .../query_bar/components/query_bar_input.tsx | 2 +- .../search_bar/components/search_bar.tsx | 2 +- .../search_bar}/lib/saved_query_service.ts | 4 +- .../data/public/search/search_service.ts | 4 + .../public/dashboard/dashboard_app.html | 3 + .../kibana/public/dashboard/dashboard_app.tsx | 105 ++++++++++-------- .../dashboard/dashboard_state_manager.ts | 8 +- .../kibana/public/dashboard/types.ts | 2 +- 8 files changed, 80 insertions(+), 50 deletions(-) rename src/legacy/core_plugins/data/public/{query/query_bar => search/search_bar}/lib/saved_query_service.ts (96%) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index fb4548a1dc578..4eaa9881fd8b0 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -43,7 +43,7 @@ import { getQueryLog } from '../lib/get_query_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; import { SavedQueryRow } from './saved_query_row'; import { SavedQueryAttributes, SavedQuery } from '../../../search/search_bar'; -import { findSavedQueries } from '../lib/saved_query_service'; +import { findSavedQueries } from '../../../search/search_bar/lib/saved_query_service'; interface Props { indexPatterns: Array; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 60c4777a5f7d9..022f4351feb31 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -38,7 +38,7 @@ import { toastNotifications } from 'ui/notify'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; import { SavedQuery, SavedQueryAttributes } from '../index'; -import { saveQuery } from '../../../query/query_bar/lib/saved_query_service'; +import { saveQuery } from '../lib/saved_query_service'; import { SavedQueryMeta, SaveQueryForm } from './save_query_form'; import { EuiFlexGroup, EuiFlexItem } from '../../../../../../../../../eui/src/components/flex'; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts similarity index 96% rename from src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts rename to src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index dea4eb9d118c6..f91ef9b60d497 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -18,8 +18,8 @@ */ import chrome from 'ui/chrome'; -import { SavedObjectAttributes } from 'src/core/server'; -import { SavedQueryAttributes, SavedQuery } from '../../../search/search_bar'; +import { SavedObjectAttributes } from 'kibana/server'; +import { SavedQueryAttributes, SavedQuery } from '../index'; interface SerializedSavedQueryAttributes extends SavedObjectAttributes { title: string; diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index 6f9a97fd4c0c8..765a261f55ee6 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -19,6 +19,7 @@ import { once } from 'lodash'; import { SearchBar, setupDirective as setupSearchBarDirective } from './search_bar'; +import * as savedQueryService from './search_bar/lib/saved_query_service'; /** * Search Service @@ -30,6 +31,9 @@ export class SearchService { ui: { SearchBar, }, + services: { + savedQueryService, + }, loadLegacyDirectives: once(setupSearchBarDirective), }; } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html index d73e15287077c..805afdce5842a 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html @@ -10,6 +10,7 @@
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 82056f3574f2b..bfbe685b2f7bd 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -63,7 +63,8 @@ import { Filter } from '@kbn/es-query'; import { TimeRange } from 'ui/timefilter/time_history'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; -import { StaticIndexPattern, Query, SavedQuery } from 'src/legacy/core_plugins/data/public'; +import { StaticIndexPattern, Query, SavedQuery } from 'plugins/data'; +import { data } from 'plugins/data/setup'; import { SaveOptions } from 'ui/saved_objects/saved_object'; import moment from 'moment'; import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard'; @@ -91,6 +92,8 @@ import { DashboardViewMode } from './dashboard_view_mode'; import { getDashboardTitle } from './dashboard_strings'; import { panelActionsStore } from './store/panel_actions_store'; +const { savedQueryService } = data.search.services; + type ConfirmModalFn = ( message: string, confirmOptions: { @@ -124,7 +127,6 @@ interface DashboardAppScope extends ng.IScope { screenTitle: string; model: { query: Query | string; - savedQuery: SavedQuery; filters: Filter[]; timeRestore: boolean; title: string; @@ -134,6 +136,7 @@ interface DashboardAppScope extends ng.IScope { | { to: string | moment.Moment | undefined; from: string | moment.Moment | undefined }; refreshInterval: any; }; + savedQuery?: SavedQuery; refreshInterval: any; panels: SavedDashboardPanel[]; indexPatterns: StaticIndexPattern[]; @@ -150,6 +153,8 @@ interface DashboardAppScope extends ng.IScope { $listenAndDigestAsync: any; onCancelApplyFilters: () => void; onApplyFilters: (filters: Filter[]) => void; + onQuerySaved: (savedQuery: SavedQuery) => void; + onSavedQueryUpdated: (savedQuery: SavedQuery) => void; topNavMenu: any; showFilterBar: () => boolean; showAddPanel: any; @@ -265,7 +270,6 @@ class DashboardAppController { // https://github.com/angular/angular.js/wiki/Understanding-Scopes $scope.model = { query: dashboardStateManager.getQuery(), - savedQuery: dashboardStateManager.getSavedQuery(), filters: queryFilter.getFilters(), timeRestore: dashboardStateManager.getTimeRestore(), title: dashboardStateManager.getTitle(), @@ -286,47 +290,6 @@ class DashboardAppController { }); }); } - const savedQuery = dashboardStateManager.getSavedQuery(); - if (savedQuery) { - $scope.savedQuery = savedQuery; - } - // savedQuery: function (AppState, Private) { - // const appState = new AppState(); - // const savedQueryId = appState.savedQuery; - // const savedObjectsClient = Private(SavedObjectsClientProvider); - - // if (savedQueryId) { - // return savedObjectsClient.get('query', savedQueryId).then(savedQuery => { - // if (savedQuery.error) return; - // let filters = savedQuery.attributes.filters; - // if (filters) { - // filters = JSON.parse(filters); - // } - // let time = savedQuery.attributes.timefilter; - // if (time) { - // time = JSON.parse(savedQuery.attributes.timefilter); - - // timefilter.setTime({ - // from: time.timeFrom, - // to: time.timeTo, - // }); - - // if (time.refreshInterval) { - // timefilter.setRefreshInterval(time.refreshInterval); - // } - // } - - // return { - // id: savedQuery.id, - // attributes: { - // ...savedQuery.attributes, - // filters: filters, - // timefilter: time, - // } - // }; - // }); - // } - // } }; // Part of the exposed plugin API - do not remove without careful consideration. @@ -434,6 +397,60 @@ class DashboardAppController { $scope.appState.$newFilters = []; }; + $scope.onQuerySaved = savedQuery => { + $scope.savedQuery = savedQuery; + }; + + $scope.onSavedQueryUpdated = savedQuery => { + $scope.savedQuery = savedQuery; + }; + + const updateStateFromSavedQuery = (savedQuery: SavedQuery) => { + queryFilter.setFilters(savedQuery.attributes.filters || []); + dashboardStateManager.applyFilters( + savedQuery.attributes.query, + savedQuery.attributes.filters || [] + ); + if (savedQuery.attributes.timefilter) { + timefilter.setTime({ + from: savedQuery.attributes.timefilter.timeFrom, + to: savedQuery.attributes.timefilter.timeTo, + }); + if (savedQuery.attributes.timefilter.refreshInterval) { + timefilter.setRefreshInterval(savedQuery.attributes.timefilter.refreshInterval); + } + } + $scope.refresh(); + }; + + $scope.$watch('savedQuery', (newSavedQuery: SavedQuery, oldSavedQuery: SavedQuery) => { + if (!newSavedQuery) return; + dashboardStateManager.setSavedQueryId(newSavedQuery.id); + + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } + }); + + $scope.$watch( + () => { + return dashboardStateManager.getSavedQueryId(); + }, + newSavedQueryId => { + if (!newSavedQueryId) { + $scope.savedQuery = undefined; + return; + } + + savedQueryService.getSavedQuery(newSavedQueryId).then((savedQuery: SavedQuery) => { + $scope.$evalAsync(() => { + $scope.savedQuery = savedQuery; + updateStateFromSavedQuery(savedQuery); + }); + }); + } + ); + $scope.$watch('appState.$newFilters', (filters: Filter[] = []) => { if (filters.length === 1) { $scope.onApplyFilters(filters); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts index 8a1a656cce40c..6b7e59e43aef5 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts @@ -418,9 +418,15 @@ export class DashboardStateManager { return this.appState.query; } - public getSavedQuery() { + public getSavedQueryId() { return this.appState.savedQuery; } + + public setSavedQueryId(id: string) { + this.appState.savedQuery = id; + this.saveState(); + } + public getUseMargins() { // Existing dashboards that don't define this should default to false. return this.appState.options.useMargins === undefined diff --git a/src/legacy/core_plugins/kibana/public/dashboard/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/types.ts index b854d3eb73a0f..c25bfdad04039 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/types.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/types.ts @@ -100,9 +100,9 @@ export interface DashboardAppStateParameters { useMargins: boolean; }; query: Query | string; - savedQuery: SavedQuery; filters: Filter[]; viewMode: DashboardViewMode; + savedQuery?: string; } // This could probably be improved if we flesh out AppState more... though AppState will be going away From 36d1e2f63066c8a1ef53c4873c96cccc0238247f Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 20 Jun 2019 11:31:26 -0500 Subject: [PATCH 029/189] Add back changes lost in merge --- .../kibana/public/dashboard/dashboard_app.tsx | 1 - .../dashboard/dashboard_app_controller.tsx | 59 ++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 8d13bec32da71..509f6900f2ec4 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -51,7 +51,6 @@ import { TimeRange } from 'ui/timefilter/time_history'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; import { StaticIndexPattern, Query, SavedQuery } from 'plugins/data'; -import { data } from 'plugins/data/setup'; import moment from 'moment'; import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard'; import { DashboardAppState, SavedDashboardPanel, ConfirmModalFn, AddFilterFn } from './types'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index 557c780f6192e..0cad77afdf8db 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -53,7 +53,8 @@ import { KbnUrl } from 'ui/url/kbn_url'; import { Filter } from '@kbn/es-query'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; -import { Query } from 'src/legacy/core_plugins/data/public'; +import { Query, SavedQuery } from 'plugins/data'; +import { data } from 'plugins/data/setup'; import { SaveOptions } from 'ui/saved_objects/saved_object'; import { DashboardAppState, @@ -78,6 +79,8 @@ import { getDashboardTitle } from './dashboard_strings'; import { panelActionsStore } from './store/panel_actions_store'; import { DashboardAppScope } from './dashboard_app'; +const { savedQueryService } = data.search.services; + export class DashboardAppController { // Part of the exposed plugin API - do not remove without careful consideration. appStatus: { @@ -287,6 +290,60 @@ export class DashboardAppController { $scope.appState.$newFilters = []; }; + $scope.onQuerySaved = savedQuery => { + $scope.savedQuery = savedQuery; + }; + + $scope.onSavedQueryUpdated = savedQuery => { + $scope.savedQuery = savedQuery; + }; + + const updateStateFromSavedQuery = (savedQuery: SavedQuery) => { + queryFilter.setFilters(savedQuery.attributes.filters || []); + dashboardStateManager.applyFilters( + savedQuery.attributes.query, + savedQuery.attributes.filters || [] + ); + if (savedQuery.attributes.timefilter) { + timefilter.setTime({ + from: savedQuery.attributes.timefilter.timeFrom, + to: savedQuery.attributes.timefilter.timeTo, + }); + if (savedQuery.attributes.timefilter.refreshInterval) { + timefilter.setRefreshInterval(savedQuery.attributes.timefilter.refreshInterval); + } + } + $scope.refresh(); + }; + + $scope.$watch('savedQuery', (newSavedQuery: SavedQuery, oldSavedQuery: SavedQuery) => { + if (!newSavedQuery) return; + dashboardStateManager.setSavedQueryId(newSavedQuery.id); + + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } + }); + + $scope.$watch( + () => { + return dashboardStateManager.getSavedQueryId(); + }, + newSavedQueryId => { + if (!newSavedQueryId) { + $scope.savedQuery = undefined; + return; + } + + savedQueryService.getSavedQuery(newSavedQueryId).then((savedQuery: SavedQuery) => { + $scope.$evalAsync(() => { + $scope.savedQuery = savedQuery; + updateStateFromSavedQuery(savedQuery); + }); + }); + } + ); + $scope.$watch('appState.$newFilters', (filters: Filter[] = []) => { if (filters.length === 1) { $scope.onApplyFilters(filters); From 103c08232948338b88e56516e4445fe23583ec66 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 20 Jun 2019 12:17:55 -0500 Subject: [PATCH 030/189] add saved query to visualize --- .../public/visualize/editor/editor.html | 3 ++ .../kibana/public/visualize/editor/editor.js | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html index 64905d18ccfb8..291655ce778a8 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html @@ -30,6 +30,7 @@
diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index 6938437bdde33..52e0ea9e5307b 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -53,7 +53,9 @@ import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs'; import { npStart } from 'ui/new_platform'; +import { data } from 'plugins/data/setup'; +const { savedQueryService } = data.search.services; uiRoutes .when(VisualizeConstants.CREATE_PATH, { @@ -465,6 +467,56 @@ function VisEditor( }); }; + $scope.onQuerySaved = savedQuery => { + $scope.savedQuery = savedQuery; + }; + + $scope.onSavedQueryUpdated = savedQuery => { + $scope.savedQuery = savedQuery; + }; + + const updateStateFromSavedQuery = (savedQuery) => { + $state.query = savedQuery.attributes.query; + queryFilter.setFilters(savedQuery.attributes.filters || []); + + if (savedQuery.attributes.timefilter) { + timefilter.setTime({ + from: savedQuery.attributes.timefilter.timeFrom, + to: savedQuery.attributes.timefilter.timeTo, + }); + if (savedQuery.attributes.timefilter.refreshInterval) { + timefilter.setRefreshInterval(savedQuery.attributes.timefilter.refreshInterval); + } + } + + $scope.fetch(); + }; + + $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { + if (!newSavedQuery) return; + $state.savedQuery = newSavedQuery.id; + $state.save(); + + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } + }); + + $scope.$watch('state.savedQuery', newSavedQueryId => { + if (!newSavedQueryId) { + $scope.savedQuery = undefined; + return; + } + + savedQueryService.getSavedQuery(newSavedQueryId).then((savedQuery) => { + $scope.$evalAsync(() => { + $scope.savedQuery = savedQuery; + updateStateFromSavedQuery(savedQuery); + }); + }); + } + ); + /** * Called when the user clicks "Save" button. */ From 5bb384c3132257c2cb0320b5327aabdd6f5a8141 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 20 Jun 2019 12:27:40 -0500 Subject: [PATCH 031/189] Update Discover to be consistent with Dashboard and Visualize --- .../public/discover/controllers/discover.js | 83 ++++++++----------- .../kibana/public/visualize/editor/editor.js | 3 +- 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 37a701b5b998d..7a07db1fa844d 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -73,6 +73,8 @@ import 'ui/capabilities/route_setup'; import { data } from 'plugins/data/setup'; data.search.loadLegacyDirectives(); +const { savedQueryService } = data.search.services; + const fetchStatuses = { UNINITIALIZED: 'uninitialized', LOADING: 'loading', @@ -166,43 +168,6 @@ uiRoutes 'search': '/discover', 'index-pattern': '/management/kibana/objects/savedSearches/' + $route.current.params.id })); - }, - savedQuery: function (AppState, Private) { - const appState = new AppState(); - const savedQueryId = appState.savedQuery; - const savedObjectsClient = Private(SavedObjectsClientProvider); - - if (savedQueryId) { - return savedObjectsClient.get('query', savedQueryId).then(savedQuery => { - if (savedQuery.error) return; - let filters = savedQuery.attributes.filters; - if (filters) { - filters = JSON.parse(filters); - } - let time = savedQuery.attributes.timefilter; - if (time) { - time = JSON.parse(savedQuery.attributes.timefilter); - - timefilter.setTime({ - from: time.timeFrom, - to: time.timeTo, - }); - - if (time.refreshInterval) { - timefilter.setRefreshInterval(time.refreshInterval); - } - } - - return { - id: savedQuery.id, - attributes: { - ...savedQuery.attributes, - filters: filters, - timefilter: time, - } - }; - }); - } } } }); @@ -932,30 +897,54 @@ function discoverController( $scope.showAllRows = function () { $scope.minimumVisibleRows = $scope.hits; }; - $scope.onQuerySaved = function (savedQuery) { + + $scope.onQuerySaved = savedQuery => { $scope.savedQuery = savedQuery; }; - $scope.onSavedQueryUpdated = function (savedQuery) { + $scope.onSavedQueryUpdated = savedQuery => { $scope.savedQuery = savedQuery; }; - $scope.$watch('savedQuery', (newSavedQuery) => { - if (!newSavedQuery) return; + const updateStateFromSavedQuery = (savedQuery) => { + $state.query = savedQuery.attributes.query; + queryFilter.setFilters(savedQuery.attributes.filters || []); - if (newSavedQuery.attributes.timefilter) { + if (savedQuery.attributes.timefilter) { timefilter.setTime({ - from: newSavedQuery.attributes.timefilter.timeFrom, - to: newSavedQuery.attributes.timefilter.timeTo, + from: savedQuery.attributes.timefilter.timeFrom, + to: savedQuery.attributes.timefilter.timeTo, }); + if (savedQuery.attributes.timefilter.refreshInterval) { + timefilter.setRefreshInterval(savedQuery.attributes.timefilter.refreshInterval); + } } - queryFilter.setFilters(newSavedQuery.attributes.filters || []); + $scope.fetch(); + }; - $state.query = newSavedQuery.attributes.query; + $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { + if (!newSavedQuery) return; $state.savedQuery = newSavedQuery.id; $state.save(); - $scope.fetch(); + + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } + }); + + $scope.$watch('state.savedQuery', newSavedQueryId => { + if (!newSavedQueryId) { + $scope.savedQuery = undefined; + return; + } + + savedQueryService.getSavedQuery(newSavedQueryId).then((savedQuery) => { + $scope.$evalAsync(() => { + $scope.savedQuery = savedQuery; + updateStateFromSavedQuery(savedQuery); + }); + }); }); async function setupVisualization() { diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index 52e0ea9e5307b..5e7ca3275892f 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -514,8 +514,7 @@ function VisEditor( updateStateFromSavedQuery(savedQuery); }); }); - } - ); + }); /** * Called when the user clicks "Save" button. From 8b57318d7ce2c83b05aac6af3719424a12c794e7 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 20 Jun 2019 16:19:53 -0500 Subject: [PATCH 032/189] Update app permissions to include saved query access --- .../xpack_main/server/lib/register_oss_features.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/xpack_main/server/lib/register_oss_features.ts b/x-pack/plugins/xpack_main/server/lib/register_oss_features.ts index d55db153cce14..b416b46421e76 100644 --- a/x-pack/plugins/xpack_main/server/lib/register_oss_features.ts +++ b/x-pack/plugins/xpack_main/server/lib/register_oss_features.ts @@ -20,7 +20,7 @@ const buildKibanaFeatures = (savedObjectTypes: string[]) => { privileges: { all: { savedObject: { - all: ['search', 'url'], + all: ['search', 'url', 'query'], read: ['index-pattern'], }, ui: ['show', 'createShortUrl', 'save'], @@ -28,7 +28,7 @@ const buildKibanaFeatures = (savedObjectTypes: string[]) => { read: { savedObject: { all: [], - read: ['index-pattern', 'search'], + read: ['index-pattern', 'search', 'query'], }, ui: ['show'], }, @@ -46,7 +46,7 @@ const buildKibanaFeatures = (savedObjectTypes: string[]) => { privileges: { all: { savedObject: { - all: ['visualization', 'url'], + all: ['visualization', 'url', 'query'], read: ['index-pattern', 'search'], }, ui: ['show', 'createShortUrl', 'delete', 'save'], @@ -54,7 +54,7 @@ const buildKibanaFeatures = (savedObjectTypes: string[]) => { read: { savedObject: { all: [], - read: ['index-pattern', 'search', 'visualization'], + read: ['index-pattern', 'search', 'visualization', 'query'], }, ui: ['show'], }, @@ -72,7 +72,7 @@ const buildKibanaFeatures = (savedObjectTypes: string[]) => { privileges: { all: { savedObject: { - all: ['dashboard', 'url'], + all: ['dashboard', 'url', 'query'], read: [ 'index-pattern', 'search', @@ -95,6 +95,7 @@ const buildKibanaFeatures = (savedObjectTypes: string[]) => { 'canvas-workpad', 'map', 'dashboard', + 'query', ], }, ui: ['show'], From a8fb548b528bfe38c746ffba1ded7a1c0031c242 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 20 Jun 2019 15:12:09 -0700 Subject: [PATCH 033/189] Fixes types --- .../query/query_bar/components/query_bar.test.tsx | 10 ++++++++++ .../public/search/search_bar/components/search_bar.tsx | 3 ++- .../search/search_bar/lib/saved_query_service.ts | 7 +++++-- .../public/dashboard/dashboard_app_controller.tsx | 2 +- .../core_plugins/kibana/public/dashboard/types.ts | 2 +- src/legacy/ui/public/autocomplete_providers/index.d.ts | 2 +- 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx index 2be00deb16d9e..37936af8667fa 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx @@ -80,6 +80,11 @@ describe('QueryBar', () => { indexPatterns={[mockIndexPattern]} store={createMockStorage()} intl={null as any} + onChange={noop} + onSave={noop} + onSaveNew={noop} + onLoadSavedQuery={noop} + isDirty={false} /> ); @@ -97,6 +102,11 @@ describe('QueryBar', () => { store={createMockStorage()} disableAutoFocus={true} intl={null as any} + onChange={noop} + onSave={noop} + onSaveNew={noop} + onLoadSavedQuery={noop} + isDirty={false} /> ); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 022f4351feb31..c25428604bc80 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -24,6 +24,8 @@ import { EuiPopoverTitle, EuiButtonEmpty, EuiButton, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { Filter } from '@kbn/es-query'; import { InjectedIntl, injectI18n, FormattedMessage } from '@kbn/i18n/react'; @@ -40,7 +42,6 @@ import { FilterBar } from '../../../filter/filter_bar'; import { SavedQuery, SavedQueryAttributes } from '../index'; import { saveQuery } from '../lib/saved_query_service'; import { SavedQueryMeta, SaveQueryForm } from './save_query_form'; -import { EuiFlexGroup, EuiFlexItem } from '../../../../../../../../../eui/src/components/flex'; interface DateRange { from: string; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index f91ef9b60d497..fc96446bf61cf 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -18,7 +18,7 @@ */ import chrome from 'ui/chrome'; -import { SavedObjectAttributes } from 'kibana/server'; +import { SavedObjectAttributes } from 'src/core/server'; import { SavedQueryAttributes, SavedQuery } from '../index'; interface SerializedSavedQueryAttributes extends SavedObjectAttributes { @@ -104,7 +104,10 @@ export const findSavedQueries = async (searchText: string = ''): Promise parseSavedQueryObject(savedObject)); + return response.savedObjects.map( + (savedObject: { id: string; attributes: SerializedSavedQueryAttributes }) => + parseSavedQueryObject(savedObject) + ); }; export const getSavedQuery = async (id: string): Promise => { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index 0cad77afdf8db..83647aa42f9db 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -54,8 +54,8 @@ import { Filter } from '@kbn/es-query'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; import { Query, SavedQuery } from 'plugins/data'; -import { data } from 'plugins/data/setup'; import { SaveOptions } from 'ui/saved_objects/saved_object'; +import { data } from '../../../data/public/setup'; import { DashboardAppState, EmbeddableFactoryRegistry, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/types.ts index 4475b91286cc7..aa3c78eb51811 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/types.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/types.ts @@ -21,7 +21,7 @@ import { EmbeddableFactory } from 'ui/embeddable'; import { AppState } from 'ui/state_management/app_state'; import { UIRegistry } from 'ui/registry/_registry'; import { Filter } from '@kbn/es-query'; -import { Query, SavedQuery } from 'src/legacy/core_plugins/data/public'; +import { Query } from 'src/legacy/core_plugins/data/public'; import { AppState as TAppState } from 'ui/state_management/app_state'; import { DashboardViewMode } from './dashboard_view_mode'; diff --git a/src/legacy/ui/public/autocomplete_providers/index.d.ts b/src/legacy/ui/public/autocomplete_providers/index.d.ts index 05b8c556a6709..d3d4bbe3c5c53 100644 --- a/src/legacy/ui/public/autocomplete_providers/index.d.ts +++ b/src/legacy/ui/public/autocomplete_providers/index.d.ts @@ -21,7 +21,7 @@ * WARNING: these typings are incomplete */ import { StaticIndexPattern } from 'ui/index_patterns'; -import { SavedQuery } from 'plugins/data'; +import { SavedQuery } from '../../../../legacy/core_plugins/data/public'; export type AutocompleteProvider = ( args: { From 2135698d3458251432385371eca24ca8ac45129c Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 20 Jun 2019 18:42:35 -0500 Subject: [PATCH 034/189] Start tests for saved query service --- .../lib/saved_query_service.test.ts | 124 ++++++++++++++++++ .../search_bar/lib/saved_query_service.ts | 15 +-- 2 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts new file mode 100644 index 0000000000000..ec4b7da7fcff0 --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -0,0 +1,124 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedQueryAttributes } from '../index'; +import { saveQuery } from './saved_query_service'; +import { FilterStateStore } from '@kbn/es-query'; + +const savedQueryAttributes: SavedQueryAttributes = { + title: 'foo', + description: 'bar', + query: { + language: 'kuery', + query: 'response:200', + }, +}; + +const savedQueryAttributesWithFilters: SavedQueryAttributes = { + ...savedQueryAttributes, + filters: [ + { + query: { match_all: {} }, + $state: { store: FilterStateStore.APP_STATE }, + meta: { + disabled: false, + negate: false, + alias: null, + }, + }, + ], + timefilter: { + timeTo: 'now', + timeFrom: 'now-15m', + refreshInterval: { + pause: false, + value: 0, + }, + }, +}; + +const mockSavedObjectsClient = { + create: jest.fn(), +}; + +jest.mock('ui/chrome', () => { + return { + getSavedObjectsClient: () => { + return mockSavedObjectsClient; + }, + }; +}); + +describe('saved query service', () => { + afterEach(() => { + mockSavedObjectsClient.create.mockReset(); + }); + + describe('saveQuery', function() { + it('should create a saved object for the given attributes', async () => { + mockSavedObjectsClient.create.mockReturnValue({ + id: '1234', + attributes: savedQueryAttributes, + }); + + const response = await saveQuery(savedQueryAttributes); + expect(mockSavedObjectsClient.create).toHaveBeenCalledWith('query', savedQueryAttributes); + expect(response).toEqual({ id: '1234', attributes: savedQueryAttributes }); + }); + + it('should accept an explicit ID', async () => { + mockSavedObjectsClient.create.mockReturnValue({ + id: '1234', + attributes: savedQueryAttributes, + }); + + const response = await saveQuery(savedQueryAttributes, '1234'); + expect(mockSavedObjectsClient.create).toHaveBeenCalledWith('query', savedQueryAttributes, { + id: '1234', + overwrite: true, + }); + expect(response).toEqual({ id: '1234', attributes: savedQueryAttributes }); + }); + + it('should optionally accept filters and timefilters in object format', async () => { + const serializedSavedQueryAttributesWithFilters = { + ...savedQueryAttributesWithFilters, + filters: JSON.stringify(savedQueryAttributesWithFilters.filters), + timefilter: JSON.stringify(savedQueryAttributesWithFilters.timefilter), + }; + + mockSavedObjectsClient.create.mockReturnValue({ + id: '1234', + attributes: serializedSavedQueryAttributesWithFilters, + }); + + const response = await saveQuery(savedQueryAttributesWithFilters); + + expect(mockSavedObjectsClient.create).toHaveBeenCalledWith( + 'query', + serializedSavedQueryAttributesWithFilters + ); + expect(response).toEqual({ id: '1234', attributes: savedQueryAttributesWithFilters }); + }); + + it('should throw an error when saved objects client returns error', () => { + throw new Error('implement me'); + }); + }); +}); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index fc96446bf61cf..5aed31b85e1dd 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -32,7 +32,7 @@ interface SerializedSavedQueryAttributes extends SavedObjectAttributes { timefilter?: string; } -export const saveQuery = async (attributes: SavedQueryAttributes, id: string = '') => { +export const saveQuery = async (attributes: SavedQueryAttributes, id?: string) => { const savedObjectsClient = chrome.getSavedObjectsClient(); const query = { @@ -80,18 +80,7 @@ export const saveQuery = async (attributes: SavedQueryAttributes, id: string = ' throw new Error(rawQueryResponse.error.message); } - const responseObject: SavedQueryAttributes = { - title: rawQueryResponse.attributes.title, - description: rawQueryResponse.attributes.description, - query: rawQueryResponse.attributes.query, - }; - if (rawQueryResponse.attributes.filters) { - responseObject.filters = JSON.parse(rawQueryResponse.attributes.filters); - } - if (rawQueryResponse.attributes.timefilter) { - responseObject.timefilter = JSON.parse(rawQueryResponse.attributes.timefilter); - } - return { id: rawQueryResponse.id, attributes: responseObject }; + return parseSavedQueryObject(rawQueryResponse); }; export const findSavedQueries = async (searchText: string = ''): Promise => { From c320f793b5656998cece2f672e0950f2b76bcb15 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 21 Jun 2019 07:49:24 -0700 Subject: [PATCH 035/189] Adds 'undefined' as a type to SavedObjectsAttributes --- src/core/server/server.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 14dea167d3189..42b0c7af16160 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -422,7 +422,7 @@ export interface SavedObject { // @public (undocumented) export interface SavedObjectAttributes { // (undocumented) - [key: string]: SavedObjectAttributes | string | number | boolean | null; + [key: string]: SavedObjectAttributes | string | number | boolean | null | undefined; } // @public From f46c70c37c3fcec115391e52d8b59dca5739b170 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 21 Jun 2019 12:27:46 -0700 Subject: [PATCH 036/189] Adds Error thrown test --- .../search_bar/lib/saved_query_service.test.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index ec4b7da7fcff0..90d0415ab369d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -55,6 +55,7 @@ const savedQueryAttributesWithFilters: SavedQueryAttributes = { const mockSavedObjectsClient = { create: jest.fn(), + error: jest.fn(), }; jest.mock('ui/chrome', () => { @@ -117,8 +118,21 @@ describe('saved query service', () => { expect(response).toEqual({ id: '1234', attributes: savedQueryAttributesWithFilters }); }); - it('should throw an error when saved objects client returns error', () => { - throw new Error('implement me'); + it('should throw an error when saved objects client returns error', async () => { + mockSavedObjectsClient.create.mockReturnValue({ + error: { + error: '123', + message: 'An Error', + }, + }); + + let error = null; + try { + await saveQuery(savedQueryAttributes); + } catch (e) { + error = e; + } + expect(error).not.toBe(null); }); }); }); From b786886b4bbaebd6820025d0d1a852f256ec1914 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 24 Jun 2019 12:23:36 -0700 Subject: [PATCH 037/189] Updates QueryBar and QueryBarInput snapshots --- .../components/__snapshots__/query_bar.test.tsx.snap | 4 ++++ .../components/__snapshots__/query_bar_input.test.tsx.snap | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap index bc0b15ca88e3e..9d9721efa4521 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap @@ -29,7 +29,11 @@ exports[`QueryBar Should render the given query 1`] = ` }, ] } + isDirty={false} onChange={[Function]} + onLoadSavedQuery={[Function]} + onSave={[Function]} + onSaveNew={[Function]} onSubmit={[Function]} query={ Object { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap index 130e303fda58a..efa2097abef49 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap @@ -335,6 +335,7 @@ exports[`QueryBarInput Should disable autoFocus on EuiFieldText when disableAuto Date: Mon, 24 Jun 2019 13:27:00 -0700 Subject: [PATCH 038/189] Fixes saved searches icon in test --- test/api_integration/apis/management/saved_objects/find.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api_integration/apis/management/saved_objects/find.js b/test/api_integration/apis/management/saved_objects/find.js index 9508a2a1624c3..0e7cf08fa5c26 100644 --- a/test/api_integration/apis/management/saved_objects/find.js +++ b/test/api_integration/apis/management/saved_objects/find.js @@ -227,7 +227,7 @@ export default function ({ getService }) { .then(resp => { expect(resp.body.saved_objects).to.have.length(1); expect(resp.body.saved_objects[0].meta).to.eql({ - icon: 'search', + icon: 'discoverApp', title: 'OneRecord', editUrl: '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { From 21b3d626f8a2d664919f0cf103a411554882d8a1 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 24 Jun 2019 15:04:55 -0700 Subject: [PATCH 039/189] Replaces query-bar with search-bar --- .../plugins/maps/public/angular/map.html | 20 +++++++++++++++++-- .../maps/public/angular/map_controller.js | 14 +++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/legacy/plugins/maps/public/angular/map.html index c95117d906d16..0dc31cf118955 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map.html +++ b/x-pack/legacy/plugins/maps/public/angular/map.html @@ -5,7 +5,23 @@
- +
diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 44fc9e33d2bcf..f03cf1f42f80d 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -100,6 +100,20 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage mapStateJSON: savedMap.mapStateJSON, globalState: globalState, }); + /* + Saved Queries required moving the query bar state caching to the search bar. Using the search bar instead of the query bar in Maps requires obscuring the filter functionality within the app. + The following is a hack, implemented to enable use of the search bar instead of the query bar. + */ + $scope.filters = []; + $scope.onFiltersUpdated = function () { + return; + }; + $scope.showFilterBar = function () { + return false; + }; + + /* END HACK */ + $scope.refreshConfig = getInitialRefreshConfig({ mapStateJSON: savedMap.mapStateJSON, globalState: globalState, From aef3d4b31a0c3ae9bc9ecc89339bdc2cce252db1 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 24 Jun 2019 15:08:49 -0700 Subject: [PATCH 040/189] Changes search icon from search to discoverApp for saved search integration test --- .../apis/management/saved_objects/relationships.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api_integration/apis/management/saved_objects/relationships.js b/test/api_integration/apis/management/saved_objects/relationships.js index ee369e5c8827a..a1495b3722f41 100644 --- a/test/api_integration/apis/management/saved_objects/relationships.js +++ b/test/api_integration/apis/management/saved_objects/relationships.js @@ -262,7 +262,7 @@ export default function ({ getService }) { type: 'search', relationship: 'child', meta: { - icon: 'search', + icon: 'discoverApp', title: 'OneRecord', editUrl: '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { From cb433228b6586d98cd0cde730a34636648567350 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 24 Jun 2019 15:10:00 -0700 Subject: [PATCH 041/189] changes icon from search to discoverApp for saved_search integration test --- .../apis/management/saved_objects/relationships.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api_integration/apis/management/saved_objects/relationships.js b/test/api_integration/apis/management/saved_objects/relationships.js index ee369e5c8827a..a1495b3722f41 100644 --- a/test/api_integration/apis/management/saved_objects/relationships.js +++ b/test/api_integration/apis/management/saved_objects/relationships.js @@ -262,7 +262,7 @@ export default function ({ getService }) { type: 'search', relationship: 'child', meta: { - icon: 'search', + icon: 'discoverApp', title: 'OneRecord', editUrl: '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { From c3e35045be03aae887e16e40aa699b283951dbff Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 24 Jun 2019 16:48:50 -0700 Subject: [PATCH 042/189] Replaces the query-bar with the search-bar, hiding the filters from the ui --- x-pack/legacy/plugins/maps/public/angular/map.html | 1 + .../legacy/plugins/maps/public/angular/map_controller.js | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/legacy/plugins/maps/public/angular/map.html index 0dc31cf118955..11e177b301bf8 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map.html +++ b/x-pack/legacy/plugins/maps/public/angular/map.html @@ -14,6 +14,7 @@ filters="filters" on-filters-updated="onFiltersUpdated" show-filter-bar="showFilterBar()" + show-query-bar="showQueryBar()" show-date-picker="showDatePicker" date-range-from="time.from" date-range-to="time.to" diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index f03cf1f42f80d..586647d2677a9 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -49,6 +49,8 @@ import { getInitialQuery } from './get_initial_query'; import { getInitialTimeFilters } from './get_initial_time_filters'; import { getInitialRefreshConfig } from './get_initial_refresh_config'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; +import { data } from 'plugins/data/setup'; +data.search.loadLegacyDirectives(); const REACT_ANCHOR_DOM_ELEMENT_ID = 'react-maps-root'; @@ -109,7 +111,10 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage return; }; $scope.showFilterBar = function () { - return false; + return $scope.filters.length > 0; + }; + $scope.showQueryBar = function () { + return true; }; /* END HACK */ From 86cd42e847103d5222e652f444c61bedeb5749f0 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 25 Jun 2019 08:08:16 -0700 Subject: [PATCH 043/189] Changes 'search' icon to 'discoverApp' for saved searches missed before --- .../apis/management/saved_objects/relationships.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/api_integration/apis/management/saved_objects/relationships.js b/test/api_integration/apis/management/saved_objects/relationships.js index a1495b3722f41..1b7b3255cd538 100644 --- a/test/api_integration/apis/management/saved_objects/relationships.js +++ b/test/api_integration/apis/management/saved_objects/relationships.js @@ -299,7 +299,7 @@ export default function ({ getService }) { id: '960372e0-3224-11e8-a572-ffca06da1357', type: 'search', meta: { - icon: 'search', + icon: 'discoverApp', title: 'OneRecord', editUrl: '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { @@ -342,7 +342,7 @@ export default function ({ getService }) { type: 'search', relationship: 'parent', meta: { - icon: 'search', + icon: 'discoverApp', title: 'OneRecord', editUrl: '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { @@ -379,7 +379,7 @@ export default function ({ getService }) { id: '960372e0-3224-11e8-a572-ffca06da1357', type: 'search', meta: { - icon: 'search', + icon: 'discoverApp', title: 'OneRecord', editUrl: '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { From 45b1cc7ad2f09056565bb5d574228b510b1b695b Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 25 Jun 2019 13:14:13 -0700 Subject: [PATCH 044/189] removes commented out code, undoes comma deletion in kibana/index.js --- src/legacy/core_plugins/kibana/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 5e09881ea1f70..06e2f9e589d3b 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -67,7 +67,6 @@ export default function (kibana) { 'plugins/kibana/visualize/saved_visualizations/saved_visualization_register', 'plugins/kibana/discover/saved_searches/saved_search_register', 'plugins/kibana/dashboard/saved_dashboard/saved_dashboard_register', - // 'plugins/data/saved_queriesfilters/saved_queriesfilters_register' ], app: { id: 'kibana', @@ -223,7 +222,7 @@ export default function (kibana) { getTitle(obj) { return `Advanced Settings [${obj.id}]`; }, - } + }, }, savedObjectSchemas: { From 75c000d271f0de3639a9ea04452caa4124ac3a04 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 25 Jun 2019 14:55:16 -0700 Subject: [PATCH 045/189] Adds Search Bar back --- x-pack/legacy/plugins/maps/public/angular/map.html | 13 ------------- .../plugins/maps/public/angular/map_controller.js | 6 +++--- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/legacy/plugins/maps/public/angular/map.html index 11e177b301bf8..8f3671e19c35d 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map.html +++ b/x-pack/legacy/plugins/maps/public/angular/map.html @@ -22,19 +22,6 @@ refresh-interval="refreshConfig.interval" on-refresh-change="onRefreshChange" > - -
diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 0c2e1d916f893..8becca8978c6b 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -103,8 +103,9 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage globalState: globalState, }); /* + START QueryBar replacement with SearchBar Saved Queries required moving the query bar state caching to the search bar. Using the search bar instead of the query bar in Maps requires obscuring the filter functionality within the app. - The following is a hack, implemented to enable use of the search bar instead of the query bar. + The following is allows us to use the search bar without the filters. */ $scope.filters = []; $scope.onFiltersUpdated = function () { @@ -116,8 +117,7 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage $scope.showQueryBar = function () { return true; }; - - /* END HACK */ + /* END QueryBar replacement with SearchBar */ $scope.refreshConfig = getInitialRefreshConfig({ mapStateJSON: savedMap.mapStateJSON, From 22d027387131c8e5e32a3e88541a5348633a2865 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 25 Jun 2019 16:29:53 -0700 Subject: [PATCH 046/189] Changes QueryBar to SearchBar in the FilterEditor, adds a customSubmitButton to the SearchBar that passes the JSX prop down to the QueryBar --- .../search/search_bar/components/search_bar.tsx | 4 ++++ .../layer_panel/filter_editor/filter_editor.js | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index c25428604bc80..0ebe771266d2e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -75,6 +75,7 @@ interface Props { onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; onSaved?: (savedQuery: SavedQuery) => void; onSavedQueryUpdated: (savedQuery: SavedQuery) => void; + customSubmitButton?: any; } interface State { @@ -457,6 +458,9 @@ class SearchBarUI extends Component { onChange={this.onQueryBarChange} onLoadSavedQuery={this.onLoadSavedQuery} isDirty={this.isDirty()} + customSubmitButton={ + this.props.customSubmitButton ? this.props.customSubmitButton : undefined + } /> ) : ( '' diff --git a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js index 1e3617ff076a7..02d1f4d9667c2 100644 --- a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js @@ -23,7 +23,7 @@ import { indexPatternService } from '../../../kibana_services'; import { Storage } from 'ui/storage'; import { data } from 'plugins/data/setup'; -const { QueryBar } = data.query.ui; +const { SearchBar } = data.search.ui; const settings = chrome.getUiSettingsClient(); const localStorage = new Storage(window.localStorage); @@ -79,7 +79,9 @@ export class FilterEditor extends Component { this.props.setLayerQuery(this.props.layer.getId(), query); this._close(); } - + _onFiltersUpdated = () => { + return; + } _renderQueryPopover() { const layerQuery = this.props.layer.getQuery(); @@ -92,12 +94,17 @@ export class FilterEditor extends Component { anchorPosition="leftCenter" >
- Date: Wed, 26 Jun 2019 07:16:33 -0700 Subject: [PATCH 047/189] Adds saved_object type query to test expectation array --- test/api_integration/apis/saved_objects/export.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api_integration/apis/saved_objects/export.js b/test/api_integration/apis/saved_objects/export.js index 29856020cdb86..ca2b4894ea2d2 100644 --- a/test/api_integration/apis/saved_objects/export.js +++ b/test/api_integration/apis/saved_objects/export.js @@ -139,7 +139,7 @@ export default function ({ getService }) { statusCode: 400, error: 'Bad Request', message: 'child "type" fails because ["type" at position 0 fails because ' + - '["0" must be one of [config, index-pattern, visualization, search, dashboard, url]]]', + '["0" must be one of [config, query, index-pattern, visualization, search, dashboard, url]]]', validation: { source: 'payload', keys: ['type.0'], From 74aacc8d02ea4ca1930af83a313a480973efe6b0 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 26 Jun 2019 16:30:53 -0700 Subject: [PATCH 048/189] Allows Saved Queries to be saved and edited in Maps --- .../query_bar/components/saved_query_row.tsx | 22 +++++------ .../plugins/maps/public/angular/map.html | 3 ++ .../maps/public/angular/map_controller.js | 39 +++++++++++++++++++ 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 48f39171918d6..e823ca17f18de 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -51,14 +51,12 @@ export const SavedQueryRow: FunctionComponent = ({ rowContent = ( -

- Save changes to {savedQuery.title} -

+ + Save changes to query: {savedQuery.title} +
-

- Save as new -

+ Save as new
); @@ -66,12 +64,12 @@ export const SavedQueryRow: FunctionComponent = ({ rowContent = ( -

{savedQuery.title}

+ + {savedQuery.title} +
-

- Save as new -

+ Save as new
); @@ -84,8 +82,8 @@ export const SavedQueryRow: FunctionComponent = ({ ); } else { rowContent = ( - - Manage Saved Queries + + Manage Saved Queries ); } diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/legacy/plugins/maps/public/angular/map.html index 8f3671e19c35d..e3fc76148c356 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map.html +++ b/x-pack/legacy/plugins/maps/public/angular/map.html @@ -21,6 +21,9 @@ is-refresh-paused="refreshConfig.isPaused" refresh-interval="refreshConfig.interval" on-refresh-change="onRefreshChange" + saved-query="savedQuery" + on-saved="onQuerySaved" + on-saved-query-updated="onSavedQueryUpdated" >
diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 8becca8978c6b..35cca83267008 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -51,6 +51,7 @@ import { getInitialRefreshConfig } from './get_initial_refresh_config'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; import { data } from 'plugins/data/setup'; data.search.loadLegacyDirectives(); +const { savedQueryService } = data.search.services; const REACT_ANCHOR_DOM_ELEMENT_ID = 'react-maps-root'; @@ -119,6 +120,44 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage }; /* END QueryBar replacement with SearchBar */ + /* Saved Queries */ + $scope.onQuerySaved = savedQuery => { + $scope.savedQuery = savedQuery; + }; + + $scope.onSavedQueryUpdated = savedQuery => { + $scope.savedQuery = savedQuery; + }; + + const updateStateFromSavedQuery = (savedQuery) => { + $scope.query = savedQuery.attributes.query; + if (savedQuery.attributes.timefilter) { + $scope.time.setTime({ + from: savedQuery.attributes.timefilter.timeFrom, + to: savedQuery.attributes.timefilter.timeTo, + }); + if (savedQuery.attributes.timefilter.refreshInterval) { + $scope.refreshInterval = savedQuery.attributes.timefilter.refreshInterval; + } + } + syncAppAndGlobalState(); + }; + + $scope.$watch('state.savedQuery', newSavedQueryId => { + if (!newSavedQueryId) { + $scope.savedQuery = undefined; + return; + } + + savedQueryService.getSavedQuery(newSavedQueryId).then((savedQuery) => { + $scope.$evalAsync(() => { + $scope.savedQuery = savedQuery; + updateStateFromSavedQuery(savedQuery); + }); + }); + }); + /* end Saved Queries */ + $scope.refreshConfig = getInitialRefreshConfig({ mapStateJSON: savedMap.mapStateJSON, globalState: globalState, From 6b1ba4f46b757c8c8543f46dc58a0326e11d0170 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 27 Jun 2019 08:57:18 -0700 Subject: [PATCH 049/189] Deletes unused Eui component from saved_query_row --- .../data/public/query/query_bar/components/saved_query_row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index e823ca17f18de..65db1b4ba6245 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -18,7 +18,7 @@ */ import React, { FunctionComponent, Fragment } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiLink } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { SavedQueryAttributes } from '../../../search/search_bar'; import { Query } from '../index'; From d2d62eec3d9b8170f71e528c06754c1708474416 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 27 Jun 2019 16:18:23 -0700 Subject: [PATCH 050/189] Fixes maps nav bar to handle saved queries --- .../maps/public/angular/map_controller.js | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 35cca83267008..7c8b81c1e9a78 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -103,6 +103,31 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage mapStateJSON: savedMap.mapStateJSON, globalState: globalState, }); + + $scope.refreshConfig = getInitialRefreshConfig({ + mapStateJSON: savedMap.mapStateJSON, + globalState: globalState, + }); + syncAppAndGlobalState(); + + $scope.indexPatterns = []; + $scope.updateQueryAndDispatch = function ({ dateRange, query }) { + $scope.query = query; + $scope.time = dateRange; + syncAppAndGlobalState(); + + store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time })); + }; + $scope.onRefreshChange = function ({ isPaused, refreshInterval }) { + $scope.refreshConfig = { + isPaused, + interval: refreshInterval ? refreshInterval : $scope.refreshConfig.interval + }; + syncAppAndGlobalState(); + + store.dispatch(setRefreshConfig($scope.refreshConfig)); + }; + /* START QueryBar replacement with SearchBar Saved Queries required moving the query bar state caching to the search bar. Using the search bar instead of the query bar in Maps requires obscuring the filter functionality within the app. @@ -143,6 +168,15 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage syncAppAndGlobalState(); }; + $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { + if (!newSavedQuery) return; + $state.savedQuery = newSavedQuery.id; + + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } + }); + $scope.$watch('state.savedQuery', newSavedQueryId => { if (!newSavedQueryId) { $scope.savedQuery = undefined; @@ -157,31 +191,6 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage }); }); /* end Saved Queries */ - - $scope.refreshConfig = getInitialRefreshConfig({ - mapStateJSON: savedMap.mapStateJSON, - globalState: globalState, - }); - syncAppAndGlobalState(); - - $scope.indexPatterns = []; - $scope.updateQueryAndDispatch = function ({ dateRange, query }) { - $scope.query = query; - $scope.time = dateRange; - syncAppAndGlobalState(); - - store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time })); - }; - $scope.onRefreshChange = function ({ isPaused, refreshInterval }) { - $scope.refreshConfig = { - isPaused, - interval: refreshInterval ? refreshInterval : $scope.refreshConfig.interval - }; - syncAppAndGlobalState(); - - store.dispatch(setRefreshConfig($scope.refreshConfig)); - }; - function renderMap() { // clear old UI state store.dispatch(setSelectedLayer(null)); From 2beb9270a25bbada0d833d445b38ec0222f79d46 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 28 Jun 2019 13:28:04 -0700 Subject: [PATCH 051/189] Loads a saved query into the layer filter query bar, allows saving changes to the existing query, allows saving new queries. --- .../filter_editor/filter_editor.js | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js index 02d1f4d9667c2..01fd1ee1c7831 100644 --- a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js @@ -24,6 +24,7 @@ import { Storage } from 'ui/storage'; import { data } from 'plugins/data/setup'; const { SearchBar } = data.search.ui; +const { savedQueryService } = data.search.services; const settings = chrome.getUiSettingsClient(); const localStorage = new Storage(window.localStorage); @@ -33,6 +34,7 @@ export class FilterEditor extends Component { state = { isPopoverOpen: false, indexPatterns: [], + savedQuery: null, } componentDidMount() { @@ -40,6 +42,22 @@ export class FilterEditor extends Component { this._loadIndexPatterns(); } + componentDidUpdate(prevProps, prevState) { + const prevQuery = prevProps.layer.getQuery(); + const currentQuery = this.props.layer.getQuery(); + const prevSavedQuery = prevState.savedQuery; + const currentSavedQuery = this.state.savedQuery; + if (prevSavedQuery !== currentSavedQuery) { + console.log(`the savedQuery prop has changed from ${prevSavedQuery} to ${currentSavedQuery}`); + } + if (prevQuery !== currentQuery) { + console.log(`the query prop has changed from ${prevQuery} to ${currentQuery}`); + } + if (prevState.savedQuery !== this.state.savedQuery) { + console.log(`the incomming savedQuery ${prevState.savedQuery} is different to the one on state ${this.state.savedQuery}`); + } + } + componentWillUnmount() { this._isMounted = false; } @@ -79,9 +97,43 @@ export class FilterEditor extends Component { this.props.setLayerQuery(this.props.layer.getId(), query); this._close(); } + _onFiltersUpdated = () => { return; } + + _onQuerySaved = (savedQuery) => { + console.log('savedQuery received from search bar after save:', savedQuery); + const oldSavedQuery = this.state.savedQuery; + if (!savedQuery) return; + // execute the code that triggers on the state.savedQuery watcher in map_controller + this._getSavedQueryFromService(savedQuery); + this.setState({ savedQuery: savedQuery.id }); + if (savedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + this.props.setLayerQuery(this.props.layer.getId(), savedQuery.attributes.query); + } + } + + _getSavedQueryFromService = async (savedQuery) => { + if (!savedQuery.id) return; + const newSavedQuery = await savedQueryService.getSavedQuery(savedQuery.id); + await this.setState({ savedQuery: newSavedQuery }); + this.props.setLayerQuery(this.props.layer.getId(), newSavedQuery.attributes.query); + + } + + _onSavedQueryChange = (changedSavedQuery) => { + console.log('changedSavedQuery received from search bar after a change:', changedSavedQuery); + const oldSavedQuery = this.state.savedQuery; + if (!changedSavedQuery) return; + // execute the code that triggers on the state.savedQuery watcher in map_controller + this._getSavedQueryFromService(changedSavedQuery); + this.setState({ savedQuery: changedSavedQuery.id }); + if (changedSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + this.props.setLayerQuery(this.props.layer.getId(), changedSavedQuery.attributes.query); + } + } + _renderQueryPopover() { const layerQuery = this.props.layer.getQuery(); @@ -106,6 +158,9 @@ export class FilterEditor extends Component { showFilterBar={false} showQueryBar={true} store={localStorage} + savedQuery={this.state.savedQuery} + onSaved={this._onQuerySaved} + onSavedQueryUpdated={this._onSavedQueryChange} customSubmitButton={ Date: Fri, 28 Jun 2019 14:22:28 -0700 Subject: [PATCH 052/189] Layer filter query search bar closes after selecting and saving a saved query debug --- .../filter_editor/filter_editor.js | 64 +++++++++---------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js index 01fd1ee1c7831..9dd4b57628a55 100644 --- a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js @@ -42,21 +42,21 @@ export class FilterEditor extends Component { this._loadIndexPatterns(); } - componentDidUpdate(prevProps, prevState) { - const prevQuery = prevProps.layer.getQuery(); - const currentQuery = this.props.layer.getQuery(); - const prevSavedQuery = prevState.savedQuery; - const currentSavedQuery = this.state.savedQuery; - if (prevSavedQuery !== currentSavedQuery) { - console.log(`the savedQuery prop has changed from ${prevSavedQuery} to ${currentSavedQuery}`); - } - if (prevQuery !== currentQuery) { - console.log(`the query prop has changed from ${prevQuery} to ${currentQuery}`); - } - if (prevState.savedQuery !== this.state.savedQuery) { - console.log(`the incomming savedQuery ${prevState.savedQuery} is different to the one on state ${this.state.savedQuery}`); - } - } + // componentDidUpdate(prevProps, prevState) { + // const prevQuery = prevProps.layer.getQuery(); + // const currentQuery = this.props.layer.getQuery(); + // const prevSavedQuery = prevState.savedQuery; + // const currentSavedQuery = this.state.savedQuery; + // if (prevSavedQuery !== currentSavedQuery) { + // console.log(`the savedQuery prop has changed from ${prevSavedQuery} to ${currentSavedQuery}`); + // } + // if (prevQuery !== currentQuery) { + // console.log(`the query prop has changed from ${prevQuery} to ${currentQuery}`); + // } + // if (prevState.savedQuery !== this.state.savedQuery) { + // console.log(`the incomming savedQuery ${prevState.savedQuery} is different to the one on state ${this.state.savedQuery}`); + // } + // } componentWillUnmount() { this._isMounted = false; @@ -93,6 +93,10 @@ export class FilterEditor extends Component { this.setState({ isPopoverOpen: false }); } + _open = () => { + this.setState({ isPopoverOpen: true }); + } + _onQueryChange = ({ query }) => { this.props.setLayerQuery(this.props.layer.getId(), query); this._close(); @@ -103,15 +107,7 @@ export class FilterEditor extends Component { } _onQuerySaved = (savedQuery) => { - console.log('savedQuery received from search bar after save:', savedQuery); - const oldSavedQuery = this.state.savedQuery; - if (!savedQuery) return; - // execute the code that triggers on the state.savedQuery watcher in map_controller - this._getSavedQueryFromService(savedQuery); - this.setState({ savedQuery: savedQuery.id }); - if (savedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - this.props.setLayerQuery(this.props.layer.getId(), savedQuery.attributes.query); - } + this._addOrUpdateSavedQuery(savedQuery, this.state.savedQuery); } _getSavedQueryFromService = async (savedQuery) => { @@ -119,19 +115,21 @@ export class FilterEditor extends Component { const newSavedQuery = await savedQueryService.getSavedQuery(savedQuery.id); await this.setState({ savedQuery: newSavedQuery }); this.props.setLayerQuery(this.props.layer.getId(), newSavedQuery.attributes.query); - } _onSavedQueryChange = (changedSavedQuery) => { - console.log('changedSavedQuery received from search bar after a change:', changedSavedQuery); - const oldSavedQuery = this.state.savedQuery; - if (!changedSavedQuery) return; - // execute the code that triggers on the state.savedQuery watcher in map_controller - this._getSavedQueryFromService(changedSavedQuery); - this.setState({ savedQuery: changedSavedQuery.id }); - if (changedSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - this.props.setLayerQuery(this.props.layer.getId(), changedSavedQuery.attributes.query); + this._addOrUpdateSavedQuery(changedSavedQuery, this.state.savedQuery); + } + + _addOrUpdateSavedQuery = (currentSavedQuery, oldSavedQuery) => { + if (!currentSavedQuery) return; + this._getSavedQueryFromService(currentSavedQuery); + // this.setState({ currentSavedQuery: currentSavedQuery.id }); + this.setState({ savedQuery: currentSavedQuery }); + if (currentSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + this.props.setLayerQuery(this.props.layer.getId(), currentSavedQuery.attributes.query); } + this._renderOpenButton(); } _renderQueryPopover() { From 52a49b746be65104ead4bb1b8cfeac45af3b4dc2 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 28 Jun 2019 14:32:50 -0700 Subject: [PATCH 053/189] Changes QueryBar to SearchBar in maps layer_panel join_editor where_expression --- .../layer_panel/filter_editor/filter_editor.js | 2 ++ .../join_editor/resources/where_expression.js | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js index 02d1f4d9667c2..bd114e0d23f73 100644 --- a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js @@ -79,9 +79,11 @@ export class FilterEditor extends Component { this.props.setLayerQuery(this.props.layer.getId(), query); this._close(); } + _onFiltersUpdated = () => { return; } + _renderQueryPopover() { const layerQuery = this.props.layer.getQuery(); diff --git a/x-pack/legacy/plugins/maps/public/components/layer_panel/join_editor/resources/where_expression.js b/x-pack/legacy/plugins/maps/public/components/layer_panel/join_editor/resources/where_expression.js index 922b74d36c386..3cfea41d6ce0d 100644 --- a/x-pack/legacy/plugins/maps/public/components/layer_panel/join_editor/resources/where_expression.js +++ b/x-pack/legacy/plugins/maps/public/components/layer_panel/join_editor/resources/where_expression.js @@ -14,7 +14,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { data } from 'plugins/data/setup'; -const { QueryBar } = data.query.ui; +const { SearchBar } = data.search.ui; import { Storage } from 'ui/storage'; const settings = chrome.getUiSettingsClient(); @@ -43,6 +43,10 @@ export class WhereExpression extends Component { this._closePopover(); } + _onFiltersUpdated = () => { + return; + } + render() { const { whereQuery, indexPattern } = this.props; const expressionValue = whereQuery && whereQuery.query @@ -72,11 +76,16 @@ export class WhereExpression extends Component { } >
- Date: Fri, 28 Jun 2019 16:22:45 -0700 Subject: [PATCH 054/189] Enables saving queries, saving changes to saved queries, using saved queries and saving a new saved query from an existing query in layer filters --- .../filter_editor/filter_editor.js | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js index 9dd4b57628a55..a1221361112ff 100644 --- a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js @@ -42,22 +42,6 @@ export class FilterEditor extends Component { this._loadIndexPatterns(); } - // componentDidUpdate(prevProps, prevState) { - // const prevQuery = prevProps.layer.getQuery(); - // const currentQuery = this.props.layer.getQuery(); - // const prevSavedQuery = prevState.savedQuery; - // const currentSavedQuery = this.state.savedQuery; - // if (prevSavedQuery !== currentSavedQuery) { - // console.log(`the savedQuery prop has changed from ${prevSavedQuery} to ${currentSavedQuery}`); - // } - // if (prevQuery !== currentQuery) { - // console.log(`the query prop has changed from ${prevQuery} to ${currentQuery}`); - // } - // if (prevState.savedQuery !== this.state.savedQuery) { - // console.log(`the incomming savedQuery ${prevState.savedQuery} is different to the one on state ${this.state.savedQuery}`); - // } - // } - componentWillUnmount() { this._isMounted = false; } @@ -115,21 +99,21 @@ export class FilterEditor extends Component { const newSavedQuery = await savedQueryService.getSavedQuery(savedQuery.id); await this.setState({ savedQuery: newSavedQuery }); this.props.setLayerQuery(this.props.layer.getId(), newSavedQuery.attributes.query); + this._close(); } _onSavedQueryChange = (changedSavedQuery) => { this._addOrUpdateSavedQuery(changedSavedQuery, this.state.savedQuery); } - _addOrUpdateSavedQuery = (currentSavedQuery, oldSavedQuery) => { + _addOrUpdateSavedQuery = async (currentSavedQuery, oldSavedQuery) => { if (!currentSavedQuery) return; - this._getSavedQueryFromService(currentSavedQuery); - // this.setState({ currentSavedQuery: currentSavedQuery.id }); - this.setState({ savedQuery: currentSavedQuery }); + await this._getSavedQueryFromService(currentSavedQuery); + await this.setState({ savedQuery: currentSavedQuery }); if (currentSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { this.props.setLayerQuery(this.props.layer.getId(), currentSavedQuery.attributes.query); + this._close(); } - this._renderOpenButton(); } _renderQueryPopover() { From d5a152b0b10536c7d3d0dc7b72f342ebf7b5ee53 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 28 Jun 2019 16:35:13 -0700 Subject: [PATCH 055/189] Enables saved queries to be used, created and modified in the where clause component, deletes unused method in filter_editor --- .../filter_editor/filter_editor.js | 4 --- .../join_editor/resources/where_expression.js | 32 ++++++++++++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js index a1221361112ff..cd36267f74b0f 100644 --- a/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/legacy/plugins/maps/public/components/layer_panel/filter_editor/filter_editor.js @@ -77,10 +77,6 @@ export class FilterEditor extends Component { this.setState({ isPopoverOpen: false }); } - _open = () => { - this.setState({ isPopoverOpen: true }); - } - _onQueryChange = ({ query }) => { this.props.setLayerQuery(this.props.layer.getId(), query); this._close(); diff --git a/x-pack/legacy/plugins/maps/public/components/layer_panel/join_editor/resources/where_expression.js b/x-pack/legacy/plugins/maps/public/components/layer_panel/join_editor/resources/where_expression.js index 3cfea41d6ce0d..b7ec94c627f76 100644 --- a/x-pack/legacy/plugins/maps/public/components/layer_panel/join_editor/resources/where_expression.js +++ b/x-pack/legacy/plugins/maps/public/components/layer_panel/join_editor/resources/where_expression.js @@ -19,11 +19,12 @@ import { Storage } from 'ui/storage'; const settings = chrome.getUiSettingsClient(); const localStorage = new Storage(window.localStorage); - +const { savedQueryService } = data.search.services; export class WhereExpression extends Component { state = { isPopoverOpen: false, + savedQuery: null, }; _togglePopover = () => { @@ -47,6 +48,32 @@ export class WhereExpression extends Component { return; } + _onQuerySaved = (savedQuery) => { + this._addOrUpdateSavedQuery(savedQuery, this.state.savedQuery); + } + + _getSavedQueryFromService = async (savedQuery) => { + if (!savedQuery.id) return; + const newSavedQuery = await savedQueryService.getSavedQuery(savedQuery.id); + await this.setState({ savedQuery: newSavedQuery }); + this.props.onChange(newSavedQuery.attributes.query); + this._closePopover(); + } + + _onSavedQueryChange = (changedSavedQuery) => { + this._addOrUpdateSavedQuery(changedSavedQuery, this.state.savedQuery); + } + + _addOrUpdateSavedQuery = async (currentSavedQuery, oldSavedQuery) => { + if (!currentSavedQuery) return; + await this._getSavedQueryFromService(currentSavedQuery); + await this.setState({ savedQuery: currentSavedQuery }); + if (currentSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + this.props.onChange(currentSavedQuery.attributes.query); + this._closePopover(); + } + } + render() { const { whereQuery, indexPattern } = this.props; const expressionValue = whereQuery && whereQuery.query @@ -88,6 +115,9 @@ export class WhereExpression extends Component { onFiltersUpdated={this._onFiltersUpdated} indexPatterns={[indexPattern]} store={localStorage} + savedQuery={this.state.savedQuery} + onSaved={this._onQuerySaved} + onSavedQueryUpdated={this._onSavedQueryChange} customSubmitButton={ Date: Mon, 8 Jul 2019 15:37:14 -0700 Subject: [PATCH 056/189] fixes setting time filter in map_controller --- x-pack/legacy/plugins/maps/public/angular/map_controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 7c8b81c1e9a78..e88f3a0992138 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -157,10 +157,10 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage const updateStateFromSavedQuery = (savedQuery) => { $scope.query = savedQuery.attributes.query; if (savedQuery.attributes.timefilter) { - $scope.time.setTime({ + $scope.time = { from: savedQuery.attributes.timefilter.timeFrom, to: savedQuery.attributes.timefilter.timeTo, - }); + }; if (savedQuery.attributes.timefilter.refreshInterval) { $scope.refreshInterval = savedQuery.attributes.timefilter.refreshInterval; } From ecb89257c57e0380f929fc4843012a553dc35b25 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 8 Jul 2019 16:28:25 -0700 Subject: [PATCH 057/189] Conditionally shows filters and time filter option in the save query form --- .../search_bar/components/save_query_form.tsx | 57 ++++++++++++------- .../search_bar/components/search_bar.tsx | 4 ++ 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index b32f88f75a288..dbf20ff29f6fb 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -38,6 +38,8 @@ interface Props { savedQuery?: SavedQueryAttributes; onSave: (savedQueryMeta: SavedQueryMeta) => void; onClose: () => void; + showFilterOption: boolean; + showTimeFilterOption: boolean; } export interface SavedQueryMeta { @@ -47,7 +49,13 @@ export interface SavedQueryMeta { shouldIncludeTimefilter: boolean; } -export const SaveQueryForm: FunctionComponent = ({ savedQuery, onSave, onClose }) => { +export const SaveQueryForm: FunctionComponent = ({ + savedQuery, + onSave, + onClose, + showFilterOption, + showTimeFilterOption, +}) => { const [title, setTitle] = useState(savedQuery ? savedQuery.title : ''); const [description, setDescription] = useState(savedQuery ? savedQuery.description : ''); const [shouldIncludeFilters, setShouldIncludeFilters] = useState( @@ -56,6 +64,8 @@ export const SaveQueryForm: FunctionComponent = ({ savedQuery, onSave, on const [shouldIncludeTimefilter, setIncludeTimefilter] = useState( !!(savedQuery && savedQuery.timefilter) ); + const [shouldShowFiltersOption] = useState(!!showFilterOption); + const [shouldShowTimeFilterOption] = useState(!!showTimeFilterOption); const saveQueryForm = ( @@ -78,28 +88,31 @@ export const SaveQueryForm: FunctionComponent = ({ savedQuery, onSave, on }} /> + {shouldShowFiltersOption && ( + + { + setShouldIncludeFilters(!shouldIncludeFilters); + }} + /> + + )} - - { - setShouldIncludeFilters(!shouldIncludeFilters); - }} - /> - - - - { - setIncludeTimefilter(!shouldIncludeTimefilter); - }} - /> - + {shouldShowTimeFilterOption && ( + + { + setIncludeTimefilter(!shouldIncludeTimefilter); + }} + /> + + )} ); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 0ebe771266d2e..0999ff924d235 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -496,12 +496,16 @@ class SearchBarUI extends Component { savedQuery={this.props.savedQuery ? this.props.savedQuery.attributes : undefined} onSave={this.onSave} onClose={() => this.setState({ showSaveQueryModal: false })} + showFilterOption={this.props.showFilterBar} + showTimeFilterOption={this.props.showDatePicker} /> ) : null} {this.state.showSaveNewQueryModal ? ( this.onSave(savedQueryMeta, true)} onClose={() => this.setState({ showSaveNewQueryModal: false })} + showFilterOption={this.props.showFilterBar} + showTimeFilterOption={this.props.showDatePicker} /> ) : null}
From ad667b31478e93ae5eceb102068c40a8a575ee31 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 9 Jul 2019 08:21:39 -0700 Subject: [PATCH 058/189] fixes show filter options type --- .../public/search/search_bar/components/save_query_form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index dbf20ff29f6fb..b8f82dd8f71c0 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -39,7 +39,7 @@ interface Props { onSave: (savedQueryMeta: SavedQueryMeta) => void; onClose: () => void; showFilterOption: boolean; - showTimeFilterOption: boolean; + showTimeFilterOption: boolean | undefined; } export interface SavedQueryMeta { From f86b201bfba61631ff00b5c082642ac747cea6eb Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 9 Jul 2019 12:19:26 -0700 Subject: [PATCH 059/189] corrects routing to saved objects --- .../public/query/query_bar/components/saved_query_row.tsx | 7 ++++++- .../search/search_bar/components/save_query_form.tsx | 8 +++----- .../public/search/search_bar/components/search_bar.tsx | 7 ++++++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 65db1b4ba6245..675e598eb4b15 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -19,6 +19,7 @@ import React, { FunctionComponent, Fragment } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; +import chrome from 'ui/chrome'; import { SavedQueryAttributes } from '../../../search/search_bar'; import { Query } from '../index'; @@ -83,7 +84,11 @@ export const SavedQueryRow: FunctionComponent = ({ } else { rowContent = ( - Manage Saved Queries + + Manage Saved Queries + ); } diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index b8f82dd8f71c0..88979fe988f5f 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -38,7 +38,7 @@ interface Props { savedQuery?: SavedQueryAttributes; onSave: (savedQueryMeta: SavedQueryMeta) => void; onClose: () => void; - showFilterOption: boolean; + showFilterOption: boolean | undefined; showTimeFilterOption: boolean | undefined; } @@ -64,8 +64,6 @@ export const SaveQueryForm: FunctionComponent = ({ const [shouldIncludeTimefilter, setIncludeTimefilter] = useState( !!(savedQuery && savedQuery.timefilter) ); - const [shouldShowFiltersOption] = useState(!!showFilterOption); - const [shouldShowTimeFilterOption] = useState(!!showTimeFilterOption); const saveQueryForm = ( @@ -88,7 +86,7 @@ export const SaveQueryForm: FunctionComponent = ({ }} /> - {shouldShowFiltersOption && ( + {!!showFilterOption && ( = ({ )} - {shouldShowTimeFilterOption && ( + {!!showTimeFilterOption && ( {
- {}} href={`#/management/kibana/objects`}> + Date: Tue, 9 Jul 2019 12:21:19 -0700 Subject: [PATCH 060/189] typing --- .../data/public/search/search_bar/components/search_bar.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index fa8efe02c6a24..64ec9d09e7c15 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -33,7 +33,6 @@ import classNames from 'classnames'; import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; import chrome from 'ui/chrome'; -import rison from 'rison-node'; import { IndexPattern } from 'ui/index_patterns'; import { Storage } from 'ui/storage'; import { get, isEqual } from 'lodash'; From 5218ab56d450e2a5bb45a7fed36e028d1ecbe81c Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 9 Jul 2019 16:34:47 -0700 Subject: [PATCH 061/189] begins work on the clear saved query functionality --- .../query/query_bar/components/query_bar.tsx | 2 + .../query_bar/components/query_bar_input.tsx | 3 ++ .../query_bar/components/saved_query_row.tsx | 44 +++++++++++++------ .../search_bar/components/search_bar.tsx | 11 +++++ 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 42c92f1e4ad8b..d8ee6cea33c14 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -72,6 +72,7 @@ interface Props { onSaveNew: () => void; onLoadSavedQuery: (savedQuery: SavedQuery) => void; isDirty: boolean; + onClose: () => void; } interface State { @@ -190,6 +191,7 @@ export class QueryBarUI extends Component { onSaveNew={this.props.onSaveNew} onLoadSavedQuery={this.props.onLoadSavedQuery} isDirty={this.props.isDirty} + onClose={this.props.onClose} /> {this.renderUpdateButton()} diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 4eaa9881fd8b0..6814685f4866c 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -64,6 +64,7 @@ interface Props { onSave?: () => void; onSaveNew?: () => void; isDirty?: boolean; + onClose?: () => void; } interface State { @@ -481,6 +482,7 @@ export class QueryBarInputUI extends Component { 'onSaveNew', 'onLoadSavedQuery', 'isDirty', + 'onClose', ]); return ( @@ -565,6 +567,7 @@ export class QueryBarInputUI extends Component { onSave={this.props.onSave} onSaveNew={this.props.onSaveNew} isDirty={this.props.isDirty} + onClose={this.props.onClose} /> ) : null } diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 675e598eb4b15..7ff7b2c102364 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -18,7 +18,7 @@ */ import React, { FunctionComponent, Fragment } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiIcon } from '@elastic/eui'; import chrome from 'ui/chrome'; import { SavedQueryAttributes } from '../../../search/search_bar'; import { Query } from '../index'; @@ -29,6 +29,7 @@ interface Props { onSave: () => void; onSaveNew: () => void; isDirty: boolean; + onClose: () => void; } export interface SavedQueryDetails { @@ -45,17 +46,25 @@ export const SavedQueryRow: FunctionComponent = ({ onSave, onSaveNew, isDirty, + onClose, }) => { let rowContent; if (savedQuery) { if (isDirty) { rowContent = ( - - - Save changes to query: {savedQuery.title} - - + + + + Save changes to query: {savedQuery.title} + + + + + + + + Save as new @@ -64,11 +73,18 @@ export const SavedQueryRow: FunctionComponent = ({ } else { rowContent = ( - - - {savedQuery.title} - - + + + + {savedQuery.title} + + + + + + + + Save as new @@ -94,8 +110,8 @@ export const SavedQueryRow: FunctionComponent = ({ } return ( - - {rowContent} - + + {rowContent} + ); }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 64ec9d09e7c15..b1ae775dd668d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -77,6 +77,7 @@ interface Props { onSaved?: (savedQuery: SavedQuery) => void; onSavedQueryUpdated: (savedQuery: SavedQuery) => void; customSubmitButton?: any; + onClose?: () => void; } interface State { @@ -286,6 +287,15 @@ class SearchBarUI extends Component { }); }; + public onCloseSavedQuery = () => { + this.setState({ query: { query: '', language: this.state.query.language } }); + if (this.state.isFiltersVisible && this.props.filters.length > 0) { + // I want to trigger the filter's removeAll method but it is private. + alert('clear the filters'); + } + // we also need to clear the saved query from the saved query row. + }; + public onQueryBarChange = (queryAndDateRange: { dateRange: DateRange; query: Query }) => { this.setState({ query: queryAndDateRange.query, @@ -465,6 +475,7 @@ class SearchBarUI extends Component { customSubmitButton={ this.props.customSubmitButton ? this.props.customSubmitButton : undefined } + onClose={this.onCloseSavedQuery} /> ) : ( '' From 9de64a6b402e02f55acd9d2b055966579123624a Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 9 Jul 2019 16:41:49 -0700 Subject: [PATCH 062/189] fixes onClose type --- .../public/query/query_bar/components/query_bar.test.tsx | 2 ++ .../public/query/query_bar/components/query_bar_input.tsx | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx index 37936af8667fa..9c262599fbaa5 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx @@ -85,6 +85,7 @@ describe('QueryBar', () => { onSaveNew={noop} onLoadSavedQuery={noop} isDirty={false} + onClose={noop} /> ); @@ -107,6 +108,7 @@ describe('QueryBar', () => { onSaveNew={noop} onLoadSavedQuery={noop} isDirty={false} + onClose={noop} /> ); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 6814685f4866c..8d170cedc44b8 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -560,7 +560,10 @@ export class QueryBarInputUI extends Component { onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} append={ - this.props.onSave && this.props.onSaveNew && this.props.isDirty !== undefined ? ( + this.props.onSave && + this.props.onSaveNew && + this.props.isDirty !== undefined && + this.props.onClose ? ( Date: Wed, 10 Jul 2019 10:25:18 -0700 Subject: [PATCH 063/189] updates query_bar snapshot --- .../query_bar/components/__snapshots__/query_bar.test.tsx.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap index 9d9721efa4521..04330ebc3b997 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap @@ -31,6 +31,7 @@ exports[`QueryBar Should render the given query 1`] = ` } isDirty={false} onChange={[Function]} + onClose={[Function]} onLoadSavedQuery={[Function]} onSave={[Function]} onSaveNew={[Function]} From 448dcfa79f2a3b6f459eef252e13b1dadbf3c9c3 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 10 Jul 2019 14:31:16 -0700 Subject: [PATCH 064/189] implements close savedQuery in Discover --- .../query_bar/components/query_bar.test.tsx | 4 +-- .../query/query_bar/components/query_bar.tsx | 4 +-- .../query_bar/components/query_bar_input.tsx | 11 +++----- .../query_bar/components/saved_query_row.tsx | 8 +++--- .../search_bar/components/search_bar.tsx | 13 ++-------- .../search/search_bar/directive/index.js | 1 + .../public/discover/controllers/discover.js | 26 ++++++++++++++----- .../kibana/public/discover/index.html | 1 + 8 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx index 9c262599fbaa5..b5271aee9ba74 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx @@ -85,7 +85,7 @@ describe('QueryBar', () => { onSaveNew={noop} onLoadSavedQuery={noop} isDirty={false} - onClose={noop} + onClearSavedQuery={noop} /> ); @@ -108,7 +108,7 @@ describe('QueryBar', () => { onSaveNew={noop} onLoadSavedQuery={noop} isDirty={false} - onClose={noop} + onClearSavedQuery={noop} /> ); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index d8ee6cea33c14..be5e02bf7a404 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -72,7 +72,7 @@ interface Props { onSaveNew: () => void; onLoadSavedQuery: (savedQuery: SavedQuery) => void; isDirty: boolean; - onClose: () => void; + onClearSavedQuery: () => void; } interface State { @@ -191,7 +191,7 @@ export class QueryBarUI extends Component { onSaveNew={this.props.onSaveNew} onLoadSavedQuery={this.props.onLoadSavedQuery} isDirty={this.props.isDirty} - onClose={this.props.onClose} + onClearSavedQuery={this.props.onClearSavedQuery} /> {this.renderUpdateButton()} diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 8d170cedc44b8..a66f5255d8d02 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -64,7 +64,7 @@ interface Props { onSave?: () => void; onSaveNew?: () => void; isDirty?: boolean; - onClose?: () => void; + onClearSavedQuery: () => void; } interface State { @@ -482,7 +482,7 @@ export class QueryBarInputUI extends Component { 'onSaveNew', 'onLoadSavedQuery', 'isDirty', - 'onClose', + 'onClearSavedQuery', ]); return ( @@ -560,17 +560,14 @@ export class QueryBarInputUI extends Component { onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} append={ - this.props.onSave && - this.props.onSaveNew && - this.props.isDirty !== undefined && - this.props.onClose ? ( + this.props.onSave && this.props.onSaveNew && this.props.isDirty !== undefined ? ( ) : null } diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 7ff7b2c102364..e650a9302b206 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -29,7 +29,7 @@ interface Props { onSave: () => void; onSaveNew: () => void; isDirty: boolean; - onClose: () => void; + onClearSavedQuery: () => void; } export interface SavedQueryDetails { @@ -46,7 +46,7 @@ export const SavedQueryRow: FunctionComponent = ({ onSave, onSaveNew, isDirty, - onClose, + onClearSavedQuery, }) => { let rowContent; if (savedQuery) { @@ -60,7 +60,7 @@ export const SavedQueryRow: FunctionComponent = ({ - + @@ -80,7 +80,7 @@ export const SavedQueryRow: FunctionComponent = ({ - + diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index b1ae775dd668d..98a07702e685e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -76,8 +76,8 @@ interface Props { onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; onSaved?: (savedQuery: SavedQuery) => void; onSavedQueryUpdated: (savedQuery: SavedQuery) => void; + onClearSavedQuery: () => void; customSubmitButton?: any; - onClose?: () => void; } interface State { @@ -287,15 +287,6 @@ class SearchBarUI extends Component { }); }; - public onCloseSavedQuery = () => { - this.setState({ query: { query: '', language: this.state.query.language } }); - if (this.state.isFiltersVisible && this.props.filters.length > 0) { - // I want to trigger the filter's removeAll method but it is private. - alert('clear the filters'); - } - // we also need to clear the saved query from the saved query row. - }; - public onQueryBarChange = (queryAndDateRange: { dateRange: DateRange; query: Query }) => { this.setState({ query: queryAndDateRange.query, @@ -475,7 +466,7 @@ class SearchBarUI extends Component { customSubmitButton={ this.props.customSubmitButton ? this.props.customSubmitButton : undefined } - onClose={this.onCloseSavedQuery} + onClearSavedQuery={this.props.onClearSavedQuery} /> ) : ( '' diff --git a/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js b/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js index e84233a11c464..b60553f57ee0f 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js +++ b/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js @@ -39,6 +39,7 @@ export function setupDirective() { ['onRefreshChange', { watchDepth: 'reference' }], ['onSaved', { watchDepth: 'reference' }], ['onSavedQueryUpdated', { watchDepth: 'reference' }], + ['onClearSavedQuery', { watchDepth: 'reference' }], ['indexPatterns', { watchDepth: 'collection' }], ['filters', { watchDepth: 'collection' }], diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 0d34f5acff1f5..e10be75773eb1 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -911,6 +911,10 @@ function discoverController( $scope.savedQuery = savedQuery; }; + $scope.onClearSavedQuery = () => { + $scope.savedQuery = undefined; + }; + const updateStateFromSavedQuery = (savedQuery) => { $state.query = savedQuery.attributes.query; queryFilter.setFilters(savedQuery.attributes.filters || []); @@ -929,12 +933,22 @@ function discoverController( }; $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { - if (!newSavedQuery) return; - $state.savedQuery = newSavedQuery.id; - $state.save(); - - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); + if (!newSavedQuery) { + $state.savedQuery = undefined; + $state.query = { + query: '', + language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') + }; + queryFilter.setFilters([]); + $state.save(); + $scope.fetch(); + } else { + $state.savedQuery = newSavedQuery.id; + $state.save(); + + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } } }); diff --git a/src/legacy/core_plugins/kibana/public/discover/index.html b/src/legacy/core_plugins/kibana/public/discover/index.html index 9e4e4afd6869b..827050ea6d519 100644 --- a/src/legacy/core_plugins/kibana/public/discover/index.html +++ b/src/legacy/core_plugins/kibana/public/discover/index.html @@ -51,6 +51,7 @@

From aecfc189cd79a773148732604f0b70ed38840601 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 10 Jul 2019 14:50:10 -0700 Subject: [PATCH 065/189] Updates query_bar snapshot --- .../query_bar/components/__snapshots__/query_bar.test.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap index 04330ebc3b997..249630a7068cd 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap @@ -31,7 +31,7 @@ exports[`QueryBar Should render the given query 1`] = ` } isDirty={false} onChange={[Function]} - onClose={[Function]} + onClearSavedQuery={[Function]} onLoadSavedQuery={[Function]} onSave={[Function]} onSaveNew={[Function]} From 703c38e8f809d3aae034c5efab57a4783c8f2c51 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 11 Jul 2019 12:03:03 -0400 Subject: [PATCH 066/189] Support close button in Visualize --- .../public/visualize/editor/editor.html | 1 + .../kibana/public/visualize/editor/editor.js | 24 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html index 291655ce778a8..afad8c242bf39 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html @@ -48,6 +48,7 @@ on-refresh-change="onRefreshChange" on-saved="onQuerySaved" on-saved-query-updated="onSavedQueryUpdated" + on-clear-saved-query="onClearSavedQuery" >
diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index c6aed9be5d865..11f6adea38b8b 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -476,6 +476,10 @@ function VisEditor( $scope.savedQuery = savedQuery; }; + $scope.onClearSavedQuery = () => { + $scope.savedQuery = undefined; + }; + const updateStateFromSavedQuery = (savedQuery) => { $state.query = savedQuery.attributes.query; queryFilter.setFilters(savedQuery.attributes.filters || []); @@ -494,12 +498,22 @@ function VisEditor( }; $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { - if (!newSavedQuery) return; - $state.savedQuery = newSavedQuery.id; - $state.save(); + if (!newSavedQuery) { + $state.savedQuery = undefined; + $state.query = { + query: '', + language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') + }; + queryFilter.setFilters([]); + $state.save(); + $scope.fetch(); + } else { + $state.savedQuery = newSavedQuery.id; + $state.save(); - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } } }); From 47cd2f38dc0fc3f6986824161c4973836fadfab3 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 11 Jul 2019 12:10:23 -0400 Subject: [PATCH 067/189] Support close button in Dashboard --- .../public/dashboard/dashboard_app.html | 1 + .../kibana/public/dashboard/dashboard_app.tsx | 1 + .../dashboard/dashboard_app_controller.tsx | 25 ++++++++++++++++--- .../dashboard/dashboard_state_manager.ts | 2 +- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html index 805afdce5842a..3f4bbb3ed5a4f 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html @@ -26,6 +26,7 @@ on-refresh-change="onRefreshChange" on-saved="onQuerySaved" on-saved-query-updated="onSavedQueryUpdated" + on-clear-saved-query="onClearSavedQuery" >
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 956bf4ea4e614..cb2afefd8af3a 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -101,6 +101,7 @@ export interface DashboardAppScope extends ng.IScope { onApplyFilters: (filters: Filter[]) => void; onQuerySaved: (savedQuery: SavedQuery) => void; onSavedQueryUpdated: (savedQuery: SavedQuery) => void; + onClearSavedQuery: () => void; topNavMenu: any; showFilterBar: () => boolean; showAddPanel: any; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index 83647aa42f9db..fad0f8ded5acd 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -298,6 +298,10 @@ export class DashboardAppController { $scope.savedQuery = savedQuery; }; + $scope.onClearSavedQuery = () => { + $scope.savedQuery = undefined; + }; + const updateStateFromSavedQuery = (savedQuery: SavedQuery) => { queryFilter.setFilters(savedQuery.attributes.filters || []); dashboardStateManager.applyFilters( @@ -317,11 +321,24 @@ export class DashboardAppController { }; $scope.$watch('savedQuery', (newSavedQuery: SavedQuery, oldSavedQuery: SavedQuery) => { - if (!newSavedQuery) return; - dashboardStateManager.setSavedQueryId(newSavedQuery.id); + if (!newSavedQuery) { + dashboardStateManager.setSavedQueryId(undefined); + queryFilter.setFilters([]); + dashboardStateManager.applyFilters( + { + query: '', + language: + localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage'), + }, + [] + ); + $scope.refresh(); + } else { + dashboardStateManager.setSavedQueryId(newSavedQuery.id); - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } } }); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts index bee7b97e7d6d1..0fee9acfcfcac 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts @@ -421,7 +421,7 @@ export class DashboardStateManager { return this.appState.savedQuery; } - public setSavedQueryId(id: string) { + public setSavedQueryId(id?: string) { this.appState.savedQuery = id; this.saveState(); } From dedca563acfca28d5d6f03604227d128ce99ae53 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 11 Jul 2019 13:32:17 -0400 Subject: [PATCH 068/189] Support close button in Maps main search bar --- .../plugins/maps/public/angular/map.html | 1 + .../maps/public/angular/map_controller.js | 24 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/legacy/plugins/maps/public/angular/map.html index e3fc76148c356..db7101f16a6f5 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map.html +++ b/x-pack/legacy/plugins/maps/public/angular/map.html @@ -24,6 +24,7 @@ saved-query="savedQuery" on-saved="onQuerySaved" on-saved-query-updated="onSavedQueryUpdated" + on-clear-saved-query="onClearSavedQuery" >
diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 71732c34a02fb..49825e69e00ba 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -158,6 +158,10 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage $scope.savedQuery = savedQuery; }; + $scope.onClearSavedQuery = () => { + $scope.savedQuery = undefined; + }; + const updateStateFromSavedQuery = (savedQuery) => { $scope.query = savedQuery.attributes.query; if (savedQuery.attributes.timefilter) { @@ -170,18 +174,28 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage } } syncAppAndGlobalState(); + store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time })); }; $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { - if (!newSavedQuery) return; - $state.savedQuery = newSavedQuery.id; + if (!newSavedQuery) { + $state.savedQuery = undefined; + $scope.query = { + query: '', + language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') + }; + syncAppAndGlobalState(); + store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time })); + } else { + $state.savedQuery = newSavedQuery.id; - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); + } } }); - $scope.$watch('state.savedQuery', newSavedQueryId => { + $scope.$watch(() => $state.savedQuery, newSavedQueryId => { if (!newSavedQueryId) { $scope.savedQuery = undefined; return; From 6ee50e43de67c1d0d60abc92442a6d0c47fb0933 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 11 Jul 2019 13:42:33 -0400 Subject: [PATCH 069/189] Make onClearSavedQuery handler optional --- .../public/query/query_bar/components/query_bar_input.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index efd8cfe63c9b5..9eb1dc9db0374 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -64,7 +64,7 @@ interface Props { onSave?: () => void; onSaveNew?: () => void; isDirty?: boolean; - onClearSavedQuery: () => void; + onClearSavedQuery?: () => void; } interface State { @@ -560,7 +560,10 @@ export class QueryBarInputUI extends Component { onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} append={ - this.props.onSave && this.props.onSaveNew && this.props.isDirty !== undefined ? ( + this.props.onSave && + this.props.onSaveNew && + this.props.onClearSavedQuery && + this.props.isDirty !== undefined ? ( Date: Thu, 11 Jul 2019 13:53:51 -0400 Subject: [PATCH 070/189] Add privileges for saved queries if user has access to maps --- x-pack/legacy/plugins/maps/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index 4e56ff5ff1958..ab12548140fc7 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -119,7 +119,7 @@ export function maps(kibana) { privileges: { all: { savedObject: { - all: ['map'], + all: ['map', 'query'], read: ['index-pattern'] }, ui: ['save', 'show'], @@ -127,7 +127,7 @@ export function maps(kibana) { read: { savedObject: { all: [], - read: ['map', 'index-pattern'] + read: ['map', 'index-pattern', 'query'] }, ui: ['show'], }, From e1e15a524adea8e5ff1169bfbf9ccba89d7e7330 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 11 Jul 2019 17:39:57 -0400 Subject: [PATCH 071/189] Fix issue where watcher was overriding saved search that had just been loaded --- .../dashboard/dashboard_app_controller.tsx | 34 ++++++++--------- .../public/discover/controllers/discover.js | 37 +++++++++---------- .../kibana/public/visualize/editor/editor.js | 30 +++++++-------- .../maps/public/angular/map_controller.js | 26 ++++++------- 4 files changed, 60 insertions(+), 67 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index fad0f8ded5acd..eeb950e8ef152 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -299,7 +299,18 @@ export class DashboardAppController { }; $scope.onClearSavedQuery = () => { - $scope.savedQuery = undefined; + delete $scope.savedQuery; + dashboardStateManager.setSavedQueryId(undefined); + queryFilter.setFilters([]); + dashboardStateManager.applyFilters( + { + query: '', + language: + localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage'), + }, + [] + ); + $scope.refresh(); }; const updateStateFromSavedQuery = (savedQuery: SavedQuery) => { @@ -321,24 +332,11 @@ export class DashboardAppController { }; $scope.$watch('savedQuery', (newSavedQuery: SavedQuery, oldSavedQuery: SavedQuery) => { - if (!newSavedQuery) { - dashboardStateManager.setSavedQueryId(undefined); - queryFilter.setFilters([]); - dashboardStateManager.applyFilters( - { - query: '', - language: - localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage'), - }, - [] - ); - $scope.refresh(); - } else { - dashboardStateManager.setSavedQueryId(newSavedQuery.id); + if (!newSavedQuery) return; + dashboardStateManager.setSavedQueryId(newSavedQuery.id); - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); - } + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); } }); diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 362dc0ac73ef7..a1bd67520829e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -381,7 +381,7 @@ function discoverController( // searchSource which applies time range const timeRangeSearchSource = savedSearch.searchSource.create(); - if(isDefaultTypeIndexPattern($scope.indexPattern)) { + if (isDefaultTypeIndexPattern($scope.indexPattern)) { timeRangeSearchSource.setField('filter', () => { return timefilter.createFilter($scope.indexPattern); }); @@ -398,7 +398,7 @@ function discoverController( if (savedSearch.id && savedSearch.title) { chrome.breadcrumbs.set([{ text: discoverBreadcrumbsTitle, - href: '#/discover' + href: '#/discover', }, { text: savedSearch.title }]); } else { chrome.breadcrumbs.set([{ @@ -891,7 +891,15 @@ function discoverController( }; $scope.onClearSavedQuery = () => { - $scope.savedQuery = undefined; + delete $scope.savedQuery; + delete $state.savedQuery; + $state.query = { + query: '', + language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage'), + }; + queryFilter.setFilters([]); + $state.save(); + $scope.fetch(); }; const updateStateFromSavedQuery = (savedQuery) => { @@ -912,22 +920,13 @@ function discoverController( }; $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { - if (!newSavedQuery) { - $state.savedQuery = undefined; - $state.query = { - query: '', - language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') - }; - queryFilter.setFilters([]); - $state.save(); - $scope.fetch(); - } else { - $state.savedQuery = newSavedQuery.id; - $state.save(); - - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); - } + if (!newSavedQuery) return; + + $state.savedQuery = newSavedQuery.id; + $state.save(); + + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); } }); diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index 11f6adea38b8b..6f4d32041d82c 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -477,7 +477,15 @@ function VisEditor( }; $scope.onClearSavedQuery = () => { - $scope.savedQuery = undefined; + delete $scope.savedQuery; + delete $state.savedQuery; + $state.query = { + query: '', + language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') + }; + queryFilter.setFilters([]); + $state.save(); + $scope.fetch(); }; const updateStateFromSavedQuery = (savedQuery) => { @@ -498,22 +506,12 @@ function VisEditor( }; $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { - if (!newSavedQuery) { - $state.savedQuery = undefined; - $state.query = { - query: '', - language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') - }; - queryFilter.setFilters([]); - $state.save(); - $scope.fetch(); - } else { - $state.savedQuery = newSavedQuery.id; - $state.save(); + if (!newSavedQuery) return; + $state.savedQuery = newSavedQuery.id; + $state.save(); - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); - } + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); } }); diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 49825e69e00ba..72d984351490d 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -159,7 +159,14 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage }; $scope.onClearSavedQuery = () => { - $scope.savedQuery = undefined; + delete $scope.savedQuery; + delete $state.savedQuery; + $scope.query = { + query: '', + language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') + }; + syncAppAndGlobalState(); + store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time })); }; const updateStateFromSavedQuery = (savedQuery) => { @@ -178,20 +185,11 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage }; $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { - if (!newSavedQuery) { - $state.savedQuery = undefined; - $scope.query = { - query: '', - language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') - }; - syncAppAndGlobalState(); - store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time })); - } else { - $state.savedQuery = newSavedQuery.id; + if (!newSavedQuery) return; + $state.savedQuery = newSavedQuery.id; - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); - } + if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { + updateStateFromSavedQuery(newSavedQuery); } }); From 13e4c6f7d3ee6a3fe8483c1c1ca8bc1eb5d103ab Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 11 Jul 2019 19:20:53 -0400 Subject: [PATCH 072/189] Conditionally show query saving UI based on uiCapabilities --- .../query/query_bar/components/query_bar.tsx | 2 + .../query_bar/components/query_bar_input.tsx | 3 + .../query_bar/components/saved_query_row.tsx | 31 +++++---- .../search_bar/components/search_bar.tsx | 63 ++++++++++--------- .../search/search_bar/directive/index.js | 1 + .../public/discover/controllers/discover.js | 5 ++ .../kibana/public/discover/index.html | 1 + .../server/lib/register_oss_features.ts | 2 +- 8 files changed, 68 insertions(+), 40 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index be5e02bf7a404..c6be4dcd635ac 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -66,6 +66,7 @@ interface Props { isRefreshPaused?: boolean; refreshInterval?: number; showAutoRefreshOnly?: boolean; + showSaveQuery?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; customSubmitButton?: any; onSave: () => void; @@ -187,6 +188,7 @@ export class QueryBarUI extends Component { onSubmit={this.onInputSubmit} persistedLog={this.persistedLog} savedQuery={this.props.savedQuery} + showSaveQuery={this.props.showSaveQuery} onSave={this.props.onSave} onSaveNew={this.props.onSaveNew} onLoadSavedQuery={this.props.onLoadSavedQuery} diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 9eb1dc9db0374..743589eb2c339 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -58,6 +58,7 @@ interface Props { persistedLog?: PersistedLog; bubbleSubmitEvent?: boolean; languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; + showSaveQuery?: boolean; onChange?: (query: Query) => void; onSubmit?: (query: Query) => void; onLoadSavedQuery?: (savedQuery: SavedQuery) => void; @@ -483,6 +484,7 @@ export class QueryBarInputUI extends Component { 'onLoadSavedQuery', 'isDirty', 'onClearSavedQuery', + 'showSaveQuery', ]); return ( @@ -567,6 +569,7 @@ export class QueryBarInputUI extends Component { void; onSaveNew: () => void; isDirty: boolean; @@ -45,12 +47,13 @@ export const SavedQueryRow: FunctionComponent = ({ savedQuery, onSave, onSaveNew, + showSaveQuery, isDirty, onClearSavedQuery, }) => { let rowContent; if (savedQuery) { - if (isDirty) { + if (isDirty && showSaveQuery) { rowContent = ( @@ -85,19 +88,21 @@ export const SavedQueryRow: FunctionComponent = ({ - - Save as new - + {showSaveQuery && ( + + Save as new + + )} ); } - } else if (query.query.length !== 0) { + } else if (query.query.length !== 0 && showSaveQuery) { rowContent = ( Save this query for reuse ); - } else { + } else if (capabilities.get().savedObjectsManagement.read) { rowContent = ( = ({ ); } - return ( - - {rowContent} - - ); + if (rowContent) { + return ( + + {rowContent} + + ); + } else { + return ; + } }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 98a07702e685e..449acd1a2a3c5 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -38,6 +38,7 @@ import { Storage } from 'ui/storage'; import { get, isEqual } from 'lodash'; import { toastNotifications } from 'ui/notify'; +import { capabilities } from 'ui/capabilities'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; import { SavedQuery, SavedQueryAttributes } from '../index'; @@ -73,6 +74,7 @@ interface Props { isRefreshPaused?: boolean; refreshInterval?: number; showAutoRefreshOnly?: boolean; + showSaveQuery?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; onSaved?: (savedQuery: SavedQuery) => void; onSavedQueryUpdated: (savedQuery: SavedQuery) => void; @@ -402,34 +404,38 @@ class SearchBarUI extends Component { - - { - if (this.props.savedQuery) { - this.onInitiateSave(); - } else { - this.onInitiateSaveNew(); - } - }} - > - - - - - - - - + {this.props.showSaveQuery && ( + + { + if (this.props.savedQuery) { + this.onInitiateSave(); + } else { + this.onInitiateSaveNew(); + } + }} + > + + + + )} + {capabilities.get().savedObjectsManagement.read && ( + + + + + + )} @@ -457,6 +463,7 @@ class SearchBarUI extends Component { isRefreshPaused={this.props.isRefreshPaused} refreshInterval={this.props.refreshInterval} showAutoRefreshOnly={this.props.showAutoRefreshOnly} + showSaveQuery={this.props.showSaveQuery} onRefreshChange={this.props.onRefreshChange} onSave={this.onInitiateSave} onSaveNew={this.onInitiateSaveNew} diff --git a/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js b/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js index b60553f57ee0f..a27b81f64181b 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js +++ b/src/legacy/core_plugins/data/public/search/search_bar/directive/index.js @@ -49,6 +49,7 @@ export function setupDirective() { 'showFilterBar', 'showQueryBar', 'showDatePicker', + 'showSaveQuery', 'dateRangeFrom', 'dateRangeTo', 'isRefreshPaused', diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index a1bd67520829e..0804d4ce6fbf9 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -223,6 +223,11 @@ function discoverController( $scope.fetchStatus = fetchStatuses.UNINITIALIZED; $scope.refreshInterval = timefilter.getRefreshInterval(); $scope.savedQuery = $route.current.locals.savedQuery; + $scope.showSaveQuery = uiCapabilities.discover.saveQuery; + + $scope.$watch(() => uiCapabilities.discover.saveQuery, (newCapability) => { + $scope.showSaveQuery = newCapability; + }); $scope.intervalEnabled = function (interval) { return interval.val !== 'custom'; diff --git a/src/legacy/core_plugins/kibana/public/discover/index.html b/src/legacy/core_plugins/kibana/public/discover/index.html index 827050ea6d519..213ef038cd1f2 100644 --- a/src/legacy/core_plugins/kibana/public/discover/index.html +++ b/src/legacy/core_plugins/kibana/public/discover/index.html @@ -48,6 +48,7 @@

{ all: ['search', 'url', 'query'], read: ['index-pattern'], }, - ui: ['show', 'createShortUrl', 'save'], + ui: ['show', 'createShortUrl', 'save', 'saveQuery'], }, read: { savedObject: { From e60c63fbde93e56f6b3fc7b42c140b57215700cf Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 12 Jul 2019 09:31:49 -0700 Subject: [PATCH 073/189] Adds security around saved queries to dashboard --- .../kibana/public/dashboard/dashboard_app.html | 1 + .../kibana/public/dashboard/dashboard_app.tsx | 1 + .../kibana/public/dashboard/dashboard_app_controller.tsx | 9 +++++++++ .../xpack_main/server/lib/register_oss_features.ts | 2 +- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html index 3f4bbb3ed5a4f..8a7e996f4978b 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html @@ -23,6 +23,7 @@ date-range-to="model.timeRange.to" is-refresh-paused="model.refreshInterval.pause" refresh-interval="model.refreshInterval.value" + show-save-query="showSaveQuery" on-refresh-change="onRefreshChange" on-saved="onQuerySaved" on-saved-query-updated="onSavedQueryUpdated" diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index cb2afefd8af3a..f1a7aa40b9e76 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -105,6 +105,7 @@ export interface DashboardAppScope extends ng.IScope { topNavMenu: any; showFilterBar: () => boolean; showAddPanel: any; + showSaveQuery: boolean; kbnTopNav: any; enterEditMode: () => void; $listen: any; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index eeb950e8ef152..144c259479fa3 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -55,6 +55,7 @@ import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; import { Query, SavedQuery } from 'plugins/data'; import { SaveOptions } from 'ui/saved_objects/saved_object'; +import { capabilities } from 'ui/capabilities'; import { data } from '../../../data/public/setup'; import { DashboardAppState, @@ -158,6 +159,7 @@ export class DashboardAppController { if (dashboardStateManager.getIsTimeSavedWithDashboard() && !getAppState.previouslyStored()) { dashboardStateManager.syncTimefilterWithDashboard(timefilter); } + $scope.showSaveQuery = capabilities.get().dashboard.saveQuery as boolean; const updateState = () => { // Following the "best practice" of always have a '.' in your ng-models – @@ -372,6 +374,13 @@ export class DashboardAppController { $scope.updateQueryAndFetch({ query }); }); + $scope.$watch( + () => capabilities.get().dashboard.saveQuery, + newCapability => { + $scope.showSaveQuery = newCapability as boolean; + } + ); + $scope.$listenAndDigestAsync(timefilter, 'fetch', () => { dashboardStateManager.handleTimeChange(timefilter.getTime()); // Currently discover relies on this logic to re-fetch. We need to refactor it to rely instead on the diff --git a/x-pack/legacy/plugins/xpack_main/server/lib/register_oss_features.ts b/x-pack/legacy/plugins/xpack_main/server/lib/register_oss_features.ts index c10ecc00e7908..ea6fb7459a9bd 100644 --- a/x-pack/legacy/plugins/xpack_main/server/lib/register_oss_features.ts +++ b/x-pack/legacy/plugins/xpack_main/server/lib/register_oss_features.ts @@ -82,7 +82,7 @@ const buildKibanaFeatures = (savedObjectTypes: string[]) => { 'map', ], }, - ui: ['createNew', 'show', 'showWriteControls'], + ui: ['createNew', 'show', 'showWriteControls', 'saveQuery'], }, read: { savedObject: { From f6707e8f5dcf8f67e57c8e2bcaf4c66199d65025 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 12 Jul 2019 09:48:52 -0700 Subject: [PATCH 074/189] Adds security aroung saved queries to visualize --- .../core_plugins/kibana/public/visualize/editor/editor.html | 1 + .../core_plugins/kibana/public/visualize/editor/editor.js | 6 ++++++ .../plugins/xpack_main/server/lib/register_oss_features.ts | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html index afad8c242bf39..b5b576f9d9295 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html @@ -46,6 +46,7 @@ refresh-interval="refreshInterval.value" show-auto-refresh-only="showAutoRefreshOnlyInQueryBar" on-refresh-change="onRefreshChange" + show-save-query="showSaveQuery" on-saved="onQuerySaved" on-saved-query-updated="onSavedQueryUpdated" on-clear-saved-query="onClearSavedQuery" diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index 6f4d32041d82c..787fa6f85a32e 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -329,6 +329,12 @@ function VisEditor( } }); + $scope.showSaveQuery = capabilities.get().visualize.saveQuery; + + $scope.$watch(() => capabilities.get().visualize.saveQuery, (newCapability) => { + $scope.showSaveQuery = newCapability; + }); + function init() { // export some objects $scope.savedVis = savedVis; diff --git a/x-pack/legacy/plugins/xpack_main/server/lib/register_oss_features.ts b/x-pack/legacy/plugins/xpack_main/server/lib/register_oss_features.ts index ea6fb7459a9bd..1a988656481bd 100644 --- a/x-pack/legacy/plugins/xpack_main/server/lib/register_oss_features.ts +++ b/x-pack/legacy/plugins/xpack_main/server/lib/register_oss_features.ts @@ -49,7 +49,7 @@ const buildKibanaFeatures = (savedObjectTypes: string[]) => { all: ['visualization', 'url', 'query'], read: ['index-pattern', 'search'], }, - ui: ['show', 'createShortUrl', 'delete', 'save'], + ui: ['show', 'createShortUrl', 'delete', 'save', 'saveQuery'], }, read: { savedObject: { From 86c8f11ebf3c7a552ebcccd228a99c1a6b800d72 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 12 Jul 2019 10:53:57 -0700 Subject: [PATCH 075/189] Adds security around saved queries to main map query bar with date picker --- x-pack/legacy/plugins/maps/index.js | 2 +- x-pack/legacy/plugins/maps/public/angular/map.html | 1 + x-pack/legacy/plugins/maps/public/angular/map_controller.js | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index ab12548140fc7..cabb96e487163 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -122,7 +122,7 @@ export function maps(kibana) { all: ['map', 'query'], read: ['index-pattern'] }, - ui: ['save', 'show'], + ui: ['save', 'show', 'saveQuery'], }, read: { savedObject: { diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/legacy/plugins/maps/public/angular/map.html index db7101f16a6f5..6f4712876c9ed 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map.html +++ b/x-pack/legacy/plugins/maps/public/angular/map.html @@ -20,6 +20,7 @@ date-range-to="time.to" is-refresh-paused="refreshConfig.isPaused" refresh-interval="refreshConfig.interval" + show-save-query="showSaveQuery" on-refresh-change="onRefreshChange" saved-query="savedQuery" on-saved="onQuerySaved" diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 72d984351490d..e157b98312481 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -206,6 +206,12 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage }); }); }); + + $scope.showSaveQuery = capabilities.get().maps.saveQuery; + + $scope.$watch(() => capabilities.get().maps.saveQuery, (newCapability) => { + $scope.showSaveQuery = newCapability; + }); /* end Saved Queries */ function hasUnsavedChanges() { From 7856d931c7705a6bfbd5651e16c8b4130713ed79 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 16 Jul 2019 11:13:39 -0400 Subject: [PATCH 076/189] Use the ID as a key if the suggestion type is savedQuery --- .../components/typeahead/suggestions_component.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx index f84d9d9d65f3c..b6a3e0dfeb5e7 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx @@ -50,7 +50,11 @@ export class SuggestionsComponent extends Component { onClick={this.props.onClick} onMouseEnter={() => this.props.onMouseEnter(index)} ariaId={'suggestion-' + index} - key={`${suggestion.type} - ${suggestion.text}`} + key={ + suggestion.savedQuery + ? suggestion.savedQuery.id + : `${suggestion.type} - ${suggestion.text}` + } /> ); }); From 3f170c3923831e256867aa032dfa7ca965a04edb Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 16 Jul 2019 11:17:46 -0400 Subject: [PATCH 077/189] Tighten type definition for prepend prop --- .../data/public/query/query_bar/components/query_bar.tsx | 2 +- .../data/public/query/query_bar/components/query_bar_input.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index c6be4dcd635ac..34f9ee129d920 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -59,7 +59,7 @@ interface Props { indexPatterns: Array; store: Storage; intl: InjectedIntl; - prepend?: any; + prepend?: React.ReactNode; showDatePicker?: boolean; dateRangeFrom?: string; dateRangeTo?: string; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 743589eb2c339..f42f6ecfcb6ba 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -53,7 +53,7 @@ interface Props { appName: string; disableAutoFocus?: boolean; screenTitle?: string; - prepend?: any; + prepend?: React.ReactNode; store?: Storage; persistedLog?: PersistedLog; bubbleSubmitEvent?: boolean; From a3845c6d71b3d9db511644e06fc66b962dc760fd Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 17 Jul 2019 13:36:58 -0400 Subject: [PATCH 078/189] Add security functional tests for the all permissions scenario --- .../query_bar/components/saved_query_row.tsx | 24 +++++++----------- .../search_bar/components/save_query_form.tsx | 5 ++++ test/functional/services/query_bar.js | 25 +++++++++++++++++++ .../feature_controls/discover_security.ts | 21 ++++++++++++++++ 4 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index 26aac4f0ee70a..f5e8e9b32e697 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -19,8 +19,6 @@ import React, { FunctionComponent, Fragment } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiIcon } from '@elastic/eui'; -import chrome from 'ui/chrome'; -import { capabilities } from 'ui/capabilities'; import { SavedQueryAttributes } from '../../../search/search_bar'; import { Query } from '../index'; @@ -58,7 +56,7 @@ export const SavedQueryRow: FunctionComponent = ({ - + Save changes to query: {savedQuery.title} @@ -69,7 +67,9 @@ export const SavedQueryRow: FunctionComponent = ({ - Save as new + + Save as new + ); @@ -90,7 +90,9 @@ export const SavedQueryRow: FunctionComponent = ({ {showSaveQuery && ( - Save as new + + Save as new + )} @@ -99,16 +101,8 @@ export const SavedQueryRow: FunctionComponent = ({ } else if (query.query.length !== 0 && showSaveQuery) { rowContent = ( - Save this query for reuse - - ); - } else if (capabilities.get().savedObjectsManagement.read) { - rowContent = ( - - - Manage Saved Queries + + Save this query for reuse ); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index 88979fe988f5f..73411877dae89 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -74,6 +74,7 @@ export const SaveQueryForm: FunctionComponent = ({ onChange={event => { setTitle(event.target.value); }} + data-test-subj="saveQueryFormTitle" /> @@ -84,6 +85,7 @@ export const SaveQueryForm: FunctionComponent = ({ onChange={event => { setDescription(event.target.value); }} + data-test-subj="saveQueryFormDescription" /> {!!showFilterOption && ( @@ -95,6 +97,7 @@ export const SaveQueryForm: FunctionComponent = ({ onChange={() => { setShouldIncludeFilters(!shouldIncludeFilters); }} + data-test-subj="saveQueryFormIncludeFiltersOption" /> )} @@ -108,6 +111,7 @@ export const SaveQueryForm: FunctionComponent = ({ onChange={() => { setIncludeTimefilter(!shouldIncludeTimefilter); }} + data-test-subj="saveQueryFormIncludeTimeFilterOption" /> )} @@ -136,6 +140,7 @@ export const SaveQueryForm: FunctionComponent = ({ }) } fill + data-test-subj="savedQueryFormSaveButton" > Save diff --git a/test/functional/services/query_bar.js b/test/functional/services/query_bar.js index eb11ff10c8ed8..c63fdcca8309c 100644 --- a/test/functional/services/query_bar.js +++ b/test/functional/services/query_bar.js @@ -60,6 +60,31 @@ export function QueryBarProvider({ getService, getPageObjects }) { await testSubjects.click('querySubmitButton'); } + async saveNewQuery(title, description, includeFilters, includeTimeFilter) { + await testSubjects.click('savedQuerySaveNew'); + await this.submitSaveQueryForm(title, description, includeFilters, includeTimeFilter); + } + + async submitSaveQueryForm(title, description, includeFilters, includeTimeFilter) { + await testSubjects.setValue('saveQueryFormTitle', title); + await testSubjects.setValue('saveQueryFormDescription', description); + + const currentIncludeFiltersValue = (await testSubjects.getAttribute('saveQueryFormIncludeFiltersOption', 'checked')) === 'true'; + if (currentIncludeFiltersValue !== includeFilters) { + await testSubjects.click('saveQueryFormIncludeFiltersOption'); + } + + const currentIncludeTimeFilterValue = (await testSubjects.getAttribute('saveQueryFormIncludeTimeFilterOption', 'checked')) === 'true'; + if (currentIncludeTimeFilterValue !== includeTimeFilter) { + await testSubjects.click('saveQueryFormIncludeTimeFilterOption'); + } + + await testSubjects.click('savedQueryFormSaveButton'); + } + + async openSuggestionsDropDown() { + await testSubjects.click('queryInput'); + } } return new QueryBar(); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 2e5f9a9f041b4..ec940526b2035 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -22,6 +22,7 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa ]); const testSubjects = getService('testSubjects'); const appsMenu = getService('appsMenu'); + const queryBar = getService('queryBar'); async function setDiscoverTimeRange() { const fromTime = '2015-09-19 06:31:44.000'; @@ -101,6 +102,26 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa it('Permalinks shows create short-url button', async () => { await PageObjects.share.openShareMenuItem('Permalinks'); await PageObjects.share.createShortUrlExistOrFail(); + // close the menu + await PageObjects.share.clickShareTopNavButton(); + }); + + it('show the save query button in the query bar in dirty state with no query loaded', async () => { + await queryBar.setQuery('response'); + await testSubjects.existOrFail('savedQuerySaveNew'); + }); + + it('show the save as new query button in the query bar with non-dirty state and query loaded', async () => { + await queryBar.setQuery('response:200 '); + await queryBar.saveNewQuery('OK Responses', '200 OK', true, true); + await queryBar.openSuggestionsDropDown(); + await testSubjects.existOrFail('savedQuerySaveAsNew'); + }); + + it('show the save changes to existing and save as new buttons in the query bar with a dirty state and a query loaded', async () => { + await queryBar.setQuery('response:404 '); + await testSubjects.existOrFail('savedQuerySaveChanges'); + await testSubjects.existOrFail('savedQuerySaveAsNew'); }); }); From 52dd391c9980bff32be71fc4bfa07afec9ed268e Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 17 Jul 2019 18:17:09 -0400 Subject: [PATCH 079/189] Add security functional tests for the read only scenario --- .../query_bar/components/query_bar_input.tsx | 38 ++++++++++--------- .../query_bar/components/saved_query_row.tsx | 27 +++++++------ .../typeahead/suggestion_component.tsx | 3 ++ .../typeahead/suggestions_component.tsx | 3 +- .../feature_controls/discover_security.ts | 18 +++++++++ .../feature_controls/security/data.json | 22 +++++++++++ .../feature_controls/security/mappings.json | 26 +++++++++++++ 7 files changed, 104 insertions(+), 33 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index f42f6ecfcb6ba..638dcc3a12a54 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -487,6 +487,22 @@ export class QueryBarInputUI extends Component { 'showSaveQuery', ]); + const savedQueryRow = + this.props.onSave && + this.props.onSaveNew && + this.props.onClearSavedQuery && + this.props.isDirty !== undefined ? ( + + ) : null; + return (
{
0) + } suggestions={this.state.suggestions.slice(0, this.state.suggestionLimit)} index={this.state.index} onClick={this.onClickSuggestion} onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} - append={ - this.props.onSave && - this.props.onSaveNew && - this.props.onClearSavedQuery && - this.props.isDirty !== undefined ? ( - - ) : null - } + append={savedQueryRow} />

diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx index f5e8e9b32e697..d2eac057f9596 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx @@ -76,18 +76,17 @@ export const SavedQueryRow: FunctionComponent = ({ } else { rowContent = ( - - - - {savedQuery.title} - - - - - - - - + + + {savedQuery.title} + + + + + + + + {showSaveQuery && ( @@ -110,11 +109,11 @@ export const SavedQueryRow: FunctionComponent = ({ if (rowContent) { return ( - + {rowContent} ); } else { - return ; + return null; } }; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx index 7c65bc11f17ca..bdb3d5ff36451 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx @@ -64,6 +64,9 @@ export const SuggestionComponent: FunctionComponent = props => { ref={props.innerRef} id={props.ariaId} aria-selected={props.selected} + data-test-subj={`autocompleteSuggestion-${ + props.suggestion.type + }-${props.suggestion.text.replace(/\s/g, '-')}`} >
diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx index b6a3e0dfeb5e7..3c7594419513c 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx @@ -17,7 +17,6 @@ * under the License. */ -import { isEmpty } from 'lodash'; import React, { Component } from 'react'; import { AutocompleteSuggestion } from 'ui/autocomplete_providers'; import { SuggestionComponent } from './suggestion_component'; @@ -37,7 +36,7 @@ export class SuggestionsComponent extends Component { private parentNode: HTMLDivElement | null = null; public render() { - if (!this.props.show || isEmpty(this.props.suggestions)) { + if (!this.props.show) { return null; } diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index ec940526b2035..b330be774c79e 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -189,6 +189,24 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa await PageObjects.share.openShareMenuItem('Permalinks'); await PageObjects.share.createShortUrlMissingOrFail(); }); + + it('does not show the save query button in the query bar in dirty state with no query loaded', async () => { + await queryBar.setQuery('response'); + await testSubjects.missingOrFail('savedQuerySaveNew'); + }); + + it('does not show the save as new query button in the query bar with non-dirty state and query loaded', async () => { + await queryBar.setQuery('OK Jpgs'); + await testSubjects.click('autocompleteSuggestion-savedQuery-OK-Jpgs'); + await queryBar.openSuggestionsDropDown(); + await testSubjects.missingOrFail('savedQuerySaveAsNew'); + }); + + it('does not show the save changes to existing or save as new button in the query bar with a dirty state and a query loaded', async () => { + await queryBar.setQuery('response:404 '); + await testSubjects.missingOrFail('savedQuerySaveChanges'); + await testSubjects.missingOrFail('savedQuerySaveAsNew'); + }); }); describe('discover and visualize privileges', () => { diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json b/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json index 3c0613005c950..f70ac5fb20d28 100644 --- a/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json +++ b/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json @@ -35,3 +35,25 @@ } } } + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "query:okjpgs", + "source": { + "query": { + "title": "OK Jpgs", + "description": "Ok responses for jpg files", + "query": { + "query": "response:200", + "language": "kuery" + }, + "filters": "[{\"meta\":{\"index\":\"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b\",\"alias\":null,\"negate\":false,\"type\":\"phrase\",\"key\":\"extension.raw\",\"value\":\"jpg\",\"params\":{\"query\":\"jpg\"},\"disabled\":false},\"query\":{\"match\":{\"extension.raw\":{\"query\":\"jpg\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]" + }, + "type": "query", + "updated_at": "2019-07-17T17:54:26.378Z" + } + } +} diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json index 35696c187537f..00d11f15db6c3 100644 --- a/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json +++ b/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json @@ -453,6 +453,32 @@ "type": "text" } } + }, + "query": { + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + }, + "query": { + "properties": { + "language": { + "type": "text" + }, + "query": { + "type": "text" + } + } + }, + "filters": { + "type": "text" + }, + "timefilter": { + "type": "text" + } + } } } } From 1eace53c2939907472685337b8b63f80ec709cf2 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 18 Jul 2019 08:17:13 -0700 Subject: [PATCH 080/189] Fixes suggestions and suggestion component jest tests --- .../__snapshots__/suggestion_component.test.tsx.snap | 2 ++ .../components/typeahead/suggestions_component.test.tsx | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/__snapshots__/suggestion_component.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/__snapshots__/suggestion_component.test.tsx.snap index 92ca296ed8058..38b570c86c6c5 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/__snapshots__/suggestion_component.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/__snapshots__/suggestion_component.test.tsx.snap @@ -4,6 +4,7 @@ exports[`SuggestionComponent Should display the suggestion and use the provided
{ expect(component.isEmptyRender()).toBe(true); }); - it('Should not display anything if there are no suggestions', () => { + it('Should display the saved query row if there are no suggestions and the show prop is true', () => { const component = shallow( { loadMore={noop} /> ); - - expect(component.isEmptyRender()).toBe(true); + expect(component.isEmptyRender()).toBe(false); }); it('Should display given suggestions if the show prop is true', () => { From ccb0802a2ec9ec38aed88fe1b7ceaef5a33505fd Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 18 Jul 2019 13:33:22 -0700 Subject: [PATCH 081/189] Adds security tests for saved queries to the dashboard functional tests --- .../feature_controls/dashboard_security.ts | 41 +++++++++++++++++++ .../feature_controls/security/data.json | 22 ++++++++++ .../feature_controls/security/mappings.json | 26 ++++++++++++ 3 files changed, 89 insertions(+) diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index 84458d4c17223..741b0d7fdecce 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -21,6 +21,7 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa const panelActions = getService('dashboardPanelActions'); const testSubjects = getService('testSubjects'); const globalNav = getService('globalNav'); + const queryBar = getService('queryBar'); describe('dashboard security', () => { before(async () => { @@ -188,6 +189,26 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa await panelActions.openContextMenu(); await panelActions.expectExistsEditPanelAction(); }); + + it('show the save query button in the query bar in dirty state with no query loaded', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.gotoDashboardEditMode('A Dashboard'); + await queryBar.setQuery('response'); + await testSubjects.existOrFail('savedQuerySaveNew'); + }); + + it('show the save as new query button in the query bar with non-dirty state and query loaded', async () => { + await queryBar.setQuery('response:200 '); + await queryBar.saveNewQuery('OK Responses', '200 OK', true, true); + await queryBar.openSuggestionsDropDown(); + await testSubjects.existOrFail('savedQuerySaveAsNew'); + }); + + it('show the save changes to existing and save as new buttons in the query bar with a dirty state and a query loaded', async () => { + await queryBar.setQuery('response:404 '); + await testSubjects.existOrFail('savedQuerySaveChanges'); + await testSubjects.existOrFail('savedQuerySaveAsNew'); + }); }); describe('global dashboard read-only privileges', () => { @@ -273,6 +294,26 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa it(`Permalinks doesn't show create short-url button`, async () => { await PageObjects.share.openShareMenuItem('Permalinks'); await PageObjects.share.createShortUrlMissingOrFail(); + // close the menu + await PageObjects.share.clickShareTopNavButton(); + }); + + it('does not show the save query button in the query bar in dirty state with no query loaded', async () => { + await queryBar.setQuery('response'); + await testSubjects.missingOrFail('savedQuerySaveNew'); + }); + + it('does not show the save as new query button in the query bar with non-dirty state and query loaded', async () => { + await queryBar.setQuery('OK Jpgs'); + await testSubjects.click('autocompleteSuggestion-savedQuery-OK-Jpgs'); + await queryBar.openSuggestionsDropDown(); + await testSubjects.missingOrFail('savedQuerySaveAsNew'); + }); + + it('does not show the save changes to existing or save as new button in the query bar with a dirty state and a query loaded', async () => { + await queryBar.setQuery('response:404 '); + await testSubjects.missingOrFail('savedQuerySaveChanges'); + await testSubjects.missingOrFail('savedQuerySaveAsNew'); }); }); diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json index 46cec764158d5..d467d56919dae 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json @@ -169,3 +169,25 @@ } } } + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "query:okjpgs", + "source": { + "query": { + "title": "OK Jpgs", + "description": "Ok responses for jpg files", + "query": { + "query": "response:200", + "language": "kuery" + }, + "filters": "[{\"meta\":{\"index\":\"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b\",\"alias\":null,\"negate\":false,\"type\":\"phrase\",\"key\":\"extension.raw\",\"value\":\"jpg\",\"params\":{\"query\":\"jpg\"},\"disabled\":false},\"query\":{\"match\":{\"extension.raw\":{\"query\":\"jpg\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]" + }, + "type": "query", + "updated_at": "2019-07-17T17:54:26.378Z" + } + } +} diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json index 032a109804b35..183a0d03f2bd8 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json @@ -481,6 +481,32 @@ "type": "text" } } + }, + "query": { + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + }, + "query": { + "properties": { + "language": { + "type": "text" + }, + "query": { + "type": "text" + } + } + }, + "filters": { + "type": "text" + }, + "timefilter": { + "type": "text" + } + } } } } From bf06d9f3e057cb3edc7b41de9c92b2860a847975 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 18 Jul 2019 13:45:22 -0700 Subject: [PATCH 082/189] Adds security tests for saved queries to the visualize functional tests --- .../feature_controls/visualize_security.ts | 39 +++++++++++++++++++ .../es_archives/visualize/default/data.json | 22 +++++++++++ .../visualize/default/mappings.json | 26 +++++++++++++ 3 files changed, 87 insertions(+) diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 8447c2fffe6a7..4fb9de9a5966f 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -22,6 +22,7 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa const testSubjects = getService('testSubjects'); const appsMenu = getService('appsMenu'); const globalNav = getService('globalNav'); + const queryBar = getService('queryBar'); describe('feature controls security', () => { before(async () => { @@ -113,6 +114,26 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa it('Permalinks shows create short-url button', async () => { await PageObjects.share.openShareMenuItem('Permalinks'); await PageObjects.share.createShortUrlExistOrFail(); + // close menu + await PageObjects.share.clickShareTopNavButton(); + }); + + it('show the save query button in the query bar in dirty state with no query loaded', async () => { + await queryBar.setQuery('response'); + await testSubjects.existOrFail('savedQuerySaveNew'); + }); + + it('show the save as new query button in the query bar with non-dirty state and query loaded', async () => { + await queryBar.setQuery('response:200 '); + await queryBar.saveNewQuery('OK Responses', '200 OK', true, true); + await queryBar.openSuggestionsDropDown(); + await testSubjects.existOrFail('savedQuerySaveAsNew'); + }); + + it('show the save changes to existing and save as new buttons in the query bar with a dirty state and a query loaded', async () => { + await queryBar.setQuery('response:404 '); + await testSubjects.existOrFail('savedQuerySaveChanges'); + await testSubjects.existOrFail('savedQuerySaveAsNew'); }); }); @@ -196,6 +217,24 @@ export default function({ getPageObjects, getService }: KibanaFunctionalTestDefa await PageObjects.share.openShareMenuItem('Permalinks'); await PageObjects.share.createShortUrlMissingOrFail(); }); + + it('does not show the save query button in the query bar in dirty state with no query loaded', async () => { + await queryBar.setQuery('response'); + await testSubjects.missingOrFail('savedQuerySaveNew'); + }); + + it('does not show the save as new query button in the query bar with non-dirty state and query loaded', async () => { + await queryBar.setQuery('OK Jpgs'); + await testSubjects.click('autocompleteSuggestion-savedQuery-OK-Jpgs'); + await queryBar.openSuggestionsDropDown(); + await testSubjects.missingOrFail('savedQuerySaveAsNew'); + }); + + it('does not show the save changes to existing or save as new button in the query bar with a dirty state and a query loaded', async () => { + await queryBar.setQuery('response:404 '); + await testSubjects.missingOrFail('savedQuerySaveChanges'); + await testSubjects.missingOrFail('savedQuerySaveAsNew'); + }); }); describe('no visualize privileges', () => { diff --git a/x-pack/test/functional/es_archives/visualize/default/data.json b/x-pack/test/functional/es_archives/visualize/default/data.json index 250af4d5c5c13..83a46b8a5de3f 100644 --- a/x-pack/test/functional/es_archives/visualize/default/data.json +++ b/x-pack/test/functional/es_archives/visualize/default/data.json @@ -108,3 +108,25 @@ } } } + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "query:okjpgs", + "source": { + "query": { + "title": "OK Jpgs", + "description": "Ok responses for jpg files", + "query": { + "query": "response:200", + "language": "kuery" + }, + "filters": "[{\"meta\":{\"index\":\"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b\",\"alias\":null,\"negate\":false,\"type\":\"phrase\",\"key\":\"extension.raw\",\"value\":\"jpg\",\"params\":{\"query\":\"jpg\"},\"disabled\":false},\"query\":{\"match\":{\"extension.raw\":{\"query\":\"jpg\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]" + }, + "type": "query", + "updated_at": "2019-07-17T17:54:26.378Z" + } + } +} diff --git a/x-pack/test/functional/es_archives/visualize/default/mappings.json b/x-pack/test/functional/es_archives/visualize/default/mappings.json index 35696c187537f..00d11f15db6c3 100644 --- a/x-pack/test/functional/es_archives/visualize/default/mappings.json +++ b/x-pack/test/functional/es_archives/visualize/default/mappings.json @@ -453,6 +453,32 @@ "type": "text" } } + }, + "query": { + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + }, + "query": { + "properties": { + "language": { + "type": "text" + }, + "query": { + "type": "text" + } + } + }, + "filters": { + "type": "text" + }, + "timefilter": { + "type": "text" + } + } } } } From db622a99013d2827816019eb957cd5f6afd65ae1 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 18 Jul 2019 17:41:44 -0400 Subject: [PATCH 083/189] Create saved query manager component --- .../components/saved_query_manager.tsx | 119 ++++++++++++++++ .../search_bar/components/search_bar.tsx | 131 ++---------------- 2 files changed, 130 insertions(+), 120 deletions(-) create mode 100644 src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx new file mode 100644 index 0000000000000..fc8f4b826898b --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -0,0 +1,119 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + EuiPopover, + EuiPopoverTitle, + EuiButtonEmpty, + EuiButton, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent, useState } from 'react'; +import { SavedQuery } from '../index'; + +interface Props { + showSaveQuery?: boolean; + savedQuery?: SavedQuery; + isDirty: boolean; + onSave: () => void; + onSaveAsNew: () => void; +} + +export const SavedQueryManager: FunctionComponent = ({ + showSaveQuery, + savedQuery, + isDirty, + onSave, + onSaveAsNew, +}) => { + const [isOpen, setIsOpen] = useState(false); + + const savedQueryDescriptionText = i18n.translate( + 'data.search.searchBar.savedQueryDescriptionText', + { + defaultMessage: 'Saved queries allow you to store sets of queries, filters and time filters.', + } + ); + + const savedQueryPopoverTitleText = i18n.translate( + 'data.search.searchBar.savedQueryPopoverTitleText', + { + defaultMessage: 'Saved Queries', + } + ); + + const filterTriggerButton = ( + { + setIsOpen(!isOpen); + }} + > + # + + ); + + return ( + { + setIsOpen(false); + }} + anchorPosition="downLeft" + > + {savedQueryPopoverTitleText} + + +

{savedQueryDescriptionText}

+
+ + + {showSaveQuery && ( + + { + if (savedQuery) { + onSave(); + } else { + onSaveAsNew(); + } + }} + > + {isDirty + ? i18n.translate('data.search.searchBar.savedQueryPopoverSaveChangesButtonText', { + defaultMessage: 'Save changes', + }) + : i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { + defaultMessage: 'Save', + })} + + + )} + + +
+ ); +}; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 449acd1a2a3c5..ebe2f64269b34 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -17,33 +17,22 @@ * under the License. */ -// @ts-ignore -import { - EuiFilterButton, - EuiPopover, - EuiPopoverTitle, - EuiButtonEmpty, - EuiButton, - EuiFlexGroup, - EuiFlexItem, -} from '@elastic/eui'; import { Filter } from '@kbn/es-query'; -import { InjectedIntl, injectI18n, FormattedMessage } from '@kbn/i18n/react'; +import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; -import chrome from 'ui/chrome'; import { IndexPattern } from 'ui/index_patterns'; import { Storage } from 'ui/storage'; import { get, isEqual } from 'lodash'; import { toastNotifications } from 'ui/notify'; -import { capabilities } from 'ui/capabilities'; import { Query, QueryBar } from '../../../query/query_bar'; import { FilterBar } from '../../../filter/filter_bar'; import { SavedQuery, SavedQueryAttributes } from '../index'; import { saveQuery } from '../lib/saved_query_service'; import { SavedQueryMeta, SaveQueryForm } from './save_query_form'; +import { SavedQueryManager } from './saved_query_manager'; interface DateRange { from: string; @@ -197,24 +186,6 @@ class SearchBarUI extends Component { public ro = new ResizeObserver(this.setFilterBarHeight); /* eslint-enable */ - public showSavedQueryPopover = () => { - this.setState({ - showSavedQueryPopover: true, - }); - }; - - public hideSavedQueryPopover = () => { - this.setState({ - showSavedQueryPopover: false, - }); - }; - - public toggleSavedQueryPopover = () => { - this.setState({ - showSavedQueryPopover: !this.state.showSavedQueryPopover, - }); - }; - public onSave = async (savedQueryMeta: SavedQueryMeta, saveAsNew = false) => { const savedQueryAttributes: SavedQueryAttributes = { title: savedQueryMeta.title, @@ -351,94 +322,14 @@ class SearchBarUI extends Component { } public render() { - const filtersAppliedText = this.props.intl.formatMessage({ - id: 'data.search.searchBar.filtersButtonFiltersAppliedTitle', - defaultMessage: 'filters applied.', - }); - const clickToShowOrHideText = this.state.isFiltersVisible - ? this.props.intl.formatMessage({ - id: 'data.search.searchBar.filtersButtonClickToShowTitle', - defaultMessage: 'Select to hide', - }) - : this.props.intl.formatMessage({ - id: 'data.search.searchBar.filtersButtonClickToHideTitle', - defaultMessage: 'Select to show', - }); - - const savedQueryDescriptionText = this.props.intl.formatMessage({ - id: 'data.search.searchBar.savedQueryDescriptionText', - defaultMessage: 'Saved queries allow you to store sets of queries, filters and time filters.', - }); - - const savedQueryPopoverTitleText = this.props.intl.formatMessage({ - id: 'data.search.searchBar.savedQueryPopoverTitleText', - defaultMessage: 'Saved Queries', - }); - - const filterTriggerButton = ( - 0 ? this.props.filters.length : undefined} - aria-controls="GlobalFilterGroup" - aria-expanded={!!this.state.isFiltersVisible} - title={`${this.props.filters.length} ${filtersAppliedText} ${clickToShowOrHideText}`} - > - Manage - - ); - - const savedQueryPopover = ( - - {savedQueryPopoverTitleText} - - -

{savedQueryDescriptionText}

-
- - - {this.props.showSaveQuery && ( - - { - if (this.props.savedQuery) { - this.onInitiateSave(); - } else { - this.onInitiateSaveNew(); - } - }} - > - - - - )} - {capabilities.get().savedObjectsManagement.read && ( - - - - - - )} - - -
+ const savedQueryManager = ( + ); const classes = classNames('globalFilterGroup__wrapper', { @@ -456,7 +347,7 @@ class SearchBarUI extends Component { appName={this.props.appName} indexPatterns={this.props.indexPatterns} store={this.props.store} - prepend={this.props.showFilterBar ? savedQueryPopover : ''} + prepend={this.props.showFilterBar ? savedQueryManager : ''} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} dateRangeTo={this.state.dateRangeTo} From 51cf96e88cae6f003f46d0383b1bd3d427b98c8f Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 18 Jul 2019 18:43:25 -0400 Subject: [PATCH 084/189] Add Saved Query links to the management popover --- .../components/saved_query_manager.tsx | 46 +++++++++++++++++-- .../search_bar/components/search_bar.tsx | 3 +- .../lib/saved_query_service.test.ts | 4 ++ .../search_bar/lib/saved_query_service.ts | 13 ++++++ 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index fc8f4b826898b..f832657d059b5 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -27,25 +27,39 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { FunctionComponent, useState } from 'react'; +import React, { FunctionComponent, useEffect, useState } from 'react'; +import { sortBy } from 'lodash'; import { SavedQuery } from '../index'; +import { getAllSavedQueries } from '../lib/saved_query_service'; interface Props { showSaveQuery?: boolean; - savedQuery?: SavedQuery; + loadedSavedQuery?: SavedQuery; isDirty: boolean; onSave: () => void; onSaveAsNew: () => void; + onLoad: (savedQuery: SavedQuery) => void; } export const SavedQueryManager: FunctionComponent = ({ showSaveQuery, - savedQuery, + loadedSavedQuery, isDirty, onSave, onSaveAsNew, + onLoad, }) => { const [isOpen, setIsOpen] = useState(false); + const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); + + useEffect(() => { + const fetchQueries = async () => { + const allSavedQueries = await getAllSavedQueries(); + const sortedAllSavedQueries = sortBy(allSavedQueries, 'attributes.title'); + setSavedQueries(sortedAllSavedQueries); + }; + fetchQueries(); + }, []); const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', @@ -73,6 +87,22 @@ export const SavedQueryManager: FunctionComponent = ({ ); + const savedQueryRows = savedQueries.map(savedQuery => { + return ( +
  • + { + onLoad(savedQuery); + setIsOpen(false); + }} + flush="left" + > + {savedQuery.attributes.title} + +
  • + ); + }); + return ( = ({ {savedQueryPopoverTitleText} -

    {savedQueryDescriptionText}

    + {savedQueryDescriptionText} +
    + + + +
      {savedQueryRows}
    +
    @@ -95,7 +131,7 @@ export const SavedQueryManager: FunctionComponent = ({ { - if (savedQuery) { + if (loadedSavedQuery) { onSave(); } else { onSaveAsNew(); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index ebe2f64269b34..e0f3b9e8e035d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -326,9 +326,10 @@ class SearchBarUI extends Component { ); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index 90d0415ab369d..f25cc43547086 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -134,5 +134,9 @@ describe('saved query service', () => { } expect(error).not.toBe(null); }); + + it('should test all the other service methods', () => { + throw new Error('implement me'); + }); }); }); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index 5aed31b85e1dd..f30953de76e11 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -83,6 +83,19 @@ export const saveQuery = async (attributes: SavedQueryAttributes, id?: string) = return parseSavedQueryObject(rawQueryResponse); }; +export const getAllSavedQueries = async (): Promise => { + const savedObjectsClient = chrome.getSavedObjectsClient(); + + const response = await savedObjectsClient.find({ + type: 'query', + }); + + return response.savedObjects.map( + (savedObject: { id: string; attributes: SerializedSavedQueryAttributes }) => + parseSavedQueryObject(savedObject) + ); +}; + export const findSavedQueries = async (searchText: string = ''): Promise => { const savedObjectsClient = chrome.getSavedObjectsClient(); From 309b5adb1e3eef9fc64d61399e089ec0619a4e4b Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 19 Jul 2019 11:05:49 -0700 Subject: [PATCH 085/189] Adds paragraph content to the saved query manager when there are no saved queries --- .../search/search_bar/components/saved_query_manager.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index f832657d059b5..44e52639da7b3 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -121,7 +121,14 @@ export const SavedQueryManager: FunctionComponent = ({ -
      {savedQueryRows}
    + {savedQueries.length > 0 ? ( +
      {savedQueryRows}
    + ) : ( +

    + There are no saved queries. You can save search snippets and filters for later use.{' '} + this, enter a query and click 'Save query for reuse'. +

    + )}
    From d3353b5e9b686eb35b9e0ff38ab5748e41d5bd76 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 19 Jul 2019 15:57:12 -0700 Subject: [PATCH 086/189] Adds all buttons and an icon for deleting a saved query. The buttons are not aligned properly yet --- .../components/saved_query_manager.tsx | 116 +++++++++++------- .../search_bar/components/search_bar.tsx | 2 + 2 files changed, 76 insertions(+), 42 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 44e52639da7b3..a748640d8e20b 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -24,13 +24,15 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem, + EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { FunctionComponent, useEffect, useState } from 'react'; +import React, { FunctionComponent, useEffect, useState, Fragment } from 'react'; import { sortBy } from 'lodash'; import { SavedQuery } from '../index'; import { getAllSavedQueries } from '../lib/saved_query_service'; +import { Query } from '../../../query'; interface Props { showSaveQuery?: boolean; @@ -39,6 +41,8 @@ interface Props { onSave: () => void; onSaveAsNew: () => void; onLoad: (savedQuery: SavedQuery) => void; + query: Query; + onClearSavedQuery: () => void; } export const SavedQueryManager: FunctionComponent = ({ @@ -48,6 +52,8 @@ export const SavedQueryManager: FunctionComponent = ({ onSave, onSaveAsNew, onLoad, + query, + onClearSavedQuery, }) => { const [isOpen, setIsOpen] = useState(false); const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); @@ -59,7 +65,7 @@ export const SavedQueryManager: FunctionComponent = ({ setSavedQueries(sortedAllSavedQueries); }; fetchQueries(); - }, []); + }); // we need the effect to trigger on every render because saved queries change and we need the updates. const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', @@ -99,6 +105,11 @@ export const SavedQueryManager: FunctionComponent = ({ > {savedQuery.attributes.title} + alert(`saved query ${savedQuery.id} to be deleted`)} + iconType="trash" + color="danger" + /> ); }); @@ -114,49 +125,70 @@ export const SavedQueryManager: FunctionComponent = ({ anchorPosition="downLeft" > {savedQueryPopoverTitleText} - - - {savedQueryDescriptionText} - - - - - {savedQueries.length > 0 ? ( -
      {savedQueryRows}
    - ) : ( -

    - There are no saved queries. You can save search snippets and filters for later use.{' '} - this, enter a query and click 'Save query for reuse'. -

    + {savedQueries.length > 0 ? ( + + + {savedQueryDescriptionText} + + + +
      {savedQueryRows}
    +
    +
    +
    + ) : ( + +

    + There are no saved queries. You can save search snippets and filters for later use.{' '} + this, enter a query and click 'Save query for reuse'. +

    +
    + )} + {query.query !== '' ? ( + + {showSaveQuery && ( + + { + if (loadedSavedQuery) { + onSave(); + } else { + onSaveAsNew(); + } + }} + > + {isDirty && loadedSavedQuery + ? i18n.translate('data.search.searchBar.savedQueryPopoverSaveChangesButtonText', { + defaultMessage: 'Save changes', + }) + : i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { + defaultMessage: 'Save', + })} + + + )} + {showSaveQuery && isDirty && loadedSavedQuery && ( + + onSaveAsNew()}> + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { + defaultMessage: 'Save As New', + })} + + )} -
    -
    - - - {showSaveQuery && ( - { - if (loadedSavedQuery) { - onSave(); - } else { - onSaveAsNew(); - } - }} - > - {isDirty - ? i18n.translate('data.search.searchBar.savedQueryPopoverSaveChangesButtonText', { - defaultMessage: 'Save changes', - }) - : i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { - defaultMessage: 'Save', - })} - + onClearSavedQuery()}> + {loadedSavedQuery && + i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { + defaultMessage: 'Clear', + })} + - )} - - +
    + ) : ( + '' + )}
    ); }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index e0f3b9e8e035d..07281eb91468a 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -330,6 +330,8 @@ class SearchBarUI extends Component { onSave={this.onInitiateSave} onSaveAsNew={this.onInitiateSaveNew} onLoad={this.onLoadSavedQuery} + query={this.state.query} + onClearSavedQuery={this.props.onClearSavedQuery} > ); From ae1697a68991dd0b670a8b24c2f3cc1ffc226749 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 19 Jul 2019 16:39:57 -0700 Subject: [PATCH 087/189] Button grouping --- .../components/saved_query_manager.tsx | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index a748640d8e20b..3592adde5de71 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -145,34 +145,36 @@ export const SavedQueryManager: FunctionComponent = ({ )} {query.query !== '' ? ( - - {showSaveQuery && ( + + {showSaveQuery && isDirty && loadedSavedQuery && ( - { - if (loadedSavedQuery) { - onSave(); - } else { - onSaveAsNew(); - } - }} - > - {isDirty && loadedSavedQuery - ? i18n.translate('data.search.searchBar.savedQueryPopoverSaveChangesButtonText', { - defaultMessage: 'Save changes', - }) - : i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { - defaultMessage: 'Save', + + + onSaveAsNew()}> + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { + defaultMessage: 'Save As New', })} - + + + + + onSave()}> + {i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', + { + defaultMessage: 'Save changes', + } + )} + + + )} - {showSaveQuery && isDirty && loadedSavedQuery && ( + {showSaveQuery && !isDirty && ( - onSaveAsNew()}> - {i18n.translate('data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { - defaultMessage: 'Save As New', + onSave()}> + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { + defaultMessage: 'Save', })} From 2e1f903953f2ff667a9866f75fd335b3dd33cc8d Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 1 Aug 2019 18:16:01 -0400 Subject: [PATCH 088/189] Add files missed in merge commit --- src/legacy/core_plugins/data/public/index.ts | 2 +- .../query_bar/components/query_bar.test.tsx | 12 ++ .../query/query_bar/components/query_bar.tsx | 32 +-- .../search_bar/components/search_bar.tsx | 199 +++++++----------- .../data/public/search/search_service.ts | 2 +- .../public/top_nav_menu/top_nav_menu.tsx | 5 + .../ui/public/kbn_top_nav/kbn_top_nav2.js | 5 + src/plugins/data/public/index.ts | 2 +- 8 files changed, 120 insertions(+), 139 deletions(-) diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index 35b80697da494..1a739afead823 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -30,7 +30,7 @@ export function plugin() { export { ExpressionRenderer, ExpressionRendererProps, ExpressionRunner } from './expressions'; /** @public types */ -export { IndexPattern, IndexPatterns, StaticIndexPattern, StaticIndexPatternField, Field } from './index_patterns'; +export { IndexPattern, IndexPatterns, StaticIndexPattern, Field } from './index_patterns'; export { Query, QueryBar, QueryBarInput } from './query'; export { FilterBar, ApplyFiltersPopover } from './filter'; export { SearchBar, SearchBarProps, SavedQueryAttributes, SavedQuery } from './search'; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx index 27895f0b9c761..c01ef7e755391 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.test.tsx @@ -122,6 +122,8 @@ describe('QueryBar', () => { const component = shallowWithIntl( { const component = shallowWithIntl( { const component = shallowWithIntl( { { { const component = shallowWithIntl( void; - onChange: (payload: { dateRange: DateRange; query: Query }) => void; + onChange: (payload: { dateRange: DateRange; query?: Query }) => void; disableAutoFocus?: boolean; appName: string; screenTitle?: string; indexPatterns?: Array; - store: Storage; + store?: Storage; intl: InjectedIntl; prepend?: React.ReactNode; showQueryInput?: boolean; @@ -70,11 +70,11 @@ interface Props { showSaveQuery?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; customSubmitButton?: any; - onSave: () => void; - onSaveNew: () => void; - onLoadSavedQuery: (savedQuery: SavedQuery) => void; + onSave?: () => void; + onSaveNew?: () => void; + onLoadSavedQuery?: (savedQuery: SavedQuery) => void; isDirty: boolean; - onClearSavedQuery: () => void; + onClearSavedQuery?: () => void; } interface State { @@ -82,7 +82,6 @@ interface State { } export class QueryBarUI extends Component { - public static defaultProps = { showQueryInput: true, showDatePicker: true, @@ -152,7 +151,7 @@ export class QueryBarUI extends Component { ); }; - public onSubmit = ({ query, dateRange }: { query: Query; dateRange: DateRange }) => { + public onSubmit = ({ query, dateRange }: { query?: Query; dateRange: DateRange }) => { this.handleLuceneSyntaxWarning(); timeHistory.add(this.getDateRange()); @@ -206,13 +205,13 @@ export class QueryBarUI extends Component { onChange={this.onQueryChange} onSubmit={this.onInputSubmit} persistedLog={this.persistedLog} - savedQuery={this.props.savedQuery} - showSaveQuery={this.props.showSaveQuery} - onSave={this.props.onSave} - onSaveNew={this.props.onSaveNew} - onLoadSavedQuery={this.props.onLoadSavedQuery} - isDirty={this.props.isDirty} - onClearSavedQuery={this.props.onClearSavedQuery} + savedQuery={this.props.savedQuery} + showSaveQuery={this.props.showSaveQuery} + onSave={this.props.onSave} + onSaveNew={this.props.onSaveNew} + onLoadSavedQuery={this.props.onLoadSavedQuery} + isDirty={this.props.isDirty} + onClearSavedQuery={this.props.onClearSavedQuery} /> ); @@ -300,7 +299,7 @@ export class QueryBarUI extends Component { if ( language === 'kuery' && typeof query === 'string' && - !store.get('kibana.luceneSyntaxWarningOptOut') && + (!store || !store.get('kibana.luceneSyntaxWarningOptOut')) && doesKueryExpressionHaveLuceneSyntaxError(query) ) { const toast = toastNotifications.addWarning({ @@ -341,6 +340,7 @@ export class QueryBarUI extends Component { } private onLuceneSyntaxWarningOptOut(toast: Toast) { + if (!this.props.store) return; this.props.store.set('kibana.luceneSyntaxWarningOptOut', true); toastNotifications.remove(toast); } diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 11a04fd4ad993..e7f4e3eab776f 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -19,7 +19,6 @@ import { Filter } from '@kbn/es-query'; import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; @@ -69,8 +68,8 @@ export interface SearchBarProps { showSaveQuery?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; onSaved?: (savedQuery: SavedQuery) => void; - onSavedQueryUpdated: (savedQuery: SavedQuery) => void; - onClearSavedQuery: () => void; + onSavedQueryUpdated?: (savedQuery: SavedQuery) => void; + onClearSavedQuery?: () => void; customSubmitButton?: any; } @@ -79,8 +78,8 @@ interface State { showSaveQueryModal: boolean; showSaveNewQueryModal: boolean; showSavedQueryPopover: boolean; - currentProps?: Props; - query: Query; + currentProps?: SearchBarProps; + query?: Query; dateRangeFrom: string; dateRangeTo: string; } @@ -96,18 +95,22 @@ class SearchBarUI extends Component { public filterBarRef: Element | null = null; public filterBarWrapperRef: Element | null = null; - public static getDerivedStateFromProps(nextProps: Props, prevState: State) { + public static getDerivedStateFromProps(nextProps: SearchBarProps, prevState: State) { if (isEqual(prevState.currentProps, nextProps)) { return null; } let nextQuery = null; - if (nextProps.query.query !== get(prevState, 'currentProps.query.query')) { + if (nextProps.query && nextProps.query.query !== get(prevState, 'currentProps.query.query')) { nextQuery = { query: nextProps.query.query, language: nextProps.query.language, }; - } else if (nextProps.query.language !== prevState.query.language) { + } else if ( + nextProps.query && + prevState.query && + nextProps.query.language !== prevState.query.language + ) { nextQuery = { query: '', language: nextProps.query.language, @@ -156,32 +159,23 @@ class SearchBarUI extends Component { showSaveNewQueryModal: false, showSavedQueryPopover: false, currentProps: this.props, - query: { - query: this.props.query.query, - language: this.props.query.language, - }, + query: this.props.query ? { ...this.props.query } : undefined, dateRangeFrom: _.get(this.props, 'dateRangeFrom', 'now-15m'), dateRangeTo: _.get(this.props, 'dateRangeTo', 'now'), }; public isDirty = () => { - if (!this.props.showDatePicker) { + if (!this.props.showDatePicker && this.state.query && this.props.query) { return this.state.query.query !== this.props.query.query; } return ( - this.state.query.query !== this.props.query.query || + (this.state.query && this.props.query && this.state.query.query !== this.props.query.query) || this.state.dateRangeFrom !== this.props.dateRangeFrom || this.state.dateRangeTo !== this.props.dateRangeTo ); }; - private getFilterLength() { - if (this.props.showFilterBar && this.props.filters) { - return this.props.filters.length; - } - } - private getFilterUpdateFunction() { if (this.props.showFilterBar && this.props.onFiltersUpdated) { return this.props.onFiltersUpdated; @@ -192,7 +186,7 @@ class SearchBarUI extends Component { private shouldRenderQueryBar() { const showDatePicker = this.props.showDatePicker || this.props.showAutoRefreshOnly; const showQueryInput = - this.props.showQueryInput && this.props.indexPatterns && this.props.query; + this.props.showQueryInput && this.props.indexPatterns && this.state.query; return this.props.showQueryBar && (showDatePicker || showQueryInput); } @@ -200,46 +194,6 @@ class SearchBarUI extends Component { return this.props.showFilterBar && this.props.filters && this.props.indexPatterns; } - private getFilterTriggerButton() { - const filterCount = this.getFilterLength(); - const filtersAppliedText = this.props.intl.formatMessage( - { - id: 'data.search.searchBar.searchBar.filtersButtonFiltersAppliedTitle', - defaultMessage: - '{filterCount} {filterCount, plural, one {filter} other {filters}} applied.', - }, - { - filterCount, - } - ); - const clickToShowOrHideText = this.state.isFiltersVisible - ? this.props.intl.formatMessage({ - id: 'data.search.searchBar.searchBar.filtersButtonClickToShowTitle', - defaultMessage: 'Select to hide', - }) - : this.props.intl.formatMessage({ - id: 'data.search.searchBar.searchBar.filtersButtonClickToHideTitle', - defaultMessage: 'Select to show', - }); - - return ( - - {i18n.translate('data.search.searchBar.searchBar.filtersButtonLabel', { - defaultMessage: 'Filters', - description: 'The noun "filter" in plural.', - })} - - ); - } - public setFilterBarHeight = () => { requestAnimationFrame(() => { const height = @@ -256,6 +210,8 @@ class SearchBarUI extends Component { /* eslint-enable */ public onSave = async (savedQueryMeta: SavedQueryMeta, saveAsNew = false) => { + if (!this.state.query) return; + const savedQueryAttributes: SavedQueryAttributes = { title: savedQueryMeta.title, description: savedQueryMeta.description, @@ -329,7 +285,7 @@ class SearchBarUI extends Component { }); }; - public onQueryBarChange = (queryAndDateRange: { dateRange: DateRange; query: Query }) => { + public onQueryBarChange = (queryAndDateRange: { dateRange: DateRange; query?: Query }) => { this.setState({ query: queryAndDateRange.query, dateRangeFrom: queryAndDateRange.dateRange.from, @@ -337,7 +293,7 @@ class SearchBarUI extends Component { }); }; - public onQueryBarSubmit = (queryAndDateRange: { dateRange?: DateRange; query: Query }) => { + public onQueryBarSubmit = (queryAndDateRange: { dateRange?: DateRange; query?: Query }) => { this.setState( { query: queryAndDateRange.query, @@ -348,13 +304,15 @@ class SearchBarUI extends Component { (queryAndDateRange.dateRange && queryAndDateRange.dateRange.to) || this.state.dateRangeTo, }, () => { - this.props.onQuerySubmit({ - query: this.state.query, - dateRange: { - from: this.state.dateRangeFrom, - to: this.state.dateRangeTo, - }, - }); + if (this.props.onQuerySubmit) { + this.props.onQuerySubmit({ + query: this.state.query, + dateRange: { + from: this.state.dateRangeFrom, + to: this.state.dateRangeTo, + }, + }); + } } ); }; @@ -373,7 +331,9 @@ class SearchBarUI extends Component { dateRangeTo, }); - this.props.onSavedQueryUpdated(savedQuery); + if (this.props.onSavedQueryUpdated) { + this.props.onSavedQueryUpdated(savedQuery); + } }; public componentDidMount() { @@ -391,7 +351,7 @@ class SearchBarUI extends Component { } public render() { - const savedQueryManager = ( + const savedQueryManager = this.state.query && this.props.onClearSavedQuery && ( { ); } - let filterBar; - if (this.shouldRenderFilterBar()) { - const filterGroupClasses = classNames('globalFilterGroup__wrapper', { - 'globalFilterGroup__wrapper-isVisible': this.state.isFiltersVisible, - }); - filterBar = ( -
    { this.filterBarWrapperRef = node; }} className={filterGroupClasses} - > + >
    { - this.filterBarRef = node; - }} + ref={node => { + this.filterBarRef = node; + }} > - +
    -
    - ); - } +
    + ); + } - return ( -
    - {queryBar} - {filterBar} - - {this.state.showSaveQueryModal ? ( - this.setState({ showSaveQueryModal: false })} - showFilterOption={this.props.showFilterBar} - showTimeFilterOption={this.props.showDatePicker} - /> - ) : null} - {this.state.showSaveNewQueryModal ? ( - this.onSave(savedQueryMeta, true)} - onClose={() => this.setState({ showSaveNewQueryModal: false })} - showFilterOption={this.props.showFilterBar} - showTimeFilterOption={this.props.showDatePicker} - /> - ) : null} -
    - ); - } - } + return ( +
    + {queryBar} + {filterBar} + + {this.state.showSaveQueryModal ? ( + this.setState({ showSaveQueryModal: false })} + showFilterOption={this.props.showFilterBar} + showTimeFilterOption={this.props.showDatePicker} + /> + ) : null} + {this.state.showSaveNewQueryModal ? ( + this.onSave(savedQueryMeta, true)} + onClose={() => this.setState({ showSaveNewQueryModal: false })} + showFilterOption={this.props.showFilterBar} + showTimeFilterOption={this.props.showDatePicker} + /> + ) : null} +
    + ); + } +} - export const SearchBar = injectI18n(SearchBarUI); - } +export const SearchBar = injectI18n(SearchBarUI); diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index b3b4ab5ed6078..9ea2d28cf7fcb 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -17,7 +17,7 @@ * under the License. */ -import { SearchBar, setupDirective as setupSearchBarDirective } from './search_bar'; +import { SearchBar } from './search_bar'; import * as savedQueryService from './search_bar/lib/saved_query_service'; /** diff --git a/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.tsx b/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.tsx index e0c705ece7b4b..cb805990c21b9 100644 --- a/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.tsx +++ b/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.tsx @@ -76,6 +76,11 @@ export function TopNavMenu(props: Props) { refreshInterval={props.refreshInterval} indexPatterns={props.indexPatterns} store={props.store} + savedQuery={props.savedQuery} + showSaveQuery={props.showSaveQuery} + onClearSavedQuery={props.onClearSavedQuery} + onSaved={props.onSaved} + onSavedQueryUpdated={props.onSavedQueryUpdated} /> ); } diff --git a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav2.js b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav2.js index 8c1feb922a25b..51e8841f7fbf0 100644 --- a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav2.js +++ b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav2.js @@ -88,6 +88,7 @@ module.directive('kbnTopNavV2Helper', (reactDirective) => { ['disabledButtons', { watchDepth: 'reference' }], ['query', { watchDepth: 'reference' }], + ['savedQuery', { watchDepth: 'reference' }], ['store', { watchDepth: 'reference' }], ['intl', { watchDepth: 'reference' }], ['store', { watchDepth: 'reference' }], @@ -95,6 +96,9 @@ module.directive('kbnTopNavV2Helper', (reactDirective) => { ['onQuerySubmit', { watchDepth: 'reference' }], ['onFiltersUpdated', { watchDepth: 'reference' }], ['onRefreshChange', { watchDepth: 'reference' }], + ['onClearSavedQuery', { watchDepth: 'reference' }], + ['onSaved', { watchDepth: 'reference' }], + ['onSavedQueryUpdated', { watchDepth: 'reference' }], ['indexPatterns', { watchDepth: 'collection' }], ['filters', { watchDepth: 'collection' }], @@ -106,6 +110,7 @@ module.directive('kbnTopNavV2Helper', (reactDirective) => { 'showQueryBar', 'showQueryInput', 'showDatePicker', + 'showSaveQuery', 'appName', 'screenTitle', diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 532a062b5b8da..97bb5ecfd0af6 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -17,7 +17,7 @@ * under the License. */ -import { PluginInitializerContext } from '../../../core/public'; +import { PluginInitializerContext } from 'kibana/public'; import { DataPublicPlugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { From f972fa3215ce97436917a15938c2659bd048bc03 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 1 Aug 2019 18:41:18 -0400 Subject: [PATCH 089/189] Fix infinite loop and refresh saved query list when management popover is opened. --- .../search/search_bar/components/saved_query_manager.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 3592adde5de71..d683517f7c3b9 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -64,8 +64,10 @@ export const SavedQueryManager: FunctionComponent = ({ const sortedAllSavedQueries = sortBy(allSavedQueries, 'attributes.title'); setSavedQueries(sortedAllSavedQueries); }; - fetchQueries(); - }); // we need the effect to trigger on every render because saved queries change and we need the updates. + if (isOpen) { + fetchQueries(); + } + }, [isOpen]); const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', From 564339e7c2c47c9157887a3da57900d65042c13a Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Fri, 2 Aug 2019 13:03:18 -0400 Subject: [PATCH 090/189] Remove saved queries from maps for now --- .../search_bar/components/search_bar.tsx | 4 +- x-pack/legacy/plugins/maps/index.js | 6 +- .../maps/public/angular/map_controller.js | 86 ------------------- .../filter_editor/filter_editor.js | 10 ++- .../join_editor/resources/where_expression.js | 10 ++- 5 files changed, 17 insertions(+), 99 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index e7f4e3eab776f..3210564f361a8 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -70,7 +70,7 @@ export interface SearchBarProps { onSaved?: (savedQuery: SavedQuery) => void; onSavedQueryUpdated?: (savedQuery: SavedQuery) => void; onClearSavedQuery?: () => void; - customSubmitButton?: any; + customSubmitButton?: React.ReactNode; } interface State { @@ -375,7 +375,7 @@ class SearchBarUI extends Component { appName={this.props.appName} indexPatterns={this.props.indexPatterns} store={this.props.store} - prepend={this.props.showFilterBar ? savedQueryManager : ''} + prepend={this.props.showFilterBar ? savedQueryManager : undefined} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} dateRangeTo={this.state.dateRangeTo} diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index f84fa5f8b48c9..2934a44504bcf 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -119,15 +119,15 @@ export function maps(kibana) { privileges: { all: { savedObject: { - all: ['map', 'query'], + all: ['map'], read: ['index-pattern'] }, - ui: ['save', 'show', 'saveQuery'], + ui: ['save', 'show'], }, read: { savedObject: { all: [], - read: ['map', 'index-pattern', 'query'] + read: ['map', 'index-pattern'] }, ui: ['show'], }, diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 1bb24e6d8a169..49f771bc23964 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -52,9 +52,6 @@ import { MAP_SAVED_OBJECT_TYPE, MAP_APP_PATH } from '../../common/constants'; -import { data } from 'plugins/data/setup'; -data.search.loadLegacyDirectives(); -const { savedQueryService } = data.search.services; const REACT_ANCHOR_DOM_ELEMENT_ID = 'react-maps-root'; @@ -132,90 +129,7 @@ app.controller('GisMapController', ($scope, $route, kbnUrl, localStorage, AppSta store.dispatch(setRefreshConfig($scope.refreshConfig)); }; - /* - START QueryBar replacement with SearchBar - Saved Queries required moving the query bar state caching to the search bar. Using the search bar instead of the query bar in Maps requires obscuring the filter functionality within the app. - The following is allows us to use the search bar without the filters. - */ - $scope.filters = []; - $scope.onFiltersUpdated = function () { - return; - }; - $scope.showFilterBar = function () { - return $scope.filters.length > 0; - }; - $scope.showQueryBar = function () { - return true; - }; - /* END QueryBar replacement with SearchBar */ - - /* Saved Queries */ - $scope.onQuerySaved = savedQuery => { - $scope.savedQuery = savedQuery; - }; - - $scope.onSavedQueryUpdated = savedQuery => { - $scope.savedQuery = savedQuery; - }; - - $scope.onClearSavedQuery = () => { - delete $scope.savedQuery; - delete $state.savedQuery; - $scope.query = { - query: '', - language: localStorage.get('kibana.userQueryLanguage') || chrome.getUiSettingsClient().get('search:queryLanguage') - }; - syncAppAndGlobalState(); - store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time })); - }; - - const updateStateFromSavedQuery = (savedQuery) => { - $scope.query = savedQuery.attributes.query; - if (savedQuery.attributes.timefilter) { - $scope.time = { - from: savedQuery.attributes.timefilter.timeFrom, - to: savedQuery.attributes.timefilter.timeTo, - }; - if (savedQuery.attributes.timefilter.refreshInterval) { - $scope.refreshInterval = savedQuery.attributes.timefilter.refreshInterval; - } - } - syncAppAndGlobalState(); - store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time })); - }; - - $scope.$watch('savedQuery', (newSavedQuery, oldSavedQuery) => { - if (!newSavedQuery) return; - $state.savedQuery = newSavedQuery.id; - - if (newSavedQuery.id === (oldSavedQuery && oldSavedQuery.id)) { - updateStateFromSavedQuery(newSavedQuery); - } - }); - - $scope.$watch(() => $state.savedQuery, newSavedQueryId => { - if (!newSavedQueryId) { - $scope.savedQuery = undefined; - return; - } - - savedQueryService.getSavedQuery(newSavedQueryId).then((savedQuery) => { - $scope.$evalAsync(() => { - $scope.savedQuery = savedQuery; - updateStateFromSavedQuery(savedQuery); - }); - }); - }); - - $scope.showSaveQuery = capabilities.get().maps.saveQuery; - - $scope.$watch(() => capabilities.get().maps.saveQuery, (newCapability) => { - $scope.showSaveQuery = newCapability; - }); - /* end Saved Queries */ - function hasUnsavedChanges() { - const state = store.getState(); const layerList = getLayerListRaw(state); const layerListConfigOnly = copyPersistentState(layerList); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js index ad74b57389355..a30582dc313f5 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { indexPatternService } from '../../../kibana_services'; import { Storage } from 'ui/storage'; -import { QueryBar } from 'plugins/data'; +import { SearchBar } from 'plugins/data'; const settings = chrome.getUiSettingsClient(); const localStorage = new Storage(window.localStorage); @@ -91,11 +91,13 @@ export class FilterEditor extends Component { anchorPosition="leftCenter" >
    - - Date: Fri, 2 Aug 2019 13:22:31 -0400 Subject: [PATCH 091/189] Correct spacing between query manager buttons --- .../public/search/search_bar/components/saved_query_manager.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index d683517f7c3b9..5f27a474180ca 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -181,6 +181,7 @@ export const SavedQueryManager: FunctionComponent = ({ )} + onClearSavedQuery()}> {loadedSavedQuery && From c5aaa306cc8dc36d790f949c57bcbdb016ce7ba0 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Fri, 2 Aug 2019 13:45:56 -0400 Subject: [PATCH 092/189] Hook up delete button in saved query manager --- .../search_bar/components/saved_query_manager.tsx | 14 ++++++++++++-- .../search/search_bar/lib/saved_query_service.ts | 5 +++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 5f27a474180ca..806a24b8edcf1 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -31,7 +31,7 @@ import { i18n } from '@kbn/i18n'; import React, { FunctionComponent, useEffect, useState, Fragment } from 'react'; import { sortBy } from 'lodash'; import { SavedQuery } from '../index'; -import { getAllSavedQueries } from '../lib/saved_query_service'; +import { getAllSavedQueries, deleteSavedQuery } from '../lib/saved_query_service'; import { Query } from '../../../query'; interface Props { @@ -83,6 +83,16 @@ export const SavedQueryManager: FunctionComponent = ({ } ); + const onDeleteSavedQuery = (savedQuery: SavedQuery) => { + setSavedQueries(savedQueries.filter(currentSavedQuery => currentSavedQuery !== savedQuery)); + + if (loadedSavedQuery && loadedSavedQuery.id === savedQuery.id) { + onClearSavedQuery(); + } + + deleteSavedQuery(savedQuery.id); + }; + const filterTriggerButton = ( = ({ {savedQuery.attributes.title} alert(`saved query ${savedQuery.id} to be deleted`)} + onClick={() => onDeleteSavedQuery(savedQuery)} iconType="trash" color="danger" /> diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index f30953de76e11..da44d367c93ae 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -119,6 +119,11 @@ export const getSavedQuery = async (id: string): Promise => { return parseSavedQueryObject(response); }; +export const deleteSavedQuery = async (id: string) => { + const savedObjectsClient = chrome.getSavedObjectsClient(); + return await savedObjectsClient.delete('query', id); +}; + const parseSavedQueryObject = (savedQuery: { id: string; attributes: SerializedSavedQueryAttributes; From b44c416efce240d1f1218111a23ab1a6c0943e90 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 2 Aug 2019 11:36:22 -0700 Subject: [PATCH 093/189] resolved type error, adds query to expectInvalidTypeSpecified array --- src/plugins/data/public/index.ts | 2 +- .../test/saved_object_api_integration/common/suites/export.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 97bb5ecfd0af6..532a062b5b8da 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -17,7 +17,7 @@ * under the License. */ -import { PluginInitializerContext } from 'kibana/public'; +import { PluginInitializerContext } from '../../../core/public'; import { DataPublicPlugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/x-pack/test/saved_object_api_integration/common/suites/export.ts b/x-pack/test/saved_object_api_integration/common/suites/export.ts index 548f803b1a969..5f1eef440b6f4 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/export.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/export.ts @@ -60,7 +60,7 @@ export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest Date: Fri, 2 Aug 2019 13:06:40 -0700 Subject: [PATCH 094/189] deletes unused translations --- x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 023df1ea6591c..5c62020eb6111 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -798,7 +798,6 @@ "data.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) は、シンプルなクエリ構文とスクリプトフィールドのサポートを提供します。また、KQL はベーシックライセンス以上をご利用の場合、自動入力も提供します。KQL をオフにすると、Kibana は Lucene を使用します。", "data.query.queryBar.syntaxOptionsDescription.docsLinkText": "こちら", "data.query.queryBar.syntaxOptionsTitle": "構文オプション", - "data.search.searchBar.searchBar.filtersButtonClickToHideTitle": "選択して表示", "data.search.searchBar.searchBar.filtersButtonClickToShowTitle": "選択して非表示", "embeddableApi.actionPanel.title": "オプション", "embeddableApi.actions.applyFilterActionTitle": "現在のビューにフィルターを適用", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 7d4d5b6f8b9d9..a051d925c83f9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -798,7 +798,6 @@ "data.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) 提供简化查询语法并支持脚本字段。如果您具有基本许可或更高级别的许可,KQL 还提供自动填充功能。如果关闭 KQL,Kibana 将使用 Lucene。", "data.query.queryBar.syntaxOptionsDescription.docsLinkText": "此处", "data.query.queryBar.syntaxOptionsTitle": "语法选项", - "data.search.searchBar.searchBar.filtersButtonClickToHideTitle": "选择以显示", "data.search.searchBar.searchBar.filtersButtonClickToShowTitle": "选择以隐藏", "embeddableApi.actionPanel.title": "选项", "embeddableApi.actions.applyFilterActionTitle": "将筛选应用于当前视图", From e99bfcf8206df9fb08f27c74efedb62653e4576b Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 2 Aug 2019 13:13:40 -0700 Subject: [PATCH 095/189] Adds jest tests for saved_query_service --- .../lib/saved_query_service.test.ts | 75 ++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index f25cc43547086..56846a3394af3 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -18,7 +18,7 @@ */ import { SavedQueryAttributes } from '../index'; -import { saveQuery } from './saved_query_service'; +import { saveQuery, findSavedQueries, getSavedQuery } from './saved_query_service'; import { FilterStateStore } from '@kbn/es-query'; const savedQueryAttributes: SavedQueryAttributes = { @@ -56,6 +56,8 @@ const savedQueryAttributesWithFilters: SavedQueryAttributes = { const mockSavedObjectsClient = { create: jest.fn(), error: jest.fn(), + find: jest.fn(), + get: jest.fn(), }; jest.mock('ui/chrome', () => { @@ -69,6 +71,8 @@ jest.mock('ui/chrome', () => { describe('saved query service', () => { afterEach(() => { mockSavedObjectsClient.create.mockReset(); + mockSavedObjectsClient.find.mockReset(); + mockSavedObjectsClient.get.mockReset(); }); describe('saveQuery', function() { @@ -134,9 +138,74 @@ describe('saved query service', () => { } expect(error).not.toBe(null); }); + }); + describe('findSavedQueries', function() { + it('should find and return saved queries without search text', async () => { + mockSavedObjectsClient.find.mockReturnValue({ + savedObjects: [{ id: '1234', attributes: savedQueryAttributes }], + }); + + const response = await findSavedQueries(); + expect(response).toEqual([{ id: '1234', attributes: savedQueryAttributes }]); + }); + + it('should find and return saved queries with search text matching the title field', async () => { + mockSavedObjectsClient.find.mockReturnValue({ + savedObjects: [{ id: '1234', attributes: savedQueryAttributes }], + }); + const response = await findSavedQueries('foo'); + expect(mockSavedObjectsClient.find).toHaveBeenCalledWith({ + search: 'foo', + searchFields: ['title^5', 'description'], + sortField: '_score', + type: 'query', + }); + expect(response).toEqual([{ id: '1234', attributes: savedQueryAttributes }]); + }); + it('should find and return parsed filters and timefilters items', async () => { + const serializedSavedQueryAttributesWithFilters = { + ...savedQueryAttributesWithFilters, + filters: JSON.stringify(savedQueryAttributesWithFilters.filters), + timefilter: JSON.stringify(savedQueryAttributesWithFilters.timefilter), + }; + mockSavedObjectsClient.find.mockReturnValue({ + savedObjects: [{ id: '1234', attributes: serializedSavedQueryAttributesWithFilters }], + }); + const response = await findSavedQueries('bar'); + expect(response).toEqual([{ id: '1234', attributes: savedQueryAttributesWithFilters }]); + }); + it('should return an array of saved queries', async () => { + mockSavedObjectsClient.find.mockReturnValue({ + savedObjects: [{ id: '1234', attributes: savedQueryAttributes }], + }); + const response = await findSavedQueries(); + expect(response).toEqual( + expect.objectContaining([ + { + attributes: { + description: 'bar', + query: { language: 'kuery', query: 'response:200' }, + title: 'foo', + }, + id: '1234', + }, + ]) + ); + }); + }); + + describe('getSavedQuery', function() { + it('should retrieve a saved query by id', async () => { + mockSavedObjectsClient.get.mockReturnValue({ id: '1234', attributes: savedQueryAttributes }); + + const response = await getSavedQuery('1234'); + expect(response).toEqual({ id: '1234', attributes: savedQueryAttributes }); + }); + it('should only return saved queries', async () => { + mockSavedObjectsClient.get.mockReturnValue({ id: '1234', attributes: savedQueryAttributes }); - it('should test all the other service methods', () => { - throw new Error('implement me'); + await getSavedQuery('1234'); + expect(mockSavedObjectsClient.get).toHaveBeenCalledWith('query', '1234'); }); }); }); From ce480c61f04c9087854995b9f6f0c7709dabe1e1 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 2 Aug 2019 14:39:33 -0700 Subject: [PATCH 096/189] Adds description to the saved search popover on save --- .../search_bar/components/save_query_form.tsx | 12 ++ .../components/saved_query_manager.tsx | 140 +++++++++--------- .../public/discover/controllers/discover.js | 3 + .../components/saved_object_save_modal.tsx | 7 + 4 files changed, 96 insertions(+), 66 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index 73411877dae89..43eacf167d3f4 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -32,6 +32,8 @@ import { EuiFieldText, EuiSwitch, } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { SavedQueryAttributes } from '../index'; interface Props { @@ -64,9 +66,19 @@ export const SaveQueryForm: FunctionComponent = ({ const [shouldIncludeTimefilter, setIncludeTimefilter] = useState( !!(savedQuery && savedQuery.timefilter) ); + const savedQueryDescriptionText = i18n.translate( + 'data.search.searchBar.savedQueryDescriptionText', + { + defaultMessage: + 'Saved queries allow you to save common search snippets and filters for later use.', + } + ); const saveQueryForm = ( + + {savedQueryDescriptionText} + = ({ const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', { - defaultMessage: 'Saved queries allow you to store sets of queries, filters and time filters.', + defaultMessage: + 'Saved queries allow you to save common search snippets and filters for later use.', } ); @@ -136,74 +137,81 @@ export const SavedQueryManager: FunctionComponent = ({ }} anchorPosition="downLeft" > - {savedQueryPopoverTitleText} - {savedQueries.length > 0 ? ( - - - {savedQueryDescriptionText} - - - -
      {savedQueryRows}
    -
    -
    -
    - ) : ( - -

    - There are no saved queries. You can save search snippets and filters for later use.{' '} - this, enter a query and click 'Save query for reuse'. -

    -
    - )} - {query.query !== '' ? ( - - {showSaveQuery && isDirty && loadedSavedQuery && ( - - - - onSaveAsNew()}> - {i18n.translate('data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { - defaultMessage: 'Save As New', - })} - - +
    + {savedQueryPopoverTitleText} + {savedQueries.length > 0 ? ( + + + + {savedQueryDescriptionText} + + + + +
      {savedQueryRows}
    +
    +
    +
    + ) : ( + +

    + There are no saved queries. You can save search snippets and filters for later use.{' '} + this, enter a query and click 'Save query for reuse'. +

    +
    + )} + {query.query !== '' ? ( + + {showSaveQuery && isDirty && loadedSavedQuery && ( + + + + onSaveAsNew()}> + {i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', + { + defaultMessage: 'Save As New', + } + )} + + - - onSave()}> - {i18n.translate( - 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', - { - defaultMessage: 'Save changes', - } - )} - - - - - )} - {showSaveQuery && !isDirty && ( + + onSave()}> + {i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', + { + defaultMessage: 'Save changes', + } + )} + + + + + )} + {showSaveQuery && !isDirty && ( + + onSave()}> + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { + defaultMessage: 'Save', + })} + + + )} + - onSave()}> - {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { - defaultMessage: 'Save', - })} - + onClearSavedQuery()}> + {loadedSavedQuery && + i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { + defaultMessage: 'Clear', + })} + - )} - - - onClearSavedQuery()}> - {loadedSavedQuery && - i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { - defaultMessage: 'Clear', - })} - - - - ) : ( - '' - )} + + ) : ( + '' + )} +
    ); }; diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index eb8a4726bc1d7..8ef6483de556e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -291,6 +291,9 @@ function discoverController( title={savedSearch.title} showCopyOnSave={savedSearch.id ? true : false} objectType="search" + description={i18n.translate('kbn.discover.localMenu.saveSaveSearchDescription', { + defaultMessage: 'Save your Discover search so you can use it in visualizations and dashboards', + })} />); showSaveModal(saveModal); } diff --git a/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx b/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx index 2abf598b23ad0..b6802758b958a 100644 --- a/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx +++ b/src/legacy/ui/public/saved_objects/components/saved_object_save_modal.tsx @@ -34,6 +34,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Fragment } from 'react'; +import { EuiText } from '@elastic/eui'; interface OnSaveProps { newTitle: string; @@ -50,6 +51,7 @@ interface Props { objectType: string; confirmButtonLabel?: React.ReactNode; options?: React.ReactNode; + description?: string; } interface State { @@ -94,6 +96,11 @@ export class SavedObjectSaveModal extends React.Component { {this.renderDuplicateTitleCallout()} + {this.props.description && ( + + {this.props.description} + + )} {this.renderCopyOnSave()} Date: Fri, 2 Aug 2019 15:01:58 -0700 Subject: [PATCH 097/189] fixes unused translations --- x-pack/plugins/translations/translations/ja-JP.json | 13 ++++++------- x-pack/plugins/translations/translations/zh-CN.json | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5c62020eb6111..a70b8b2da705a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -481,11 +481,6 @@ "common.ui.indexPattern.unableWriteLabel": "インデックスパターンを書き込めません!このインデックスパターンへの最新の変更を取得するにな、ページを更新してください。", "common.ui.indexPattern.unknownFieldErrorMessage": "インデックスパターン「{title}」のフィールド「{name}」が不明なフィールドタイプを使用しています。", "common.ui.indexPattern.unknownFieldHeader": "不明なフィールドタイプ {type}", - "inspector.closeButton": "インスペクターを閉じる", - "inspector.reqTimestampDescription": "リクエストの開始が記録された時刻です", - "inspector.reqTimestampKey": "リクエストのタイムスタンプ", - "inspector.title": "インスペクター", - "inspector.view": "{viewName} を表示", "common.ui.legacyBrowserMessage": "この Kibana インストレーションは、現在ご使用のブラウザが満たしていない厳格なセキュリティ要件が有効になっています。", "common.ui.legacyBrowserTitle": "ブラウザをアップグレードしてください", "common.ui.management.breadcrumb": "管理", @@ -609,6 +604,11 @@ "common.ui.visualize.queryGeohashBounds.unableToGetBoundErrorTitle": "バウンドを取得できませんでした", "common.ui.welcomeErrorMessage": "Kibana が正常に読み込まれませんでした。詳細はサーバーアウトプットを確認してください。", "common.ui.welcomeMessage": "Kibana を読み込み中", + "inspector.closeButton": "インスペクターを閉じる", + "inspector.reqTimestampDescription": "リクエストの開始が記録された時刻です", + "inspector.reqTimestampKey": "リクエストのタイムスタンプ", + "inspector.title": "インスペクター", + "inspector.view": "{viewName} を表示", "core.chrome.legacyBrowserWarning": "ご使用のブラウザが Kibana のセキュリティ要件を満たしていません。", "core.euiBasicTable.selectAllRows": "すべての行を選択", "core.euiBasicTable.selectThisRow": "この行を選択", @@ -798,7 +798,6 @@ "data.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) は、シンプルなクエリ構文とスクリプトフィールドのサポートを提供します。また、KQL はベーシックライセンス以上をご利用の場合、自動入力も提供します。KQL をオフにすると、Kibana は Lucene を使用します。", "data.query.queryBar.syntaxOptionsDescription.docsLinkText": "こちら", "data.query.queryBar.syntaxOptionsTitle": "構文オプション", - "data.search.searchBar.searchBar.filtersButtonClickToShowTitle": "選択して非表示", "embeddableApi.actionPanel.title": "オプション", "embeddableApi.actions.applyFilterActionTitle": "現在のビューにフィルターを適用", "embeddableApi.addPanel.createNew": "新規 {factoryName} を作成", @@ -10624,4 +10623,4 @@ "xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "ログテキストが必要です。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } -} +} \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a051d925c83f9..d5e07abd7bcb4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -481,11 +481,6 @@ "common.ui.indexPattern.unableWriteLabel": "无法写入索引模式!请刷新页面以获取此索引模式的最新更改。", "common.ui.indexPattern.unknownFieldErrorMessage": "indexPattern “{title}” 中的字段 “{name}” 使用未知字段类型。", "common.ui.indexPattern.unknownFieldHeader": "未知字段类型 {type}", - "inspector.closeButton": "关闭检查器", - "inspector.reqTimestampDescription": "记录请求启动的时间", - "inspector.reqTimestampKey": "请求时间戳", - "inspector.title": "检查器", - "inspector.view": "视图:{viewName}", "common.ui.legacyBrowserMessage": "此 Kibana 安装启用了当前浏览器未满足的严格安全要求。", "common.ui.legacyBrowserTitle": "请升级您的浏览器", "common.ui.management.breadcrumb": "管理", @@ -609,6 +604,11 @@ "common.ui.visualize.queryGeohashBounds.unableToGetBoundErrorTitle": "无法获取边界", "common.ui.welcomeErrorMessage": "Kibana 未正确加载。检查服务器输出以了解详情。", "common.ui.welcomeMessage": "正在加载 Kibana", + "inspector.closeButton": "关闭检查器", + "inspector.reqTimestampDescription": "记录请求启动的时间", + "inspector.reqTimestampKey": "请求时间戳", + "inspector.title": "检查器", + "inspector.view": "视图:{viewName}", "core.chrome.legacyBrowserWarning": "您的浏览器不满足 Kibana 的安全要求。", "core.euiBasicTable.selectAllRows": "选择所有行", "core.euiBasicTable.selectThisRow": "选择此行", @@ -798,7 +798,6 @@ "data.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) 提供简化查询语法并支持脚本字段。如果您具有基本许可或更高级别的许可,KQL 还提供自动填充功能。如果关闭 KQL,Kibana 将使用 Lucene。", "data.query.queryBar.syntaxOptionsDescription.docsLinkText": "此处", "data.query.queryBar.syntaxOptionsTitle": "语法选项", - "data.search.searchBar.searchBar.filtersButtonClickToShowTitle": "选择以隐藏", "embeddableApi.actionPanel.title": "选项", "embeddableApi.actions.applyFilterActionTitle": "将筛选应用于当前视图", "embeddableApi.addPanel.createNew": "创建新的{factoryName}", @@ -10765,4 +10764,4 @@ "xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "“日志文本”必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} +} \ No newline at end of file From 968514500f61e7935fc195e052ad22bcd5df56f0 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 5 Aug 2019 13:36:59 -0400 Subject: [PATCH 098/189] Remove saved query UI from autocomplete suggestions --- .../__snapshots__/query_bar.test.tsx.snap | 7 +- .../query_bar_input.test.tsx.snap | 3 - .../query_bar/components/query_bar.test.tsx | 8 -- .../query/query_bar/components/query_bar.tsx | 14 --- .../query_bar/components/query_bar_input.tsx | 77 +----------- .../query_bar/components/saved_query_row.tsx | 119 ------------------ .../typeahead/suggestion_component.tsx | 2 - .../typeahead/suggestions_component.test.tsx | 5 +- .../typeahead/suggestions_component.tsx | 11 +- .../search_bar/components/search_bar.tsx | 6 - .../public/autocomplete_providers/index.d.ts | 5 +- .../maps/public/angular/map_controller.js | 3 +- 12 files changed, 15 insertions(+), 245 deletions(-) delete mode 100644 src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap index 9b15365254543..4122a7ac46720 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar.test.tsx.snap @@ -29,12 +29,7 @@ exports[`QueryBar Should render the given query 1`] = ` }, ] } - isDirty={false} onChange={[Function]} - onClearSavedQuery={[Function]} - onLoadSavedQuery={[Function]} - onSave={[Function]} - onSaveNew={[Function]} onSubmit={[Function]} query={ Object { @@ -107,7 +102,7 @@ exports[`QueryBar Should render the given query 1`] = ` data-test-subj="querySubmitButton" isDisabled={false} isLoading={false} - needsUpdate={true} + needsUpdate={false} onClick={[Function]} />
    diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap index 230f092157809..00576d05bdcac 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap @@ -337,7 +337,6 @@ exports[`QueryBarInput Should disable autoFocus on EuiFieldText when disableAuto
    { store={createMockStorage()} intl={null as any} onChange={noop} - onSave={noop} - onSaveNew={noop} - onLoadSavedQuery={noop} isDirty={false} - onClearSavedQuery={noop} /> ); @@ -107,11 +103,7 @@ describe('QueryBar', () => { disableAutoFocus={true} intl={null as any} onChange={noop} - onSave={noop} - onSaveNew={noop} - onLoadSavedQuery={noop} isDirty={false} - onClearSavedQuery={noop} /> ); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 57fbe23d9b08f..2a7c738c3916e 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -39,7 +39,6 @@ import { QueryBarInput } from './query_bar_input'; import { getQueryLog } from '../lib/get_query_log'; import { Query } from '../index'; -import { SavedQuery, SavedQueryAttributes } from '../../../search/search_bar'; const config = chrome.getUiSettingsClient(); @@ -50,7 +49,6 @@ interface DateRange { interface Props { query?: Query; - savedQuery?: SavedQueryAttributes; onSubmit: (payload: { dateRange: DateRange; query?: Query }) => void; onChange: (payload: { dateRange: DateRange; query?: Query }) => void; disableAutoFocus?: boolean; @@ -67,14 +65,9 @@ interface Props { isRefreshPaused?: boolean; refreshInterval?: number; showAutoRefreshOnly?: boolean; - showSaveQuery?: boolean; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; customSubmitButton?: any; - onSave?: () => void; - onSaveNew?: () => void; - onLoadSavedQuery?: (savedQuery: SavedQuery) => void; isDirty: boolean; - onClearSavedQuery?: () => void; } interface State { @@ -205,13 +198,6 @@ export class QueryBarUI extends Component { onChange={this.onQueryChange} onSubmit={this.onInputSubmit} persistedLog={this.persistedLog} - savedQuery={this.props.savedQuery} - showSaveQuery={this.props.showSaveQuery} - onSave={this.props.onSave} - onSaveNew={this.props.onSaveNew} - onLoadSavedQuery={this.props.onLoadSavedQuery} - isDirty={this.props.isDirty} - onClearSavedQuery={this.props.onClearSavedQuery} />
    ); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 8811d7825b01a..7e75c4ad908a4 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -41,15 +41,11 @@ import { QueryLanguageSwitcher } from './language_switcher'; import { SuggestionsComponent } from './typeahead/suggestions_component'; import { getQueryLog } from '../lib/get_query_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; -import { SavedQueryRow } from './saved_query_row'; -import { SavedQueryAttributes, SavedQuery } from '../../../search/search_bar'; -import { findSavedQueries } from '../../../search/search_bar/lib/saved_query_service'; interface Props { indexPatterns: Array; intl: InjectedIntl; query: Query; - savedQuery?: SavedQueryAttributes; appName: string; disableAutoFocus?: boolean; screenTitle?: string; @@ -58,14 +54,8 @@ interface Props { persistedLog?: PersistedLog; bubbleSubmitEvent?: boolean; languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; - showSaveQuery?: boolean; onChange?: (query: Query) => void; onSubmit?: (query: Query) => void; - onLoadSavedQuery?: (savedQuery: SavedQuery) => void; - onSave?: () => void; - onSaveNew?: () => void; - isDirty?: boolean; - onClearSavedQuery?: () => void; } interface State { @@ -92,7 +82,6 @@ const KEY_CODES = { const config = chrome.getUiSettingsClient(); const recentSearchType: AutocompleteSuggestionType = 'recentSearch'; -const savedQueryType: AutocompleteSuggestionType = 'savedQuery'; export class QueryBarInputUI extends Component { public state: State = { @@ -138,7 +127,6 @@ export class QueryBarInputUI extends Component { const queryString = this.getQueryString(); const recentSearchSuggestions = this.getRecentSearchSuggestions(queryString); - const savedQuerySuggestions = await this.getSavedQueriesSuggestions(queryString); const autocompleteProvider = getAutocompleteProvider(language); if ( @@ -146,7 +134,7 @@ export class QueryBarInputUI extends Component { !Array.isArray(this.state.indexPatterns) || compact(this.state.indexPatterns).length === 0 ) { - return [...recentSearchSuggestions, ...savedQuerySuggestions]; + return recentSearchSuggestions; } const indexPatterns = this.state.indexPatterns; @@ -162,8 +150,7 @@ export class QueryBarInputUI extends Component { selectionStart, selectionEnd, }); - - return [...suggestions, ...recentSearchSuggestions, ...savedQuerySuggestions]; + return [...suggestions, ...recentSearchSuggestions]; }; private getRecentSearchSuggestions = (query: string) => { @@ -183,17 +170,6 @@ export class QueryBarInputUI extends Component { }); }; - private getSavedQueriesSuggestions = async (searchText: string) => { - const savedQueries = await findSavedQueries(searchText); - return savedQueries.map((savedQuery: SavedQuery) => { - const text = toUser(savedQuery.attributes.title); - const start = 0; - const end = searchText.length; - const description = savedQuery.attributes.description; - return { type: savedQueryType, text, start, end, description, savedQuery }; - }); - }; - private updateSuggestions = debounce(async () => { const suggestions = (await this.getSuggestions()) || []; if (!this.componentIsUnmounting) { @@ -211,14 +187,6 @@ export class QueryBarInputUI extends Component { } }; - private onLoadSavedQuery = (savedQuery: SavedQuery) => { - this.updateSuggestions(); - - if (this.props.onLoadSavedQuery) { - this.props.onLoadSavedQuery(savedQuery); - } - }; - private onChange = (query: Query) => { this.updateSuggestions(); @@ -329,31 +297,23 @@ export class QueryBarInputUI extends Component { text, start, end, - savedQuery, }: { type: AutocompleteSuggestionType; text: string; start: number; end: number; - savedQuery?: SavedQuery; }) => { if (!this.inputRef) { return; } - if (savedQuery) { - this.setState({ isSuggestionsVisible: false, index: null }); - this.onLoadSavedQuery(savedQuery); - return; - } - - const queryString = this.getQueryString(); + const query = this.getQueryString(); const { selectionStart, selectionEnd } = this.inputRef; if (selectionStart === null || selectionEnd === null) { return; } - const value = queryString.substr(0, selectionStart) + queryString.substr(selectionEnd); + const value = query.substr(0, selectionStart) + query.substr(selectionEnd); const newQueryString = value.substr(0, start) + text + value.substr(end); this.onQueryStringChange(newQueryString); @@ -468,7 +428,6 @@ export class QueryBarInputUI extends Component { 'indexPatterns', 'intl', 'query', - 'savedQuery', 'appName', 'disableAutoFocus', 'screenTitle', @@ -479,30 +438,8 @@ export class QueryBarInputUI extends Component { 'languageSwitcherPopoverAnchorPosition', 'onChange', 'onSubmit', - 'onSave', - 'onSaveNew', - 'onLoadSavedQuery', - 'isDirty', - 'onClearSavedQuery', - 'showSaveQuery', ]); - const savedQueryRow = - this.props.onSave && - this.props.onSaveNew && - this.props.onClearSavedQuery && - this.props.isDirty !== undefined ? ( - - ) : null; - return (
    {
    0) - } + show={this.state.isSuggestionsVisible} suggestions={this.state.suggestions.slice(0, this.state.suggestionLimit)} index={this.state.index} onClick={this.onClickSuggestion} onMouseEnter={this.onMouseEnterSuggestion} loadMore={this.increaseLimit} - append={savedQueryRow} />
    diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx deleted file mode 100644 index d2eac057f9596..0000000000000 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/saved_query_row.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { FunctionComponent, Fragment } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiIcon } from '@elastic/eui'; -import { SavedQueryAttributes } from '../../../search/search_bar'; -import { Query } from '../index'; - -interface Props { - query: Query; - savedQuery?: SavedQueryAttributes; - showSaveQuery?: boolean; - onSave: () => void; - onSaveNew: () => void; - isDirty: boolean; - onClearSavedQuery: () => void; -} - -export interface SavedQueryDetails { - title: string; - description: string; - includeFilters: boolean; - includeTimeFilter: boolean; - query: Query; -} - -export const SavedQueryRow: FunctionComponent = ({ - query, - savedQuery, - onSave, - onSaveNew, - showSaveQuery, - isDirty, - onClearSavedQuery, -}) => { - let rowContent; - if (savedQuery) { - if (isDirty && showSaveQuery) { - rowContent = ( - - - - - Save changes to query: {savedQuery.title} - - - - - - - - - - - Save as new - - - - ); - } else { - rowContent = ( - - - - {savedQuery.title} - - - - - - - - - {showSaveQuery && ( - - - Save as new - - - )} - - ); - } - } else if (query.query.length !== 0 && showSaveQuery) { - rowContent = ( - - - Save this query for reuse - - - ); - } - - if (rowContent) { - return ( - - {rowContent} - - ); - } else { - return null; - } -}; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx index bdb3d5ff36451..64c805968e43c 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestion_component.tsx @@ -34,8 +34,6 @@ function getEuiIconType(type: string) { return 'kqlSelector'; case 'operator': return 'kqlOperand'; - case 'savedQuery': - return 'save'; default: throw new Error(`Unknown type: ${type}`); } diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.test.tsx index be4817910724c..3a321dfd86a71 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.test.tsx @@ -60,7 +60,7 @@ describe('SuggestionsComponent', () => { expect(component.isEmptyRender()).toBe(true); }); - it('Should display the saved query row if there are no suggestions and the show prop is true', () => { + it('Should not display anything if there are no suggestions', () => { const component = shallow( { loadMore={noop} /> ); - expect(component.isEmptyRender()).toBe(false); + + expect(component.isEmptyRender()).toBe(true); }); it('Should display given suggestions if the show prop is true', () => { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx index 3c7594419513c..f7cd249d3dbb7 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/typeahead/suggestions_component.tsx @@ -17,6 +17,7 @@ * under the License. */ +import { isEmpty } from 'lodash'; import React, { Component } from 'react'; import { AutocompleteSuggestion } from 'ui/autocomplete_providers'; import { SuggestionComponent } from './suggestion_component'; @@ -28,7 +29,6 @@ interface Props { show: boolean; suggestions: AutocompleteSuggestion[]; loadMore: () => void; - append?: React.ReactNode; } export class SuggestionsComponent extends Component { @@ -36,7 +36,7 @@ export class SuggestionsComponent extends Component { private parentNode: HTMLDivElement | null = null; public render() { - if (!this.props.show) { + if (!this.props.show || isEmpty(this.props.suggestions)) { return null; } @@ -49,11 +49,7 @@ export class SuggestionsComponent extends Component { onClick={this.props.onClick} onMouseEnter={() => this.props.onMouseEnter(index)} ariaId={'suggestion-' + index} - key={ - suggestion.savedQuery - ? suggestion.savedQuery.id - : `${suggestion.type} - ${suggestion.text}` - } + key={`${suggestion.type} - ${suggestion.text}`} /> ); }); @@ -71,7 +67,6 @@ export class SuggestionsComponent extends Component { > {suggestions} - {this.props.append} diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 3210564f361a8..7a5fd421aca95 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -369,7 +369,6 @@ class SearchBarUI extends Component { queryBar = ( { isRefreshPaused={this.props.isRefreshPaused} refreshInterval={this.props.refreshInterval} showAutoRefreshOnly={this.props.showAutoRefreshOnly} - showSaveQuery={this.props.showSaveQuery} showQueryInput={this.props.showQueryInput} onRefreshChange={this.props.onRefreshChange} - onSave={this.onInitiateSave} - onSaveNew={this.onInitiateSaveNew} onChange={this.onQueryBarChange} - onLoadSavedQuery={this.onLoadSavedQuery} isDirty={this.isDirty()} customSubmitButton={ this.props.customSubmitButton ? this.props.customSubmitButton : undefined } - onClearSavedQuery={this.props.onClearSavedQuery} /> ); } diff --git a/src/legacy/ui/public/autocomplete_providers/index.d.ts b/src/legacy/ui/public/autocomplete_providers/index.d.ts index 9d5a670891c84..92061051b1b0e 100644 --- a/src/legacy/ui/public/autocomplete_providers/index.d.ts +++ b/src/legacy/ui/public/autocomplete_providers/index.d.ts @@ -21,7 +21,6 @@ * WARNING: these typings are incomplete */ import { StaticIndexPattern } from 'ui/index_patterns'; -import { SavedQuery } from '../../../../legacy/core_plugins/data/public'; export type AutocompleteProvider = (args: { config: { @@ -42,8 +41,7 @@ export type AutocompleteSuggestionType = | 'value' | 'operator' | 'conjunction' - | 'recentSearch' - | 'savedQuery'; + | 'recentSearch'; export interface AutocompleteSuggestion { description?: string; @@ -51,7 +49,6 @@ export interface AutocompleteSuggestion { start: number; text: string; type: AutocompleteSuggestionType; - savedQuery?: SavedQuery; } export function addAutocompleteProvider(language: string, provider: AutocompleteProvider): void; diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 49f771bc23964..43f2276f2a969 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -104,7 +104,6 @@ app.controller('GisMapController', ($scope, $route, kbnUrl, localStorage, AppSta mapStateJSON: savedMap.mapStateJSON, globalState: globalState, }); - $scope.refreshConfig = getInitialRefreshConfig({ mapStateJSON: savedMap.mapStateJSON, globalState: globalState, @@ -129,7 +128,9 @@ app.controller('GisMapController', ($scope, $route, kbnUrl, localStorage, AppSta store.dispatch(setRefreshConfig($scope.refreshConfig)); }; + function hasUnsavedChanges() { + const state = store.getState(); const layerList = getLayerListRaw(state); const layerListConfigOnly = copyPersistentState(layerList); From 6e9deefaa4181290dd649f4cec25fb10b3a44d03 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 5 Aug 2019 14:11:49 -0400 Subject: [PATCH 099/189] Use the same class the datepicker uses to make the prepend element the correct height --- .../public/search/search_bar/components/saved_query_manager.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 806a24b8edcf1..3e30498e40af0 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -95,6 +95,7 @@ export const SavedQueryManager: FunctionComponent = ({ const filterTriggerButton = ( { From 644c410e73f4ea69ca02828b67d2fde2ed48ffcf Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 5 Aug 2019 14:35:52 -0400 Subject: [PATCH 100/189] always show save as new when a saved query is loaded --- .../search_bar/components/saved_query_manager.tsx | 12 +++++++----- .../search/search_bar/components/search_bar.tsx | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 3e30498e40af0..36d8af0e4d155 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -37,7 +37,6 @@ import { Query } from '../../../query'; interface Props { showSaveQuery?: boolean; loadedSavedQuery?: SavedQuery; - isDirty: boolean; onSave: () => void; onSaveAsNew: () => void; onLoad: (savedQuery: SavedQuery) => void; @@ -48,7 +47,6 @@ interface Props { export const SavedQueryManager: FunctionComponent = ({ showSaveQuery, loadedSavedQuery, - isDirty, onSave, onSaveAsNew, onLoad, @@ -159,7 +157,7 @@ export const SavedQueryManager: FunctionComponent = ({ )} {query.query !== '' ? ( - {showSaveQuery && isDirty && loadedSavedQuery && ( + {showSaveQuery && loadedSavedQuery && ( @@ -171,7 +169,11 @@ export const SavedQueryManager: FunctionComponent = ({ - onSave()}> + onSave()} + disabled={query.query === loadedSavedQuery.attributes.query.query} + > {i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', { @@ -183,7 +185,7 @@ export const SavedQueryManager: FunctionComponent = ({ )} - {showSaveQuery && !isDirty && ( + {showSaveQuery && !loadedSavedQuery && ( onSave()}> {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 7a5fd421aca95..afc93abdd891e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -353,7 +353,6 @@ class SearchBarUI extends Component { public render() { const savedQueryManager = this.state.query && this.props.onClearSavedQuery && ( Date: Mon, 5 Aug 2019 15:05:56 -0400 Subject: [PATCH 101/189] Fix search bar jest tests --- .../search/search_bar/components/search_bar.test.tsx | 7 +++++++ .../public/search/search_bar/components/search_bar.tsx | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx index 0fccf8bd6f5a4..a43035bb49a51 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx @@ -29,6 +29,13 @@ jest.mock('../../../../../data/public', () => { }; }); +jest.mock('ui/notify', () => ({ + toastNotifications: { + addSuccess: () => {}, + addDanger: () => {}, + }, +})); + const noop = jest.fn(); const createMockWebStorage = () => ({ diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index afc93abdd891e..9c79769576bd1 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -160,8 +160,8 @@ class SearchBarUI extends Component { showSavedQueryPopover: false, currentProps: this.props, query: this.props.query ? { ...this.props.query } : undefined, - dateRangeFrom: _.get(this.props, 'dateRangeFrom', 'now-15m'), - dateRangeTo: _.get(this.props, 'dateRangeTo', 'now'), + dateRangeFrom: get(this.props, 'dateRangeFrom', 'now-15m'), + dateRangeTo: get(this.props, 'dateRangeTo', 'now'), }; public isDirty = () => { From e196c8a5467b261c930f5f1c41e3665d187591de Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 5 Aug 2019 19:41:19 -0400 Subject: [PATCH 102/189] fix functional test --- test/api_integration/apis/saved_objects/export.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api_integration/apis/saved_objects/export.js b/test/api_integration/apis/saved_objects/export.js index 5444299215082..0564c095faa88 100644 --- a/test/api_integration/apis/saved_objects/export.js +++ b/test/api_integration/apis/saved_objects/export.js @@ -139,7 +139,7 @@ export default function ({ getService }) { statusCode: 400, error: 'Bad Request', message: 'child "type" fails because ["type" at position 0 fails because ' + - '["0" must be one of [config, dashboard, index-pattern, search, url, visualization]]]', + '["0" must be one of [config, dashboard, index-pattern, query, search, url, visualization]]]', validation: { source: 'payload', keys: ['type.0'], From ad610eec0a45a2e3f75b42f4c2fdf5564565e5c3 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 6 Aug 2019 17:27:53 -0400 Subject: [PATCH 103/189] Include filters by default in saved queries --- .../search_bar/components/save_query_form.tsx | 4 +- .../components/saved_query_manager.tsx | 93 ++++++++----------- 2 files changed, 43 insertions(+), 54 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index 43eacf167d3f4..f12574272c19d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -61,10 +61,10 @@ export const SaveQueryForm: FunctionComponent = ({ const [title, setTitle] = useState(savedQuery ? savedQuery.title : ''); const [description, setDescription] = useState(savedQuery ? savedQuery.description : ''); const [shouldIncludeFilters, setShouldIncludeFilters] = useState( - !!(savedQuery && savedQuery.filters) + savedQuery ? !!savedQuery.filters : true ); const [shouldIncludeTimefilter, setIncludeTimefilter] = useState( - !!(savedQuery && savedQuery.timefilter) + savedQuery ? !!savedQuery.timefilter : false ); const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 6f61bb4c4eb08..be787a7b7f524 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -159,61 +159,50 @@ export const SavedQueryManager: FunctionComponent = ({

    )} - {query.query !== '' ? ( - - {showSaveQuery && loadedSavedQuery && ( - - - - onSaveAsNew()}> - {i18n.translate( - 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', - { - defaultMessage: 'Save As New', - } - )} - - + + {showSaveQuery && loadedSavedQuery && ( + + + + onSaveAsNew()}> + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { + defaultMessage: 'Save As New', + })} + + - - onSave()} - disabled={query.query === loadedSavedQuery.attributes.query.query} - > - {i18n.translate( - 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', - { - defaultMessage: 'Save changes', - } - )} - - - - - )} - {showSaveQuery && !loadedSavedQuery && ( - - onSave()}> - {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { - defaultMessage: 'Save', - })} - - - )} - + + onSave()}> + {i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', + { + defaultMessage: 'Save changes', + } + )} + + + + + )} + {showSaveQuery && !loadedSavedQuery && ( - onClearSavedQuery()}> - {loadedSavedQuery && - i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { - defaultMessage: 'Clear', - })} - + onSave()}> + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { + defaultMessage: 'Save', + })} + - - ) : ( - '' - )} + )} + + + onClearSavedQuery()}> + {loadedSavedQuery && + i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { + defaultMessage: 'Clear', + })} + + +
    ); From e41e85f6eb59622fe392c1e752d3ba7089858164 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 6 Aug 2019 18:38:30 -0400 Subject: [PATCH 104/189] Don't allow saved queries with duplicate titles --- .../search/search_bar/components/save_query_form.tsx | 8 +++++++- .../public/search/search_bar/lib/saved_query_service.ts | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index f12574272c19d..be304cee9b233 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -79,8 +79,14 @@ export const SaveQueryForm: FunctionComponent = ({ {savedQueryDescriptionText} - + { diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index da44d367c93ae..71e51a3e0a7c0 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -68,7 +68,9 @@ export const saveQuery = async (attributes: SavedQueryAttributes, id?: string) = let rawQueryResponse; if (id === undefined) { - rawQueryResponse = await savedObjectsClient.create('query', queryObject); + rawQueryResponse = await savedObjectsClient.create('query', queryObject, { + id: attributes.title, + }); } else { rawQueryResponse = await savedObjectsClient.create('query', queryObject, { id, From 99df809bbc39c19932cbac54612f9f93290a2502 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 7 Aug 2019 11:32:00 -0700 Subject: [PATCH 105/189] Adds pagination to the saved query manager --- .../core_plugins/data/public/index.scss | 1 + .../data/public/search/search_bar/_index.scss | 1 + .../components/_saved_query_manager.scss | 10 ++++++ .../components/saved_query_manager.tsx | 36 +++++++++++++++---- 4 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 src/legacy/core_plugins/data/public/search/search_bar/_index.scss create mode 100644 src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss diff --git a/src/legacy/core_plugins/data/public/index.scss b/src/legacy/core_plugins/data/public/index.scss index 993e52665defa..14274d27c13ee 100644 --- a/src/legacy/core_plugins/data/public/index.scss +++ b/src/legacy/core_plugins/data/public/index.scss @@ -4,3 +4,4 @@ @import './filter/filter_bar/index'; +@import './search/search_bar/index'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/_index.scss b/src/legacy/core_plugins/data/public/search/search_bar/_index.scss new file mode 100644 index 0000000000000..11e911e1ad498 --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/search_bar/_index.scss @@ -0,0 +1 @@ +@import './components/saved_query_manager'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss new file mode 100644 index 0000000000000..b1545a82258cb --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss @@ -0,0 +1,10 @@ +.saved-query-manager-popover { + width: 400px; +} +.saved-query-list { + @include euiYScrollWithShadows; +} +.saved-query-list-wrapper { + height: 20vh; + overflow-y:scroll; +} diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index be787a7b7f524..321299070ef4c 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -24,6 +24,7 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem, + EuiPagination, EuiText, } from '@elastic/eui'; @@ -34,6 +35,8 @@ import { SavedQuery } from '../index'; import { getAllSavedQueries, deleteSavedQuery } from '../lib/saved_query_service'; import { Query } from '../../../query'; +const pageCount = 10; + interface Props { showSaveQuery?: boolean; loadedSavedQuery?: SavedQuery; @@ -56,6 +59,8 @@ export const SavedQueryManager: FunctionComponent = ({ const [isOpen, setIsOpen] = useState(false); const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); + const [activePage, setActivePage] = useState(0); + useEffect(() => { const fetchQueries = async () => { const allSavedQueries = await getAllSavedQueries(); @@ -67,6 +72,10 @@ export const SavedQueryManager: FunctionComponent = ({ } }, [isOpen]); + const goToPage = (pageNumber: number) => { + setActivePage(pageNumber); + }; + const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', { @@ -105,8 +114,12 @@ export const SavedQueryManager: FunctionComponent = ({
    ); - const savedQueryRows = savedQueries.map(savedQuery => { - return ( + const savedQueryRows = () => { + const savedQueriesDisplayRows = savedQueries.slice( + activePage * pageCount, + activePage * pageCount + pageCount + ); + return savedQueriesDisplayRows.map(savedQuery => (
  • { @@ -123,8 +136,8 @@ export const SavedQueryManager: FunctionComponent = ({ color="danger" />
  • - ); - }); + )); + }; return ( = ({ }} anchorPosition="downLeft" > -
    +
    {savedQueryPopoverTitleText} {savedQueries.length > 0 ? ( @@ -146,8 +159,17 @@ export const SavedQueryManager: FunctionComponent = ({ - -
      {savedQueryRows}
    + +
      {savedQueryRows()}
    +
    +
    + + +
    From 00ecc89e78792e116f8ed715acf5f659934b3170 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 7 Aug 2019 11:34:43 -0700 Subject: [PATCH 106/189] changes page count to 50 --- .../public/search/search_bar/components/saved_query_manager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 321299070ef4c..e7d7703f67b1d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -35,7 +35,7 @@ import { SavedQuery } from '../index'; import { getAllSavedQueries, deleteSavedQuery } from '../lib/saved_query_service'; import { Query } from '../../../query'; -const pageCount = 10; +const pageCount = 50; interface Props { showSaveQuery?: boolean; From c20f97f5e1f01ebbf2a1ac5b358b9723acfa8178 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 17:33:10 -0400 Subject: [PATCH 107/189] Use overflow hidden so native scroll bar doesn't appear --- .../search/search_bar/components/_saved_query_manager.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss index b1545a82258cb..a39f52b47cd15 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss @@ -6,5 +6,5 @@ } .saved-query-list-wrapper { height: 20vh; - overflow-y:scroll; + overflow-y:hidden; } From 5f71f651b7783a531feb31ddfa989930432869df Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:14:52 -0400 Subject: [PATCH 108/189] Add saved query deletion confirmation modal --- .../search_bar/components/save_query_form.tsx | 2 +- .../components/saved_query_manager.tsx | 208 +++++++++++------- 2 files changed, 127 insertions(+), 83 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index be304cee9b233..9e3c5ec976b3e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -138,7 +138,7 @@ export const SaveQueryForm: FunctionComponent = ({ return ( - + Save query diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index e7d7703f67b1d..0699fc9de5113 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -26,6 +26,8 @@ import { EuiFlexItem, EuiPagination, EuiText, + EuiConfirmModal, + EuiOverlayMask, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -58,8 +60,8 @@ export const SavedQueryManager: FunctionComponent = ({ }) => { const [isOpen, setIsOpen] = useState(false); const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); - const [activePage, setActivePage] = useState(0); + const [confirmDeletionModal, setConfirmDeletionModal] = useState(null as React.ReactNode); useEffect(() => { const fetchQueries = async () => { @@ -131,7 +133,42 @@ export const SavedQueryManager: FunctionComponent = ({ {savedQuery.attributes.title} onDeleteSavedQuery(savedQuery)} + onClick={() => { + setConfirmDeletionModal( + + { + onDeleteSavedQuery(savedQuery); + setConfirmDeletionModal(null); + }} + onCancel={() => { + setConfirmDeletionModal(null); + }} + > + + ); + }} iconType="trash" color="danger" /> @@ -140,92 +177,99 @@ export const SavedQueryManager: FunctionComponent = ({ }; return ( - { - setIsOpen(false); - }} - anchorPosition="downLeft" - > -
    - {savedQueryPopoverTitleText} - {savedQueries.length > 0 ? ( - - - - {savedQueryDescriptionText} - - - - -
      {savedQueryRows()}
    -
    -
    - - - - - -
    - ) : ( - -

    - There are no saved queries. You can save search snippets and filters for later use.{' '} - this, enter a query and click 'Save query for reuse'. -

    -
    - )} - - {showSaveQuery && loadedSavedQuery && ( - + + { + setIsOpen(false); + }} + anchorPosition="downLeft" + ownFocus + > +
    + {savedQueryPopoverTitleText} + {savedQueries.length > 0 ? ( + + + + {savedQueryDescriptionText} + + - - onSaveAsNew()}> - {i18n.translate('data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { - defaultMessage: 'Save As New', - })} - + +
      {savedQueryRows()}
    - - - onSave()}> - {i18n.translate( - 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', - { - defaultMessage: 'Save changes', - } - )} - +
    + + + - +
    + ) : ( + +

    + There are no saved queries. You can save search snippets and filters for later use.{' '} + this, enter a query and click 'Save query for reuse'. +

    +
    )} - {showSaveQuery && !loadedSavedQuery && ( + + {showSaveQuery && loadedSavedQuery && ( + + + + onSaveAsNew()}> + {i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', + { + defaultMessage: 'Save As New', + } + )} + + + + + onSave()}> + {i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', + { + defaultMessage: 'Save changes', + } + )} + + + + + )} + {showSaveQuery && !loadedSavedQuery && ( + + onSave()}> + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { + defaultMessage: 'Save', + })} + + + )} + - onSave()}> - {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { - defaultMessage: 'Save', - })} - + onClearSavedQuery()}> + {loadedSavedQuery && + i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { + defaultMessage: 'Clear', + })} + - )} - - - onClearSavedQuery()}> - {loadedSavedQuery && - i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { - defaultMessage: 'Clear', - })} - - - -
    -
    +
    +
    +
    + {confirmDeletionModal} + ); }; From 8634211ff2b22349baae0edafa2b5a02ff156f72 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:21:04 -0400 Subject: [PATCH 109/189] fix "no saved queries" copy and internationalize it --- .../search_bar/components/saved_query_manager.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 0699fc9de5113..750d95351d9d5 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -86,6 +86,13 @@ export const SavedQueryManager: FunctionComponent = ({ } ); + const noSavedQueriesDescriptionText = + i18n.translate('data.search.searchBar.savedQueryDescriptionText', { + defaultMessage: 'There are no saved queries.', + }) + + ' ' + + savedQueryDescriptionText; + const savedQueryPopoverTitleText = i18n.translate( 'data.search.searchBar.savedQueryPopoverTitleText', { @@ -213,12 +220,7 @@ export const SavedQueryManager: FunctionComponent = ({ ) : ( - -

    - There are no saved queries. You can save search snippets and filters for later use.{' '} - this, enter a query and click 'Save query for reuse'. -

    -
    + {noSavedQueriesDescriptionText} )} {showSaveQuery && loadedSavedQuery && ( From 77142c6dab7ea4e1da37d1f56e0c2c67fbcef010 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:30:26 -0400 Subject: [PATCH 110/189] add aria-labeledby to saved query list so users know what the list is about --- .../components/saved_query_manager.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 750d95351d9d5..09ade9c4a8476 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -110,7 +110,7 @@ export const SavedQueryManager: FunctionComponent = ({ deleteSavedQuery(savedQuery.id); }; - const filterTriggerButton = ( + const savedQueryPopoverButton = ( = ({ onClick={() => { setIsOpen(!isOpen); }} + aria-label={i18n.translate('data.search.searchBar.savedQueryPopoverButtonText', { + defaultMessage: 'See saved queries', + })} > # @@ -187,7 +190,7 @@ export const SavedQueryManager: FunctionComponent = ({ { setIsOpen(false); @@ -196,7 +199,9 @@ export const SavedQueryManager: FunctionComponent = ({ ownFocus >
    - {savedQueryPopoverTitleText} + + {savedQueryPopoverTitleText} + {savedQueries.length > 0 ? ( @@ -206,7 +211,12 @@ export const SavedQueryManager: FunctionComponent = ({ -
      {savedQueryRows()}
    +
      + {savedQueryRows()} +
    From a7b4421fa9b2be285574b39b4bcacc602a972e16 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:36:42 -0400 Subject: [PATCH 111/189] Add extra description of open button for screen reader --- .../search/search_bar/components/saved_query_manager.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 09ade9c4a8476..0480f62494a3e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -28,6 +28,7 @@ import { EuiText, EuiConfirmModal, EuiOverlayMask, + EuiScreenReaderOnly, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -140,6 +141,13 @@ export const SavedQueryManager: FunctionComponent = ({ }} flush="left" > + + + {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { + defaultMessage: 'Open saved query', + })} + + {savedQuery.attributes.title} Date: Wed, 7 Aug 2019 18:40:50 -0400 Subject: [PATCH 112/189] add aria-label to delete button --- .../search/search_bar/components/saved_query_manager.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 0480f62494a3e..aecdb0526519c 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -183,12 +183,19 @@ export const SavedQueryManager: FunctionComponent = ({ onCancel={() => { setConfirmDeletionModal(null); }} - > + /> ); }} iconType="trash" color="danger" + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', + { + defaultMessage: 'Delete saved query {title}', + values: { title: savedQuery.attributes.title }, + } + )} /> )); From 1f2d98adaf8e50377843bdaa1b0ea009e8b45999 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:41:21 -0400 Subject: [PATCH 113/189] sentence case --- .../public/search/search_bar/components/saved_query_manager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index aecdb0526519c..91b78a9a835ad 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -256,7 +256,7 @@ export const SavedQueryManager: FunctionComponent = ({ {i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { - defaultMessage: 'Save As New', + defaultMessage: 'Save as new', } )} From 1785437cb540de3be0ae80b24d7e04ca8b2c647c Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:44:02 -0400 Subject: [PATCH 114/189] Add aria-label to the save as new button --- .../search_bar/components/saved_query_manager.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 91b78a9a835ad..79bf4b662702e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -252,7 +252,15 @@ export const SavedQueryManager: FunctionComponent = ({ - onSaveAsNew()}> + onSaveAsNew()} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel', + { + defaultMessage: 'Save as a new saved query', + } + )} + > {i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { From dc3c7b6a892c1ce38b962aefc9cb84609933ee80 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:48:39 -0400 Subject: [PATCH 115/189] add aria-label to save changes button --- .../search_bar/components/saved_query_manager.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 79bf4b662702e..84020a7dbd397 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -271,7 +271,17 @@ export const SavedQueryManager: FunctionComponent = ({ - onSave()}> + onSave()} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveChangesButtonAriaLabel', + { + defaultMessage: 'Save changes to {title}', + values: { title: loadedSavedQuery.attributes.title }, + } + )} + > {i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', { From 1916608a7b9d1a91e6843203adadf5ab4f998113 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:51:14 -0400 Subject: [PATCH 116/189] Add aria-label to clear button --- .../search/search_bar/components/saved_query_manager.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 84020a7dbd397..98ea82c805e86 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -304,7 +304,13 @@ export const SavedQueryManager: FunctionComponent = ({ )} - onClearSavedQuery()}> + onClearSavedQuery()} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverClearButtonAriaLabel', + { defaultMessage: 'Clear current saved query' } + )} + > {loadedSavedQuery && i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { defaultMessage: 'Clear', From a2a5a14da23b295972d18fcf6479d7a1b78ad70b Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 7 Aug 2019 18:53:41 -0400 Subject: [PATCH 117/189] Add aria-label to save button --- .../search/search_bar/components/saved_query_manager.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 98ea82c805e86..76967c3cf606a 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -295,7 +295,14 @@ export const SavedQueryManager: FunctionComponent = ({ )} {showSaveQuery && !loadedSavedQuery && ( - onSave()}> + onSave()} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverSaveButtonAriaLabel', + { defaultMessage: 'Save a new saved query' } + )} + > {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { defaultMessage: 'Save', })} From 9c032e9a2108bacf1ea91dc77681d97383cd6f35 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 12:37:37 -0400 Subject: [PATCH 118/189] Translate static text --- .../search_bar/components/save_query_form.tsx | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index 9e3c5ec976b3e..c96bad48dc054 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -80,7 +80,9 @@ export const SaveQueryForm: FunctionComponent = ({ {savedQueryDescriptionText} = ({ /> - + = ({ { setShouldIncludeFilters(!shouldIncludeFilters); @@ -124,7 +132,9 @@ export const SaveQueryForm: FunctionComponent = ({ { setIncludeTimefilter(!shouldIncludeTimefilter); @@ -140,13 +150,21 @@ export const SaveQueryForm: FunctionComponent = ({ - Save query + + {i18n.translate('data.search.searchBar.savedQueryFormTitle', { + defaultMessage: 'Save query', + })} + {saveQueryForm} - Cancel + + {i18n.translate('data.search.searchBar.savedQueryFormCancelButtonText', { + defaultMessage: 'Cancel', + })} + @@ -160,7 +178,9 @@ export const SaveQueryForm: FunctionComponent = ({ fill data-test-subj="savedQueryFormSaveButton" > - Save + {i18n.translate('data.search.searchBar.savedQueryFormSaveButtonText', { + defaultMessage: 'Save', + })} From ae5ecfff026c4f0ae7fafba2de62822fab6f2792 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 12:40:51 -0400 Subject: [PATCH 119/189] translate query bar message we noticed was static --- .../data/public/query/query_bar/components/query_bar.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 2a7c738c3916e..429c6922a6db4 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -315,7 +315,10 @@ export class QueryBarUI extends Component { this.onLuceneSyntaxWarningOptOut(toast)}> - Don't show again + From 9f7c71c2027b5d6b5833ea49ed4402a030893b6b Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 12:51:32 -0400 Subject: [PATCH 120/189] Move conditional one level up so we hide the button as well as the text --- .../components/saved_query_manager.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 76967c3cf606a..3444410eae132 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -311,18 +311,19 @@ export const SavedQueryManager: FunctionComponent = ({ )} - onClearSavedQuery()} - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverClearButtonAriaLabel', - { defaultMessage: 'Clear current saved query' } - )} - > - {loadedSavedQuery && - i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { + {loadedSavedQuery && ( + onClearSavedQuery()} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverClearButtonAriaLabel', + { defaultMessage: 'Clear current saved query' } + )} + > + {i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { defaultMessage: 'Clear', })} - + + )}
    From 969a49e3c195c1b8b19cf22e1ac7e0793f57985c Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 12:56:21 -0400 Subject: [PATCH 121/189] Remove duplicate translation ID --- .../public/search/search_bar/components/saved_query_manager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 3444410eae132..cfd43c281ad37 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -88,7 +88,7 @@ export const SavedQueryManager: FunctionComponent = ({ ); const noSavedQueriesDescriptionText = - i18n.translate('data.search.searchBar.savedQueryDescriptionText', { + i18n.translate('data.search.searchBar.savedQueryNoSavedQueriesText', { defaultMessage: 'There are no saved queries.', }) + ' ' + From 3d178c657fd221bbb8c08371d9a9e7ca529819fe Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 13:17:13 -0400 Subject: [PATCH 122/189] update saved query service tests now that title is used as ID --- .../search_bar/components/search_bar.tsx | 2 +- .../lib/saved_query_service.test.ts | 53 ++++++++++--------- .../search_bar/lib/saved_query_service.ts | 6 +-- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 9c79769576bd1..8c837a0972208 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -242,7 +242,7 @@ class SearchBarUI extends Component { try { let response; if (this.props.savedQuery && !saveAsNew) { - response = await saveQuery(savedQueryAttributes, this.props.savedQuery.id); + response = await saveQuery(savedQueryAttributes, { overwrite: true }); } else { response = await saveQuery(savedQueryAttributes); } diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index 56846a3394af3..5b3f2b92df421 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -78,27 +78,29 @@ describe('saved query service', () => { describe('saveQuery', function() { it('should create a saved object for the given attributes', async () => { mockSavedObjectsClient.create.mockReturnValue({ - id: '1234', + id: 'foo', attributes: savedQueryAttributes, }); const response = await saveQuery(savedQueryAttributes); - expect(mockSavedObjectsClient.create).toHaveBeenCalledWith('query', savedQueryAttributes); - expect(response).toEqual({ id: '1234', attributes: savedQueryAttributes }); + expect(mockSavedObjectsClient.create).toHaveBeenCalledWith('query', savedQueryAttributes, { + id: 'foo', + }); + expect(response).toEqual({ id: 'foo', attributes: savedQueryAttributes }); }); - it('should accept an explicit ID', async () => { + it('should allow overwriting an existing saved query', async () => { mockSavedObjectsClient.create.mockReturnValue({ - id: '1234', + id: 'foo', attributes: savedQueryAttributes, }); - const response = await saveQuery(savedQueryAttributes, '1234'); + const response = await saveQuery(savedQueryAttributes, { overwrite: true }); expect(mockSavedObjectsClient.create).toHaveBeenCalledWith('query', savedQueryAttributes, { - id: '1234', + id: 'foo', overwrite: true, }); - expect(response).toEqual({ id: '1234', attributes: savedQueryAttributes }); + expect(response).toEqual({ id: 'foo', attributes: savedQueryAttributes }); }); it('should optionally accept filters and timefilters in object format', async () => { @@ -109,7 +111,7 @@ describe('saved query service', () => { }; mockSavedObjectsClient.create.mockReturnValue({ - id: '1234', + id: 'foo', attributes: serializedSavedQueryAttributesWithFilters, }); @@ -117,9 +119,10 @@ describe('saved query service', () => { expect(mockSavedObjectsClient.create).toHaveBeenCalledWith( 'query', - serializedSavedQueryAttributesWithFilters + serializedSavedQueryAttributesWithFilters, + { id: 'foo' } ); - expect(response).toEqual({ id: '1234', attributes: savedQueryAttributesWithFilters }); + expect(response).toEqual({ id: 'foo', attributes: savedQueryAttributesWithFilters }); }); it('should throw an error when saved objects client returns error', async () => { @@ -142,16 +145,16 @@ describe('saved query service', () => { describe('findSavedQueries', function() { it('should find and return saved queries without search text', async () => { mockSavedObjectsClient.find.mockReturnValue({ - savedObjects: [{ id: '1234', attributes: savedQueryAttributes }], + savedObjects: [{ id: 'foo', attributes: savedQueryAttributes }], }); const response = await findSavedQueries(); - expect(response).toEqual([{ id: '1234', attributes: savedQueryAttributes }]); + expect(response).toEqual([{ id: 'foo', attributes: savedQueryAttributes }]); }); it('should find and return saved queries with search text matching the title field', async () => { mockSavedObjectsClient.find.mockReturnValue({ - savedObjects: [{ id: '1234', attributes: savedQueryAttributes }], + savedObjects: [{ id: 'foo', attributes: savedQueryAttributes }], }); const response = await findSavedQueries('foo'); expect(mockSavedObjectsClient.find).toHaveBeenCalledWith({ @@ -160,7 +163,7 @@ describe('saved query service', () => { sortField: '_score', type: 'query', }); - expect(response).toEqual([{ id: '1234', attributes: savedQueryAttributes }]); + expect(response).toEqual([{ id: 'foo', attributes: savedQueryAttributes }]); }); it('should find and return parsed filters and timefilters items', async () => { const serializedSavedQueryAttributesWithFilters = { @@ -169,14 +172,14 @@ describe('saved query service', () => { timefilter: JSON.stringify(savedQueryAttributesWithFilters.timefilter), }; mockSavedObjectsClient.find.mockReturnValue({ - savedObjects: [{ id: '1234', attributes: serializedSavedQueryAttributesWithFilters }], + savedObjects: [{ id: 'foo', attributes: serializedSavedQueryAttributesWithFilters }], }); const response = await findSavedQueries('bar'); - expect(response).toEqual([{ id: '1234', attributes: savedQueryAttributesWithFilters }]); + expect(response).toEqual([{ id: 'foo', attributes: savedQueryAttributesWithFilters }]); }); it('should return an array of saved queries', async () => { mockSavedObjectsClient.find.mockReturnValue({ - savedObjects: [{ id: '1234', attributes: savedQueryAttributes }], + savedObjects: [{ id: 'foo', attributes: savedQueryAttributes }], }); const response = await findSavedQueries(); expect(response).toEqual( @@ -187,7 +190,7 @@ describe('saved query service', () => { query: { language: 'kuery', query: 'response:200' }, title: 'foo', }, - id: '1234', + id: 'foo', }, ]) ); @@ -196,16 +199,16 @@ describe('saved query service', () => { describe('getSavedQuery', function() { it('should retrieve a saved query by id', async () => { - mockSavedObjectsClient.get.mockReturnValue({ id: '1234', attributes: savedQueryAttributes }); + mockSavedObjectsClient.get.mockReturnValue({ id: 'foo', attributes: savedQueryAttributes }); - const response = await getSavedQuery('1234'); - expect(response).toEqual({ id: '1234', attributes: savedQueryAttributes }); + const response = await getSavedQuery('foo'); + expect(response).toEqual({ id: 'foo', attributes: savedQueryAttributes }); }); it('should only return saved queries', async () => { - mockSavedObjectsClient.get.mockReturnValue({ id: '1234', attributes: savedQueryAttributes }); + mockSavedObjectsClient.get.mockReturnValue({ id: 'foo', attributes: savedQueryAttributes }); - await getSavedQuery('1234'); - expect(mockSavedObjectsClient.get).toHaveBeenCalledWith('query', '1234'); + await getSavedQuery('foo'); + expect(mockSavedObjectsClient.get).toHaveBeenCalledWith('query', 'foo'); }); }); }); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index 71e51a3e0a7c0..9d4a298058425 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -32,7 +32,7 @@ interface SerializedSavedQueryAttributes extends SavedObjectAttributes { timefilter?: string; } -export const saveQuery = async (attributes: SavedQueryAttributes, id?: string) => { +export const saveQuery = async (attributes: SavedQueryAttributes, { overwrite = false } = {}) => { const savedObjectsClient = chrome.getSavedObjectsClient(); const query = { @@ -67,13 +67,13 @@ export const saveQuery = async (attributes: SavedQueryAttributes, id?: string) = } let rawQueryResponse; - if (id === undefined) { + if (!overwrite) { rawQueryResponse = await savedObjectsClient.create('query', queryObject, { id: attributes.title, }); } else { rawQueryResponse = await savedObjectsClient.create('query', queryObject, { - id, + id: attributes.title, overwrite: true, }); } From 2e2f74636b33c2bb98bee40e63724df899684218 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 13:34:36 -0400 Subject: [PATCH 123/189] add test for deleteSavedQuery method --- .../search_bar/lib/saved_query_service.test.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index 5b3f2b92df421..8fdbd237b6b9d 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -18,7 +18,12 @@ */ import { SavedQueryAttributes } from '../index'; -import { saveQuery, findSavedQueries, getSavedQuery } from './saved_query_service'; +import { + saveQuery, + findSavedQueries, + getSavedQuery, + deleteSavedQuery, +} from './saved_query_service'; import { FilterStateStore } from '@kbn/es-query'; const savedQueryAttributes: SavedQueryAttributes = { @@ -58,6 +63,7 @@ const mockSavedObjectsClient = { error: jest.fn(), find: jest.fn(), get: jest.fn(), + delete: jest.fn(), }; jest.mock('ui/chrome', () => { @@ -211,4 +217,11 @@ describe('saved query service', () => { expect(mockSavedObjectsClient.get).toHaveBeenCalledWith('query', 'foo'); }); }); + + describe('deleteSavedQuery', function() { + it('should delete the saved query for the given ID', async () => { + await deleteSavedQuery('foo'); + expect(mockSavedObjectsClient.delete).toHaveBeenCalledWith('query', 'foo'); + }); + }); }); From 4ca6e1b016617535c78b40afffef9ee7cdb7842a Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 14:56:33 -0400 Subject: [PATCH 124/189] Updating discover security functional tests to check popover instead of autocomplete drop down --- .../components/saved_query_manager.tsx | 9 +- test/functional/services/index.ts | 3 + test/functional/services/query_bar.js | 25 ----- .../services/saved_query_manager.js | 99 +++++++++++++++++++ .../feature_controls/discover_security.ts | 26 ++--- 5 files changed, 124 insertions(+), 38 deletions(-) create mode 100644 test/functional/services/saved_query_manager.js diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index cfd43c281ad37..6bd1473415652 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -122,6 +122,7 @@ export const SavedQueryManager: FunctionComponent = ({ aria-label={i18n.translate('data.search.searchBar.savedQueryPopoverButtonText', { defaultMessage: 'See saved queries', })} + data-test-subj="saved-query-manager-popover-button" > #
    @@ -140,6 +141,7 @@ export const SavedQueryManager: FunctionComponent = ({ setIsOpen(false); }} flush="left" + data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} > @@ -196,6 +198,7 @@ export const SavedQueryManager: FunctionComponent = ({ values: { title: savedQuery.attributes.title }, } )} + data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} /> )); @@ -213,7 +216,7 @@ export const SavedQueryManager: FunctionComponent = ({ anchorPosition="downLeft" ownFocus > -
    +
    {savedQueryPopoverTitleText} @@ -260,6 +263,7 @@ export const SavedQueryManager: FunctionComponent = ({ defaultMessage: 'Save as a new saved query', } )} + data-test-subj="saved-query-manager-save-as-new-button" > {i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', @@ -281,6 +285,7 @@ export const SavedQueryManager: FunctionComponent = ({ values: { title: loadedSavedQuery.attributes.title }, } )} + data-test-subj="saved-query-manager-save-changes-button" > {i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', @@ -302,6 +307,7 @@ export const SavedQueryManager: FunctionComponent = ({ 'data.search.searchBar.savedQueryPopoverSaveButtonAriaLabel', { defaultMessage: 'Save a new saved query' } )} + data-test-subj="saved-query-manager-save-button" > {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { defaultMessage: 'Save', @@ -318,6 +324,7 @@ export const SavedQueryManager: FunctionComponent = ({ 'data.search.searchBar.savedQueryPopoverClearButtonAriaLabel', { defaultMessage: 'Clear current saved query' } )} + data-test-subj="saved-query-manager-clear-button" > {i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { defaultMessage: 'Clear', diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index aca945e3ee592..296e2b11cda41 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -60,6 +60,8 @@ import { ToastsProvider } from './toasts'; import { PieChartProvider } from './visualizations'; // @ts-ignore not TS yet import { VisualizeListingTableProvider } from './visualize_listing_table'; +// @ts-ignore not TS yet +import { SavedQueryManagerProvider } from './saved_query_manager'; export const services = { ...commonServiceProviders, @@ -89,4 +91,5 @@ export const services = { appsMenu: AppsMenuProvider, globalNav: GlobalNavProvider, toasts: ToastsProvider, + savedQueryManager: SavedQueryManagerProvider, }; diff --git a/test/functional/services/query_bar.js b/test/functional/services/query_bar.js index c63fdcca8309c..eb11ff10c8ed8 100644 --- a/test/functional/services/query_bar.js +++ b/test/functional/services/query_bar.js @@ -60,31 +60,6 @@ export function QueryBarProvider({ getService, getPageObjects }) { await testSubjects.click('querySubmitButton'); } - async saveNewQuery(title, description, includeFilters, includeTimeFilter) { - await testSubjects.click('savedQuerySaveNew'); - await this.submitSaveQueryForm(title, description, includeFilters, includeTimeFilter); - } - - async submitSaveQueryForm(title, description, includeFilters, includeTimeFilter) { - await testSubjects.setValue('saveQueryFormTitle', title); - await testSubjects.setValue('saveQueryFormDescription', description); - - const currentIncludeFiltersValue = (await testSubjects.getAttribute('saveQueryFormIncludeFiltersOption', 'checked')) === 'true'; - if (currentIncludeFiltersValue !== includeFilters) { - await testSubjects.click('saveQueryFormIncludeFiltersOption'); - } - - const currentIncludeTimeFilterValue = (await testSubjects.getAttribute('saveQueryFormIncludeTimeFilterOption', 'checked')) === 'true'; - if (currentIncludeTimeFilterValue !== includeTimeFilter) { - await testSubjects.click('saveQueryFormIncludeTimeFilterOption'); - } - - await testSubjects.click('savedQueryFormSaveButton'); - } - - async openSuggestionsDropDown() { - await testSubjects.click('queryInput'); - } } return new QueryBar(); diff --git a/test/functional/services/saved_query_manager.js b/test/functional/services/saved_query_manager.js new file mode 100644 index 0000000000000..bd09bb737769a --- /dev/null +++ b/test/functional/services/saved_query_manager.js @@ -0,0 +1,99 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; + +export function SavedQueryManagerProvider({ getService }) { + const testSubjects = getService('testSubjects'); + const queryBar = getService('queryBar'); + + class SavedQueryManager { + + async saveNewQuery(name, description, includeFilters, includeTimeFilter) { + await this.openSavedQueryManager(); + await testSubjects.click('saved-query-manager-save-button'); + await this.submitSaveQueryForm(name, description, includeFilters, includeTimeFilter); + } + + async saveCurrentlyLoadedAsNewQuery(name, description, includeFilters, includeTimeFilter) { + await this.openSavedQueryManager(); + await testSubjects.click('saved-query-manager-save-as-new-button'); + await this.submitSaveQueryForm(name, description, includeFilters, includeTimeFilter); + } + + async updateCurrentlyLoadedQuery(description, includeFilters, includeTimeFilter) { + await this.openSavedQueryManager(); + await testSubjects.click('saved-query-manager-save-changes-button'); + await this.submitSaveQueryForm(null, description, includeFilters, includeTimeFilter); + } + + async loadSavedQuery(title) { + await this.openSavedQueryManager(); + await testSubjects.click(`load-saved-query-${title}-button`); + } + + async clearCurrentlyLoadedQuery() { + await this.openSavedQueryManager(); + await testSubjects.click('saved-query-manager-clear-button'); + await this.closeSavedQueryManager(); + const queryString = await queryBar.getQueryString(); + expect(queryString).to.be.empty(); + } + + async submitSaveQueryForm(title, description, includeFilters, includeTimeFilter) { + if (title) { + await testSubjects.setValue('saveQueryFormTitle', title); + } + await testSubjects.setValue('saveQueryFormDescription', description); + + const currentIncludeFiltersValue = (await testSubjects.getAttribute('saveQueryFormIncludeFiltersOption', 'checked')) === 'true'; + if (currentIncludeFiltersValue !== includeFilters) { + await testSubjects.click('saveQueryFormIncludeFiltersOption'); + } + + const currentIncludeTimeFilterValue = (await testSubjects.getAttribute('saveQueryFormIncludeTimeFilterOption', 'checked')) === 'true'; + if (currentIncludeTimeFilterValue !== includeTimeFilter) { + await testSubjects.click('saveQueryFormIncludeTimeFilterOption'); + } + + await testSubjects.click('savedQueryFormSaveButton'); + } + + async savedQueryExistOrFail(title) { + await testSubjects.click('saved-query-manager-popover-button'); + await testSubjects.existOrFail(`load-saved-query-${title}-button`); + } + + async openSavedQueryManager() { + const isOpenAlready = await testSubjects.exists('saved-query-manager-popover'); + if (isOpenAlready) return; + + await testSubjects.click('saved-query-manager-popover-button'); + } + + async closeSavedQueryManager() { + const isOpenAlready = await testSubjects.exists('saved-query-manager-popover'); + if (!isOpenAlready) return; + + await testSubjects.click('saved-query-manager-popover-button'); + } + } + + return new SavedQueryManager(); +} diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index ba9a1b429f21a..0de25a788efa2 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -22,6 +22,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const appsMenu = getService('appsMenu'); const queryBar = getService('queryBar'); + const savedQueryManager = getService('savedQueryManager'); async function setDiscoverTimeRange() { const fromTime = '2015-09-19 06:31:44.000'; @@ -105,22 +106,23 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.clickShareTopNavButton(); }); - it('show the save query button in the query bar in dirty state with no query loaded', async () => { - await queryBar.setQuery('response'); - await testSubjects.existOrFail('savedQuerySaveNew'); + it('allow saving via the saved query manager popover with no query loaded', async () => { + await savedQueryManager.saveNewQuery('foo', 'bar', true, false); + await savedQueryManager.savedQueryExistOrFail('foo'); }); - it('show the save as new query button in the query bar with non-dirty state and query loaded', async () => { - await queryBar.setQuery('response:200 '); - await queryBar.saveNewQuery('OK Responses', '200 OK', true, true); - await queryBar.openSuggestionsDropDown(); - await testSubjects.existOrFail('savedQuerySaveAsNew'); + it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { + await savedQueryManager.saveCurrentlyLoadedAsNewQuery('foo2', 'bar2', true, false); + await savedQueryManager.savedQueryExistOrFail('foo2'); }); - it('show the save changes to existing and save as new buttons in the query bar with a dirty state and a query loaded', async () => { - await queryBar.setQuery('response:404 '); - await testSubjects.existOrFail('savedQuerySaveChanges'); - await testSubjects.existOrFail('savedQuerySaveAsNew'); + it('allow saving changes to a currently loaded query via the saved query manager', async () => { + await queryBar.setQuery('response:404'); + await savedQueryManager.updateCurrentlyLoadedQuery('bar2', false, false); + await savedQueryManager.clearCurrentlyLoadedQuery(); + await savedQueryManager.loadSavedQuery('foo2'); + const queryString = await queryBar.getQueryString(); + expect(queryString).to.eql('response:404'); }); }); From fcfbf03cbf6dc44b7b2652f9e8e12f05955ee984 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 17:39:57 -0400 Subject: [PATCH 125/189] Add info icon with description of saved query --- .../components/saved_query_manager.tsx | 139 ++++++++++-------- 1 file changed, 78 insertions(+), 61 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 6bd1473415652..34f2873870a14 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -29,6 +29,7 @@ import { EuiConfirmModal, EuiOverlayMask, EuiScreenReaderOnly, + EuiIconTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -135,71 +136,87 @@ export const SavedQueryManager: FunctionComponent = ({ ); return savedQueriesDisplayRows.map(savedQuery => (
  • - { - onLoad(savedQuery); - setIsOpen(false); - }} - flush="left" - data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} - > - - - {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { - defaultMessage: 'Open saved query', - })} - - - {savedQuery.attributes.title} - - { - setConfirmDeletionModal( - - + + { + onLoad(savedQuery); + setIsOpen(false); + }} + flush="left" + data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} + > + + + {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { + defaultMessage: 'Open saved query', + })} + + + {savedQuery.attributes.title} + + + + + + {savedQuery.attributes.description && ( + + )} + + + + { + setConfirmDeletionModal( + + { + onDeleteSavedQuery(savedQuery); + setConfirmDeletionModal(null); + }} + onCancel={() => { + setConfirmDeletionModal(null); + }} + /> + + ); + }} + iconType="trash" + color="danger" + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', { - defaultMessage: 'Cancel', + defaultMessage: 'Delete saved query {title}', + values: { title: savedQuery.attributes.title }, } )} - onConfirm={() => { - onDeleteSavedQuery(savedQuery); - setConfirmDeletionModal(null); - }} - onCancel={() => { - setConfirmDeletionModal(null); - }} + data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} /> - - ); - }} - iconType="trash" - color="danger" - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', - { - defaultMessage: 'Delete saved query {title}', - values: { title: savedQuery.attributes.title }, - } - )} - data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} - /> + + + +
  • )); }; From 5bc3520084ece104146ddd6d0ab18550fcaaf9c4 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 18:44:35 -0400 Subject: [PATCH 126/189] don't show delete button when user doesn't have save query write perms --- .../components/saved_query_manager.tsx | 98 ++++++++++--------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index 34f2873870a14..fa7d824a69d92 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -165,54 +165,56 @@ export const SavedQueryManager: FunctionComponent = ({ - { - setConfirmDeletionModal( - - { - onDeleteSavedQuery(savedQuery); - setConfirmDeletionModal(null); - }} - onCancel={() => { - setConfirmDeletionModal(null); - }} - /> - - ); - }} - iconType="trash" - color="danger" - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', - { - defaultMessage: 'Delete saved query {title}', - values: { title: savedQuery.attributes.title }, - } - )} - data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} - /> + {showSaveQuery && ( + { + setConfirmDeletionModal( + + { + onDeleteSavedQuery(savedQuery); + setConfirmDeletionModal(null); + }} + onCancel={() => { + setConfirmDeletionModal(null); + }} + /> + + ); + }} + iconType="trash" + color="danger" + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', + { + defaultMessage: 'Delete saved query {title}', + values: { title: savedQuery.attributes.title }, + } + )} + data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} + /> + )} From 0395adb1fc88be7b39d3c0977a75e1bb1421984e Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 8 Aug 2019 19:02:19 -0400 Subject: [PATCH 127/189] Test that delete button shows when user has saved query write perms --- test/functional/services/saved_query_manager.js | 13 ++++++++++++- .../discover/feature_controls/discover_security.ts | 5 +++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/test/functional/services/saved_query_manager.js b/test/functional/services/saved_query_manager.js index bd09bb737769a..f640bb0b5d12a 100644 --- a/test/functional/services/saved_query_manager.js +++ b/test/functional/services/saved_query_manager.js @@ -48,6 +48,12 @@ export function SavedQueryManagerProvider({ getService }) { await testSubjects.click(`load-saved-query-${title}-button`); } + async deleteSavedQuery(title) { + await this.openSavedQueryManager(); + await testSubjects.click(`delete-saved-query-${title}-button`); + await testSubjects.click('confirmModalConfirmButton'); + } + async clearCurrentlyLoadedQuery() { await this.openSavedQueryManager(); await testSubjects.click('saved-query-manager-clear-button'); @@ -76,10 +82,15 @@ export function SavedQueryManagerProvider({ getService }) { } async savedQueryExistOrFail(title) { - await testSubjects.click('saved-query-manager-popover-button'); + await this.openSavedQueryManager(); await testSubjects.existOrFail(`load-saved-query-${title}-button`); } + async savedQueryMissingOrFail(title) { + await this.openSavedQueryManager(); + await testSubjects.missingOrFail(`load-saved-query-${title}-button`); + } + async openSavedQueryManager() { const isOpenAlready = await testSubjects.exists('saved-query-manager-popover'); if (isOpenAlready) return; diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 0de25a788efa2..c7226bbf5fa70 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -124,6 +124,11 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:404'); }); + + it('allows deleting saved queries in the saved query manager ', async () => { + await savedQueryManager.deleteSavedQuery('foo2'); + await savedQueryManager.savedQueryMissingOrFail('foo2'); + }); }); describe('global discover read-only privileges', () => { From 0e7b2a303f14b2da9597667884a2272ecd36a9c4 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 09:36:14 -0700 Subject: [PATCH 128/189] Adds empty query as delay for deleting a saved query test --- .../apps/discover/feature_controls/discover_security.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index c7226bbf5fa70..b490ccb94834c 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; +import { Driver } from 'selenium-webdriver/firefox'; import { SecurityService } from '../../../../common/services'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -127,6 +128,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('allows deleting saved queries in the saved query manager ', async () => { await savedQueryManager.deleteSavedQuery('foo2'); + // add a manual delay + await queryBar.setQuery(''); await savedQueryManager.savedQueryMissingOrFail('foo2'); }); }); From 72753df15a46c24e1d142e734e4fc7a727941135 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 10:21:40 -0700 Subject: [PATCH 129/189] Adds test for loading a saved query as a discover read-only user --- .../feature_controls/discover_security.ts | 21 ++++--------------- .../feature_controls/security/data.json | 2 +- .../feature_controls/security/data.json | 2 +- .../es_archives/visualize/default/data.json | 2 +- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index b490ccb94834c..ca5619b8a95e5 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { Driver } from 'selenium-webdriver/firefox'; import { SecurityService } from '../../../../common/services'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -199,22 +198,10 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.createShortUrlMissingOrFail(); }); - it('does not show the save query button in the query bar in dirty state with no query loaded', async () => { - await queryBar.setQuery('response'); - await testSubjects.missingOrFail('savedQuerySaveNew'); - }); - - it('does not show the save as new query button in the query bar with non-dirty state and query loaded', async () => { - await queryBar.setQuery('OK Jpgs'); - await testSubjects.click('autocompleteSuggestion-savedQuery-OK-Jpgs'); - await queryBar.openSuggestionsDropDown(); - await testSubjects.missingOrFail('savedQuerySaveAsNew'); - }); - - it('does not show the save changes to existing or save as new button in the query bar with a dirty state and a query loaded', async () => { - await queryBar.setQuery('response:404 '); - await testSubjects.missingOrFail('savedQuerySaveChanges'); - await testSubjects.missingOrFail('savedQuerySaveAsNew'); + it('allows loading a saved query via the saved query manager', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + const queryString = await queryBar.getQueryString(); + expect(queryString).to.eql('response:200'); }); }); diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json index d467d56919dae..b2453db328317 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json @@ -178,7 +178,7 @@ "id": "query:okjpgs", "source": { "query": { - "title": "OK Jpgs", + "title": "OKJpgs", "description": "Ok responses for jpg files", "query": { "query": "response:200", diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json b/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json index f70ac5fb20d28..ace3f175ceaaf 100644 --- a/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json +++ b/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json @@ -44,7 +44,7 @@ "id": "query:okjpgs", "source": { "query": { - "title": "OK Jpgs", + "title": "OKJpgs", "description": "Ok responses for jpg files", "query": { "query": "response:200", diff --git a/x-pack/test/functional/es_archives/visualize/default/data.json b/x-pack/test/functional/es_archives/visualize/default/data.json index 83a46b8a5de3f..c905bb8ca6c17 100644 --- a/x-pack/test/functional/es_archives/visualize/default/data.json +++ b/x-pack/test/functional/es_archives/visualize/default/data.json @@ -117,7 +117,7 @@ "id": "query:okjpgs", "source": { "query": { - "title": "OK Jpgs", + "title": "OKJpgs", "description": "Ok responses for jpg files", "query": { "query": "response:200", From 95abe156dd2707c71e55940ec746816f8c9602f3 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 10:38:55 -0700 Subject: [PATCH 130/189] Adds test for a readonly discover user not being able to delete a saved query --- test/functional/services/saved_query_manager.js | 10 ++++++++++ .../discover/feature_controls/discover_security.ts | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/test/functional/services/saved_query_manager.js b/test/functional/services/saved_query_manager.js index f640bb0b5d12a..46827ee66aa3c 100644 --- a/test/functional/services/saved_query_manager.js +++ b/test/functional/services/saved_query_manager.js @@ -104,6 +104,16 @@ export function SavedQueryManagerProvider({ getService }) { await testSubjects.click('saved-query-manager-popover-button'); } + + async saveNewQueryMissingOrFail() { + await this.openSavedQueryManager(); + await testSubjects.missingOrFail('saved-query-manager-save-button'); + } + + async deleteSavedQueryMissingOrFail(title) { + await this.openSavedQueryManager(); + await testSubjects.missingOrFail(`delete-saved-query-${title}-button`); + } } return new SavedQueryManager(); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index ca5619b8a95e5..fc1b69bdf813a 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -203,6 +203,14 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:200'); }); + + it('does not allow saving via the saved query manager popover with no query loaded', async () => { + await savedQueryManager.saveNewQueryMissingOrFail(); + }); + + it('does not allow deleting a saved query from the saved query manager', async () => { + await savedQueryManager.deleteSavedQueryMissingOrFail('OKJpgs'); + }); }); describe('discover and visualize privileges', () => { From 70a078316db4eda398c50020d5ee56889181574e Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 10:45:56 -0700 Subject: [PATCH 131/189] Adds test to ensure discovery readonly user cannot save changes to a saved query --- test/functional/services/saved_query_manager.js | 5 +++++ .../apps/discover/feature_controls/discover_security.ts | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/test/functional/services/saved_query_manager.js b/test/functional/services/saved_query_manager.js index 46827ee66aa3c..7cd8c0dd5870d 100644 --- a/test/functional/services/saved_query_manager.js +++ b/test/functional/services/saved_query_manager.js @@ -110,6 +110,11 @@ export function SavedQueryManagerProvider({ getService }) { await testSubjects.missingOrFail('saved-query-manager-save-button'); } + async updateCurrentlyLoadedQueryMissingOrFail() { + await this.openSavedQueryManager(); + await testSubjects.missingOrFail('saved-query-manager-save-changes-button'); + } + async deleteSavedQueryMissingOrFail(title) { await this.openSavedQueryManager(); await testSubjects.missingOrFail(`delete-saved-query-${title}-button`); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index fc1b69bdf813a..294d8d4e6c44c 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -208,6 +208,12 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await savedQueryManager.saveNewQueryMissingOrFail(); }); + it('does not allow saving changes to saved query from the saved query manager', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + await queryBar.setQuery('response:404'); + await savedQueryManager.updateCurrentlyLoadedQueryMissingOrFail(); + }); + it('does not allow deleting a saved query from the saved query manager', async () => { await savedQueryManager.deleteSavedQueryMissingOrFail('OKJpgs'); }); From 750aa30c0cd6592894bb084f1b4865c590d8bf9d Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 10:50:07 -0700 Subject: [PATCH 132/189] Test that clear button works as a discover readonly user --- .../apps/discover/feature_controls/discover_security.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 294d8d4e6c44c..5751746c39b05 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -217,6 +217,11 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('does not allow deleting a saved query from the saved query manager', async () => { await savedQueryManager.deleteSavedQueryMissingOrFail('OKJpgs'); }); + + it('allows clearing the currently loaded saved query', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManager.clearCurrentlyLoadedQuery(); + }); }); describe('discover and visualize privileges', () => { From b9e7ec00a60f4ee0bc17846194260e6bc7b157d5 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 12:05:20 -0700 Subject: [PATCH 133/189] Adds saved query tests to dashboard as a dashboard & embeddable all privileges user --- .../feature_controls/dashboard_security.ts | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index 3f780d709e253..ec6d0ac449df1 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -21,6 +21,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const globalNav = getService('globalNav'); const queryBar = getService('queryBar'); + const savedQueryManager = getService('savedQueryManager'); describe('dashboard security', () => { before(async () => { @@ -189,24 +190,30 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await panelActions.expectExistsEditPanelAction(); }); - it('show the save query button in the query bar in dirty state with no query loaded', async () => { - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.gotoDashboardEditMode('A Dashboard'); - await queryBar.setQuery('response'); - await testSubjects.existOrFail('savedQuerySaveNew'); + it('allow saving via the saved query manager popover with no query loaded', async () => { + await savedQueryManager.saveNewQuery('foo', 'bar', true, false); + await savedQueryManager.savedQueryExistOrFail('foo'); }); - it('show the save as new query button in the query bar with non-dirty state and query loaded', async () => { - await queryBar.setQuery('response:200 '); - await queryBar.saveNewQuery('OK Responses', '200 OK', true, true); - await queryBar.openSuggestionsDropDown(); - await testSubjects.existOrFail('savedQuerySaveAsNew'); + it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { + await savedQueryManager.saveCurrentlyLoadedAsNewQuery('foo2', 'bar2', true, false); + await savedQueryManager.savedQueryExistOrFail('foo2'); }); - it('show the save changes to existing and save as new buttons in the query bar with a dirty state and a query loaded', async () => { - await queryBar.setQuery('response:404 '); - await testSubjects.existOrFail('savedQuerySaveChanges'); - await testSubjects.existOrFail('savedQuerySaveAsNew'); + it('allow saving changes to a currently loaded query via the saved query manager', async () => { + await queryBar.setQuery('response:404'); + await savedQueryManager.updateCurrentlyLoadedQuery('bar2', false, false); + await savedQueryManager.clearCurrentlyLoadedQuery(); + await savedQueryManager.loadSavedQuery('foo2'); + const queryString = await queryBar.getQueryString(); + expect(queryString).to.eql('response:404'); + }); + + it('allows deleting saved queries in the saved query manager ', async () => { + await savedQueryManager.deleteSavedQuery('foo2'); + // add a manual delay + await queryBar.setQuery('response:503'); + await savedQueryManager.savedQueryMissingOrFail('foo2'); }); }); From 9eea8ec474a5bc5ca7504e7f69a6a3bd83aed724 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 12:31:49 -0700 Subject: [PATCH 134/189] Adds saved query manager interactions tests as a dashboard read-only user --- .../feature_controls/dashboard_security.ts | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index ec6d0ac449df1..8f9b4f2b4bddc 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -304,22 +304,29 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.clickShareTopNavButton(); }); - it('does not show the save query button in the query bar in dirty state with no query loaded', async () => { - await queryBar.setQuery('response'); - await testSubjects.missingOrFail('savedQuerySaveNew'); + it('allows loading a saved query via the saved query manager', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + const queryString = await queryBar.getQueryString(); + expect(queryString).to.eql('response:200'); + }); + + it('does not allow saving via the saved query manager popover with no query loaded', async () => { + await savedQueryManager.saveNewQueryMissingOrFail(); }); - it('does not show the save as new query button in the query bar with non-dirty state and query loaded', async () => { - await queryBar.setQuery('OK Jpgs'); - await testSubjects.click('autocompleteSuggestion-savedQuery-OK-Jpgs'); - await queryBar.openSuggestionsDropDown(); - await testSubjects.missingOrFail('savedQuerySaveAsNew'); + it('does not allow saving changes to saved query from the saved query manager', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + await queryBar.setQuery('response:404'); + await savedQueryManager.updateCurrentlyLoadedQueryMissingOrFail(); }); - it('does not show the save changes to existing or save as new button in the query bar with a dirty state and a query loaded', async () => { - await queryBar.setQuery('response:404 '); - await testSubjects.missingOrFail('savedQuerySaveChanges'); - await testSubjects.missingOrFail('savedQuerySaveAsNew'); + it('does not allow deleting a saved query from the saved query manager', async () => { + await savedQueryManager.deleteSavedQueryMissingOrFail('OKJpgs'); + }); + + it('allows clearing the currently loaded saved query', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManager.clearCurrentlyLoadedQuery(); }); }); From 057933cc4b93d4d4ff26414e61a22fcb4cc462c4 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 14:02:33 -0700 Subject: [PATCH 135/189] Adds saved query manager interaction tests to visualize --- .../feature_controls/visualize_security.ts | 66 ++++++++++++------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 93721df7cabfa..5e1ac57d914e7 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -22,6 +22,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const appsMenu = getService('appsMenu'); const globalNav = getService('globalNav'); const queryBar = getService('queryBar'); + const savedQueryManager = getService('savedQueryManager'); describe('feature controls security', () => { before(async () => { @@ -117,22 +118,30 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.clickShareTopNavButton(); }); - it('show the save query button in the query bar in dirty state with no query loaded', async () => { - await queryBar.setQuery('response'); - await testSubjects.existOrFail('savedQuerySaveNew'); + it('allow saving via the saved query manager popover with no query loaded', async () => { + await savedQueryManager.saveNewQuery('foo', 'bar', true, false); + await savedQueryManager.savedQueryExistOrFail('foo'); }); - it('show the save as new query button in the query bar with non-dirty state and query loaded', async () => { - await queryBar.setQuery('response:200 '); - await queryBar.saveNewQuery('OK Responses', '200 OK', true, true); - await queryBar.openSuggestionsDropDown(); - await testSubjects.existOrFail('savedQuerySaveAsNew'); + it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { + await savedQueryManager.saveCurrentlyLoadedAsNewQuery('foo2', 'bar2', true, false); + await savedQueryManager.savedQueryExistOrFail('foo2'); }); - it('show the save changes to existing and save as new buttons in the query bar with a dirty state and a query loaded', async () => { - await queryBar.setQuery('response:404 '); - await testSubjects.existOrFail('savedQuerySaveChanges'); - await testSubjects.existOrFail('savedQuerySaveAsNew'); + it('allow saving changes to a currently loaded query via the saved query manager', async () => { + await queryBar.setQuery('response:404'); + await savedQueryManager.updateCurrentlyLoadedQuery('bar2', false, false); + await savedQueryManager.clearCurrentlyLoadedQuery(); + await savedQueryManager.loadSavedQuery('foo2'); + const queryString = await queryBar.getQueryString(); + expect(queryString).to.eql('response:404'); + }); + + it('allows deleting saved queries in the saved query manager ', async () => { + await savedQueryManager.deleteSavedQuery('foo2'); + // add a manual delay + await queryBar.setQuery('response:503'); + await savedQueryManager.savedQueryMissingOrFail('foo2'); }); }); @@ -215,24 +224,33 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it(`Permalinks doesn't show create short-url button`, async () => { await PageObjects.share.openShareMenuItem('Permalinks'); await PageObjects.share.createShortUrlMissingOrFail(); + // close the menu + await PageObjects.share.clickShareTopNavButton(); + }); + + it('allows loading a saved query via the saved query manager', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + const queryString = await queryBar.getQueryString(); + expect(queryString).to.eql('response:200'); + }); + + it('does not allow saving via the saved query manager popover with no query loaded', async () => { + await savedQueryManager.saveNewQueryMissingOrFail(); }); - it('does not show the save query button in the query bar in dirty state with no query loaded', async () => { - await queryBar.setQuery('response'); - await testSubjects.missingOrFail('savedQuerySaveNew'); + it('does not allow saving changes to saved query from the saved query manager', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + await queryBar.setQuery('response:404'); + await savedQueryManager.updateCurrentlyLoadedQueryMissingOrFail(); }); - it('does not show the save as new query button in the query bar with non-dirty state and query loaded', async () => { - await queryBar.setQuery('OK Jpgs'); - await testSubjects.click('autocompleteSuggestion-savedQuery-OK-Jpgs'); - await queryBar.openSuggestionsDropDown(); - await testSubjects.missingOrFail('savedQuerySaveAsNew'); + it('does not allow deleting a saved query from the saved query manager', async () => { + await savedQueryManager.deleteSavedQueryMissingOrFail('OKJpgs'); }); - it('does not show the save changes to existing or save as new button in the query bar with a dirty state and a query loaded', async () => { - await queryBar.setQuery('response:404 '); - await testSubjects.missingOrFail('savedQuerySaveChanges'); - await testSubjects.missingOrFail('savedQuerySaveAsNew'); + it('allows clearing the currently loaded saved query', async () => { + await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManager.clearCurrentlyLoadedQuery(); }); }); From 4918baa9eed978b8e986b7f70975754fbf059240 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 9 Aug 2019 16:38:00 -0700 Subject: [PATCH 136/189] Fixes typo in visualize security test --- .../apps/visualize/feature_controls/visualize_security.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 5e1ac57d914e7..7f9e7a967112d 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -118,17 +118,21 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.clickShareTopNavButton(); }); - it('allow saving via the saved query manager popover with no query loaded', async () => { + it('allow saving via the saved query manager popover with no saved query loaded', async () => { + await queryBar.setQuery('response:200'); await savedQueryManager.saveNewQuery('foo', 'bar', true, false); await savedQueryManager.savedQueryExistOrFail('foo'); + await savedQueryManager.closeSavedQueryManager(); }); it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { await savedQueryManager.saveCurrentlyLoadedAsNewQuery('foo2', 'bar2', true, false); await savedQueryManager.savedQueryExistOrFail('foo2'); + await savedQueryManager.closeSavedQueryManager(); }); it('allow saving changes to a currently loaded query via the saved query manager', async () => { + await savedQueryManager.loadSavedQuery('foo2'); await queryBar.setQuery('response:404'); await savedQueryManager.updateCurrentlyLoadedQuery('bar2', false, false); await savedQueryManager.clearCurrentlyLoadedQuery(); From 259370e00bebb9c08aee751d4b3646607a533064 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 13:37:47 -0400 Subject: [PATCH 137/189] Fix type error --- .../data/public/query/query_bar/components/query_bar.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index fcb75ef2f7328..f5c40b1e2fdf8 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -201,7 +201,7 @@ export class QueryBarUI extends Component { prepend={this.props.prepend} query={this.props.query!} screenTitle={this.props.screenTitle} - store={this.props.store} + store={this.props.store!} onChange={this.onQueryChange} onSubmit={this.onInputSubmit} persistedLog={this.persistedLog} @@ -216,7 +216,9 @@ export class QueryBarUI extends Component { } private shouldRenderQueryInput() { - return this.props.showQueryInput && this.props.indexPatterns && this.props.query; + return ( + this.props.showQueryInput && this.props.indexPatterns && this.props.query && this.props.store + ); } private renderUpdateButton() { From 4ec7c169d303a8e902d1353a61d42272623d6e91 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 13:41:21 -0400 Subject: [PATCH 138/189] Use uiSettings for time defaults --- .../data/public/query/query_bar/components/query_bar.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index f5c40b1e2fdf8..32d1494f86e5f 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -97,9 +97,10 @@ export class QueryBarUI extends Component { }; public getDateRange() { + const defaultTimeSetting = this.props.uiSettings.get('timepicker:timeDefaults'); return { - from: this.props.dateRangeFrom || 'now-15m', - to: this.props.dateRangeTo || 'now', + from: this.props.dateRangeFrom || defaultTimeSetting.from, + to: this.props.dateRangeTo || defaultTimeSetting.to, }; } From 53e07b05d39eb339f2eb00c485fec1315b9657f2 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 13:48:30 -0400 Subject: [PATCH 139/189] Be consistent with our variable use --- .../data/public/query/query_bar/components/query_bar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx index 32d1494f86e5f..edc00877d397a 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar.tsx @@ -146,7 +146,7 @@ export class QueryBarUI extends Component { public onSubmit = ({ query, dateRange }: { query?: Query; dateRange: DateRange }) => { this.handleLuceneSyntaxWarning(); - timeHistory.add(this.getDateRange()); + timeHistory.add(dateRange); this.props.onSubmit({ query, dateRange }); }; From 1c01c53c2061590496f4162588e9344170ab997d Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 13:49:38 -0400 Subject: [PATCH 140/189] condense imports --- .../public/search/search_bar/components/save_query_form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index c96bad48dc054..024fd9b925aa7 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -31,8 +31,8 @@ import { EuiFormRow, EuiFieldText, EuiSwitch, + EuiText, } from '@elastic/eui'; -import { EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { SavedQueryAttributes } from '../index'; From 85d8fc320fd5e64ab021eef2f31b1a231b25a85e Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 13:57:39 -0400 Subject: [PATCH 141/189] use default prop values --- .../search/search_bar/components/save_query_form.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index 024fd9b925aa7..65f777662d91a 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -55,8 +55,8 @@ export const SaveQueryForm: FunctionComponent = ({ savedQuery, onSave, onClose, - showFilterOption, - showTimeFilterOption, + showFilterOption = true, + showTimeFilterOption = true, }) => { const [title, setTitle] = useState(savedQuery ? savedQuery.title : ''); const [description, setDescription] = useState(savedQuery ? savedQuery.description : ''); @@ -112,7 +112,7 @@ export const SaveQueryForm: FunctionComponent = ({ data-test-subj="saveQueryFormDescription" /> - {!!showFilterOption && ( + {showFilterOption && ( = ({ )} - {!!showTimeFilterOption && ( + {showTimeFilterOption && ( Date: Mon, 12 Aug 2019 14:01:44 -0400 Subject: [PATCH 142/189] Explain why time filter is not included by default --- .../public/search/search_bar/components/save_query_form.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index 65f777662d91a..57b123503d98f 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -63,6 +63,9 @@ export const SaveQueryForm: FunctionComponent = ({ const [shouldIncludeFilters, setShouldIncludeFilters] = useState( savedQuery ? !!savedQuery.filters : true ); + // Defaults to false because saved queries are meant to be as portable as possible and loading + // a saved query with a time filter will override whatever the current value of the global timepicker + // is. We expect this option to be used rarely and only when the user knows they want this behavior. const [shouldIncludeTimefilter, setIncludeTimefilter] = useState( savedQuery ? !!savedQuery.timefilter : false ); From b04108ea8d419fcfedc22c5de122ff89a6fbafdf Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 14:05:50 -0400 Subject: [PATCH 143/189] Get rid of our own RefreshInterval --- .../core_plugins/data/public/search/search_bar/index.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index 94ab794822b1e..55f65e0e22505 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -18,15 +18,11 @@ */ import { Filter } from '@kbn/es-query'; +import { RefreshInterval } from 'ui/timefilter/timefilter'; import { Query } from '../../query/query_bar'; export * from './components'; -interface RefreshInterval { - pause: boolean; - value: number; -} - export interface SavedQuery { id: string; attributes: SavedQueryAttributes; From 8c9cc229b14f1a7974476243e17cb36c1fdf613f Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 14:32:39 -0400 Subject: [PATCH 144/189] Use the timefilter TimeRange type --- .../search/search_bar/components/search_bar.tsx | 12 ++++-------- .../data/public/search/search_bar/index.tsx | 12 ++++++------ .../search_bar/lib/saved_query_service.test.ts | 4 ++-- .../public/dashboard/dashboard_app_controller.tsx | 4 ++-- .../kibana/public/discover/controllers/discover.js | 4 ++-- .../kibana/public/visualize/editor/editor.js | 4 ++-- src/legacy/ui/public/timefilter/timefilter.d.ts | 2 +- 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index f6cde88311531..0a324e4b85ba3 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -232,8 +232,8 @@ class SearchBarUI extends Component { this.props.isRefreshPaused !== undefined ) { savedQueryAttributes.timefilter = { - timeFrom: this.state.dateRangeFrom, - timeTo: this.state.dateRangeTo, + from: this.state.dateRangeFrom, + to: this.state.dateRangeTo, refreshInterval: { value: this.props.refreshInterval, pause: this.props.isRefreshPaused, @@ -320,12 +320,8 @@ class SearchBarUI extends Component { }; public onLoadSavedQuery = (savedQuery: SavedQuery) => { - const dateRangeFrom = get( - savedQuery, - 'attributes.timefilter.timeFrom', - this.state.dateRangeFrom - ); - const dateRangeTo = get(savedQuery, 'attributes.timefilter.timeTo', this.state.dateRangeTo); + const dateRangeFrom = get(savedQuery, 'attributes.timefilter.from', this.state.dateRangeFrom); + const dateRangeTo = get(savedQuery, 'attributes.timefilter.to', this.state.dateRangeTo); this.setState({ query: savedQuery.attributes.query, diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index 55f65e0e22505..d110c420cdc07 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -18,11 +18,15 @@ */ import { Filter } from '@kbn/es-query'; -import { RefreshInterval } from 'ui/timefilter/timefilter'; +import { RefreshInterval, TimeRange } from 'ui/timefilter/timefilter'; import { Query } from '../../query/query_bar'; export * from './components'; +type SavedQueryTimeFilter = TimeRange & { + refreshInterval: RefreshInterval; +}; + export interface SavedQuery { id: string; attributes: SavedQueryAttributes; @@ -33,9 +37,5 @@ export interface SavedQueryAttributes { description: string; query: Query; filters?: Filter[]; - timefilter?: { - timeFrom: string; - timeTo: string; - refreshInterval: RefreshInterval; - }; + timefilter?: SavedQueryTimeFilter; } diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index 8fdbd237b6b9d..648dd7c749e39 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -49,8 +49,8 @@ const savedQueryAttributesWithFilters: SavedQueryAttributes = { }, ], timefilter: { - timeTo: 'now', - timeFrom: 'now-15m', + to: 'now', + from: 'now-15m', refreshInterval: { pause: false, value: 0, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index ba1c3bfbafada..02192c2d11e8b 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -457,8 +457,8 @@ export class DashboardAppController { ); if (savedQuery.attributes.timefilter) { timefilter.setTime({ - from: savedQuery.attributes.timefilter.timeFrom, - to: savedQuery.attributes.timefilter.timeTo, + from: savedQuery.attributes.timefilter.from, + to: savedQuery.attributes.timefilter.to, }); if (savedQuery.attributes.timefilter.refreshInterval) { timefilter.setRefreshInterval(savedQuery.attributes.timefilter.refreshInterval); diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index be2e432559d24..8bebf96a192ea 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -944,8 +944,8 @@ function discoverController( if (savedQuery.attributes.timefilter) { timefilter.setTime({ - from: savedQuery.attributes.timefilter.timeFrom, - to: savedQuery.attributes.timefilter.timeTo, + from: savedQuery.attributes.timefilter.from, + to: savedQuery.attributes.timefilter.to, }); if (savedQuery.attributes.timefilter.refreshInterval) { timefilter.setRefreshInterval(savedQuery.attributes.timefilter.refreshInterval); diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index bbf2a129438d9..2af53af75f429 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -498,8 +498,8 @@ function VisEditor( if (savedQuery.attributes.timefilter) { timefilter.setTime({ - from: savedQuery.attributes.timefilter.timeFrom, - to: savedQuery.attributes.timefilter.timeTo, + from: savedQuery.attributes.timefilter.from, + to: savedQuery.attributes.timefilter.to, }); if (savedQuery.attributes.timefilter.refreshInterval) { timefilter.setRefreshInterval(savedQuery.attributes.timefilter.refreshInterval); diff --git a/src/legacy/ui/public/timefilter/timefilter.d.ts b/src/legacy/ui/public/timefilter/timefilter.d.ts index 9dd2aafd994a0..4d56b9885db4b 100644 --- a/src/legacy/ui/public/timefilter/timefilter.d.ts +++ b/src/legacy/ui/public/timefilter/timefilter.d.ts @@ -23,7 +23,7 @@ import { RefreshInterval } from '../../../../plugins/data/public'; // NOTE: These types are somewhat guessed, they may be incorrect. -export { RefreshInterval }; +export { RefreshInterval, TimeRange }; export interface Timefilter { time: TimeRange; From 4478b392254e792f23a958952e562059154e232a Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 17:05:58 -0400 Subject: [PATCH 145/189] Use version 8 style import --- .../kibana/public/dashboard/dashboard_app_controller.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index 02192c2d11e8b..1de13a7e94620 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -51,7 +51,7 @@ import { KbnUrl } from 'ui/url/kbn_url'; import { Filter } from '@kbn/es-query'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; -import { Query, SavedQuery } from 'plugins/data'; +import { Query, SavedQuery } from 'src/legacy/core_plugins/data/public'; import { SaveOptions } from 'ui/saved_objects/saved_object'; import { capabilities } from 'ui/capabilities'; import { Subscription } from 'rxjs'; From 8c69437dfcc10dd280a615a298ef7248b86cbd92 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 17:27:13 -0400 Subject: [PATCH 146/189] Reuse existing type now that it is not throwing an error --- .../search/search_bar/lib/saved_query_service.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index 9d4a298058425..64d5568ee4638 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -43,16 +43,7 @@ export const saveQuery = async (attributes: SavedQueryAttributes, { overwrite = language: attributes.query.language, }; - const queryObject: { - title: string; - description: string; - query: { - query: string; - language: string; - }; - filters?: string; - timefilter?: string; - } = { + const queryObject: SerializedSavedQueryAttributes = { title: attributes.title, description: attributes.description, query, From c83873db11417e0f569353699b328c61b09a6578 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 18:28:46 -0400 Subject: [PATCH 147/189] SearchBar is already exported for the top level of the plugin --- src/legacy/core_plugins/data/public/search/search_service.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index 9ea2d28cf7fcb..42684e3999266 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -17,7 +17,6 @@ * under the License. */ -import { SearchBar } from './search_bar'; import * as savedQueryService from './search_bar/lib/saved_query_service'; /** @@ -28,9 +27,6 @@ import * as savedQueryService from './search_bar/lib/saved_query_service'; export class SearchService { public setup() { return { - ui: { - SearchBar, - }, services: { savedQueryService, }, From 763f350aff15ca22696b5cfd7d6f15c494b37227 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 18:35:53 -0400 Subject: [PATCH 148/189] Use removeAll --- .../kibana/public/dashboard/dashboard_app_controller.tsx | 2 +- .../core_plugins/kibana/public/discover/controllers/discover.js | 2 +- .../core_plugins/kibana/public/visualize/editor/editor.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index 1de13a7e94620..6c10cb1cdd5cf 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -437,7 +437,7 @@ export class DashboardAppController { $scope.onClearSavedQuery = () => { delete $scope.savedQuery; dashboardStateManager.setSavedQueryId(undefined); - queryFilter.setFilters([]); + queryFilter.removeAll(); dashboardStateManager.applyFilters( { query: '', diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 8bebf96a192ea..e6fb058b99899 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -933,7 +933,7 @@ function discoverController( query: '', language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage'), }; - queryFilter.setFilters([]); + queryFilter.removeAll(); $state.save(); $scope.fetch(); }; diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index 2af53af75f429..b2e133d4ba547 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -487,7 +487,7 @@ function VisEditor( query: '', language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage') }; - queryFilter.setFilters([]); + queryFilter.removeAll(); $state.save(); $scope.fetch(); }; From 1dc9044fe629d9efef02c81662234d9a4a1fa740 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 19:09:39 -0400 Subject: [PATCH 149/189] Use existing type definition --- .../plugin_discovery/plugin_spec/plugin_spec_options.d.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts index c672a6ec5dbf9..a6afd83e94536 100644 --- a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts +++ b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts @@ -18,12 +18,14 @@ */ import { Server } from '../../server/kbn_server'; import { Capabilities } from '../../../core/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { SavedObjectsManagementDefinition } from '../../../core/server/saved_objects/management'; export type InitPluginFunction = (server: Server) => void; export interface UiExports { injectDefaultVars?: (server: Server) => { [key: string]: any }; styleSheetPaths?: string; - savedObjectsManagement?: any; // Not ideal + savedObjectsManagement?: SavedObjectsManagementDefinition; mappings?: any; // Not ideal visTypes?: string[]; interpreter?: string[]; From ffe3a134ae2efe33e665ea2668cb7fa88b203c28 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Mon, 12 Aug 2019 19:13:02 -0400 Subject: [PATCH 150/189] Give tooltip an aria-label --- .../search/search_bar/components/saved_query_manager.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx index fa7d824a69d92..027b0f030c204 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx @@ -160,7 +160,11 @@ export const SavedQueryManager: FunctionComponent = ({ {savedQuery.attributes.description && ( - + )} From ba733b2741aa133110c8c681194f069e4768c08d Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 13 Aug 2019 15:25:35 -0400 Subject: [PATCH 151/189] Remove unnecessary exports --- src/legacy/core_plugins/data/public/search/index.ts | 2 +- src/legacy/core_plugins/data/public/search/search_service.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/index.ts b/src/legacy/core_plugins/data/public/search/index.ts index 675f853113031..6a9687ba7e325 100644 --- a/src/legacy/core_plugins/data/public/search/index.ts +++ b/src/legacy/core_plugins/data/public/search/index.ts @@ -17,6 +17,6 @@ * under the License. */ -export { SearchService, SearchSetup, SavedQueryAttributes, SavedQuery } from './search_service'; +export { SearchService, SearchSetup } from './search_service'; export * from './search_bar'; diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index 42684e3999266..52629a43566b6 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -39,5 +39,3 @@ export class SearchService { /** @public */ export type SearchSetup = ReturnType; - -export { SavedQuery, SavedQueryAttributes } from './search_bar'; From f68dcc707665a939740f602d01eb3a61fc854bfa Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 13 Aug 2019 16:16:54 -0400 Subject: [PATCH 152/189] Rename SavedQueryManager to SavedQueryManagementComponent --- .../data/public/search/search_bar/_index.scss | 2 +- ...=> _saved_query_management_component.scss} | 2 +- ...x => saved_query_management_component.tsx} | 21 ++++---- .../search_bar/components/search_bar.tsx | 10 ++-- test/functional/services/index.ts | 4 +- ...js => saved_query_management_component.js} | 54 +++++++++---------- .../feature_controls/dashboard_security.ts | 39 ++++++++------ .../feature_controls/discover_security.ts | 39 ++++++++------ .../feature_controls/visualize_security.ts | 45 +++++++++------- 9 files changed, 117 insertions(+), 99 deletions(-) rename src/legacy/core_plugins/data/public/search/search_bar/components/{_saved_query_manager.scss => _saved_query_management_component.scss} (80%) rename src/legacy/core_plugins/data/public/search/search_bar/components/{saved_query_manager.tsx => saved_query_management_component.tsx} (94%) rename test/functional/services/{saved_query_manager.js => saved_query_management_component.js} (69%) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/_index.scss b/src/legacy/core_plugins/data/public/search/search_bar/_index.scss index 11e911e1ad498..6813b39042dad 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/_index.scss +++ b/src/legacy/core_plugins/data/public/search/search_bar/_index.scss @@ -1 +1 @@ -@import './components/saved_query_manager'; +@import 'components/saved_query_management_component'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss similarity index 80% rename from src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss rename to src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss index a39f52b47cd15..be4197326f820 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_manager.scss +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss @@ -1,4 +1,4 @@ -.saved-query-manager-popover { +.saved-query-management-popover { width: 400px; } .saved-query-list { diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx similarity index 94% rename from src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx rename to src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx index 027b0f030c204..512247e61e1e8 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_manager.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx @@ -51,7 +51,7 @@ interface Props { onClearSavedQuery: () => void; } -export const SavedQueryManager: FunctionComponent = ({ +export const SavedQueryManagementComponent: FunctionComponent = ({ showSaveQuery, loadedSavedQuery, onSave, @@ -123,7 +123,7 @@ export const SavedQueryManager: FunctionComponent = ({ aria-label={i18n.translate('data.search.searchBar.savedQueryPopoverButtonText', { defaultMessage: 'See saved queries', })} - data-test-subj="saved-query-manager-popover-button" + data-test-subj="saved-query-management-popover-button" > # @@ -239,8 +239,11 @@ export const SavedQueryManager: FunctionComponent = ({ anchorPosition="downLeft" ownFocus > -
    - +
    + {savedQueryPopoverTitleText} {savedQueries.length > 0 ? ( @@ -254,7 +257,7 @@ export const SavedQueryManager: FunctionComponent = ({
      {savedQueryRows()}
    @@ -286,7 +289,7 @@ export const SavedQueryManager: FunctionComponent = ({ defaultMessage: 'Save as a new saved query', } )} - data-test-subj="saved-query-manager-save-as-new-button" + data-test-subj="saved-query-management-save-as-new-button" > {i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', @@ -308,7 +311,7 @@ export const SavedQueryManager: FunctionComponent = ({ values: { title: loadedSavedQuery.attributes.title }, } )} - data-test-subj="saved-query-manager-save-changes-button" + data-test-subj="saved-query-management-save-changes-button" > {i18n.translate( 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', @@ -330,7 +333,7 @@ export const SavedQueryManager: FunctionComponent = ({ 'data.search.searchBar.savedQueryPopoverSaveButtonAriaLabel', { defaultMessage: 'Save a new saved query' } )} - data-test-subj="saved-query-manager-save-button" + data-test-subj="saved-query-management-save-button" > {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { defaultMessage: 'Save', @@ -347,7 +350,7 @@ export const SavedQueryManager: FunctionComponent = ({ 'data.search.searchBar.savedQueryPopoverClearButtonAriaLabel', { defaultMessage: 'Clear current saved query' } )} - data-test-subj="saved-query-manager-clear-button" + data-test-subj="saved-query-management-clear-button" > {i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { defaultMessage: 'Clear', diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index 0a324e4b85ba3..a8755c8ed92ba 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -31,7 +31,7 @@ import { IndexPattern, Query, QueryBar, FilterBar } from '../../../../../data/pu import { SavedQuery, SavedQueryAttributes } from '../index'; import { saveQuery } from '../lib/saved_query_service'; import { SavedQueryMeta, SaveQueryForm } from './save_query_form'; -import { SavedQueryManager } from './saved_query_manager'; +import { SavedQueryManagementComponent } from './saved_query_management_component'; interface DateRange { from: string; @@ -355,8 +355,8 @@ class SearchBarUI extends Component { return null; } - const savedQueryManager = this.state.query && this.props.onClearSavedQuery && ( - { onLoad={this.onLoadSavedQuery} query={this.state.query} onClearSavedQuery={this.props.onClearSavedQuery} - > + > ); let queryBar; @@ -378,7 +378,7 @@ class SearchBarUI extends Component { appName={this.props.appName} indexPatterns={this.props.indexPatterns} store={this.props.store} - prepend={this.props.showFilterBar ? savedQueryManager : undefined} + prepend={this.props.showFilterBar ? savedQueryManagement : undefined} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} dateRangeTo={this.state.dateRangeTo} diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index 296e2b11cda41..60ab726bafd64 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -61,7 +61,7 @@ import { PieChartProvider } from './visualizations'; // @ts-ignore not TS yet import { VisualizeListingTableProvider } from './visualize_listing_table'; // @ts-ignore not TS yet -import { SavedQueryManagerProvider } from './saved_query_manager'; +import { SavedQueryManagementComponentProvider } from './saved_query_management_component'; export const services = { ...commonServiceProviders, @@ -91,5 +91,5 @@ export const services = { appsMenu: AppsMenuProvider, globalNav: GlobalNavProvider, toasts: ToastsProvider, - savedQueryManager: SavedQueryManagerProvider, + savedQueryManagementComponent: SavedQueryManagementComponentProvider, }; diff --git a/test/functional/services/saved_query_manager.js b/test/functional/services/saved_query_management_component.js similarity index 69% rename from test/functional/services/saved_query_manager.js rename to test/functional/services/saved_query_management_component.js index 7cd8c0dd5870d..924c2ad0300bc 100644 --- a/test/functional/services/saved_query_manager.js +++ b/test/functional/services/saved_query_management_component.js @@ -19,45 +19,45 @@ import expect from '@kbn/expect'; -export function SavedQueryManagerProvider({ getService }) { +export function SavedQueryManagementComponentProvider({ getService }) { const testSubjects = getService('testSubjects'); const queryBar = getService('queryBar'); - class SavedQueryManager { + class SavedQueryManagementComponent { async saveNewQuery(name, description, includeFilters, includeTimeFilter) { - await this.openSavedQueryManager(); - await testSubjects.click('saved-query-manager-save-button'); + await this.openSavedQueryManagementComponent(); + await testSubjects.click('saved-query-management-save-button'); await this.submitSaveQueryForm(name, description, includeFilters, includeTimeFilter); } async saveCurrentlyLoadedAsNewQuery(name, description, includeFilters, includeTimeFilter) { - await this.openSavedQueryManager(); - await testSubjects.click('saved-query-manager-save-as-new-button'); + await this.openSavedQueryManagementComponent(); + await testSubjects.click('saved-query-management-save-as-new-button'); await this.submitSaveQueryForm(name, description, includeFilters, includeTimeFilter); } async updateCurrentlyLoadedQuery(description, includeFilters, includeTimeFilter) { - await this.openSavedQueryManager(); - await testSubjects.click('saved-query-manager-save-changes-button'); + await this.openSavedQueryManagementComponent(); + await testSubjects.click('saved-query-management-save-changes-button'); await this.submitSaveQueryForm(null, description, includeFilters, includeTimeFilter); } async loadSavedQuery(title) { - await this.openSavedQueryManager(); + await this.openSavedQueryManagementComponent(); await testSubjects.click(`load-saved-query-${title}-button`); } async deleteSavedQuery(title) { - await this.openSavedQueryManager(); + await this.openSavedQueryManagementComponent(); await testSubjects.click(`delete-saved-query-${title}-button`); await testSubjects.click('confirmModalConfirmButton'); } async clearCurrentlyLoadedQuery() { - await this.openSavedQueryManager(); - await testSubjects.click('saved-query-manager-clear-button'); - await this.closeSavedQueryManager(); + await this.openSavedQueryManagementComponent(); + await testSubjects.click('saved-query-management-clear-button'); + await this.closeSavedQueryManagementComponent(); const queryString = await queryBar.getQueryString(); expect(queryString).to.be.empty(); } @@ -82,44 +82,44 @@ export function SavedQueryManagerProvider({ getService }) { } async savedQueryExistOrFail(title) { - await this.openSavedQueryManager(); + await this.openSavedQueryManagementComponent(); await testSubjects.existOrFail(`load-saved-query-${title}-button`); } async savedQueryMissingOrFail(title) { - await this.openSavedQueryManager(); + await this.openSavedQueryManagementComponent(); await testSubjects.missingOrFail(`load-saved-query-${title}-button`); } - async openSavedQueryManager() { - const isOpenAlready = await testSubjects.exists('saved-query-manager-popover'); + async openSavedQueryManagementComponent() { + const isOpenAlready = await testSubjects.exists('saved-query-management-popover'); if (isOpenAlready) return; - await testSubjects.click('saved-query-manager-popover-button'); + await testSubjects.click('saved-query-management-popover-button'); } - async closeSavedQueryManager() { - const isOpenAlready = await testSubjects.exists('saved-query-manager-popover'); + async closeSavedQueryManagementComponent() { + const isOpenAlready = await testSubjects.exists('saved-query-management-popover'); if (!isOpenAlready) return; - await testSubjects.click('saved-query-manager-popover-button'); + await testSubjects.click('saved-query-management-popover-button'); } async saveNewQueryMissingOrFail() { - await this.openSavedQueryManager(); - await testSubjects.missingOrFail('saved-query-manager-save-button'); + await this.openSavedQueryManagementComponent(); + await testSubjects.missingOrFail('saved-query-management-save-button'); } async updateCurrentlyLoadedQueryMissingOrFail() { - await this.openSavedQueryManager(); - await testSubjects.missingOrFail('saved-query-manager-save-changes-button'); + await this.openSavedQueryManagementComponent(); + await testSubjects.missingOrFail('saved-query-management-save-changes-button'); } async deleteSavedQueryMissingOrFail(title) { - await this.openSavedQueryManager(); + await this.openSavedQueryManagementComponent(); await testSubjects.missingOrFail(`delete-saved-query-${title}-button`); } } - return new SavedQueryManager(); + return new SavedQueryManagementComponent(); } diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index 8f9b4f2b4bddc..577ef2800fa8d 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -21,7 +21,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const globalNav = getService('globalNav'); const queryBar = getService('queryBar'); - const savedQueryManager = getService('savedQueryManager'); + const savedQueryManagementComponent = getService('savedQueryManagementComponent'); describe('dashboard security', () => { before(async () => { @@ -191,29 +191,34 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it('allow saving via the saved query manager popover with no query loaded', async () => { - await savedQueryManager.saveNewQuery('foo', 'bar', true, false); - await savedQueryManager.savedQueryExistOrFail('foo'); + await savedQueryManagementComponent.saveNewQuery('foo', 'bar', true, false); + await savedQueryManagementComponent.savedQueryExistOrFail('foo'); }); it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { - await savedQueryManager.saveCurrentlyLoadedAsNewQuery('foo2', 'bar2', true, false); - await savedQueryManager.savedQueryExistOrFail('foo2'); + await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( + 'foo2', + 'bar2', + true, + false + ); + await savedQueryManagementComponent.savedQueryExistOrFail('foo2'); }); it('allow saving changes to a currently loaded query via the saved query manager', async () => { await queryBar.setQuery('response:404'); - await savedQueryManager.updateCurrentlyLoadedQuery('bar2', false, false); - await savedQueryManager.clearCurrentlyLoadedQuery(); - await savedQueryManager.loadSavedQuery('foo2'); + await savedQueryManagementComponent.updateCurrentlyLoadedQuery('bar2', false, false); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('foo2'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:404'); }); it('allows deleting saved queries in the saved query manager ', async () => { - await savedQueryManager.deleteSavedQuery('foo2'); + await savedQueryManagementComponent.deleteSavedQuery('foo2'); // add a manual delay await queryBar.setQuery('response:503'); - await savedQueryManager.savedQueryMissingOrFail('foo2'); + await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); }); @@ -305,28 +310,28 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it('allows loading a saved query via the saved query manager', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:200'); }); it('does not allow saving via the saved query manager popover with no query loaded', async () => { - await savedQueryManager.saveNewQueryMissingOrFail(); + await savedQueryManagementComponent.saveNewQueryMissingOrFail(); }); it('does not allow saving changes to saved query from the saved query manager', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); await queryBar.setQuery('response:404'); - await savedQueryManager.updateCurrentlyLoadedQueryMissingOrFail(); + await savedQueryManagementComponent.updateCurrentlyLoadedQueryMissingOrFail(); }); it('does not allow deleting a saved query from the saved query manager', async () => { - await savedQueryManager.deleteSavedQueryMissingOrFail('OKJpgs'); + await savedQueryManagementComponent.deleteSavedQueryMissingOrFail('OKJpgs'); }); it('allows clearing the currently loaded saved query', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); - await savedQueryManager.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); }); }); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 5751746c39b05..2837c5f5ae525 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -22,7 +22,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const appsMenu = getService('appsMenu'); const queryBar = getService('queryBar'); - const savedQueryManager = getService('savedQueryManager'); + const savedQueryManagementComponent = getService('savedQueryManagementComponent'); async function setDiscoverTimeRange() { const fromTime = '2015-09-19 06:31:44.000'; @@ -107,29 +107,34 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it('allow saving via the saved query manager popover with no query loaded', async () => { - await savedQueryManager.saveNewQuery('foo', 'bar', true, false); - await savedQueryManager.savedQueryExistOrFail('foo'); + await savedQueryManagementComponent.saveNewQuery('foo', 'bar', true, false); + await savedQueryManagementComponent.savedQueryExistOrFail('foo'); }); it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { - await savedQueryManager.saveCurrentlyLoadedAsNewQuery('foo2', 'bar2', true, false); - await savedQueryManager.savedQueryExistOrFail('foo2'); + await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( + 'foo2', + 'bar2', + true, + false + ); + await savedQueryManagementComponent.savedQueryExistOrFail('foo2'); }); it('allow saving changes to a currently loaded query via the saved query manager', async () => { await queryBar.setQuery('response:404'); - await savedQueryManager.updateCurrentlyLoadedQuery('bar2', false, false); - await savedQueryManager.clearCurrentlyLoadedQuery(); - await savedQueryManager.loadSavedQuery('foo2'); + await savedQueryManagementComponent.updateCurrentlyLoadedQuery('bar2', false, false); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('foo2'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:404'); }); it('allows deleting saved queries in the saved query manager ', async () => { - await savedQueryManager.deleteSavedQuery('foo2'); + await savedQueryManagementComponent.deleteSavedQuery('foo2'); // add a manual delay await queryBar.setQuery(''); - await savedQueryManager.savedQueryMissingOrFail('foo2'); + await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); }); @@ -199,28 +204,28 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it('allows loading a saved query via the saved query manager', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:200'); }); it('does not allow saving via the saved query manager popover with no query loaded', async () => { - await savedQueryManager.saveNewQueryMissingOrFail(); + await savedQueryManagementComponent.saveNewQueryMissingOrFail(); }); it('does not allow saving changes to saved query from the saved query manager', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); await queryBar.setQuery('response:404'); - await savedQueryManager.updateCurrentlyLoadedQueryMissingOrFail(); + await savedQueryManagementComponent.updateCurrentlyLoadedQueryMissingOrFail(); }); it('does not allow deleting a saved query from the saved query manager', async () => { - await savedQueryManager.deleteSavedQueryMissingOrFail('OKJpgs'); + await savedQueryManagementComponent.deleteSavedQueryMissingOrFail('OKJpgs'); }); it('allows clearing the currently loaded saved query', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); - await savedQueryManager.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); }); }); diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 7f9e7a967112d..7d8302312d346 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -22,7 +22,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const appsMenu = getService('appsMenu'); const globalNav = getService('globalNav'); const queryBar = getService('queryBar'); - const savedQueryManager = getService('savedQueryManager'); + const savedQueryManagementComponent = getService('savedQueryManagementComponent'); describe('feature controls security', () => { before(async () => { @@ -120,32 +120,37 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('allow saving via the saved query manager popover with no saved query loaded', async () => { await queryBar.setQuery('response:200'); - await savedQueryManager.saveNewQuery('foo', 'bar', true, false); - await savedQueryManager.savedQueryExistOrFail('foo'); - await savedQueryManager.closeSavedQueryManager(); + await savedQueryManagementComponent.saveNewQuery('foo', 'bar', true, false); + await savedQueryManagementComponent.savedQueryExistOrFail('foo'); + await savedQueryManagementComponent.closeSavedQueryManagementComponent(); }); it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { - await savedQueryManager.saveCurrentlyLoadedAsNewQuery('foo2', 'bar2', true, false); - await savedQueryManager.savedQueryExistOrFail('foo2'); - await savedQueryManager.closeSavedQueryManager(); + await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( + 'foo2', + 'bar2', + true, + false + ); + await savedQueryManagementComponent.savedQueryExistOrFail('foo2'); + await savedQueryManagementComponent.closeSavedQueryManagementComponent(); }); it('allow saving changes to a currently loaded query via the saved query manager', async () => { - await savedQueryManager.loadSavedQuery('foo2'); + await savedQueryManagementComponent.loadSavedQuery('foo2'); await queryBar.setQuery('response:404'); - await savedQueryManager.updateCurrentlyLoadedQuery('bar2', false, false); - await savedQueryManager.clearCurrentlyLoadedQuery(); - await savedQueryManager.loadSavedQuery('foo2'); + await savedQueryManagementComponent.updateCurrentlyLoadedQuery('bar2', false, false); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('foo2'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:404'); }); it('allows deleting saved queries in the saved query manager ', async () => { - await savedQueryManager.deleteSavedQuery('foo2'); + await savedQueryManagementComponent.deleteSavedQuery('foo2'); // add a manual delay await queryBar.setQuery('response:503'); - await savedQueryManager.savedQueryMissingOrFail('foo2'); + await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); }); @@ -233,28 +238,28 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { }); it('allows loading a saved query via the saved query manager', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:200'); }); it('does not allow saving via the saved query manager popover with no query loaded', async () => { - await savedQueryManager.saveNewQueryMissingOrFail(); + await savedQueryManagementComponent.saveNewQueryMissingOrFail(); }); it('does not allow saving changes to saved query from the saved query manager', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); await queryBar.setQuery('response:404'); - await savedQueryManager.updateCurrentlyLoadedQueryMissingOrFail(); + await savedQueryManagementComponent.updateCurrentlyLoadedQueryMissingOrFail(); }); it('does not allow deleting a saved query from the saved query manager', async () => { - await savedQueryManager.deleteSavedQueryMissingOrFail('OKJpgs'); + await savedQueryManagementComponent.deleteSavedQueryMissingOrFail('OKJpgs'); }); it('allows clearing the currently loaded saved query', async () => { - await savedQueryManager.loadSavedQuery('OKJpgs'); - await savedQueryManager.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); }); }); From fc14cca2f588d979a703e0fe4f6d045f0b926d13 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 13 Aug 2019 15:13:28 -0700 Subject: [PATCH 153/189] Highlights currently selected saved query and moves it to the top of the list --- .../components/_saved_query_management_component.scss | 3 +++ .../components/saved_query_management_component.tsx | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss index be4197326f820..9c73627ca9a25 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss @@ -8,3 +8,6 @@ height: 20vh; overflow-y:hidden; } +.saved-query-list li:first-child .saved-query-list-item-text { + font-weight: $euiFontWeightBold; +} diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx index 512247e61e1e8..15948418010c5 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx @@ -35,6 +35,7 @@ import { import { i18n } from '@kbn/i18n'; import React, { FunctionComponent, useEffect, useState, Fragment } from 'react'; import { sortBy } from 'lodash'; +import { EuiSpacer } from '@elastic/eui'; import { SavedQuery } from '../index'; import { getAllSavedQueries, deleteSavedQuery } from '../lib/saved_query_service'; import { Query } from '../../../query'; @@ -130,7 +131,14 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ ); const savedQueryRows = () => { - const savedQueriesDisplayRows = savedQueries.slice( + const savedQueriesWithoutCurrent = savedQueries.filter(savedQuery => { + if (!loadedSavedQuery) return true; + return savedQuery.id !== loadedSavedQuery.id; + }); + const savedQueriesReordered = loadedSavedQuery + ? [loadedSavedQuery, ...savedQueriesWithoutCurrent] + : [...savedQueriesWithoutCurrent]; + const savedQueriesDisplayRows = savedQueriesReordered.slice( activePage * pageCount, activePage * pageCount + pageCount ); @@ -145,6 +153,7 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ }} flush="left" data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} + textProps={{ className: 'saved-query-list-item-text' }} > From ab46fff6e72119a44d974603c1f9269a0d7d6f45 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 13 Aug 2019 17:12:14 -0700 Subject: [PATCH 154/189] Makes a saved query bold only if it's selected --- .../_saved_query_management_component.scss | 1 + .../saved_query_management_component.tsx | 65 +++++++++++++------ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss index 9c73627ca9a25..29d187b7ecb6a 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss @@ -11,3 +11,4 @@ .saved-query-list li:first-child .saved-query-list-item-text { font-weight: $euiFontWeightBold; } + diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx index 15948418010c5..0296a86c5d7da 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx @@ -142,28 +142,53 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ activePage * pageCount, activePage * pageCount + pageCount ); - return savedQueriesDisplayRows.map(savedQuery => ( -
  • + return savedQueriesDisplayRows.map((savedQuery, index) => ( +
  • - { - onLoad(savedQuery); - setIsOpen(false); - }} - flush="left" - data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} - textProps={{ className: 'saved-query-list-item-text' }} - > - - - {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { - defaultMessage: 'Open saved query', - })} - - - {savedQuery.attributes.title} - + {loadedSavedQuery && loadedSavedQuery.id === savedQuery.id ? ( + { + onLoad(savedQuery); + setIsOpen(false); + }} + flush="left" + data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} + textProps={{ className: 'saved-query-list-item-text' }} + > + + + {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { + defaultMessage: 'Open saved query', + })} + + + {savedQuery.attributes.title} + + ) : ( + { + onLoad(savedQuery); + setIsOpen(false); + }} + flush="left" + data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} + > + + + {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { + defaultMessage: 'Open saved query', + })} + + + {savedQuery.attributes.title} + + )} From 4ce22c7d6a1a76aa0d13fdbea3669536802f60b3 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 13 Aug 2019 17:30:05 -0700 Subject: [PATCH 155/189] loading a saved query functional test fix WIP --- .../services/saved_query_management_component.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/functional/services/saved_query_management_component.js b/test/functional/services/saved_query_management_component.js index 924c2ad0300bc..d0e2866bbddc2 100644 --- a/test/functional/services/saved_query_management_component.js +++ b/test/functional/services/saved_query_management_component.js @@ -22,6 +22,7 @@ import expect from '@kbn/expect'; export function SavedQueryManagementComponentProvider({ getService }) { const testSubjects = getService('testSubjects'); const queryBar = getService('queryBar'); + const retry = getService('retry'); class SavedQueryManagementComponent { @@ -46,6 +47,12 @@ export function SavedQueryManagementComponentProvider({ getService }) { async loadSavedQuery(title) { await this.openSavedQueryManagementComponent(); await testSubjects.click(`load-saved-query-${title}-button`); + await retry.try(async () => { + await this.openSavedQueryManagementComponent(); + const selectedSavedQueryText = testSubjects.getVisibleText('saved-query-list-item-selected'); + expect(selectedSavedQueryText).to.eql(title); + }); + await this.closeSavedQueryManagementComponent(); } async deleteSavedQuery(title) { From d1b78b359f3fe1ed2ed2a00a2025502718e3665e Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 14 Aug 2019 07:43:28 -0700 Subject: [PATCH 156/189] Removes unused EuiSpacer --- .../search_bar/components/saved_query_management_component.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx index 0296a86c5d7da..5f1a12bdeb4e3 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx @@ -35,7 +35,6 @@ import { import { i18n } from '@kbn/i18n'; import React, { FunctionComponent, useEffect, useState, Fragment } from 'react'; import { sortBy } from 'lodash'; -import { EuiSpacer } from '@elastic/eui'; import { SavedQuery } from '../index'; import { getAllSavedQueries, deleteSavedQuery } from '../lib/saved_query_service'; import { Query } from '../../../query'; From 2b3103d580bc65eccb74dee1c4eaeaa3aaa1d789 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 14 Aug 2019 09:13:18 -0700 Subject: [PATCH 157/189] Finds selected saved query by additional data-test-subject selector --- .../search_bar/components/saved_query_management_component.tsx | 2 +- test/functional/services/saved_query_management_component.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx index 5f1a12bdeb4e3..82efcc0619d06 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx @@ -157,7 +157,7 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ setIsOpen(false); }} flush="left" - data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} + data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected`} textProps={{ className: 'saved-query-list-item-text' }} > diff --git a/test/functional/services/saved_query_management_component.js b/test/functional/services/saved_query_management_component.js index d0e2866bbddc2..06564098c531d 100644 --- a/test/functional/services/saved_query_management_component.js +++ b/test/functional/services/saved_query_management_component.js @@ -49,7 +49,7 @@ export function SavedQueryManagementComponentProvider({ getService }) { await testSubjects.click(`load-saved-query-${title}-button`); await retry.try(async () => { await this.openSavedQueryManagementComponent(); - const selectedSavedQueryText = testSubjects.getVisibleText('saved-query-list-item-selected'); + const selectedSavedQueryText = await testSubjects.getVisibleText('saved-query-list-item-selected'); expect(selectedSavedQueryText).to.eql(title); }); await this.closeSavedQueryManagementComponent(); From e47633815c220251769cfac4602c1cb93214499e Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 14 Aug 2019 09:37:29 -0700 Subject: [PATCH 158/189] Replaces 'manager' with 'management component' in saved queries functional test descriptions --- .../feature_controls/dashboard_security.ts | 16 ++++++++-------- .../feature_controls/discover_security.ts | 16 ++++++++-------- .../feature_controls/visualize_security.ts | 16 ++++++++-------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index 577ef2800fa8d..7a0a2025e82ac 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -190,12 +190,12 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await panelActions.expectExistsEditPanelAction(); }); - it('allow saving via the saved query manager popover with no query loaded', async () => { + it('allow saving via the saved query management component popover with no query loaded', async () => { await savedQueryManagementComponent.saveNewQuery('foo', 'bar', true, false); await savedQueryManagementComponent.savedQueryExistOrFail('foo'); }); - it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { + it('allow saving a currently loaded saved query as a new query via the saved query management component ', async () => { await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( 'foo2', 'bar2', @@ -205,7 +205,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await savedQueryManagementComponent.savedQueryExistOrFail('foo2'); }); - it('allow saving changes to a currently loaded query via the saved query manager', async () => { + it('allow saving changes to a currently loaded query via the saved query management component', async () => { await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQuery('bar2', false, false); await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); @@ -214,7 +214,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(queryString).to.eql('response:404'); }); - it('allows deleting saved queries in the saved query manager ', async () => { + it('allows deleting saved queries in the saved query management component ', async () => { await savedQueryManagementComponent.deleteSavedQuery('foo2'); // add a manual delay await queryBar.setQuery('response:503'); @@ -309,23 +309,23 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.clickShareTopNavButton(); }); - it('allows loading a saved query via the saved query manager', async () => { + it('allows loading a saved query via the saved query management component', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:200'); }); - it('does not allow saving via the saved query manager popover with no query loaded', async () => { + it('does not allow saving via the saved query management component popover with no query loaded', async () => { await savedQueryManagementComponent.saveNewQueryMissingOrFail(); }); - it('does not allow saving changes to saved query from the saved query manager', async () => { + it('does not allow saving changes to saved query from the saved query management component', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQueryMissingOrFail(); }); - it('does not allow deleting a saved query from the saved query manager', async () => { + it('does not allow deleting a saved query from the saved query management component', async () => { await savedQueryManagementComponent.deleteSavedQueryMissingOrFail('OKJpgs'); }); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 2837c5f5ae525..90746ded3940f 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -106,12 +106,12 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.clickShareTopNavButton(); }); - it('allow saving via the saved query manager popover with no query loaded', async () => { + it('allow saving via the saved query management component popover with no query loaded', async () => { await savedQueryManagementComponent.saveNewQuery('foo', 'bar', true, false); await savedQueryManagementComponent.savedQueryExistOrFail('foo'); }); - it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { + it('allow saving a currently loaded saved query as a new query via the saved query management component ', async () => { await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( 'foo2', 'bar2', @@ -121,7 +121,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await savedQueryManagementComponent.savedQueryExistOrFail('foo2'); }); - it('allow saving changes to a currently loaded query via the saved query manager', async () => { + it('allow saving changes to a currently loaded query via the saved query management component', async () => { await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQuery('bar2', false, false); await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); @@ -130,7 +130,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(queryString).to.eql('response:404'); }); - it('allows deleting saved queries in the saved query manager ', async () => { + it('allows deleting saved queries in the saved query management component ', async () => { await savedQueryManagementComponent.deleteSavedQuery('foo2'); // add a manual delay await queryBar.setQuery(''); @@ -203,23 +203,23 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.createShortUrlMissingOrFail(); }); - it('allows loading a saved query via the saved query manager', async () => { + it('allows loading a saved query via the saved query management component', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:200'); }); - it('does not allow saving via the saved query manager popover with no query loaded', async () => { + it('does not allow saving via the saved query management component popover with no query loaded', async () => { await savedQueryManagementComponent.saveNewQueryMissingOrFail(); }); - it('does not allow saving changes to saved query from the saved query manager', async () => { + it('does not allow saving changes to saved query from the saved query management component', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQueryMissingOrFail(); }); - it('does not allow deleting a saved query from the saved query manager', async () => { + it('does not allow deleting a saved query from the saved query management component', async () => { await savedQueryManagementComponent.deleteSavedQueryMissingOrFail('OKJpgs'); }); diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 7d8302312d346..5e034642b56fc 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -118,14 +118,14 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.clickShareTopNavButton(); }); - it('allow saving via the saved query manager popover with no saved query loaded', async () => { + it('allow saving via the saved query management component popover with no saved query loaded', async () => { await queryBar.setQuery('response:200'); await savedQueryManagementComponent.saveNewQuery('foo', 'bar', true, false); await savedQueryManagementComponent.savedQueryExistOrFail('foo'); await savedQueryManagementComponent.closeSavedQueryManagementComponent(); }); - it('allow saving a currently loaded saved query as a new query via the saved query manager ', async () => { + it('allow saving a currently loaded saved query as a new query via the saved query management component ', async () => { await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( 'foo2', 'bar2', @@ -136,7 +136,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await savedQueryManagementComponent.closeSavedQueryManagementComponent(); }); - it('allow saving changes to a currently loaded query via the saved query manager', async () => { + it('allow saving changes to a currently loaded query via the saved query management component', async () => { await savedQueryManagementComponent.loadSavedQuery('foo2'); await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQuery('bar2', false, false); @@ -146,7 +146,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(queryString).to.eql('response:404'); }); - it('allows deleting saved queries in the saved query manager ', async () => { + it('allows deleting saved queries in the saved query management component ', async () => { await savedQueryManagementComponent.deleteSavedQuery('foo2'); // add a manual delay await queryBar.setQuery('response:503'); @@ -237,23 +237,23 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.share.clickShareTopNavButton(); }); - it('allows loading a saved query via the saved query manager', async () => { + it('allows loading a saved query via the saved query management component', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); const queryString = await queryBar.getQueryString(); expect(queryString).to.eql('response:200'); }); - it('does not allow saving via the saved query manager popover with no query loaded', async () => { + it('does not allow saving via the saved query management component popover with no query loaded', async () => { await savedQueryManagementComponent.saveNewQueryMissingOrFail(); }); - it('does not allow saving changes to saved query from the saved query manager', async () => { + it('does not allow saving changes to saved query from the saved query management component', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQueryMissingOrFail(); }); - it('does not allow deleting a saved query from the saved query manager', async () => { + it('does not allow deleting a saved query from the saved query management component', async () => { await savedQueryManagementComponent.deleteSavedQueryMissingOrFail('OKJpgs'); }); From 6bd72b88d1ee1d989394d5323094fc4c1d3ec4a3 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 14 Aug 2019 13:24:40 -0700 Subject: [PATCH 159/189] Adds more aria labels to the saved query management component --- .../saved_query_management_component.tsx | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx index 82efcc0619d06..d99e78d4c68ae 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx @@ -159,11 +159,21 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ flush="left" data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected`} textProps={{ className: 'saved-query-list-item-text' }} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel', + { + defaultMessage: 'Saved query button selected {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, + } + )} > - {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { - defaultMessage: 'Open saved query', + {i18n.translate('data.search.searchBar.savedQueryScreenReaderSelectedText', { + defaultMessage: 'Selected saved query {savedQueryName}', + values: { + savedQueryName: `${savedQuery.attributes.title}`, + }, })} @@ -177,6 +187,13 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ }} flush="left" data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel', + { + defaultMessage: 'Saved query button {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, + } + )} > @@ -196,7 +213,13 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ )} @@ -245,8 +268,8 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ aria-label={i18n.translate( 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', { - defaultMessage: 'Delete saved query {title}', - values: { title: savedQuery.attributes.title }, + defaultMessage: 'Delete saved query {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, } )} data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} From b9bfb1877353475000088684a47abf44fabda5f7 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 14 Aug 2019 13:51:30 -0700 Subject: [PATCH 160/189] Adds tooltip to the delete button, modifies descriptions text --- .../search_bar/components/save_query_form.tsx | 5 +- .../saved_query_management_component.tsx | 114 ++++++++++-------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx index 57b123503d98f..7f7e8e1f9e9ba 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx @@ -72,8 +72,7 @@ export const SaveQueryForm: FunctionComponent = ({ const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', { - defaultMessage: - 'Saved queries allow you to save common search snippets and filters for later use.', + defaultMessage: 'Save query text and filters that you want to use again.', } ); @@ -87,7 +86,7 @@ export const SaveQueryForm: FunctionComponent = ({ defaultMessage: 'Name', })} helpText={i18n.translate('data.search.searchBar.savedQueryNameHelpText', { - defaultMessage: 'Must be unique.', + defaultMessage: 'Name must be unique.', })} > = ({ const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', { - defaultMessage: - 'Saved queries allow you to save common search snippets and filters for later use.', + defaultMessage: 'Save query text and filters that you want to use again.', } ); @@ -226,54 +226,70 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ {showSaveQuery && ( - { - setConfirmDeletionModal( - - { - onDeleteSavedQuery(savedQuery); - setConfirmDeletionModal(null); - }} - onCancel={() => { - setConfirmDeletionModal(null); - }} - /> - - ); - }} - iconType="trash" - color="danger" - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', - { - defaultMessage: 'Delete saved query {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, + + + {i18n.translate( + 'data.search.searchBar.savedQueryPopoverDeleteButtonTooltip', + { + defaultMessage: 'Delete saved query', + } + )} +

    } - )} - data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} - /> + > + { + setConfirmDeletionModal( + + { + onDeleteSavedQuery(savedQuery); + setConfirmDeletionModal(null); + }} + onCancel={() => { + setConfirmDeletionModal(null); + }} + /> + + ); + }} + iconType="trash" + color="danger" + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', + { + defaultMessage: 'Delete saved query {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, + } + )} + data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} + /> +
    +
    )}
    From a7b50a7e6ed773c4dc93f4f023bdb4020f5cd290 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 14 Aug 2019 18:27:23 -0400 Subject: [PATCH 161/189] Extract saved query list item from management component --- .../data/public/search/search_bar/_index.scss | 2 +- .../_saved_query_management_component.scss | 0 .../save_query_form.tsx | 2 +- .../saved_query_list_item.tsx | 207 ++++++++++++++++++ .../saved_query_management_component.tsx | 180 ++------------- .../search_bar/components/search_bar.tsx | 4 +- 6 files changed, 226 insertions(+), 169 deletions(-) rename src/legacy/core_plugins/data/public/search/search_bar/components/{ => saved_query_management}/_saved_query_management_component.scss (100%) rename src/legacy/core_plugins/data/public/search/search_bar/components/{ => saved_query_management}/save_query_form.tsx (99%) create mode 100644 src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx rename src/legacy/core_plugins/data/public/search/search_bar/components/{ => saved_query_management}/saved_query_management_component.tsx (56%) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/_index.scss b/src/legacy/core_plugins/data/public/search/search_bar/_index.scss index 6813b39042dad..94619b88c2ac2 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/_index.scss +++ b/src/legacy/core_plugins/data/public/search/search_bar/_index.scss @@ -1 +1 @@ -@import 'components/saved_query_management_component'; +@import 'components/saved_query_management/saved_query_management_component'; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/_saved_query_management_component.scss similarity index 100% rename from src/legacy/core_plugins/data/public/search/search_bar/components/_saved_query_management_component.scss rename to src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/_saved_query_management_component.scss diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx similarity index 99% rename from src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx rename to src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx index 7f7e8e1f9e9ba..59f4b06fd13a9 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx @@ -34,7 +34,7 @@ import { EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SavedQueryAttributes } from '../index'; +import { SavedQueryAttributes } from '../../index'; interface Props { savedQuery?: SavedQueryAttributes; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx new file mode 100644 index 0000000000000..aac9250bd8472 --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx @@ -0,0 +1,207 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiConfirmModal, + EuiOverlayMask, + EuiScreenReaderOnly, + EuiIconTip, + EuiToolTip, +} from '@elastic/eui'; + +import React, { Fragment, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { SavedQuery } from '../../index'; + +interface Props { + savedQuery: SavedQuery; + isSelected: boolean; + showWriteOperations: boolean; + onSelect: (savedQuery: SavedQuery) => void; + onDelete: (savedQuery: SavedQuery) => void; +} + +export const SavedQueryListItem = ({ + savedQuery, + isSelected, + onSelect, + onDelete, + showWriteOperations, +}: Props) => { + const [showDeletionConfirmationModal, setShowDeletionConfirmationModal] = useState(false); + + return ( + +
  • + + + {isSelected ? ( + { + onSelect(savedQuery); + }} + flush="left" + data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected`} + textProps={{ className: 'saved-query-list-item-text' }} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel', + { + defaultMessage: 'Saved query button selected {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, + } + )} + > + + + {i18n.translate('data.search.searchBar.savedQueryScreenReaderSelectedText', { + defaultMessage: 'Selected saved query {savedQueryName}', + values: { + savedQueryName: `${savedQuery.attributes.title}`, + }, + })} + + + {savedQuery.attributes.title} + + ) : ( + { + onSelect(savedQuery); + }} + flush="left" + data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel', + { + defaultMessage: 'Saved query button {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, + } + )} + > + + + {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { + defaultMessage: 'Open saved query', + })} + + + {savedQuery.attributes.title} + + )} + + + + + {savedQuery.attributes.description && ( + + )} + + + + {showWriteOperations && ( + + + {i18n.translate( + 'data.search.searchBar.savedQueryPopoverDeleteButtonTooltip', + { + defaultMessage: 'Delete saved query', + } + )} +

    + } + > + { + setShowDeletionConfirmationModal(true); + }} + iconType="trash" + color="danger" + aria-label={i18n.translate( + 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', + { + defaultMessage: 'Delete saved query {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, + } + )} + data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} + /> +
    +
    + )} +
    +
    +
    +
    +
  • + + {showDeletionConfirmationModal && ( + + { + onDelete(savedQuery); + setShowDeletionConfirmationModal(false); + }} + onCancel={() => { + setShowDeletionConfirmationModal(false); + }} + /> + + )} + + ); +}; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx similarity index 56% rename from src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx rename to src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx index f5359647fa155..790fe7c2a29f5 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx @@ -26,19 +26,15 @@ import { EuiFlexItem, EuiPagination, EuiText, - EuiConfirmModal, - EuiOverlayMask, - EuiScreenReaderOnly, - EuiIconTip, - EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { FunctionComponent, useEffect, useState, Fragment } from 'react'; import { sortBy } from 'lodash'; -import { SavedQuery } from '../index'; -import { getAllSavedQueries, deleteSavedQuery } from '../lib/saved_query_service'; -import { Query } from '../../../query'; +import { SavedQuery } from '../../index'; +import { getAllSavedQueries, deleteSavedQuery } from '../../lib/saved_query_service'; +import { Query } from '../../../../query'; +import { SavedQueryListItem } from './saved_query_list_item'; const pageCount = 50; @@ -64,7 +60,6 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ const [isOpen, setIsOpen] = useState(false); const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); const [activePage, setActivePage] = useState(0); - const [confirmDeletionModal, setConfirmDeletionModal] = useState(null as React.ReactNode); useEffect(() => { const fetchQueries = async () => { @@ -141,161 +136,17 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ activePage * pageCount, activePage * pageCount + pageCount ); - return savedQueriesDisplayRows.map((savedQuery, index) => ( -
  • - - - {loadedSavedQuery && loadedSavedQuery.id === savedQuery.id ? ( - { - onLoad(savedQuery); - setIsOpen(false); - }} - flush="left" - data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected`} - textProps={{ className: 'saved-query-list-item-text' }} - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel', - { - defaultMessage: 'Saved query button selected {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, - } - )} - > - - - {i18n.translate('data.search.searchBar.savedQueryScreenReaderSelectedText', { - defaultMessage: 'Selected saved query {savedQueryName}', - values: { - savedQueryName: `${savedQuery.attributes.title}`, - }, - })} - - - {savedQuery.attributes.title} - - ) : ( - { - onLoad(savedQuery); - setIsOpen(false); - }} - flush="left" - data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel', - { - defaultMessage: 'Saved query button {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, - } - )} - > - - - {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { - defaultMessage: 'Open saved query', - })} - - - {savedQuery.attributes.title} - - )} - - - - - {savedQuery.attributes.description && ( - - )} - - - - {showSaveQuery && ( - - - {i18n.translate( - 'data.search.searchBar.savedQueryPopoverDeleteButtonTooltip', - { - defaultMessage: 'Delete saved query', - } - )} -

    - } - > - { - setConfirmDeletionModal( - - { - onDeleteSavedQuery(savedQuery); - setConfirmDeletionModal(null); - }} - onCancel={() => { - setConfirmDeletionModal(null); - }} - /> - - ); - }} - iconType="trash" - color="danger" - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', - { - defaultMessage: 'Delete saved query {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, - } - )} - data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`} - /> -
    -
    - )} -
    -
    -
    -
    -
  • + return savedQueriesDisplayRows.map(savedQuery => ( + { + onLoad(savedQueryToSelect); + setIsOpen(false); + }} + onDelete={savedQueryToDelete => onDeleteSavedQuery(savedQueryToDelete)} + showWriteOperations={!!showSaveQuery} + /> )); }; @@ -433,7 +284,6 @@ export const SavedQueryManagementComponent: FunctionComponent = ({
    - {confirmDeletionModal} ); }; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index a8755c8ed92ba..2a2bc0b24ca02 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -30,8 +30,8 @@ import { UiSettingsClientContract } from 'src/core/public'; import { IndexPattern, Query, QueryBar, FilterBar } from '../../../../../data/public'; import { SavedQuery, SavedQueryAttributes } from '../index'; import { saveQuery } from '../lib/saved_query_service'; -import { SavedQueryMeta, SaveQueryForm } from './save_query_form'; -import { SavedQueryManagementComponent } from './saved_query_management_component'; +import { SavedQueryMeta, SaveQueryForm } from './saved_query_management/save_query_form'; +import { SavedQueryManagementComponent } from './saved_query_management/saved_query_management_component'; interface DateRange { from: string; From e7b757bc41ff7663d51e0b74f446053c9a0637d7 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 14 Aug 2019 18:28:48 -0400 Subject: [PATCH 162/189] Remove unused prop --- .../saved_query_management_component.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx index 790fe7c2a29f5..cfba137429905 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx @@ -33,7 +33,6 @@ import React, { FunctionComponent, useEffect, useState, Fragment } from 'react'; import { sortBy } from 'lodash'; import { SavedQuery } from '../../index'; import { getAllSavedQueries, deleteSavedQuery } from '../../lib/saved_query_service'; -import { Query } from '../../../../query'; import { SavedQueryListItem } from './saved_query_list_item'; const pageCount = 50; @@ -44,7 +43,6 @@ interface Props { onSave: () => void; onSaveAsNew: () => void; onLoad: (savedQuery: SavedQuery) => void; - query: Query; onClearSavedQuery: () => void; } @@ -54,7 +52,6 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ onSave, onSaveAsNew, onLoad, - query, onClearSavedQuery, }) => { const [isOpen, setIsOpen] = useState(false); From 2ad509980f2b4f8c45cd5810c5e7eede9acc0beb Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 14 Aug 2019 18:42:05 -0400 Subject: [PATCH 163/189] Combine duplicate buttons --- .../saved_query_list_item.tsx | 95 ++++++++----------- 1 file changed, 42 insertions(+), 53 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx index aac9250bd8472..6595711a72e42 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx @@ -49,6 +49,34 @@ export const SavedQueryListItem = ({ }: Props) => { const [showDeletionConfirmationModal, setShowDeletionConfirmationModal] = useState(false); + const selectButtonAriaLabelText = isSelected + ? i18n.translate( + 'data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel', + { + defaultMessage: 'Saved query button selected {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, + } + ) + : i18n.translate('data.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel', { + defaultMessage: 'Saved query button {savedQueryName}', + values: { savedQueryName: savedQuery.attributes.title }, + }); + + const selectButtonScreenReaderContent = isSelected + ? i18n.translate('data.search.searchBar.savedQueryScreenReaderSelectedText', { + defaultMessage: 'Selected saved query {savedQueryName}', + values: { + savedQueryName: `${savedQuery.attributes.title}`, + }, + }) + : i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { + defaultMessage: 'Open saved query', + }); + + const selectButtonDataTestSubj = isSelected + ? `load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected` + : `load-saved-query-${savedQuery.attributes.title}-button`; + return (
  • - {isSelected ? ( - { - onSelect(savedQuery); - }} - flush="left" - data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected`} - textProps={{ className: 'saved-query-list-item-text' }} - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel', - { - defaultMessage: 'Saved query button selected {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, - } - )} - > - - - {i18n.translate('data.search.searchBar.savedQueryScreenReaderSelectedText', { - defaultMessage: 'Selected saved query {savedQueryName}', - values: { - savedQueryName: `${savedQuery.attributes.title}`, - }, - })} - - - {savedQuery.attributes.title} - - ) : ( - { - onSelect(savedQuery); - }} - flush="left" - data-test-subj={`load-saved-query-${savedQuery.attributes.title}-button`} - aria-label={i18n.translate( - 'data.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel', - { - defaultMessage: 'Saved query button {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, - } - )} - > - - - {i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { - defaultMessage: 'Open saved query', - })} - - - {savedQuery.attributes.title} - - )} + { + onSelect(savedQuery); + }} + flush="left" + data-test-subj={selectButtonDataTestSubj} + textProps={isSelected ? { className: 'saved-query-list-item-text' } : undefined} + aria-label={selectButtonAriaLabelText} + > + + {selectButtonScreenReaderContent} + + {savedQuery.attributes.title} + From 7c49a60e00869f713c7339ea919e713aef1fdefb Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 14 Aug 2019 19:19:32 -0400 Subject: [PATCH 164/189] Add title conflict validation to save query form --- .../save_query_form.tsx | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx index 59f4b06fd13a9..e27fd55b29ac4 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React, { FunctionComponent, useState } from 'react'; +import React, { FunctionComponent, useEffect, useState } from 'react'; import { EuiButtonEmpty, EuiOverlayMask, @@ -34,7 +34,9 @@ import { EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SavedQueryAttributes } from '../../index'; +import { sortBy } from 'lodash'; +import { SavedQuery, SavedQueryAttributes } from '../../index'; +import { getAllSavedQueries } from '../../lib/saved_query_service'; interface Props { savedQuery?: SavedQueryAttributes; @@ -60,6 +62,7 @@ export const SaveQueryForm: FunctionComponent = ({ }) => { const [title, setTitle] = useState(savedQuery ? savedQuery.title : ''); const [description, setDescription] = useState(savedQuery ? savedQuery.description : ''); + const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); const [shouldIncludeFilters, setShouldIncludeFilters] = useState( savedQuery ? !!savedQuery.filters : true ); @@ -69,6 +72,16 @@ export const SaveQueryForm: FunctionComponent = ({ const [shouldIncludeTimefilter, setIncludeTimefilter] = useState( savedQuery ? !!savedQuery.timefilter : false ); + + useEffect(() => { + const fetchQueries = async () => { + const allSavedQueries = await getAllSavedQueries(); + const sortedAllSavedQueries = sortBy(allSavedQueries, 'attributes.title'); + setSavedQueries(sortedAllSavedQueries); + }; + fetchQueries(); + }, []); + const savedQueryDescriptionText = i18n.translate( 'data.search.searchBar.savedQueryDescriptionText', { @@ -76,8 +89,21 @@ export const SaveQueryForm: FunctionComponent = ({ } ); + const hasTitleConflict = !!savedQueries.find( + existingSavedQuery => !savedQuery && existingSavedQuery.attributes.title === title + ); + + const titleConflictErrorText = i18n.translate( + 'data.search.searchBar.savedQueryForm.titleConflictText', + { + defaultMessage: 'Title conflicts with an existing saved query', + } + ); + + const errors = hasTitleConflict ? [titleConflictErrorText] : []; + const saveQueryForm = ( - + {savedQueryDescriptionText} @@ -88,6 +114,7 @@ export const SaveQueryForm: FunctionComponent = ({ helpText={i18n.translate('data.search.searchBar.savedQueryNameHelpText', { defaultMessage: 'Name must be unique.', })} + isInvalid={hasTitleConflict} > = ({ setTitle(event.target.value); }} data-test-subj="saveQueryFormTitle" + isInvalid={hasTitleConflict} /> @@ -179,6 +207,7 @@ export const SaveQueryForm: FunctionComponent = ({ } fill data-test-subj="savedQueryFormSaveButton" + disabled={hasTitleConflict} > {i18n.translate('data.search.searchBar.savedQueryFormSaveButtonText', { defaultMessage: 'Save', From fd487d523e069c104f2a0c0396a7d254bd8f5e6c Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 14 Aug 2019 17:39:47 -0400 Subject: [PATCH 165/189] inject savedObjectsClient into saved query service --- src/legacy/core_plugins/data/public/plugin.ts | 2 +- .../save_query_form.tsx | 8 +- .../saved_query_management_component.tsx | 9 +- .../search_bar/components/search_bar.test.tsx | 15 ++ .../search_bar/components/search_bar.tsx | 13 +- .../lib/saved_query_service.test.ts | 19 +- .../search_bar/lib/saved_query_service.ts | 216 ++++++++++-------- .../data/public/search/search_service.ts | 7 +- .../public/top_nav_menu/top_nav_menu.test.tsx | 30 ++- .../public/top_nav_menu/top_nav_menu.tsx | 7 +- .../ui/public/kbn_top_nav/kbn_top_nav2.js | 3 + 11 files changed, 196 insertions(+), 133 deletions(-) diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index 34edb355d2ef8..a38e55e8139ed 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -85,7 +85,7 @@ export class DataPlugin implements Plugin void; onClose: () => void; showFilterOption: boolean | undefined; @@ -55,6 +56,7 @@ export interface SavedQueryMeta { export const SaveQueryForm: FunctionComponent = ({ savedQuery, + savedQueryService, onSave, onClose, showFilterOption = true, @@ -75,8 +77,8 @@ export const SaveQueryForm: FunctionComponent = ({ useEffect(() => { const fetchQueries = async () => { - const allSavedQueries = await getAllSavedQueries(); - const sortedAllSavedQueries = sortBy(allSavedQueries, 'attributes.title'); + const allSavedQueries = await savedQueryService.getAllSavedQueries(); + const sortedAllSavedQueries = sortBy(allSavedQueries, 'attributes.title') as SavedQuery[]; setSavedQueries(sortedAllSavedQueries); }; fetchQueries(); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx index cfba137429905..2d1096f907c18 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx @@ -32,7 +32,7 @@ import { i18n } from '@kbn/i18n'; import React, { FunctionComponent, useEffect, useState, Fragment } from 'react'; import { sortBy } from 'lodash'; import { SavedQuery } from '../../index'; -import { getAllSavedQueries, deleteSavedQuery } from '../../lib/saved_query_service'; +import { SavedQueryService } from '../../lib/saved_query_service'; import { SavedQueryListItem } from './saved_query_list_item'; const pageCount = 50; @@ -40,6 +40,7 @@ const pageCount = 50; interface Props { showSaveQuery?: boolean; loadedSavedQuery?: SavedQuery; + savedQueryService: SavedQueryService; onSave: () => void; onSaveAsNew: () => void; onLoad: (savedQuery: SavedQuery) => void; @@ -53,6 +54,7 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ onSaveAsNew, onLoad, onClearSavedQuery, + savedQueryService, }) => { const [isOpen, setIsOpen] = useState(false); const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); @@ -60,7 +62,7 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ useEffect(() => { const fetchQueries = async () => { - const allSavedQueries = await getAllSavedQueries(); + const allSavedQueries = await savedQueryService.getAllSavedQueries(); const sortedAllSavedQueries = sortBy(allSavedQueries, 'attributes.title'); setSavedQueries(sortedAllSavedQueries); }; @@ -101,7 +103,7 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ onClearSavedQuery(); } - deleteSavedQuery(savedQuery.id); + savedQueryService.deleteSavedQuery(savedQuery.id); }; const savedQueryPopoverButton = ( @@ -135,6 +137,7 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ ); return savedQueriesDisplayRows.map(savedQuery => ( { diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx index dd109536e5709..7aa4767e21dba 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx @@ -73,6 +73,14 @@ const mockIndexPattern = { ], } as IndexPattern; +const mockSavedQueryService = { + saveQuery: jest.fn(), + getAllSavedQueries: jest.fn(), + findSavedQueries: jest.fn(), + getSavedQuery: jest.fn(), + deleteSavedQuery: jest.fn(), +}; + const kqlQuery = { query: 'response:200', language: 'kuery', @@ -91,6 +99,7 @@ describe('SearchBar', () => { const component = mountWithIntl( { const component = mountWithIntl( { const component = mountWithIntl( { const component = mountWithIntl( { const component = mountWithIntl( { const component = mountWithIntl( { const component = mountWithIntl( { try { let response; if (this.props.savedQuery && !saveAsNew) { - response = await saveQuery(savedQueryAttributes, { overwrite: true }); + response = await this.props.savedQueryService.saveQuery(savedQueryAttributes, { + overwrite: true, + }); } else { - response = await saveQuery(savedQueryAttributes); + response = await this.props.savedQueryService.saveQuery(savedQueryAttributes); } toastNotifications.addSuccess(`Your query "${response.attributes.title}" was saved`); @@ -362,7 +365,7 @@ class SearchBarUI extends Component { onSave={this.onInitiateSave} onSaveAsNew={this.onInitiateSaveNew} onLoad={this.onLoadSavedQuery} - query={this.state.query} + savedQueryService={this.props.savedQueryService} onClearSavedQuery={this.props.onClearSavedQuery} > ); @@ -434,6 +437,7 @@ class SearchBarUI extends Component { {this.state.showSaveQueryModal ? ( this.setState({ showSaveQueryModal: false })} showFilterOption={this.props.showFilterBar} @@ -442,6 +446,7 @@ class SearchBarUI extends Component { ) : null} {this.state.showSaveNewQueryModal ? ( this.onSave(savedQueryMeta, true)} onClose={() => this.setState({ showSaveNewQueryModal: false })} showFilterOption={this.props.showFilterBar} diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index 648dd7c749e39..55720482a5be7 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -18,12 +18,7 @@ */ import { SavedQueryAttributes } from '../index'; -import { - saveQuery, - findSavedQueries, - getSavedQuery, - deleteSavedQuery, -} from './saved_query_service'; +import { createSavedQueryService } from './saved_query_service'; import { FilterStateStore } from '@kbn/es-query'; const savedQueryAttributes: SavedQueryAttributes = { @@ -66,19 +61,17 @@ const mockSavedObjectsClient = { delete: jest.fn(), }; -jest.mock('ui/chrome', () => { - return { - getSavedObjectsClient: () => { - return mockSavedObjectsClient; - }, - }; -}); +const { deleteSavedQuery, getSavedQuery, findSavedQueries, saveQuery } = createSavedQueryService( + // @ts-ignore + mockSavedObjectsClient +); describe('saved query service', () => { afterEach(() => { mockSavedObjectsClient.create.mockReset(); mockSavedObjectsClient.find.mockReset(); mockSavedObjectsClient.get.mockReset(); + mockSavedObjectsClient.delete.mockReset(); }); describe('saveQuery', function() { diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index 64d5568ee4638..b09cdba2f0485 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -17,8 +17,8 @@ * under the License. */ -import chrome from 'ui/chrome'; import { SavedObjectAttributes } from 'src/core/server'; +import { SavedObjectsClientContract } from 'src/core/public'; import { SavedQueryAttributes, SavedQuery } from '../index'; interface SerializedSavedQueryAttributes extends SavedObjectAttributes { @@ -32,117 +32,131 @@ interface SerializedSavedQueryAttributes extends SavedObjectAttributes { timefilter?: string; } -export const saveQuery = async (attributes: SavedQueryAttributes, { overwrite = false } = {}) => { - const savedObjectsClient = chrome.getSavedObjectsClient(); - - const query = { - query: - typeof attributes.query.query === 'string' - ? attributes.query.query - : JSON.stringify(attributes.query.query), - language: attributes.query.language, - }; +export interface SavedQueryService { + saveQuery: ( + attributes: SavedQueryAttributes, + config?: { overwrite: boolean } + ) => Promise; + getAllSavedQueries: () => Promise; + findSavedQueries: (searchText?: string) => Promise; + getSavedQuery: (id: string) => Promise; + deleteSavedQuery: (id: string) => Promise<{}>; +} - const queryObject: SerializedSavedQueryAttributes = { - title: attributes.title, - description: attributes.description, - query, +export const createSavedQueryService = ( + savedObjectsClient: SavedObjectsClientContract +): SavedQueryService => { + const saveQuery = async (attributes: SavedQueryAttributes, { overwrite = false } = {}) => { + const query = { + query: + typeof attributes.query.query === 'string' + ? attributes.query.query + : JSON.stringify(attributes.query.query), + language: attributes.query.language, + }; + + const queryObject: SerializedSavedQueryAttributes = { + title: attributes.title, + description: attributes.description, + query, + }; + + if (attributes.filters) { + queryObject.filters = JSON.stringify(attributes.filters); + } + + if (attributes.timefilter) { + queryObject.timefilter = JSON.stringify(attributes.timefilter); + } + + let rawQueryResponse; + if (!overwrite) { + rawQueryResponse = await savedObjectsClient.create('query', queryObject, { + id: attributes.title, + }); + } else { + rawQueryResponse = await savedObjectsClient.create('query', queryObject, { + id: attributes.title, + overwrite: true, + }); + } + + if (rawQueryResponse.error) { + throw new Error(rawQueryResponse.error.message); + } + + return parseSavedQueryObject(rawQueryResponse); }; - if (attributes.filters) { - queryObject.filters = JSON.stringify(attributes.filters); - } - - if (attributes.timefilter) { - queryObject.timefilter = JSON.stringify(attributes.timefilter); - } - - let rawQueryResponse; - if (!overwrite) { - rawQueryResponse = await savedObjectsClient.create('query', queryObject, { - id: attributes.title, - }); - } else { - rawQueryResponse = await savedObjectsClient.create('query', queryObject, { - id: attributes.title, - overwrite: true, + const getAllSavedQueries = async (): Promise => { + const response = await savedObjectsClient.find({ + type: 'query', }); - } - if (rawQueryResponse.error) { - throw new Error(rawQueryResponse.error.message); - } - - return parseSavedQueryObject(rawQueryResponse); -}; - -export const getAllSavedQueries = async (): Promise => { - const savedObjectsClient = chrome.getSavedObjectsClient(); - - const response = await savedObjectsClient.find({ - type: 'query', - }); - - return response.savedObjects.map( - (savedObject: { id: string; attributes: SerializedSavedQueryAttributes }) => - parseSavedQueryObject(savedObject) - ); -}; - -export const findSavedQueries = async (searchText: string = ''): Promise => { - const savedObjectsClient = chrome.getSavedObjectsClient(); - - const response = await savedObjectsClient.find({ - type: 'query', - search: searchText, - searchFields: ['title^5', 'description'], - sortField: '_score', - }); + return response.savedObjects.map( + (savedObject: { id: string; attributes: SerializedSavedQueryAttributes }) => + parseSavedQueryObject(savedObject) + ); + }; - return response.savedObjects.map( - (savedObject: { id: string; attributes: SerializedSavedQueryAttributes }) => - parseSavedQueryObject(savedObject) - ); -}; + const findSavedQueries = async (searchText: string = ''): Promise => { + const response = await savedObjectsClient.find({ + type: 'query', + search: searchText, + searchFields: ['title^5', 'description'], + sortField: '_score', + }); -export const getSavedQuery = async (id: string): Promise => { - const savedObjectsClient = chrome.getSavedObjectsClient(); + return response.savedObjects.map( + (savedObject: { id: string; attributes: SerializedSavedQueryAttributes }) => + parseSavedQueryObject(savedObject) + ); + }; - const response = await savedObjectsClient.get('query', id); - return parseSavedQueryObject(response); -}; + const getSavedQuery = async (id: string): Promise => { + const response = await savedObjectsClient.get('query', id); + return parseSavedQueryObject(response); + }; -export const deleteSavedQuery = async (id: string) => { - const savedObjectsClient = chrome.getSavedObjectsClient(); - return await savedObjectsClient.delete('query', id); -}; + const deleteSavedQuery = async (id: string) => { + return await savedObjectsClient.delete('query', id); + }; -const parseSavedQueryObject = (savedQuery: { - id: string; - attributes: SerializedSavedQueryAttributes; -}) => { - let queryString; - try { - queryString = JSON.parse(savedQuery.attributes.query.query); - } catch (error) { - queryString = savedQuery.attributes.query.query; - } - const savedQueryItems: SavedQueryAttributes = { - title: savedQuery.attributes.title || '', - description: savedQuery.attributes.description || '', - query: { - query: queryString, - language: savedQuery.attributes.query.language, - }, + const parseSavedQueryObject = (savedQuery: { + id: string; + attributes: SerializedSavedQueryAttributes; + }) => { + let queryString; + try { + queryString = JSON.parse(savedQuery.attributes.query.query); + } catch (error) { + queryString = savedQuery.attributes.query.query; + } + const savedQueryItems: SavedQueryAttributes = { + title: savedQuery.attributes.title || '', + description: savedQuery.attributes.description || '', + query: { + query: queryString, + language: savedQuery.attributes.query.language, + }, + }; + if (savedQuery.attributes.filters) { + savedQueryItems.filters = JSON.parse(savedQuery.attributes.filters); + } + if (savedQuery.attributes.timefilter) { + savedQueryItems.timefilter = JSON.parse(savedQuery.attributes.timefilter); + } + return { + id: savedQuery.id, + attributes: savedQueryItems, + }; }; - if (savedQuery.attributes.filters) { - savedQueryItems.filters = JSON.parse(savedQuery.attributes.filters); - } - if (savedQuery.attributes.timefilter) { - savedQueryItems.timefilter = JSON.parse(savedQuery.attributes.timefilter); - } + return { - id: savedQuery.id, - attributes: savedQueryItems, + saveQuery, + getAllSavedQueries, + findSavedQueries, + getSavedQuery, + deleteSavedQuery, }; }; diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts index 52629a43566b6..2bb996d0fd45f 100644 --- a/src/legacy/core_plugins/data/public/search/search_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -17,7 +17,8 @@ * under the License. */ -import * as savedQueryService from './search_bar/lib/saved_query_service'; +import { SavedObjectsClientContract } from 'src/core/public'; +import { createSavedQueryService } from './search_bar/lib/saved_query_service'; /** * Search Service @@ -25,10 +26,10 @@ import * as savedQueryService from './search_bar/lib/saved_query_service'; */ export class SearchService { - public setup() { + public setup(savedObjectsClient: SavedObjectsClientContract) { return { services: { - savedQueryService, + savedQueryService: createSavedQueryService(savedObjectsClient), }, }; } diff --git a/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.test.tsx b/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.test.tsx index 83212a4c7c64e..050e55aed43c3 100644 --- a/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.test.tsx +++ b/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.test.tsx @@ -24,6 +24,7 @@ import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { coreMock } from '../../../../../core/public/mocks'; const setupMock = coreMock.createSetup(); +const startMock = coreMock.createStart(); jest.mock('../../../../core_plugins/data/public', () => { return { @@ -54,14 +55,25 @@ describe('TopNavMenu', () => { ]; it('Should render nothing when no config is provided', () => { - const component = shallowWithIntl(); + const component = shallowWithIntl( + + ); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); }); it('Should render 1 menu item', () => { const component = shallowWithIntl( - + ); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(1); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); @@ -69,7 +81,12 @@ describe('TopNavMenu', () => { it('Should render multiple menu items', () => { const component = shallowWithIntl( - + ); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(menuItems.length); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); @@ -77,7 +94,12 @@ describe('TopNavMenu', () => { it('Should render search bar', () => { const component = shallowWithIntl( - + ); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0); diff --git a/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.tsx b/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.tsx index f242713849cbf..5c11e99265f10 100644 --- a/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.tsx +++ b/src/legacy/core_plugins/kibana_react/public/top_nav_menu/top_nav_menu.tsx @@ -21,14 +21,16 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { I18nProvider } from '@kbn/i18n/react'; -import { UiSettingsClientContract } from 'src/core/public'; +import { UiSettingsClientContract, SavedObjectsClientContract } from 'src/core/public'; import { TopNavMenuData } from './top_nav_menu_data'; import { TopNavMenuItem } from './top_nav_menu_item'; import { SearchBar, SearchBarProps } from '../../../../core_plugins/data/public'; +import { createSavedQueryService } from '../../../data/public/search/search_bar/lib/saved_query_service'; type Props = Partial & { name: string; uiSettings: UiSettingsClientContract; + savedObjectsClient: SavedObjectsClientContract; config?: TopNavMenuData[]; showSearchBar?: boolean; }; @@ -58,11 +60,14 @@ export function TopNavMenu(props: Props) { // Validate presense of all required fields if (!props.showSearchBar) return; + const savedQueryService = createSavedQueryService(props.savedObjectsClient); + return ( { const localStorage = new Storage(window.localStorage); child.setAttribute('store', 'store'); child.setAttribute('ui-settings', 'uiSettings'); + child.setAttribute('saved-objects-client', 'savedObjectsClient'); // Append helper directive elem.append(child); @@ -54,6 +55,7 @@ module.directive('kbnTopNavV2', () => { const linkFn = ($scope, _, $attr) => { $scope.store = localStorage; $scope.uiSettings = chrome.getUiSettingsClient(); + $scope.savedObjectsClient = chrome.getSavedObjectsClient(); // Watch config changes $scope.$watch(() => { @@ -95,6 +97,7 @@ module.directive('kbnTopNavV2Helper', (reactDirective) => { ['savedQuery', { watchDepth: 'reference' }], ['store', { watchDepth: 'reference' }], ['uiSettings', { watchDepth: 'reference' }], + ['savedObjectsClient', { watchDepth: 'reference' }], ['intl', { watchDepth: 'reference' }], ['store', { watchDepth: 'reference' }], From 2708fadd929d72077cbcc758b2a947540de10cde Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 15 Aug 2019 14:09:22 -0700 Subject: [PATCH 166/189] Closes the SavedQueryManagementComponent after deleting a saved query to force a fresh list of saved queries to be fetched --- .../saved_query_management_component.tsx | 7 +++++-- .../services/saved_query_management_component.js | 8 ++++++-- .../apps/discover/feature_controls/discover_security.ts | 9 ++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx index 2d1096f907c18..7296ebde41c71 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx @@ -96,14 +96,16 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ } ); - const onDeleteSavedQuery = (savedQuery: SavedQuery) => { + const onDeleteSavedQuery = async (savedQuery: SavedQuery) => { setSavedQueries(savedQueries.filter(currentSavedQuery => currentSavedQuery !== savedQuery)); if (loadedSavedQuery && loadedSavedQuery.id === savedQuery.id) { onClearSavedQuery(); } - savedQueryService.deleteSavedQuery(savedQuery.id); + await savedQueryService.deleteSavedQuery(savedQuery.id); + // close the modal after delete + setIsOpen(false); }; const savedQueryPopoverButton = ( @@ -124,6 +126,7 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ ); const savedQueryRows = () => { + // we should be recalculating the savedQueryRows after a delete action const savedQueriesWithoutCurrent = savedQueries.filter(savedQuery => { if (!loadedSavedQuery) return true; return savedQuery.id !== loadedSavedQuery.id; diff --git a/test/functional/services/saved_query_management_component.js b/test/functional/services/saved_query_management_component.js index 06564098c531d..a69543065b641 100644 --- a/test/functional/services/saved_query_management_component.js +++ b/test/functional/services/saved_query_management_component.js @@ -59,6 +59,7 @@ export function SavedQueryManagementComponentProvider({ getService }) { await this.openSavedQueryManagementComponent(); await testSubjects.click(`delete-saved-query-${title}-button`); await testSubjects.click('confirmModalConfirmButton'); + // await this.closeSavedQueryManagementComponent(); } async clearCurrentlyLoadedQuery() { @@ -94,8 +95,11 @@ export function SavedQueryManagementComponentProvider({ getService }) { } async savedQueryMissingOrFail(title) { - await this.openSavedQueryManagementComponent(); - await testSubjects.missingOrFail(`load-saved-query-${title}-button`); + await retry.try(async () => { + await this.openSavedQueryManagementComponent(); + await testSubjects.missingOrFail(`load-saved-query-${title}-button`); + }); + await this.closeSavedQueryManagementComponent(); } async openSavedQueryManagementComponent() { diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 90746ded3940f..6311b73de3a3e 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -130,12 +130,15 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(queryString).to.eql('response:404'); }); - it('allows deleting saved queries in the saved query management component ', async () => { + it('allows deleting a loaded saved query in the saved query management component ', async () => { await savedQueryManagementComponent.deleteSavedQuery('foo2'); - // add a manual delay - await queryBar.setQuery(''); await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); + + it('allows deleting an unselected saved query in the saved query management component ', async () => { + await savedQueryManagementComponent.deleteSavedQuery('foo'); + await savedQueryManagementComponent.savedQueryMissingOrFail('foo'); + }); }); describe('global discover read-only privileges', () => { From 2fcc1eb3e30df940e0711c5dcbf094a7c776e062 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 15 Aug 2019 15:00:14 -0700 Subject: [PATCH 167/189] Fixes bug with deleting a loaded saved query --- .../saved_query_management_component.tsx | 13 +++++++------ .../feature_controls/dashboard_security.ts | 2 -- .../discover/feature_controls/discover_security.ts | 6 +++--- .../feature_controls/visualize_security.ts | 2 -- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx index 7296ebde41c71..d9e68119a2797 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_management_component.tsx @@ -97,15 +97,15 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ ); const onDeleteSavedQuery = async (savedQuery: SavedQuery) => { - setSavedQueries(savedQueries.filter(currentSavedQuery => currentSavedQuery !== savedQuery)); + setSavedQueries( + savedQueries.filter(currentSavedQuery => currentSavedQuery.id !== savedQuery.id) + ); if (loadedSavedQuery && loadedSavedQuery.id === savedQuery.id) { onClearSavedQuery(); } await savedQueryService.deleteSavedQuery(savedQuery.id); - // close the modal after delete - setIsOpen(false); }; const savedQueryPopoverButton = ( @@ -131,9 +131,10 @@ export const SavedQueryManagementComponent: FunctionComponent = ({ if (!loadedSavedQuery) return true; return savedQuery.id !== loadedSavedQuery.id; }); - const savedQueriesReordered = loadedSavedQuery - ? [loadedSavedQuery, ...savedQueriesWithoutCurrent] - : [...savedQueriesWithoutCurrent]; + const savedQueriesReordered = + loadedSavedQuery && savedQueriesWithoutCurrent.length !== savedQueries.length + ? [loadedSavedQuery, ...savedQueriesWithoutCurrent] + : [...savedQueriesWithoutCurrent]; const savedQueriesDisplayRows = savedQueriesReordered.slice( activePage * pageCount, activePage * pageCount + pageCount diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index 7a0a2025e82ac..3bc75ff5492fa 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -216,8 +216,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('allows deleting saved queries in the saved query management component ', async () => { await savedQueryManagementComponent.deleteSavedQuery('foo2'); - // add a manual delay - await queryBar.setQuery('response:503'); await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); }); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 6311b73de3a3e..a984c1a68bff9 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -135,9 +135,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); - it('allows deleting an unselected saved query in the saved query management component ', async () => { - await savedQueryManagementComponent.deleteSavedQuery('foo'); - await savedQueryManagementComponent.savedQueryMissingOrFail('foo'); + it('allows deleting a loaded saved query in the saved query management component ', async () => { + await savedQueryManagementComponent.deleteSavedQuery('foo2'); + await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); }); diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 5e034642b56fc..d85271fe166b1 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -148,8 +148,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('allows deleting saved queries in the saved query management component ', async () => { await savedQueryManagementComponent.deleteSavedQuery('foo2'); - // add a manual delay - await queryBar.setQuery('response:503'); await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); }); From 2cc232beae8029d63da85dc2acf4d55ecf1894d5 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 15 Aug 2019 15:15:41 -0700 Subject: [PATCH 168/189] reverts local changes --- .../apps/discover/feature_controls/discover_security.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index a984c1a68bff9..67bc8bd38ff1c 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -130,12 +130,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(queryString).to.eql('response:404'); }); - it('allows deleting a loaded saved query in the saved query management component ', async () => { - await savedQueryManagementComponent.deleteSavedQuery('foo2'); - await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); - }); - - it('allows deleting a loaded saved query in the saved query management component ', async () => { + it('allows deleting saved queries in the saved query management component ', async () => { await savedQueryManagementComponent.deleteSavedQuery('foo2'); await savedQueryManagementComponent.savedQueryMissingOrFail('foo2'); }); From d57058af854b89306753ec6d5ac26ad67eddc59d Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 15 Aug 2019 15:18:33 -0700 Subject: [PATCH 169/189] deletes unused code --- test/functional/services/saved_query_management_component.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/services/saved_query_management_component.js b/test/functional/services/saved_query_management_component.js index a69543065b641..f1ca086c1ca24 100644 --- a/test/functional/services/saved_query_management_component.js +++ b/test/functional/services/saved_query_management_component.js @@ -59,7 +59,6 @@ export function SavedQueryManagementComponentProvider({ getService }) { await this.openSavedQueryManagementComponent(); await testSubjects.click(`delete-saved-query-${title}-button`); await testSubjects.click('confirmModalConfirmButton'); - // await this.closeSavedQueryManagementComponent(); } async clearCurrentlyLoadedQuery() { From f411ddf941421050a87d9ae394164c1b232d2694 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 15 Aug 2019 15:24:35 -0700 Subject: [PATCH 170/189] Removes accessibility duplication --- .../saved_query_list_item.tsx | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx index 6595711a72e42..4633bc3820017 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx @@ -53,7 +53,8 @@ export const SavedQueryListItem = ({ ? i18n.translate( 'data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel', { - defaultMessage: 'Saved query button selected {savedQueryName}', + defaultMessage: + 'Saved query button selected {savedQueryName}. Press to clear any changes.', values: { savedQueryName: savedQuery.attributes.title }, } ) @@ -62,17 +63,6 @@ export const SavedQueryListItem = ({ values: { savedQueryName: savedQuery.attributes.title }, }); - const selectButtonScreenReaderContent = isSelected - ? i18n.translate('data.search.searchBar.savedQueryScreenReaderSelectedText', { - defaultMessage: 'Selected saved query {savedQueryName}', - values: { - savedQueryName: `${savedQuery.attributes.title}`, - }, - }) - : i18n.translate('data.search.searchBar.savedQueryScreenReaderOpenText', { - defaultMessage: 'Open saved query', - }); - const selectButtonDataTestSubj = isSelected ? `load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected` : `load-saved-query-${savedQuery.attributes.title}-button`; @@ -96,9 +86,6 @@ export const SavedQueryListItem = ({ textProps={isSelected ? { className: 'saved-query-list-item-text' } : undefined} aria-label={selectButtonAriaLabelText} > - - {selectButtonScreenReaderContent} - {savedQuery.attributes.title} From f80c912977daa32f563b57b680c930c98c1f2595 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Thu, 15 Aug 2019 16:17:52 -0700 Subject: [PATCH 171/189] Removes unused imports --- .../components/saved_query_management/saved_query_list_item.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx index 4633bc3820017..c690fbaac4fb5 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/saved_query_list_item.tsx @@ -23,7 +23,6 @@ import { EuiFlexItem, EuiConfirmModal, EuiOverlayMask, - EuiScreenReaderOnly, EuiIconTip, EuiToolTip, } from '@elastic/eui'; From af592796619f5a526aff671c08e3b546588b7a1a Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 16 Aug 2019 11:27:26 -0700 Subject: [PATCH 172/189] initial setup of general saved query management component functional tests --- .../apps/discover/_saved_queries.js | 67 +++++++++++++++++++ test/functional/apps/discover/index.js | 1 + .../es_archiver/discover/mappings.json | 28 +++++++- .../es_archiver/empty_kibana/mappings.json | 28 +++++++- 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 test/functional/apps/discover/_saved_queries.js diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js new file mode 100644 index 0000000000000..c1810590b4969 --- /dev/null +++ b/test/functional/apps/discover/_saved_queries.js @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; + +export default function ({ getService, getPageObjects }) { + const log = getService('log'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); + + const defaultSettings = { + defaultIndex: 'logstash-*', + }; + const queryBar = getService('queryBar'); + const savedQueryManagementComponent = getService('savedQueryManagementComponent'); + const testSubjects = getService('testSubjects'); + + describe('saved queries as saved objects', function describeIndexTests() { + const fromTime = '2015-09-19 06:31:44.000'; + const toTime = '2015-09-23 18:31:44.000'; + + before(async function () { + log.debug('load kibana index with default index pattern'); + await esArchiver.load('discover'); + + // and load a set of makelogs data + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + log.debug('discover'); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }); + + describe('saved query management component functionality', function () { + this.tags(['skipFirefox']); + it('should show the saved query management component when there are no saved queries', async () => { + await savedQueryManagementComponent.openSavedQueryManagementComponent(); + const descriptionText = await testSubjects.getVisibleText('saved-query-management-popover'); + expect(descriptionText) + .to + .eql('SAVED QUERIES\nThere are no saved queries. Save query text and filters that you want to use again.'); + }); + it('should allow a query to be saved via the saved objects management component', async () => { + await queryBar.setQuery('response:200'); + // await savedQueryManagementComponent.saveNewQuery('OkResponse', '200 responses', false, true); + // await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); + }); + }); + }); +} diff --git a/test/functional/apps/discover/index.js b/test/functional/apps/discover/index.js index a0b2c43defa24..fe1a8a008df73 100644 --- a/test/functional/apps/discover/index.js +++ b/test/functional/apps/discover/index.js @@ -42,5 +42,6 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./_inspector')); loadTestFile(require.resolve('./_doc_navigation')); loadTestFile(require.resolve('./_date_nanos')); + loadTestFile(require.resolve('./_saved_queries')); }); } diff --git a/test/functional/fixtures/es_archiver/discover/mappings.json b/test/functional/fixtures/es_archiver/discover/mappings.json index a89fe1dfacfc8..65f2907bc2f8a 100644 --- a/test/functional/fixtures/es_archiver/discover/mappings.json +++ b/test/functional/fixtures/es_archiver/discover/mappings.json @@ -231,6 +231,32 @@ "type": "text" } } + }, + "query": { + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + }, + "query": { + "properties": { + "language": { + "type": "text" + }, + "query": { + "type": "text" + } + } + }, + "filters": { + "type": "text" + }, + "timefilter": { + "type": "text" + } + } } } }, @@ -241,4 +267,4 @@ } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/es_archiver/empty_kibana/mappings.json b/test/functional/fixtures/es_archiver/empty_kibana/mappings.json index 3e871dddb0d71..ee86729f9e26a 100644 --- a/test/functional/fixtures/es_archiver/empty_kibana/mappings.json +++ b/test/functional/fixtures/es_archiver/empty_kibana/mappings.json @@ -240,6 +240,32 @@ "type": "text" } } + }, + "query": { + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + }, + "query": { + "properties": { + "language": { + "type": "text" + }, + "query": { + "type": "text" + } + } + }, + "filters": { + "type": "text" + }, + "timefilter": { + "type": "text" + } + } } } }, @@ -250,4 +276,4 @@ } } } -} \ No newline at end of file +} From fa2a0b6543cbc30f44f1fff5496af8fb28a1843b Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 16 Aug 2019 15:01:21 -0700 Subject: [PATCH 173/189] Adds saved queries to OSS uiCapabilities --- src/legacy/core_plugins/kibana/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 3665954a9c619..f76b7e1734278 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -268,17 +268,20 @@ export default function (kibana) { show: true, createShortUrl: true, save: true, + saveQuery: true, }, visualize: { show: true, createShortUrl: true, delete: true, save: true, + saveQuery: true, }, dashboard: { createNew: true, show: true, showWriteControls: true, + saveQuery: true, }, catalogue: { discover: true, From 382b20e7a2883659cebd96ed22caa8d02e25a821 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Fri, 16 Aug 2019 17:02:25 -0700 Subject: [PATCH 174/189] Adds general saved query management component functional tests --- .../apps/discover/_saved_queries.js | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js index c1810590b4969..d48fcc8a5253c 100644 --- a/test/functional/apps/discover/_saved_queries.js +++ b/test/functional/apps/discover/_saved_queries.js @@ -28,6 +28,7 @@ export default function ({ getService, getPageObjects }) { const defaultSettings = { defaultIndex: 'logstash-*', }; + const filterBar = getService('filterBar'); const queryBar = getService('queryBar'); const savedQueryManagementComponent = getService('savedQueryManagementComponent'); const testSubjects = getService('testSubjects'); @@ -55,12 +56,70 @@ export default function ({ getService, getPageObjects }) { const descriptionText = await testSubjects.getVisibleText('saved-query-management-popover'); expect(descriptionText) .to - .eql('SAVED QUERIES\nThere are no saved queries. Save query text and filters that you want to use again.'); + .eql('SAVED QUERIES\nThere are no saved queries. Save query text and filters that you want to use again.\nSave'); }); it('should allow a query to be saved via the saved objects management component', async () => { await queryBar.setQuery('response:200'); - // await savedQueryManagementComponent.saveNewQuery('OkResponse', '200 responses', false, true); - // await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); + await savedQueryManagementComponent.saveNewQuery('OkResponse', '200 responses', false, true); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); + }); + it('allow saving a currently loaded saved query as a new query via the saved query management component ', async () => { + await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( + 'OkResponseCopy', + '200 responses copy', + true, + false + ); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponseCopy'); + }); + + it('allow saving changes to a currently loaded query via the saved query management component', async () => { + await queryBar.setQuery('response:404'); + await savedQueryManagementComponent.updateCurrentlyLoadedQuery('Not found', false, false); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('OkResponseCopy'); + const queryString = await queryBar.getQueryString(); + expect(queryString).to.eql('response:404'); + }); + + it('allows deleting saved queries in the saved query management component ', async () => { + await savedQueryManagementComponent.deleteSavedQuery('OkResponseCopy'); + await savedQueryManagementComponent.savedQueryMissingOrFail('OkResponseCopy'); + }); + + it('allows clearing the currently loaded saved query', async () => { + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + }); + + it('saves a filter as part of the saved query when include filters is selected', async () => { + await queryBar.setQuery('response:404'); + await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); + await savedQueryManagementComponent.saveNewQuery('NotFoundForJpgs', 'Jpg not found\'s', true, false); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('NotFoundForJpgs'); + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); + }); + + it('allows saving a query with a date filter', async () => { + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( + 'OkResponseWithTimeFilter', + 'timefilter enabled', + false, + true + ); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponseWithTimeFilter'); + }); + + it('sets the timefilter when a saved query is loaded that has a timefilter saved', async () => { + const fromTime = '2015-09-20 06:31:44.000'; + const toTime = '2015-09-20 18:31:44.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await savedQueryManagementComponent.loadSavedQuery('OkResponseWithTimeFilter'); + const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); + expect(timePickerValues.start).to.not.eql(fromTime); + expect(timePickerValues.end).to.not.eql(toTime); }); }); }); From 08a36839093b9d59ba238c824a250428d2fb54e9 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 19 Aug 2019 10:45:33 -0700 Subject: [PATCH 175/189] Adds changes to kbn_top_nav lost during deletion of kbn_top_nav2 --- .../kibana/public/discover/controllers/discover.js | 2 +- .../core_plugins/kibana/public/visualize/editor/editor.js | 2 +- src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index d9637668f7de7..35fbd05ad925f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -72,7 +72,7 @@ import { buildVislibDimensions } from 'ui/visualize/loader/pipeline_helpers/buil import 'ui/capabilities/route_setup'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; -import { data } from 'plugins/data/setup'; +import { setup as data } from '../../../../../core_plugins/data/public/legacy'; const { savedQueryService } = data.search.services; diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js index b2e133d4ba547..85911160f1e62 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.js @@ -54,7 +54,7 @@ import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal'; import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs'; import { npStart } from 'ui/new_platform'; -import { data } from 'plugins/data/setup'; +import { setup as data } from '../../../../../core_plugins/data/public/legacy'; import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; const { savedQueryService } = data.search.services; diff --git a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js index 6e070d401ad3c..de97a26e8f49e 100644 --- a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js +++ b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js @@ -47,6 +47,7 @@ module.directive('kbnTopNav', () => { const localStorage = new Storage(window.localStorage); child.setAttribute('store', 'store'); child.setAttribute('ui-settings', 'uiSettings'); + child.setAttribute('saved-objects-client', 'savedObjectsClient'); // Append helper directive elem.append(child); @@ -54,6 +55,7 @@ module.directive('kbnTopNav', () => { const linkFn = ($scope, _, $attr) => { $scope.store = localStorage; $scope.uiSettings = chrome.getUiSettingsClient(); + $scope.savedObjectsClient = chrome.getSavedObjectsClient(); // Watch config changes $scope.$watch(() => { @@ -92,14 +94,19 @@ module.directive('kbnTopNavHelper', (reactDirective) => { ['disabledButtons', { watchDepth: 'reference' }], ['query', { watchDepth: 'reference' }], + ['savedQuery', { watchDepth: 'reference' }], ['store', { watchDepth: 'reference' }], ['uiSettings', { watchDepth: 'reference' }], + ['savedObjectsClient', { watchDepth: 'reference' }], ['intl', { watchDepth: 'reference' }], ['store', { watchDepth: 'reference' }], ['onQuerySubmit', { watchDepth: 'reference' }], ['onFiltersUpdated', { watchDepth: 'reference' }], ['onRefreshChange', { watchDepth: 'reference' }], + ['onClearSavedQuery', { watchDepth: 'reference' }], + ['onSaved', { watchDepth: 'reference' }], + ['onSavedQueryUpdated', { watchDepth: 'reference' }], ['indexPatterns', { watchDepth: 'collection' }], ['filters', { watchDepth: 'collection' }], @@ -111,6 +118,7 @@ module.directive('kbnTopNavHelper', (reactDirective) => { 'showQueryBar', 'showQueryInput', 'showDatePicker', + 'showSaveQuery', 'appName', 'screenTitle', From 4f40b1efc175d6da8de6cd0888e93849988d653c Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 19 Aug 2019 15:13:33 -0700 Subject: [PATCH 176/189] Refactors tests and adds test for saving with a non-unique name --- .../save_query_form.tsx | 2 +- .../apps/discover/_saved_queries.js | 81 ++++++++----------- test/functional/apps/discover/index.js | 2 +- .../saved_query_management_component.js | 15 ++++ 4 files changed, 51 insertions(+), 49 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx index 4830c5cb643ec..792a55daefefe 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx @@ -192,7 +192,7 @@ export const SaveQueryForm: FunctionComponent = ({ {saveQueryForm} - + {i18n.translate('data.search.searchBar.savedQueryFormCancelButtonText', { defaultMessage: 'Cancel', })} diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js index d48fcc8a5253c..cb64d932b766d 100644 --- a/test/functional/apps/discover/_saved_queries.js +++ b/test/functional/apps/discover/_saved_queries.js @@ -33,7 +33,7 @@ export default function ({ getService, getPageObjects }) { const savedQueryManagementComponent = getService('savedQueryManagementComponent'); const testSubjects = getService('testSubjects'); - describe('saved queries as saved objects', function describeIndexTests() { + describe('saved queries saved objects', function describeIndexTests() { const fromTime = '2015-09-19 06:31:44.000'; const toTime = '2015-09-23 18:31:44.000'; @@ -51,6 +51,16 @@ export default function ({ getService, getPageObjects }) { describe('saved query management component functionality', function () { this.tags(['skipFirefox']); + before(async function () { + // set up a query with filters and a time filter + log.debug('set up a query with filters to save'); + await queryBar.setQuery('response:200'); + await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); + const fromTime = '2015-09-20 08:00:00.000'; + const toTime = '2015-09-21 08:00:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }); + it('should show the saved query management component when there are no saved queries', async () => { await savedQueryManagementComponent.openSavedQueryManagementComponent(); const descriptionText = await testSubjects.getVisibleText('saved-query-management-popover'); @@ -59,67 +69,44 @@ export default function ({ getService, getPageObjects }) { .eql('SAVED QUERIES\nThere are no saved queries. Save query text and filters that you want to use again.\nSave'); }); it('should allow a query to be saved via the saved objects management component', async () => { - await queryBar.setQuery('response:200'); - await savedQueryManagementComponent.saveNewQuery('OkResponse', '200 responses', false, true); + await savedQueryManagementComponent.saveNewQuery('OkResponse', '200 responses for .jpg over 24 hours', true, true); await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); }); - it('allow saving a currently loaded saved query as a new query via the saved query management component ', async () => { - await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( - 'OkResponseCopy', - '200 responses copy', - true, + + it('reinstates filters when a saved query has filters included', async () => { + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); + }); + + it('allows saving changes to a currently loaded query via the saved query management component', async () => { + await savedQueryManagementComponent.updateCurrentlyLoadedQuery( + 'OkResponse', + '200 responses', + false, false ); - await savedQueryManagementComponent.savedQueryExistOrFail('OkResponseCopy'); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); }); - it('allow saving changes to a currently loaded query via the saved query management component', async () => { - await queryBar.setQuery('response:404'); - await savedQueryManagementComponent.updateCurrentlyLoadedQuery('Not found', false, false); - await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); - await savedQueryManagementComponent.loadSavedQuery('OkResponseCopy'); - const queryString = await queryBar.getQueryString(); - expect(queryString).to.eql('response:404'); + it('allows saving the currently loaded query as a new query', async () => { + await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery('OkResponseCopy', '200 responses', false, false); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponseCopy'); }); - it('allows deleting saved queries in the saved query management component ', async () => { + it('allows deleting the currently loaded saved query in the saved query management component and clears the query', async () => { await savedQueryManagementComponent.deleteSavedQuery('OkResponseCopy'); await savedQueryManagementComponent.savedQueryMissingOrFail('OkResponseCopy'); + expect(await queryBar.getQueryString()).to.eql(''); }); - it('allows clearing the currently loaded saved query', async () => { - await savedQueryManagementComponent.loadSavedQuery('OkResponse'); - await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); - }); - - it('saves a filter as part of the saved query when include filters is selected', async () => { - await queryBar.setQuery('response:404'); - await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); - await savedQueryManagementComponent.saveNewQuery('NotFoundForJpgs', 'Jpg not found\'s', true, false); - await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); - await savedQueryManagementComponent.loadSavedQuery('NotFoundForJpgs'); - expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); + it('does not allow saving a query with a non-unique name', async () => { + await savedQueryManagementComponent.saveNewQueryWithDuplicateName('OkResponse'); }); - it('allows saving a query with a date filter', async () => { + it('allows clearing the currently loaded saved query', async () => { await savedQueryManagementComponent.loadSavedQuery('OkResponse'); - await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( - 'OkResponseWithTimeFilter', - 'timefilter enabled', - false, - true - ); - await savedQueryManagementComponent.savedQueryExistOrFail('OkResponseWithTimeFilter'); - }); - - it('sets the timefilter when a saved query is loaded that has a timefilter saved', async () => { - const fromTime = '2015-09-20 06:31:44.000'; - const toTime = '2015-09-20 18:31:44.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await savedQueryManagementComponent.loadSavedQuery('OkResponseWithTimeFilter'); - const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); - expect(timePickerValues.start).to.not.eql(fromTime); - expect(timePickerValues.end).to.not.eql(toTime); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); }); }); }); diff --git a/test/functional/apps/discover/index.js b/test/functional/apps/discover/index.js index fe1a8a008df73..9e4430ca71c1f 100644 --- a/test/functional/apps/discover/index.js +++ b/test/functional/apps/discover/index.js @@ -32,6 +32,7 @@ export default function ({ getService, loadTestFile }) { return esArchiver.unload('logstash_functional'); }); + loadTestFile(require.resolve('./_saved_queries')); loadTestFile(require.resolve('./_discover')); loadTestFile(require.resolve('./_errors')); loadTestFile(require.resolve('./_field_data')); @@ -42,6 +43,5 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./_inspector')); loadTestFile(require.resolve('./_doc_navigation')); loadTestFile(require.resolve('./_date_nanos')); - loadTestFile(require.resolve('./_saved_queries')); }); } diff --git a/test/functional/services/saved_query_management_component.js b/test/functional/services/saved_query_management_component.js index f1ca086c1ca24..53b4b36d1337b 100644 --- a/test/functional/services/saved_query_management_component.js +++ b/test/functional/services/saved_query_management_component.js @@ -32,6 +32,12 @@ export function SavedQueryManagementComponentProvider({ getService }) { await this.submitSaveQueryForm(name, description, includeFilters, includeTimeFilter); } + async saveNewQueryWithDuplicateName(name) { + await this.openSavedQueryManagementComponent(); + await testSubjects.click('saved-query-management-save-button'); + await this.saveNewQueryFormMissingOrFail(name); + } + async saveCurrentlyLoadedAsNewQuery(name, description, includeFilters, includeTimeFilter) { await this.openSavedQueryManagementComponent(); await testSubjects.click('saved-query-management-save-as-new-button'); @@ -88,6 +94,15 @@ export function SavedQueryManagementComponentProvider({ getService }) { await testSubjects.click('savedQueryFormSaveButton'); } + async saveNewQueryFormMissingOrFail(title) { + if (title) { + await testSubjects.setValue('saveQueryFormTitle', title); + } + const saveQueryFormSaveButtonStatus = await testSubjects.isEnabled('savedQueryFormSaveButton'); + expect(saveQueryFormSaveButtonStatus).to.not.eql(true); + await testSubjects.click('savedQueryFormCancelButton'); + } + async savedQueryExistOrFail(title) { await this.openSavedQueryManagementComponent(); await testSubjects.existOrFail(`load-saved-query-${title}-button`); From e609af26f92db2351d4f181f2c44fbfa69d0d603 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Mon, 19 Aug 2019 16:08:16 -0700 Subject: [PATCH 177/189] Adds back time filter test --- test/functional/apps/discover/_saved_queries.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js index cb64d932b766d..144402ee80aae 100644 --- a/test/functional/apps/discover/_saved_queries.js +++ b/test/functional/apps/discover/_saved_queries.js @@ -68,15 +68,22 @@ export default function ({ getService, getPageObjects }) { .to .eql('SAVED QUERIES\nThere are no saved queries. Save query text and filters that you want to use again.\nSave'); }); + it('should allow a query to be saved via the saved objects management component', async () => { await savedQueryManagementComponent.saveNewQuery('OkResponse', '200 responses for .jpg over 24 hours', true, true); await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); }); - it('reinstates filters when a saved query has filters included', async () => { + it('reinstates filters and the time filter when a saved query has filters and a time filter included', async () => { + const fromTime = '2015-09-19 06:31:44.000'; + const toTime = '2015-09-23 18:31:44.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); + expect(timePickerValues.start).to.not.eql(fromTime); + expect(timePickerValues.end).to.not.eql(toTime); }); it('allows saving changes to a currently loaded query via the saved query management component', async () => { From 5f3329b0283d231bbc9ba70e95a5140b1867e7b6 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 20 Aug 2019 10:50:18 -0700 Subject: [PATCH 178/189] Refactors checking saving changes to a currently loaded query --- test/functional/apps/discover/_saved_queries.js | 7 ++++++- .../services/saved_query_management_component.js | 16 ++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js index 144402ee80aae..5abed58325e9f 100644 --- a/test/functional/apps/discover/_saved_queries.js +++ b/test/functional/apps/discover/_saved_queries.js @@ -87,13 +87,18 @@ export default function ({ getService, getPageObjects }) { }); it('allows saving changes to a currently loaded query via the saved query management component', async () => { + await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQuery( 'OkResponse', - '200 responses', + '404 responses', false, false ); await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + expect(await queryBar.getQueryString()).to.eql(''); + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + expect(await queryBar.getQueryString()).to.eql('response:404'); }); it('allows saving the currently loaded query as a new query', async () => { diff --git a/test/functional/services/saved_query_management_component.js b/test/functional/services/saved_query_management_component.js index 53b4b36d1337b..8854418acd571 100644 --- a/test/functional/services/saved_query_management_component.js +++ b/test/functional/services/saved_query_management_component.js @@ -35,7 +35,12 @@ export function SavedQueryManagementComponentProvider({ getService }) { async saveNewQueryWithDuplicateName(name) { await this.openSavedQueryManagementComponent(); await testSubjects.click('saved-query-management-save-button'); - await this.saveNewQueryFormMissingOrFail(name); + if (name) { + await testSubjects.setValue('saveQueryFormTitle', name); + } + const saveQueryFormSaveButtonStatus = await testSubjects.isEnabled('savedQueryFormSaveButton'); + expect(saveQueryFormSaveButtonStatus).to.not.eql(true); + await testSubjects.click('savedQueryFormCancelButton'); } async saveCurrentlyLoadedAsNewQuery(name, description, includeFilters, includeTimeFilter) { @@ -94,15 +99,6 @@ export function SavedQueryManagementComponentProvider({ getService }) { await testSubjects.click('savedQueryFormSaveButton'); } - async saveNewQueryFormMissingOrFail(title) { - if (title) { - await testSubjects.setValue('saveQueryFormTitle', title); - } - const saveQueryFormSaveButtonStatus = await testSubjects.isEnabled('savedQueryFormSaveButton'); - expect(saveQueryFormSaveButtonStatus).to.not.eql(true); - await testSubjects.click('savedQueryFormCancelButton'); - } - async savedQueryExistOrFail(title) { await this.openSavedQueryManagementComponent(); await testSubjects.existOrFail(`load-saved-query-${title}-button`); From bc3fdf2f3267a356bba0aef96b8d244527b81b7c Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Tue, 20 Aug 2019 11:07:16 -0700 Subject: [PATCH 179/189] Adds check to clearing a saved query to ensure the query bar has been reset --- test/functional/apps/discover/_saved_queries.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js index 5abed58325e9f..c6af400e145ef 100644 --- a/test/functional/apps/discover/_saved_queries.js +++ b/test/functional/apps/discover/_saved_queries.js @@ -119,6 +119,7 @@ export default function ({ getService, getPageObjects }) { it('allows clearing the currently loaded saved query', async () => { await savedQueryManagementComponent.loadSavedQuery('OkResponse'); await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + expect(await queryBar.getQueryString()).to.eql(''); }); }); }); From 4787319272854f176e75b2ef7a8eabefaeffee86 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 20 Aug 2019 15:25:44 -0400 Subject: [PATCH 180/189] Improve saved query mappings --- src/legacy/core_plugins/data/mappings.ts | 11 +++++---- .../search_bar/lib/saved_query_service.ts | 23 ++++++++----------- .../es_archiver/discover/mappings.json | 11 +++++---- .../es_archiver/empty_kibana/mappings.json | 11 +++++---- .../feature_controls/security/data.json | 2 +- .../feature_controls/security/mappings.json | 11 +++++---- .../feature_controls/security/data.json | 2 +- .../feature_controls/security/mappings.json | 11 +++++---- .../es_archives/visualize/default/data.json | 2 +- .../visualize/default/mappings.json | 11 +++++---- 10 files changed, 55 insertions(+), 40 deletions(-) diff --git a/src/legacy/core_plugins/data/mappings.ts b/src/legacy/core_plugins/data/mappings.ts index 6786ce7bab713..90777ec8e3651 100644 --- a/src/legacy/core_plugins/data/mappings.ts +++ b/src/legacy/core_plugins/data/mappings.ts @@ -29,18 +29,21 @@ export const mappings = { query: { properties: { language: { - type: 'text', + type: 'keyword', }, query: { - type: 'text', + type: 'keyword', + index: false, }, }, }, filters: { - type: 'text', + type: 'object', + enabled: false, }, timefilter: { - type: 'text', + type: 'object', + enabled: false, }, }, }, diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index b09cdba2f0485..050a6ef2cb0a5 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -21,16 +21,13 @@ import { SavedObjectAttributes } from 'src/core/server'; import { SavedObjectsClientContract } from 'src/core/public'; import { SavedQueryAttributes, SavedQuery } from '../index'; -interface SerializedSavedQueryAttributes extends SavedObjectAttributes { - title: string; - description: string; - query: { - query: string; - language: string; +type SerializedSavedQueryAttributes = SavedObjectAttributes & + SavedQueryAttributes & { + query: { + query: string; + language: string; + }; }; - filters?: string; - timefilter?: string; -} export interface SavedQueryService { saveQuery: ( @@ -62,11 +59,11 @@ export const createSavedQueryService = ( }; if (attributes.filters) { - queryObject.filters = JSON.stringify(attributes.filters); + queryObject.filters = attributes.filters; } if (attributes.timefilter) { - queryObject.timefilter = JSON.stringify(attributes.timefilter); + queryObject.timefilter = attributes.timefilter; } let rawQueryResponse; @@ -141,10 +138,10 @@ export const createSavedQueryService = ( }, }; if (savedQuery.attributes.filters) { - savedQueryItems.filters = JSON.parse(savedQuery.attributes.filters); + savedQueryItems.filters = savedQuery.attributes.filters; } if (savedQuery.attributes.timefilter) { - savedQueryItems.timefilter = JSON.parse(savedQuery.attributes.timefilter); + savedQueryItems.timefilter = savedQuery.attributes.timefilter; } return { id: savedQuery.id, diff --git a/test/functional/fixtures/es_archiver/discover/mappings.json b/test/functional/fixtures/es_archiver/discover/mappings.json index 65f2907bc2f8a..82002c095bcc5 100644 --- a/test/functional/fixtures/es_archiver/discover/mappings.json +++ b/test/functional/fixtures/es_archiver/discover/mappings.json @@ -243,18 +243,21 @@ "query": { "properties": { "language": { - "type": "text" + "type": "keyword" }, "query": { - "type": "text" + "type": "keyword", + "index": false } } }, "filters": { - "type": "text" + "type": "object", + "enabled": false }, "timefilter": { - "type": "text" + "type": "object", + "enabled": false } } } diff --git a/test/functional/fixtures/es_archiver/empty_kibana/mappings.json b/test/functional/fixtures/es_archiver/empty_kibana/mappings.json index ee86729f9e26a..403a891ba1175 100644 --- a/test/functional/fixtures/es_archiver/empty_kibana/mappings.json +++ b/test/functional/fixtures/es_archiver/empty_kibana/mappings.json @@ -252,18 +252,21 @@ "query": { "properties": { "language": { - "type": "text" + "type": "keyword" }, "query": { - "type": "text" + "type": "keyword", + "index": false } } }, "filters": { - "type": "text" + "type": "object", + "enabled": false }, "timefilter": { - "type": "text" + "type": "object", + "enabled": false } } } diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json index b2453db328317..4ff13f76bc43e 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/data.json @@ -184,7 +184,7 @@ "query": "response:200", "language": "kuery" }, - "filters": "[{\"meta\":{\"index\":\"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b\",\"alias\":null,\"negate\":false,\"type\":\"phrase\",\"key\":\"extension.raw\",\"value\":\"jpg\",\"params\":{\"query\":\"jpg\"},\"disabled\":false},\"query\":{\"match\":{\"extension.raw\":{\"query\":\"jpg\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]" + "filters": [{"meta":{"index":"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b","alias":null,"negate":false,"type":"phrase","key":"extension.raw","value":"jpg","params":{"query":"jpg"},"disabled":false},"query":{"match":{"extension.raw":{"query":"jpg","type":"phrase"}}},"$state":{"store":"appState"}}] }, "type": "query", "updated_at": "2019-07-17T17:54:26.378Z" diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json index 183a0d03f2bd8..beb6aefbb4932 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json @@ -493,18 +493,21 @@ "query": { "properties": { "language": { - "type": "text" + "type": "keyword" }, "query": { - "type": "text" + "type": "keyword", + "index": false } } }, "filters": { - "type": "text" + "type": "object", + "enabled": false }, "timefilter": { - "type": "text" + "type": "object", + "enabled": false } } } diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json b/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json index ace3f175ceaaf..394393dce4962 100644 --- a/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json +++ b/x-pack/test/functional/es_archives/discover/feature_controls/security/data.json @@ -50,7 +50,7 @@ "query": "response:200", "language": "kuery" }, - "filters": "[{\"meta\":{\"index\":\"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b\",\"alias\":null,\"negate\":false,\"type\":\"phrase\",\"key\":\"extension.raw\",\"value\":\"jpg\",\"params\":{\"query\":\"jpg\"},\"disabled\":false},\"query\":{\"match\":{\"extension.raw\":{\"query\":\"jpg\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]" + "filters": [{"meta":{"index":"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b","alias":null,"negate":false,"type":"phrase","key":"extension.raw","value":"jpg","params":{"query":"jpg"},"disabled":false},"query":{"match":{"extension.raw":{"query":"jpg","type":"phrase"}}},"$state":{"store":"appState"}}] }, "type": "query", "updated_at": "2019-07-17T17:54:26.378Z" diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json index 00d11f15db6c3..11e5ac6eeea26 100644 --- a/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json +++ b/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json @@ -465,18 +465,21 @@ "query": { "properties": { "language": { - "type": "text" + "type": "keyword" }, "query": { - "type": "text" + "type": "keyword", + "index": false } } }, "filters": { - "type": "text" + "type": "object", + "enabled": false }, "timefilter": { - "type": "text" + "type": "object", + "enabled": false } } } diff --git a/x-pack/test/functional/es_archives/visualize/default/data.json b/x-pack/test/functional/es_archives/visualize/default/data.json index c905bb8ca6c17..aa3428b62a636 100644 --- a/x-pack/test/functional/es_archives/visualize/default/data.json +++ b/x-pack/test/functional/es_archives/visualize/default/data.json @@ -123,7 +123,7 @@ "query": "response:200", "language": "kuery" }, - "filters": "[{\"meta\":{\"index\":\"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b\",\"alias\":null,\"negate\":false,\"type\":\"phrase\",\"key\":\"extension.raw\",\"value\":\"jpg\",\"params\":{\"query\":\"jpg\"},\"disabled\":false},\"query\":{\"match\":{\"extension.raw\":{\"query\":\"jpg\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]" + "filters": [{"meta":{"index":"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b","alias":null,"negate":false,"type":"phrase","key":"extension.raw","value":"jpg","params":{"query":"jpg"},"disabled":false},"query":{"match":{"extension.raw":{"query":"jpg","type":"phrase"}}},"$state":{"store":"appState"}}] }, "type": "query", "updated_at": "2019-07-17T17:54:26.378Z" diff --git a/x-pack/test/functional/es_archives/visualize/default/mappings.json b/x-pack/test/functional/es_archives/visualize/default/mappings.json index 00d11f15db6c3..11e5ac6eeea26 100644 --- a/x-pack/test/functional/es_archives/visualize/default/mappings.json +++ b/x-pack/test/functional/es_archives/visualize/default/mappings.json @@ -465,18 +465,21 @@ "query": { "properties": { "language": { - "type": "text" + "type": "keyword" }, "query": { - "type": "text" + "type": "keyword", + "index": false } } }, "filters": { - "type": "text" + "type": "object", + "enabled": false }, "timefilter": { - "type": "text" + "type": "object", + "enabled": false } } } From 1d0d1f04f971602154fa18c319c66ec5ed209270 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 20 Aug 2019 15:27:00 -0400 Subject: [PATCH 181/189] remove skip firefox tag --- test/functional/apps/discover/_saved_queries.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js index c6af400e145ef..74eb9e10bcee1 100644 --- a/test/functional/apps/discover/_saved_queries.js +++ b/test/functional/apps/discover/_saved_queries.js @@ -50,7 +50,6 @@ export default function ({ getService, getPageObjects }) { }); describe('saved query management component functionality', function () { - this.tags(['skipFirefox']); before(async function () { // set up a query with filters and a time filter log.debug('set up a query with filters to save'); From f4d861c78be4cf6d41e230fbc9c4d91898bd269e Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 20 Aug 2019 16:13:28 -0400 Subject: [PATCH 182/189] Fix tests --- .../search/search_bar/lib/saved_query_service.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts index 55720482a5be7..cbf78aab31d8a 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.test.ts @@ -105,8 +105,8 @@ describe('saved query service', () => { it('should optionally accept filters and timefilters in object format', async () => { const serializedSavedQueryAttributesWithFilters = { ...savedQueryAttributesWithFilters, - filters: JSON.stringify(savedQueryAttributesWithFilters.filters), - timefilter: JSON.stringify(savedQueryAttributesWithFilters.timefilter), + filters: savedQueryAttributesWithFilters.filters, + timefilter: savedQueryAttributesWithFilters.timefilter, }; mockSavedObjectsClient.create.mockReturnValue({ @@ -167,8 +167,8 @@ describe('saved query service', () => { it('should find and return parsed filters and timefilters items', async () => { const serializedSavedQueryAttributesWithFilters = { ...savedQueryAttributesWithFilters, - filters: JSON.stringify(savedQueryAttributesWithFilters.filters), - timefilter: JSON.stringify(savedQueryAttributesWithFilters.timefilter), + filters: savedQueryAttributesWithFilters.filters, + timefilter: savedQueryAttributesWithFilters.timefilter, }; mockSavedObjectsClient.find.mockReturnValue({ savedObjects: [{ id: 'foo', attributes: serializedSavedQueryAttributesWithFilters }], From 458d04338ec8e0828bdf341b6bf69582ff2422ca Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 21 Aug 2019 07:24:41 -0700 Subject: [PATCH 183/189] Changes mappings type --- .../plugin_discovery/plugin_spec/plugin_spec_options.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts index a6afd83e94536..6e087e3ee7f87 100644 --- a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts +++ b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts @@ -26,7 +26,7 @@ export interface UiExports { injectDefaultVars?: (server: Server) => { [key: string]: any }; styleSheetPaths?: string; savedObjectsManagement?: SavedObjectsManagementDefinition; - mappings?: any; // Not ideal + mappings?: unknown; visTypes?: string[]; interpreter?: string[]; hacks?: string[]; From ae57041ffa17505961c9edca54fe71a2083f40f8 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 21 Aug 2019 07:30:32 -0700 Subject: [PATCH 184/189] uses explicity type rather than type-casting --- .../components/saved_query_management/save_query_form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx index 792a55daefefe..2fe43cc2008d5 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx @@ -64,7 +64,7 @@ export const SaveQueryForm: FunctionComponent = ({ }) => { const [title, setTitle] = useState(savedQuery ? savedQuery.title : ''); const [description, setDescription] = useState(savedQuery ? savedQuery.description : ''); - const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); + const [savedQueries, setSavedQueries] = useState([]); const [shouldIncludeFilters, setShouldIncludeFilters] = useState( savedQuery ? !!savedQuery.filters : true ); From a922444418ff4d3d24f7f239f0919798b74c499f Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 21 Aug 2019 07:36:57 -0700 Subject: [PATCH 185/189] Uses explicit SavedQuery type over 'any' --- src/legacy/core_plugins/data/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/legacy/core_plugins/data/index.ts b/src/legacy/core_plugins/data/index.ts index 549649bd2ca4a..a985d3f023108 100644 --- a/src/legacy/core_plugins/data/index.ts +++ b/src/legacy/core_plugins/data/index.ts @@ -20,6 +20,7 @@ import { resolve } from 'path'; import { Legacy } from '../../../../kibana'; import { mappings } from './mappings'; +import { SavedQuery } from './public'; // eslint-disable-next-line import/no-default-export export default function DataPlugin(kibana: any) { @@ -42,10 +43,10 @@ export default function DataPlugin(kibana: any) { icon: 'search', defaultSearchField: 'title', isImportableAndExportable: true, - getTitle(obj: any) { + getTitle(obj: SavedQuery) { return obj.attributes.title; }, - getInAppUrl(obj: any) { + getInAppUrl(obj: SavedQuery) { return { path: `/app/kibana#/discover?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`, uiCapabilitiesPath: 'discover.show', From f143236dc78f624470495e6b50e0e900c349a1b8 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 21 Aug 2019 11:03:54 -0700 Subject: [PATCH 186/189] Adds whitespace validation on a new saved query title --- .../save_query_form.tsx | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx index 2fe43cc2008d5..09ed5c2d45149 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx @@ -95,17 +95,29 @@ export const SaveQueryForm: FunctionComponent = ({ existingSavedQuery => !savedQuery && existingSavedQuery.attributes.title === title ); + const hasWhitespaceError = title.length > title.trim().length; + const titleConflictErrorText = i18n.translate( 'data.search.searchBar.savedQueryForm.titleConflictText', { defaultMessage: 'Title conflicts with an existing saved query', } ); + const whitespaceErrorText = i18n.translate( + 'data.search.searchBar.savedQueryForm.whitespaceErrorText', + { + defaultMessage: 'Title cannot contain leading or trailing white space', + } + ); - const errors = hasTitleConflict ? [titleConflictErrorText] : []; + const errors = hasWhitespaceError + ? [whitespaceErrorText] + : hasTitleConflict + ? [titleConflictErrorText] + : []; const saveQueryForm = ( - + {savedQueryDescriptionText} @@ -116,7 +128,7 @@ export const SaveQueryForm: FunctionComponent = ({ helpText={i18n.translate('data.search.searchBar.savedQueryNameHelpText', { defaultMessage: 'Name must be unique.', })} - isInvalid={hasTitleConflict} + isInvalid={hasWhitespaceError || hasTitleConflict} > = ({ setTitle(event.target.value); }} data-test-subj="saveQueryFormTitle" - isInvalid={hasTitleConflict} + isInvalid={hasWhitespaceError || hasTitleConflict} /> @@ -209,7 +221,7 @@ export const SaveQueryForm: FunctionComponent = ({ } fill data-test-subj="savedQueryFormSaveButton" - disabled={hasTitleConflict} + disabled={hasWhitespaceError || hasTitleConflict} > {i18n.translate('data.search.searchBar.savedQueryFormSaveButtonText', { defaultMessage: 'Save', From daaabb9875314b0d8eaf9157cca7f348ebe3f097 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 21 Aug 2019 11:06:11 -0700 Subject: [PATCH 187/189] Adds whitespace validation hint text --- .../components/saved_query_management/save_query_form.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx index 09ed5c2d45149..9fcd4bf4f1e76 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx @@ -126,7 +126,8 @@ export const SaveQueryForm: FunctionComponent = ({ defaultMessage: 'Name', })} helpText={i18n.translate('data.search.searchBar.savedQueryNameHelpText', { - defaultMessage: 'Name must be unique.', + defaultMessage: + 'Name cannot contain leading or trailing whitespace. Name must be unique.', })} isInvalid={hasWhitespaceError || hasTitleConflict} > From 664804dee04c52dbaaed01f12a9080b8f99966ff Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 21 Aug 2019 11:26:19 -0700 Subject: [PATCH 188/189] Trims new saved query title before save in the saved query service, makes nested ternary more explicit in the SaveQueryForm --- .../save_query_form.tsx | 20 +++++++++---------- .../search_bar/lib/saved_query_service.ts | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx index 9fcd4bf4f1e76..b9066cd8be622 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/saved_query_management/save_query_form.tsx @@ -109,15 +109,15 @@ export const SaveQueryForm: FunctionComponent = ({ defaultMessage: 'Title cannot contain leading or trailing white space', } ); + const hasErrors = hasWhitespaceError || hasTitleConflict; - const errors = hasWhitespaceError - ? [whitespaceErrorText] - : hasTitleConflict - ? [titleConflictErrorText] - : []; - + const errors = () => { + if (hasWhitespaceError) return [whitespaceErrorText]; + if (hasTitleConflict) return [titleConflictErrorText]; + return []; + }; const saveQueryForm = ( - + {savedQueryDescriptionText} @@ -129,7 +129,7 @@ export const SaveQueryForm: FunctionComponent = ({ defaultMessage: 'Name cannot contain leading or trailing whitespace. Name must be unique.', })} - isInvalid={hasWhitespaceError || hasTitleConflict} + isInvalid={hasErrors} > = ({ setTitle(event.target.value); }} data-test-subj="saveQueryFormTitle" - isInvalid={hasWhitespaceError || hasTitleConflict} + isInvalid={hasErrors} /> @@ -222,7 +222,7 @@ export const SaveQueryForm: FunctionComponent = ({ } fill data-test-subj="savedQueryFormSaveButton" - disabled={hasWhitespaceError || hasTitleConflict} + disabled={hasErrors} > {i18n.translate('data.search.searchBar.savedQueryFormSaveButtonText', { defaultMessage: 'Save', diff --git a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts index 050a6ef2cb0a5..9a68b40e5be2e 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts +++ b/src/legacy/core_plugins/data/public/search/search_bar/lib/saved_query_service.ts @@ -53,7 +53,7 @@ export const createSavedQueryService = ( }; const queryObject: SerializedSavedQueryAttributes = { - title: attributes.title, + title: attributes.title.trim(), // trim whitespace before save as an extra precaution against circumventing the front end description: attributes.description, query, }; From ee2711117a7b8a1b78af36b8043a4ffe91b290f7 Mon Sep 17 00:00:00 2001 From: Christiane Heiligers Date: Wed, 21 Aug 2019 11:44:34 -0700 Subject: [PATCH 189/189] Adds general functional test for whitespace name error --- test/functional/apps/discover/_saved_queries.js | 5 ++++- test/functional/services/saved_query_management_component.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js index 74eb9e10bcee1..b744f7d9e224b 100644 --- a/test/functional/apps/discover/_saved_queries.js +++ b/test/functional/apps/discover/_saved_queries.js @@ -112,9 +112,12 @@ export default function ({ getService, getPageObjects }) { }); it('does not allow saving a query with a non-unique name', async () => { - await savedQueryManagementComponent.saveNewQueryWithDuplicateName('OkResponse'); + await savedQueryManagementComponent.saveNewQueryWithNameError('OkResponse'); }); + it('does not allow saving a query with leading or trailing whitespace in the name', async () => { + await savedQueryManagementComponent.saveNewQueryWithNameError('OkResponse '); + }); it('allows clearing the currently loaded saved query', async () => { await savedQueryManagementComponent.loadSavedQuery('OkResponse'); await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); diff --git a/test/functional/services/saved_query_management_component.js b/test/functional/services/saved_query_management_component.js index 8854418acd571..6c8119999eb72 100644 --- a/test/functional/services/saved_query_management_component.js +++ b/test/functional/services/saved_query_management_component.js @@ -32,7 +32,7 @@ export function SavedQueryManagementComponentProvider({ getService }) { await this.submitSaveQueryForm(name, description, includeFilters, includeTimeFilter); } - async saveNewQueryWithDuplicateName(name) { + async saveNewQueryWithNameError(name) { await this.openSavedQueryManagementComponent(); await testSubjects.click('saved-query-management-save-button'); if (name) {