diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/index.ts b/src/legacy/core_plugins/data/public/query/query_bar/components/index.ts index ed4266589478e7..12b1f254895c33 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/index.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/index.ts @@ -18,3 +18,4 @@ */ export { QueryBar } from './query_bar'; +export { QueryBarInput } from './query_bar_input'; 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 573124a6302b1b..c49ce9ec08dbe0 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 { disableAutoFocus?: boolean; appName: string; screenTitle: string; - indexPatterns: IndexPattern[]; + indexPatterns: Array; store: Storage; intl: InjectedIntl; prepend?: any; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts index 7f116837fa5ed2..aba8473088351b 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts @@ -20,6 +20,21 @@ import { createKfetch } from 'ui/kfetch/kfetch'; import { setup } from 'test_utils/http_test_setup'; +const mockIndexPattern = { + id: '1234', + title: 'logstash-*', + fields: [ + { + name: 'response', + type: 'number', + esTypes: ['integer'], + aggregatable: true, + filterable: true, + searchable: true, + }, + ], +}; + const mockChromeFactory = jest.fn(() => { return { getBasePath: () => `foo`, @@ -52,6 +67,10 @@ const mockAutocompleteProvider = jest.fn(() => mockGetAutocompleteSuggestions); export const mockGetAutocompleteProvider = jest.fn(() => mockAutocompleteProvider); const mockKfetch = jest.fn(() => createKfetch(setup().http)); +export const mockFetchIndexPatterns = jest + .fn() + .mockReturnValue(Promise.resolve([mockIndexPattern])); + jest.mock('ui/chrome', () => mockChromeFactory()); jest.mock('ui/kfetch', () => ({ kfetch: () => {}, @@ -72,6 +91,10 @@ jest.mock('ui/kfetch', () => ({ kfetch: mockKfetch, })); +jest.mock('../lib/fetch_index_patterns', () => ({ + fetchIndexPatterns: mockFetchIndexPatterns, +})); + import _ from 'lodash'; // Using doMock to avoid hoisting so that I can override only the debounce method in lodash jest.doMock('lodash', () => ({ diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx index bcd007d4a601e6..458b2e90300408 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx @@ -18,6 +18,7 @@ */ import { + mockFetchIndexPatterns, mockGetAutocompleteProvider, mockGetAutocompleteSuggestions, mockPersistedLog, @@ -131,6 +132,8 @@ describe('QueryBarInput', () => { }); it('Should create a unique PersistedLog based on the appName and query language', () => { + mockPersistedLogFactory.mockClear(); + mountWithIntl( { expect(mockGetAutocompleteProvider).toHaveBeenCalledWith('kuery'); expect(mockGetAutocompleteSuggestions).toHaveBeenCalled(); }); + + it('Should accept index pattern strings and fetch the full object', () => { + mockFetchIndexPatterns.mockClear(); + + mountWithIntl( + + ); + + expect(mockFetchIndexPatterns).toHaveBeenCalledWith(['logstash-*']); + }); }); 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 42bf972889e4d4..575634e193ee29 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 @@ -28,8 +28,8 @@ import { AutocompleteSuggestionType, getAutocompleteProvider, } from 'ui/autocomplete_providers'; -import { debounce, compact } from 'lodash'; -import { IndexPattern } from 'ui/index_patterns'; +import { debounce, compact, isEqual } from 'lodash'; +import { IndexPattern, StaticIndexPattern } from 'ui/index_patterns'; import { PersistedLog } from 'ui/persisted_log'; import chrome from 'ui/chrome'; import { kfetch } from 'ui/kfetch'; @@ -38,6 +38,7 @@ import { fromUser, matchPairs, toUser } from '../lib'; 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'; interface Query { query: string; @@ -45,7 +46,7 @@ interface Query { } interface Props { - indexPatterns: IndexPattern[]; + indexPatterns: Array; intl: InjectedIntl; query: Query; appName: string; @@ -65,6 +66,7 @@ interface State { suggestionLimit: number; selectionStart: number | null; selectionEnd: number | null; + indexPatterns: StaticIndexPattern[]; } const KEY_CODES = { @@ -90,6 +92,7 @@ export class QueryBarInputUI extends Component { suggestionLimit: 50, selectionStart: null, selectionEnd: null, + indexPatterns: [], }; public inputRef: HTMLInputElement | null = null; @@ -101,6 +104,21 @@ export class QueryBarInputUI extends Component { return toUser(this.props.query.query); }; + private fetchIndexPatterns = async () => { + const stringPatterns = this.props.indexPatterns.filter( + indexPattern => typeof indexPattern === 'string' + ) as string[]; + const objectPatterns = this.props.indexPatterns.filter( + indexPattern => typeof indexPattern !== 'string' + ) as IndexPattern[]; + + const objectPatternsFromStrings = await fetchIndexPatterns(stringPatterns); + + this.setState({ + indexPatterns: [...objectPatterns, ...objectPatternsFromStrings], + }); + }; + private getSuggestions = async () => { if (!this.inputRef) { return; @@ -114,13 +132,13 @@ export class QueryBarInputUI extends Component { const autocompleteProvider = getAutocompleteProvider(language); if ( !autocompleteProvider || - !Array.isArray(this.props.indexPatterns) || - compact(this.props.indexPatterns).length === 0 + !Array.isArray(this.state.indexPatterns) || + compact(this.state.indexPatterns).length === 0 ) { return recentSearchSuggestions; } - const indexPatterns = this.props.indexPatterns; + const indexPatterns = this.state.indexPatterns; const getAutocompleteSuggestions = autocompleteProvider({ config, indexPatterns }); const { selectionStart, selectionEnd } = this.inputRef; @@ -368,14 +386,20 @@ export class QueryBarInputUI extends Component { this.persistedLog = this.props.persistedLog ? this.props.persistedLog : getQueryLog(this.props.appName, this.props.query.language); - this.updateSuggestions(); + + this.fetchIndexPatterns().then(this.updateSuggestions); } public componentDidUpdate(prevProps: Props) { this.persistedLog = this.props.persistedLog ? this.props.persistedLog : getQueryLog(this.props.appName, this.props.query.language); - this.updateSuggestions(); + + if (!isEqual(prevProps.indexPatterns, this.props.indexPatterns)) { + this.fetchIndexPatterns().then(this.updateSuggestions); + } else { + this.updateSuggestions(); + } if (this.state.selectionStart !== null && this.state.selectionEnd !== null) { if (this.inputRef) { diff --git a/src/legacy/core_plugins/data/public/query/query_bar/index.ts b/src/legacy/core_plugins/data/public/query/query_bar/index.ts index 71668581b0f88a..bbf14ce367c097 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/index.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/index.ts @@ -17,7 +17,7 @@ * under the License. */ -export { QueryBar } from './components'; +export { QueryBar, QueryBarInput } from './components'; export { fromUser } from './lib/from_user'; export { toUser } from './lib/to_user'; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/fetch_index_patterns.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/fetch_index_patterns.ts new file mode 100644 index 00000000000000..4ee6d9fc3cbc93 --- /dev/null +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/fetch_index_patterns.ts @@ -0,0 +1,54 @@ +/* + * 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 { getFromSavedObject } from 'ui/index_patterns/static_utils'; + +const config = chrome.getUiSettingsClient(); + +export async function fetchIndexPatterns(indexPatternStrings: string[]) { + const quotedIndexPatternStrings = indexPatternStrings.map( + indexPatternString => `"${indexPatternString}"` + ); + const searchString = quotedIndexPatternStrings.join(' | '); + const indexPatternsFromSavedObjects = await chrome.getSavedObjectsClient().find({ + type: 'index-pattern', + fields: ['title', 'fields'], + search: `"${searchString}"`, + searchFields: ['title'], + }); + + const exactMatches = indexPatternsFromSavedObjects.savedObjects.filter(savedObject => { + return indexPatternStrings.includes(savedObject.attributes.title as string); + }); + + const allMatches = + exactMatches.length === indexPatternStrings.length + ? exactMatches + : [...exactMatches, await fetchDefaultIndexPattern()]; + + return allMatches.map(getFromSavedObject); +} + +const fetchDefaultIndexPattern = async () => { + const savedObjectsClient = chrome.getSavedObjectsClient(); + const indexPattern = await savedObjectsClient.get('index-pattern', config.get('defaultIndex')); + + return getFromSavedObject(indexPattern); +}; diff --git a/src/legacy/core_plugins/data/public/query/query_service.ts b/src/legacy/core_plugins/data/public/query/query_service.ts index 28772f91dd39ea..f1d32586e14f2e 100644 --- a/src/legacy/core_plugins/data/public/query/query_service.ts +++ b/src/legacy/core_plugins/data/public/query/query_service.ts @@ -18,7 +18,13 @@ */ import { once } from 'lodash'; -import { QueryBar, fromUser, toUser, setupDirective as setupQueryBarDirective } from './query_bar'; +import { + QueryBar, + QueryBarInput, + fromUser, + toUser, + setupDirective as setupQueryBarDirective, +} from './query_bar'; /** * Query Service @@ -35,6 +41,7 @@ export class QueryService { }, ui: { QueryBar, + QueryBarInput, }, }; }