From 056413e8b4d6d0fac83d23e3aae39de133056cad Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 2 Nov 2022 09:58:29 +0000 Subject: [PATCH] [Logs UI] Support the Unified Search Bar for Query input (#143222) * Use Unified Search Bar for query input --- .../public/__stories__/search_bar.stories.tsx | 9 + .../public/search_bar/create_search_bar.tsx | 1 + .../public/search_bar/search_bar.test.tsx | 15 ++ .../public/search_bar/search_bar.tsx | 6 +- .../log_views/resolved_log_view.mock.ts | 12 ++ .../common/log_views/resolved_log_view.ts | 32 ++-- .../components/log_stream/log_stream.tsx | 2 +- .../containers/logs/log_filter/errors.ts | 21 +++ .../logs/log_filter/log_filter_state.ts | 170 +++++++++++------- .../log_filter/with_log_filter_url_state.tsx | 19 +- .../infra/public/hooks/use_log_view.mock.ts | 5 +- .../infra/public/hooks/use_log_view.ts | 19 +- .../pages/logs/stream/page_logs_content.tsx | 12 +- .../pages/logs/stream/page_providers.tsx | 2 +- .../public/pages/logs/stream/page_toolbar.tsx | 51 ++---- .../services/log_views/log_views_client.ts | 9 +- .../infra/public/services/log_views/types.ts | 2 +- .../plugins/infra/public/utils/url_state.tsx | 2 +- .../log_views/log_views_client.test.ts | 60 ++++++- .../services/log_views/log_views_client.ts | 9 +- .../infra/server/services/log_views/types.ts | 2 +- 21 files changed, 309 insertions(+), 151 deletions(-) create mode 100644 x-pack/plugins/infra/public/containers/logs/log_filter/errors.ts diff --git a/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx b/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx index 60e0659a6112a..51cac48fbbbbc 100644 --- a/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx +++ b/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx @@ -298,6 +298,15 @@ storiesOf('SearchBar', module) query: { query: 'Test: miaou', language: 'kuery' }, } as unknown as SearchBarProps) ) + .add('with query menu off', () => + wrapSearchBarInContext({ + showDatePicker: false, + showFilterBar: false, + showQueryInput: true, + showQueryMenu: false, + query: { query: 'Test: miaou', language: 'kuery' }, + } as unknown as SearchBarProps) + ) .add('with only the filter bar and the date picker on', () => wrapSearchBarInContext({ showDatePicker: true, diff --git a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx index 35555137f12b2..0320626bb0244 100644 --- a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx @@ -190,6 +190,7 @@ export function createSearchBar({ showAutoRefreshOnly={props.showAutoRefreshOnly} showDatePicker={props.showDatePicker} showFilterBar={props.showFilterBar} + showQueryMenu={props.showQueryMenu} showQueryInput={props.showQueryInput} showSaveQuery={props.showSaveQuery} showSubmitButton={props.showSubmitButton} diff --git a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx index 0d0ff97ae4b05..0d17014e5203d 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx @@ -138,6 +138,7 @@ describe('SearchBar', () => { const FILTER_BAR = '[data-test-subj="unifiedFilterBar"]'; const QUERY_BAR = '.kbnQueryBar'; const QUERY_INPUT = '[data-test-subj="unifiedQueryInput"]'; + const QUERY_MENU_BUTTON = '[data-test-subj="showQueryBarMenu"]'; const EDITOR = '[data-test-subj="unifiedTextLangEditor"]'; beforeEach(() => { @@ -220,6 +221,20 @@ describe('SearchBar', () => { expect(component.find(QUERY_INPUT).length).toBeFalsy(); }); + it('Should NOT render the query menu button, if disabled', () => { + const component = mount( + wrapSearchBarInContext({ + indexPatterns: [mockIndexPattern], + screenTitle: 'test screen', + onQuerySubmit: noop, + query: kqlQuery, + showQueryMenu: false, + }) + ); + + expect(component.find(QUERY_MENU_BUTTON).length).toBeFalsy(); + }); + it('Should render query bar and filter bar', () => { const component = mount( wrapSearchBarInContext({ diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index ebaa3a317c270..3b158fd20b9f3 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -48,6 +48,7 @@ export interface SearchBarOwnProps { screenTitle?: string; dataTestSubj?: string; // Togglers + showQueryMenu?: boolean; showQueryInput?: boolean; showFilterBar?: boolean; showDatePicker?: boolean; @@ -121,6 +122,7 @@ class SearchBarUI extends C State > { public static defaultProps = { + showQueryMenu: true, showFilterBar: true, showDatePicker: true, showSubmitButton: true, @@ -448,7 +450,7 @@ class SearchBarUI extends C /> ); - const queryBarMenu = ( + const queryBarMenu = this.props.showQueryMenu ? ( extends C : undefined } /> - ); + ) : undefined; let filterBar; if (this.shouldRenderFilterBar()) { diff --git a/x-pack/plugins/infra/common/log_views/resolved_log_view.mock.ts b/x-pack/plugins/infra/common/log_views/resolved_log_view.mock.ts index 268b2f692f62a..8c09f16e3b53e 100644 --- a/x-pack/plugins/infra/common/log_views/resolved_log_view.mock.ts +++ b/x-pack/plugins/infra/common/log_views/resolved_log_view.mock.ts @@ -10,6 +10,7 @@ import { createStubDataView } from '@kbn/data-views-plugin/common/stubs'; import { defaultLogViewsStaticConfig } from './defaults'; import { ResolvedLogView, resolveLogView } from './resolved_log_view'; import { LogViewAttributes } from './types'; +import { DataViewSpec } from '@kbn/data-views-plugin/common'; export const createResolvedLogViewMock = ( resolvedLogViewOverrides: Partial = {} @@ -41,15 +42,26 @@ export const createResolvedLogViewMock = ( messageColumn: { id: 'MESSAGE_COLUMN_ID' }, }, ], + dataViewReference: createStubDataView({ + spec: { + id: 'log-view-data-view-mock', + title: 'log-indices-*', + }, + }), ...resolvedLogViewOverrides, }); export const createResolvedLogViewMockFromAttributes = (logViewAttributes: LogViewAttributes) => resolveLogView( + 'log-view-id', logViewAttributes, { get: async () => createStubDataView({ spec: {} }), getFieldsForWildcard: async () => [], + create: async (spec: DataViewSpec) => + createStubDataView({ + spec, + }), } as unknown as DataViewsContract, defaultLogViewsStaticConfig ); diff --git a/x-pack/plugins/infra/common/log_views/resolved_log_view.ts b/x-pack/plugins/infra/common/log_views/resolved_log_view.ts index d7e155172a57e..2fc2fd7aa2374 100644 --- a/x-pack/plugins/infra/common/log_views/resolved_log_view.ts +++ b/x-pack/plugins/infra/common/log_views/resolved_log_view.ts @@ -23,21 +23,24 @@ export interface ResolvedLogView { fields: ResolvedLogViewField[]; runtimeMappings: estypes.MappingRuntimeFields; columns: LogViewColumnConfiguration[]; + dataViewReference: DataView; } export const resolveLogView = async ( + logViewId: string, logViewAttributes: LogViewAttributes, dataViewsService: DataViewsContract, config: LogViewsStaticConfig ): Promise => { if (logViewAttributes.logIndices.type === 'index_name') { - return await resolveLegacyReference(logViewAttributes, dataViewsService, config); + return await resolveLegacyReference(logViewId, logViewAttributes, dataViewsService, config); } else { return await resolveDataViewReference(logViewAttributes, dataViewsService); } }; const resolveLegacyReference = async ( + logViewId: string, logViewAttributes: LogViewAttributes, dataViewsService: DataViewsContract, config: LogViewsStaticConfig @@ -48,28 +51,32 @@ const resolveLegacyReference = async ( const indices = logViewAttributes.logIndices.indexName; - const fields = await dataViewsService - .getFieldsForWildcard({ - pattern: indices, - allowNoIndex: true, - }) + const dataViewReference = await dataViewsService + .create( + { + id: `log-view-${logViewId}`, + title: indices, + timeFieldName: TIMESTAMP_FIELD, + allowNoIndex: true, + }, + false, + false + ) .catch((error) => { - throw new ResolveLogViewError( - `Failed to fetch fields for indices "${indices}": ${error}`, - error - ); + throw new ResolveLogViewError(`Failed to create Data View reference: ${error}`, error); }); return { - indices: logViewAttributes.logIndices.indexName, + indices, timestampField: TIMESTAMP_FIELD, tiebreakerField: TIEBREAKER_FIELD, messageField: config.messageFields, - fields, + fields: dataViewReference.fields, runtimeMappings: {}, columns: logViewAttributes.logColumns, name: logViewAttributes.name, description: logViewAttributes.description, + dataViewReference, }; }; @@ -97,6 +104,7 @@ const resolveDataViewReference = async ( columns: logViewAttributes.logColumns, name: logViewAttributes.name, description: logViewAttributes.description, + dataViewReference: dataView, }; }; diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx b/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx index 2913b2cbb5ecf..e5aeefba983f1 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx @@ -7,11 +7,11 @@ import { buildEsQuery, Filter, Query } from '@kbn/es-query'; import { JsonValue } from '@kbn/utility-types'; -import { noop } from 'lodash'; import React, { useCallback, useEffect, useMemo } from 'react'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { noop } from 'lodash'; import { LogEntryCursor } from '../../../common/log_entry'; import { defaultLogViewsStaticConfig } from '../../../common/log_views'; import { BuiltEsQuery, useLogStream } from '../../containers/logs/log_stream'; diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/errors.ts b/x-pack/plugins/infra/public/containers/logs/log_filter/errors.ts new file mode 100644 index 0000000000000..09e3af3e241a4 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/logs/log_filter/errors.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable max-classes-per-file */ +export class UnsupportedLanguageError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class QueryParsingError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts b/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts index 70d72c25158da..4a2f6e280ebe6 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts @@ -5,12 +5,16 @@ * 2.0. */ -import { buildEsQuery, DataViewBase, Query } from '@kbn/es-query'; +import { useMemo, useEffect, useCallback, useState } from 'react'; +import { merge, of } from 'rxjs'; +import { i18n } from '@kbn/i18n'; +import { buildEsQuery, DataViewBase, Query, AggregateQuery, isOfQueryType } from '@kbn/es-query'; import createContainer from 'constate'; -import { useCallback, useState } from 'react'; -import useDebounce from 'react-use/lib/useDebounce'; import { useKibanaQuerySettings } from '../../../utils/use_kibana_query_settings'; import { BuiltEsQuery } from '../log_stream'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; +import { useSubscription } from '../../../utils/use_observable'; +import { UnsupportedLanguageError, QueryParsingError } from './errors'; interface ILogFilterState { filterQuery: { @@ -18,90 +22,126 @@ interface ILogFilterState { serializedQuery: string; originalQuery: Query; } | null; - filterQueryDraft: Query; - validationErrors: string[]; + queryStringQuery: Query | AggregateQuery | null; + validationError: Error | null; } -const initialLogFilterState: ILogFilterState = { +export const DEFAULT_QUERY = { + language: 'kuery', + query: '', +}; + +const INITIAL_LOG_FILTER_STATE = { filterQuery: null, - filterQueryDraft: { - language: 'kuery', - query: '', - }, - validationErrors: [], + queryStringQuery: null, + validationError: null, }; -const validationDebounceTimeout = 1000; // milliseconds +// Error toasts +export const errorToastTitle = i18n.translate( + 'xpack.infra.logsPage.toolbar.logFilterErrorToastTitle', + { + defaultMessage: 'Log filter error', + } +); + +const unsupportedLanguageError = i18n.translate( + 'xpack.infra.logsPage.toolbar.logFilterUnsupportedLanguageError', + { + defaultMessage: 'SQL is not supported', + } +); + +export const useLogFilterState = ({ dataView }: { dataView?: DataViewBase }) => { + const { + notifications: { toasts }, + data: { + query: { queryString }, + }, + } = useKibanaContextForPlugin().services; -export const useLogFilterState = ({ indexPattern }: { indexPattern: DataViewBase }) => { - const [logFilterState, setLogFilterState] = useState(initialLogFilterState); const kibanaQuerySettings = useKibanaQuerySettings(); + const [logFilterState, setLogFilterState] = useState(INITIAL_LOG_FILTER_STATE); + + useEffect(() => { + const handleValidationError = (error: Error) => { + if (error instanceof UnsupportedLanguageError) { + toasts.addError(error, { title: errorToastTitle }); + queryString.setQuery(DEFAULT_QUERY); + } else if (error instanceof QueryParsingError) { + toasts.addError(error, { title: errorToastTitle }); + } + }; + + if (logFilterState.validationError) { + handleValidationError(logFilterState.validationError); + } + }, [logFilterState.validationError, queryString, toasts]); + const parseQuery = useCallback( - (filterQuery: Query) => buildEsQuery(indexPattern, filterQuery, [], kibanaQuerySettings), - [indexPattern, kibanaQuerySettings] + (filterQuery: Query) => { + return buildEsQuery(dataView, filterQuery, [], kibanaQuerySettings); + }, + [dataView, kibanaQuerySettings] ); - const setLogFilterQueryDraft = useCallback((filterQueryDraft: Query) => { - setLogFilterState((previousLogFilterState) => ({ - ...previousLogFilterState, - filterQueryDraft, - validationErrors: [], - })); - }, []); - - const [, cancelPendingValidation] = useDebounce( - () => { - setLogFilterState((previousLogFilterState) => { + const getNewLogFilterState = useCallback( + (newQuery: Query | AggregateQuery) => + (previousLogFilterState: ILogFilterState): ILogFilterState => { try { - parseQuery(logFilterState.filterQueryDraft); - return { - ...previousLogFilterState, - validationErrors: [], - }; + if (!isOfQueryType(newQuery)) { + throw new UnsupportedLanguageError(unsupportedLanguageError); + } + try { + const parsedQuery = parseQuery(newQuery); + return { + filterQuery: { + parsedQuery, + serializedQuery: JSON.stringify(parsedQuery), + originalQuery: newQuery, + }, + queryStringQuery: newQuery, + validationError: null, + }; + } catch (error) { + throw new QueryParsingError(error); + } } catch (error) { return { ...previousLogFilterState, - validationErrors: [`${error}`], + queryStringQuery: newQuery, + validationError: error, }; } - }); - }, - validationDebounceTimeout, - [logFilterState.filterQueryDraft, parseQuery] + }, + [parseQuery] ); - const applyLogFilterQuery = useCallback( - (filterQuery: Query) => { - cancelPendingValidation(); - try { - const parsedQuery = parseQuery(filterQuery); - setLogFilterState((previousLogFilterState) => ({ - ...previousLogFilterState, - filterQuery: { - parsedQuery, - serializedQuery: JSON.stringify(parsedQuery), - originalQuery: filterQuery, - }, - filterQueryDraft: filterQuery, - validationErrors: [], - })); - } catch (error) { - setLogFilterState((previousLogFilterState) => ({ - ...previousLogFilterState, - validationErrors: [`${error}`], - })); - } - }, - [cancelPendingValidation, parseQuery] + useSubscription( + useMemo(() => { + return merge(of(undefined), queryString.getUpdates$()); // NOTE: getUpdates$ uses skip(1) so we do this to ensure an initial emit of a value. + }, [queryString]), + useMemo(() => { + return { + next: () => { + setLogFilterState(getNewLogFilterState(queryString.getQuery())); + }, + }; + }, [getNewLogFilterState, queryString]) ); + // NOTE: If the dataView changes the query will need to be reparsed and the filter regenerated. + useEffect(() => { + if (dataView) { + setLogFilterState(getNewLogFilterState(queryString.getQuery())); + } + }, [dataView, getNewLogFilterState, queryString]); + return { - filterQuery: logFilterState.filterQuery, - filterQueryDraft: logFilterState.filterQueryDraft, - isFilterQueryDraftValid: logFilterState.validationErrors.length === 0, - setLogFilterQueryDraft, - applyLogFilterQuery, + queryStringQuery: logFilterState.queryStringQuery, // NOTE: Query String Manager query. + filterQuery: logFilterState.filterQuery, // NOTE: Valid and syntactically correct query applied to requests etc. + validationError: logFilterState.validationError, }; }; diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx index 2a5970721f5e5..a6f6166b7cf06 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx @@ -9,24 +9,33 @@ import * as rt from 'io-ts'; import React from 'react'; import { Query } from '@kbn/es-query'; import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; -import { useLogFilterStateContext } from './log_filter_state'; +import { useLogFilterStateContext, DEFAULT_QUERY } from './log_filter_state'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export const WithLogFilterUrlState: React.FC = () => { - const { filterQuery, applyLogFilterQuery } = useLogFilterStateContext(); + const { + data: { + query: { queryString }, + }, + } = useKibanaContextForPlugin().services; + + const { queryStringQuery } = useLogFilterStateContext(); return ( { if (urlState) { - applyLogFilterQuery(urlState); + queryString.setQuery(urlState); } }} onInitialize={(urlState) => { if (urlState) { - applyLogFilterQuery(urlState); + queryString.setQuery(urlState); + } else { + queryString.setQuery(DEFAULT_QUERY); } }} /> diff --git a/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts b/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts index daebfb82b4564..8bef101abe666 100644 --- a/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts +++ b/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts @@ -17,10 +17,7 @@ const defaultLogViewId = 'default'; export const createUninitializedUseLogViewMock = (logViewId: string = defaultLogViewId) => (): IUseLogView => ({ - derivedDataView: { - fields: [], - title: 'unknown', - }, + derivedDataView: undefined, hasFailedLoading: false, hasFailedLoadingLogView: false, hasFailedLoadingLogViewStatus: false, diff --git a/x-pack/plugins/infra/public/hooks/use_log_view.ts b/x-pack/plugins/infra/public/hooks/use_log_view.ts index b1b8bb6e33cc9..1781bc7346a2f 100644 --- a/x-pack/plugins/infra/public/hooks/use_log_view.ts +++ b/x-pack/plugins/infra/public/hooks/use_log_view.ts @@ -6,7 +6,7 @@ */ import createContainer from 'constate'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import type { HttpHandler } from '@kbn/core/public'; import { LogView, LogViewAttributes, LogViewStatus, ResolvedLogView } from '../../common/log_views'; import type { ILogViewsClient } from '../services/log_views'; @@ -63,14 +63,6 @@ export const useLogView = ({ [logViews] ); - const derivedDataView = useMemo( - () => ({ - fields: resolvedLogView?.fields ?? [], - title: resolvedLogView?.indices ?? 'unknown', - }), - [resolvedLogView] - ); - const isLoadingLogView = loadLogViewRequest.state === 'pending'; const isResolvingLogView = resolveLogViewRequest.state === 'pending'; const isLoadingLogViewStatus = loadLogViewStatusRequest.state === 'pending'; @@ -97,7 +89,7 @@ export const useLogView = ({ const load = useCallback(async () => { const loadedLogView = await loadLogView(logViewId); - const resolvedLoadedLogView = await resolveLogView(loadedLogView.attributes); + const resolvedLoadedLogView = await resolveLogView(loadedLogView.id, loadedLogView.attributes); const resolvedLogViewStatus = await loadLogViewStatus(resolvedLoadedLogView); return [loadedLogView, resolvedLoadedLogView, resolvedLogViewStatus]; @@ -106,7 +98,10 @@ export const useLogView = ({ const update = useCallback( async (logViewAttributes: Partial) => { const updatedLogView = await updateLogView(logViewId, logViewAttributes); - const resolvedUpdatedLogView = await resolveLogView(updatedLogView.attributes); + const resolvedUpdatedLogView = await resolveLogView( + updatedLogView.id, + updatedLogView.attributes + ); const resolvedLogViewStatus = await loadLogViewStatus(resolvedUpdatedLogView); return [updatedLogView, resolvedUpdatedLogView, resolvedLogViewStatus]; @@ -121,7 +116,7 @@ export const useLogView = ({ return { logViewId, isUninitialized, - derivedDataView, + derivedDataView: resolvedLogView?.dataViewReference, // Failure states hasFailedLoading, diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx index 18ac30bb35e14..3b5956240d0e3 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx @@ -34,10 +34,16 @@ import { useLogViewContext } from '../../../hooks/use_log_view'; import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath'; import { LogsToolbar } from './page_toolbar'; import { PageViewLogInContext } from './page_view_log_in_context'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; const PAGE_THRESHOLD = 2; export const LogsPageLogsContent: React.FunctionComponent = () => { + const { + data: { + query: { queryString }, + }, + } = useKibanaContextForPlugin().services; const { resolvedLogView, logView, logViewId } = useLogViewContext(); const { textScale, textWrap } = useLogViewConfigurationContext(); const { @@ -65,7 +71,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { updateDateRange, lastCompleteDateRangeExpressionUpdate, } = useLogPositionStateContext(); - const { filterQuery, applyLogFilterQuery } = useLogFilterStateContext(); + const { filterQuery } = useLogFilterStateContext(); const { isReloading, @@ -193,14 +199,14 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { const setFilter = useCallback( (filter: Query, flyoutItemId: string, timeKey: TimeKey | undefined | null) => { - applyLogFilterQuery(filter); + queryString.setQuery(filter); if (timeKey) { jumpToTargetPosition(timeKey); } setSurroundingLogsId(flyoutItemId); stopLiveStreaming(); }, - [applyLogFilterQuery, jumpToTargetPosition, setSurroundingLogsId, stopLiveStreaming] + [jumpToTargetPosition, queryString, setSurroundingLogsId, stopLiveStreaming] ); return ( diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index ea2af542586a2..026119ff5c74c 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -27,7 +27,7 @@ const LogFilterState: React.FC = ({ children }) => { const { derivedDataView } = useLogViewContext(); return ( - + {children} diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 210def8f8844c..cf30518f78ede 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -6,11 +6,8 @@ */ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { QueryStringInput } from '@kbn/unified-search-plugin/public'; -import { DataView } from '@kbn/data-views-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { LogCustomizationMenu } from '../../../components/logging/log_customization_menu'; @@ -18,8 +15,6 @@ import { LogDatepicker } from '../../../components/logging/log_datepicker'; import { LogHighlightsMenu } from '../../../components/logging/log_highlights_menu'; import { LogTextScaleControls } from '../../../components/logging/log_text_scale_controls'; import { LogTextWrapControls } from '../../../components/logging/log_text_wrap_controls'; -import { useLogFilterStateContext } from '../../../containers/logs/log_filter'; -import { useLogEntryFlyoutContext } from '../../../containers/logs/log_flyout'; import { useLogHighlightsStateContext } from '../../../containers/logs/log_highlights/log_highlights'; import { useLogPositionStateContext } from '../../../containers/logs/log_position'; import { useLogViewConfigurationContext } from '../../../containers/logs/log_view_configuration'; @@ -29,11 +24,11 @@ export const LogsToolbar = () => { const { derivedDataView } = useLogViewContext(); const { availableTextScales, setTextScale, setTextWrap, textScale, textWrap } = useLogViewConfigurationContext(); - const { filterQueryDraft, isFilterQueryDraftValid, applyLogFilterQuery, setLogFilterQueryDraft } = - useLogFilterStateContext(); - const { setSurroundingLogsId } = useLogEntryFlyoutContext(); - const { http, notifications, docLinks, uiSettings, data, dataViews, storage, unifiedSearch } = - useKibanaContextForPlugin().services; + const { + unifiedSearch: { + ui: { SearchBar }, + }, + } = useKibanaContextForPlugin().services; const { setHighlightTerms, @@ -57,36 +52,20 @@ export const LogsToolbar = () => {
- { - setSurroundingLogsId(null); - setLogFilterQueryDraft(query); - }} - onSubmit={(query: Query) => { - setSurroundingLogsId(null); - applyLogFilterQuery(query); - }} placeholder={i18n.translate('xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder', { defaultMessage: 'Search for log entries… (e.g. host.name:host-1)', })} - query={filterQueryDraft} - appName={i18n.translate('xpack.infra.appName', { - defaultMessage: 'Infra logs', - })} - deps={{ - unifiedSearch, - notifications, - http, - docLinks, - uiSettings, - data, - dataViews, - storage, - }} + useDefaultBehaviors={true} + indexPatterns={derivedDataView ? [derivedDataView] : undefined} + showQueryInput={true} + showQueryMenu={false} + showFilterBar={false} + showDatePicker={false} /> diff --git a/x-pack/plugins/infra/public/services/log_views/log_views_client.ts b/x-pack/plugins/infra/public/services/log_views/log_views_client.ts index 0de44f87d719e..65bb031d3407b 100644 --- a/x-pack/plugins/infra/public/services/log_views/log_views_client.ts +++ b/x-pack/plugins/infra/public/services/log_views/log_views_client.ts @@ -53,7 +53,7 @@ export class LogViewsClient implements ILogViewsClient { public async getResolvedLogView(logViewId: string): Promise { const logView = await this.getLogView(logViewId); - const resolvedLogView = await this.resolveLogView(logView.attributes); + const resolvedLogView = await this.resolveLogView(logView.id, logView.attributes); return resolvedLogView; } @@ -118,8 +118,11 @@ export class LogViewsClient implements ILogViewsClient { return data; } - public async resolveLogView(logViewAttributes: LogViewAttributes): Promise { - return await resolveLogView(logViewAttributes, this.dataViews, this.config); + public async resolveLogView( + logViewId: string, + logViewAttributes: LogViewAttributes + ): Promise { + return await resolveLogView(logViewId, logViewAttributes, this.dataViews, this.config); } } diff --git a/x-pack/plugins/infra/public/services/log_views/types.ts b/x-pack/plugins/infra/public/services/log_views/types.ts index 20e176db4cab4..9054ef79b4a4c 100644 --- a/x-pack/plugins/infra/public/services/log_views/types.ts +++ b/x-pack/plugins/infra/public/services/log_views/types.ts @@ -32,5 +32,5 @@ export interface ILogViewsClient { getResolvedLogViewStatus(resolvedLogView: ResolvedLogView): Promise; getResolvedLogView(logViewId: string): Promise; putLogView(logViewId: string, logViewAttributes: Partial): Promise; - resolveLogView(logViewAttributes: LogViewAttributes): Promise; + resolveLogView(logViewId: string, logViewAttributes: LogViewAttributes): Promise; } diff --git a/x-pack/plugins/infra/public/utils/url_state.tsx b/x-pack/plugins/infra/public/utils/url_state.tsx index 77f04292cbde2..c45b47074f8ca 100644 --- a/x-pack/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/plugins/infra/public/utils/url_state.tsx @@ -7,11 +7,11 @@ import { parse, stringify } from 'query-string'; import { History, Location } from 'history'; -import { throttle } from 'lodash'; import React from 'react'; import { Route, RouteProps } from 'react-router-dom'; import { decode, encode, RisonValue } from 'rison-node'; import { url } from '@kbn/kibana-utils-plugin/public'; +import { throttle } from 'lodash'; interface UrlStateContainerProps { urlState: UrlState | undefined; diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_client.test.ts b/x-pack/plugins/infra/server/services/log_views/log_views_client.test.ts index 7b1debf981f8c..e517ae8aef7f0 100644 --- a/x-pack/plugins/infra/server/services/log_views/log_views_client.test.ts +++ b/x-pack/plugins/infra/server/services/log_views/log_views_client.test.ts @@ -239,7 +239,7 @@ describe('LogViewsClient class', () => { }) ); - const resolvedLogView = await logViewsClient.resolveLogView({ + const resolvedLogView = await logViewsClient.resolveLogView('log-view-id', { name: 'LOG VIEW', description: 'LOG VIEW DESCRIPTION', logIndices: { @@ -280,6 +280,64 @@ describe('LogViewsClient class', () => { }, }, ], + "dataViewReference": DataView { + "allowNoIndex": false, + "deleteFieldFormat": [Function], + "fieldAttrs": Object {}, + "fieldFormatMap": Object {}, + "fieldFormats": Object { + "deserialize": [MockFunction], + "getByFieldType": [MockFunction], + "getDefaultConfig": [MockFunction], + "getDefaultInstance": [MockFunction], + "getDefaultInstanceCacheResolver": [MockFunction], + "getDefaultInstancePlain": [MockFunction], + "getDefaultType": [MockFunction], + "getDefaultTypeName": [MockFunction], + "getInstance": [MockFunction], + "getType": [MockFunction], + "getTypeNameByEsTypes": [MockFunction], + "getTypeWithoutMetaParams": [MockFunction], + "has": [MockFunction], + "init": [MockFunction], + "parseDefaultTypeMap": [MockFunction], + "register": [MockFunction], + }, + "fields": FldList [], + "flattenHit": [Function], + "getFieldAttrs": [Function], + "getIndexPattern": [Function], + "getName": [Function], + "getOriginalSavedObjectBody": [Function], + "id": "LOG_DATA_VIEW", + "matchedIndices": Array [], + "metaFields": Array [ + "_id", + "_type", + "_source", + ], + "name": "", + "namespaces": Array [], + "originalSavedObjectBody": Object {}, + "resetOriginalSavedObjectBody": [Function], + "runtimeFieldMap": Object { + "runtime_field": Object { + "script": Object { + "source": "emit(\\"runtime value\\")", + }, + "type": "keyword", + }, + }, + "setFieldFormat": [Function], + "setIndexPattern": [Function], + "shortDotsEnable": false, + "sourceFilters": Array [], + "timeFieldName": "@timestamp", + "title": "log-indices-*", + "type": undefined, + "typeMeta": undefined, + "version": "1", + }, "description": "LOG VIEW DESCRIPTION", "fields": FldList [], "indices": "log-indices-*", diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_client.ts b/x-pack/plugins/infra/server/services/log_views/log_views_client.ts index 57c90235452ba..9f43cee871f73 100644 --- a/x-pack/plugins/infra/server/services/log_views/log_views_client.ts +++ b/x-pack/plugins/infra/server/services/log_views/log_views_client.ts @@ -67,7 +67,7 @@ export class LogViewsClient implements ILogViewsClient { public async getResolvedLogView(logViewId: string): Promise { const logView = await this.getLogView(logViewId); - const resolvedLogView = await this.resolveLogView(logView.attributes); + const resolvedLogView = await this.resolveLogView(logView.id, logView.attributes); return resolvedLogView; } @@ -98,8 +98,11 @@ export class LogViewsClient implements ILogViewsClient { return getLogViewFromSavedObject(savedObject); } - public async resolveLogView(logViewAttributes: LogViewAttributes): Promise { - return await resolveLogView(logViewAttributes, await this.dataViews, this.config); + public async resolveLogView( + logViewId: string, + logViewAttributes: LogViewAttributes + ): Promise { + return await resolveLogView(logViewId, logViewAttributes, await this.dataViews, this.config); } private async getSavedLogView(logViewId: string): Promise { diff --git a/x-pack/plugins/infra/server/services/log_views/types.ts b/x-pack/plugins/infra/server/services/log_views/types.ts index 4146b6e2fd7e3..c10bd2002163d 100644 --- a/x-pack/plugins/infra/server/services/log_views/types.ts +++ b/x-pack/plugins/infra/server/services/log_views/types.ts @@ -46,5 +46,5 @@ export interface ILogViewsClient { getLogView(logViewId: string): Promise; getResolvedLogView(logViewId: string): Promise; putLogView(logViewId: string, logViewAttributes: Partial): Promise; - resolveLogView(logViewAttributes: LogViewAttributes): Promise; + resolveLogView(logViewId: string, logViewAttributes: LogViewAttributes): Promise; }