mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Logs UI] Support the Unified Search Bar for Query input (#143222)
* Use Unified Search Bar for query input
This commit is contained in:
parent
c6a00589b0
commit
056413e8b4
21 changed files with 311 additions and 153 deletions
|
@ -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,
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -48,6 +48,7 @@ export interface SearchBarOwnProps<QT extends AggregateQuery | Query = Query> {
|
|||
screenTitle?: string;
|
||||
dataTestSubj?: string;
|
||||
// Togglers
|
||||
showQueryMenu?: boolean;
|
||||
showQueryInput?: boolean;
|
||||
showFilterBar?: boolean;
|
||||
showDatePicker?: boolean;
|
||||
|
@ -121,6 +122,7 @@ class SearchBarUI<QT extends (Query | AggregateQuery) | Query = Query> extends C
|
|||
State<QT | Query>
|
||||
> {
|
||||
public static defaultProps = {
|
||||
showQueryMenu: true,
|
||||
showFilterBar: true,
|
||||
showDatePicker: true,
|
||||
showSubmitButton: true,
|
||||
|
@ -448,7 +450,7 @@ class SearchBarUI<QT extends (Query | AggregateQuery) | Query = Query> extends C
|
|||
/>
|
||||
);
|
||||
|
||||
const queryBarMenu = (
|
||||
const queryBarMenu = this.props.showQueryMenu ? (
|
||||
<QueryBarMenu
|
||||
nonKqlMode={this.props.nonKqlMode}
|
||||
language={
|
||||
|
@ -488,7 +490,7 @@ class SearchBarUI<QT extends (Query | AggregateQuery) | Query = Query> extends C
|
|||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
) : undefined;
|
||||
|
||||
let filterBar;
|
||||
if (this.shouldRenderFilterBar()) {
|
||||
|
|
|
@ -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<ResolvedLogView> = {}
|
||||
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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<ResolvedLogView> => {
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 = {
|
||||
filterQuery: null,
|
||||
filterQueryDraft: {
|
||||
language: 'kuery',
|
||||
query: '',
|
||||
},
|
||||
validationErrors: [],
|
||||
export const DEFAULT_QUERY = {
|
||||
language: 'kuery',
|
||||
query: '',
|
||||
};
|
||||
|
||||
const validationDebounceTimeout = 1000; // milliseconds
|
||||
const INITIAL_LOG_FILTER_STATE = {
|
||||
filterQuery: null,
|
||||
queryStringQuery: null,
|
||||
validationError: null,
|
||||
};
|
||||
|
||||
// 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<ILogFilterState>(initialLogFilterState);
|
||||
const kibanaQuerySettings = useKibanaQuerySettings();
|
||||
|
||||
const [logFilterState, setLogFilterState] = useState<ILogFilterState>(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,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<UrlStateContainer
|
||||
urlState={filterQuery?.originalQuery}
|
||||
urlState={queryStringQuery}
|
||||
urlStateKey="logFilter"
|
||||
mapToUrlState={mapToFilterQuery}
|
||||
onChange={(urlState) => {
|
||||
if (urlState) {
|
||||
applyLogFilterQuery(urlState);
|
||||
queryString.setQuery(urlState);
|
||||
}
|
||||
}}
|
||||
onInitialize={(urlState) => {
|
||||
if (urlState) {
|
||||
applyLogFilterQuery(urlState);
|
||||
queryString.setQuery(urlState);
|
||||
} else {
|
||||
queryString.setQuery(DEFAULT_QUERY);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<LogViewAttributes>) => {
|
||||
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,
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -27,7 +27,7 @@ const LogFilterState: React.FC = ({ children }) => {
|
|||
const { derivedDataView } = useLogViewContext();
|
||||
|
||||
return (
|
||||
<LogFilterStateProvider indexPattern={derivedDataView}>
|
||||
<LogFilterStateProvider dataView={derivedDataView}>
|
||||
<WithLogFilterUrlState />
|
||||
{children}
|
||||
</LogFilterStateProvider>
|
||||
|
|
|
@ -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 = () => {
|
|||
<div>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween" gutterSize="l" wrap>
|
||||
<QueryBarFlexItem>
|
||||
<QueryStringInput
|
||||
disableLanguageSwitcher={true}
|
||||
iconType="search"
|
||||
indexPatterns={[derivedDataView as DataView]}
|
||||
isInvalid={!isFilterQueryDraftValid}
|
||||
onChange={(query: Query) => {
|
||||
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}
|
||||
<SearchBar
|
||||
appName={i18n.translate('xpack.infra.appName', {
|
||||
defaultMessage: 'Infra logs',
|
||||
})}
|
||||
deps={{
|
||||
unifiedSearch,
|
||||
notifications,
|
||||
http,
|
||||
docLinks,
|
||||
uiSettings,
|
||||
data,
|
||||
dataViews,
|
||||
storage,
|
||||
}}
|
||||
iconType="search"
|
||||
placeholder={i18n.translate('xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder', {
|
||||
defaultMessage: 'Search for log entries… (e.g. host.name:host-1)',
|
||||
})}
|
||||
useDefaultBehaviors={true}
|
||||
indexPatterns={derivedDataView ? [derivedDataView] : undefined}
|
||||
showQueryInput={true}
|
||||
showQueryMenu={false}
|
||||
showFilterBar={false}
|
||||
showDatePicker={false}
|
||||
/>
|
||||
</QueryBarFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -53,7 +53,7 @@ export class LogViewsClient implements ILogViewsClient {
|
|||
|
||||
public async getResolvedLogView(logViewId: string): Promise<ResolvedLogView> {
|
||||
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<ResolvedLogView> {
|
||||
return await resolveLogView(logViewAttributes, this.dataViews, this.config);
|
||||
public async resolveLogView(
|
||||
logViewId: string,
|
||||
logViewAttributes: LogViewAttributes
|
||||
): Promise<ResolvedLogView> {
|
||||
return await resolveLogView(logViewId, logViewAttributes, this.dataViews, this.config);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,5 +32,5 @@ export interface ILogViewsClient {
|
|||
getResolvedLogViewStatus(resolvedLogView: ResolvedLogView): Promise<LogViewStatus>;
|
||||
getResolvedLogView(logViewId: string): Promise<ResolvedLogView>;
|
||||
putLogView(logViewId: string, logViewAttributes: Partial<LogViewAttributes>): Promise<LogView>;
|
||||
resolveLogView(logViewAttributes: LogViewAttributes): Promise<ResolvedLogView>;
|
||||
resolveLogView(logViewId: string, logViewAttributes: LogViewAttributes): Promise<ResolvedLogView>;
|
||||
}
|
||||
|
|
|
@ -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: UrlState | undefined;
|
||||
|
|
|
@ -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-*",
|
||||
|
|
|
@ -67,7 +67,7 @@ export class LogViewsClient implements ILogViewsClient {
|
|||
|
||||
public async getResolvedLogView(logViewId: string): Promise<ResolvedLogView> {
|
||||
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<ResolvedLogView> {
|
||||
return await resolveLogView(logViewAttributes, await this.dataViews, this.config);
|
||||
public async resolveLogView(
|
||||
logViewId: string,
|
||||
logViewAttributes: LogViewAttributes
|
||||
): Promise<ResolvedLogView> {
|
||||
return await resolveLogView(logViewId, logViewAttributes, await this.dataViews, this.config);
|
||||
}
|
||||
|
||||
private async getSavedLogView(logViewId: string): Promise<LogView> {
|
||||
|
|
|
@ -46,5 +46,5 @@ export interface ILogViewsClient {
|
|||
getLogView(logViewId: string): Promise<LogView>;
|
||||
getResolvedLogView(logViewId: string): Promise<ResolvedLogView>;
|
||||
putLogView(logViewId: string, logViewAttributes: Partial<LogViewAttributes>): Promise<LogView>;
|
||||
resolveLogView(logViewAttributes: LogViewAttributes): Promise<ResolvedLogView>;
|
||||
resolveLogView(logViewId: string, logViewAttributes: LogViewAttributes): Promise<ResolvedLogView>;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue