[7.x] Update QueryBarInput to accept index pattern strings (#36916) (#37114)

backport
This commit is contained in:
Matt Bargar 2019-05-29 11:27:10 -05:00 committed by GitHub
parent 99c174111a
commit e884ebcbcb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 11 deletions

View file

@ -18,3 +18,4 @@
*/
export { QueryBar } from './query_bar';
export { QueryBarInput } from './query_bar_input';

View file

@ -59,7 +59,7 @@ interface Props {
disableAutoFocus?: boolean;
appName: string;
screenTitle: string;
indexPatterns: IndexPattern[];
indexPatterns: Array<IndexPattern | string>;
store: Storage;
intl: InjectedIntl;
prepend?: any;

View file

@ -20,6 +20,21 @@
import { createKfetch } from 'ui/kfetch/kfetch';
import { setup } from 'test_utils/kfetch_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', () => ({

View file

@ -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(
<QueryBarInput.WrappedComponent
query={kqlQuery}
@ -240,4 +243,23 @@ describe('QueryBarInput', () => {
expect(mockGetAutocompleteProvider).toHaveBeenCalledWith('kuery');
expect(mockGetAutocompleteSuggestions).toHaveBeenCalled();
});
it('Should accept index pattern strings and fetch the full object', () => {
mockFetchIndexPatterns.mockClear();
mountWithIntl(
<QueryBarInput.WrappedComponent
query={kqlQuery}
onSubmit={noop}
appName={'discover'}
screenTitle={'Another Screen'}
indexPatterns={['logstash-*']}
store={createMockStorage()}
disableAutoFocus={true}
intl={null as any}
/>
);
expect(mockFetchIndexPatterns).toHaveBeenCalledWith(['logstash-*']);
});
});

View file

@ -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<IndexPattern | string>;
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<Props, State> {
suggestionLimit: 50,
selectionStart: null,
selectionEnd: null,
indexPatterns: [],
};
public inputRef: HTMLInputElement | null = null;
@ -101,6 +104,21 @@ export class QueryBarInputUI extends Component<Props, State> {
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<Props, State> {
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<Props, State> {
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) {

View file

@ -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';

View file

@ -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);
};

View file

@ -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,
},
};
}