[Logs UI] Support the Unified Search Bar for Query input (#143222)

* Use Unified Search Bar for query input
This commit is contained in:
Kerry Gallagher 2022-11-02 09:58:29 +00:00 committed by GitHub
parent c6a00589b0
commit 056413e8b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 311 additions and 153 deletions

View file

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

View file

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

View file

@ -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({

View file

@ -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()) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 (

View file

@ -27,7 +27,7 @@ const LogFilterState: React.FC = ({ children }) => {
const { derivedDataView } = useLogViewContext();
return (
<LogFilterStateProvider indexPattern={derivedDataView}>
<LogFilterStateProvider dataView={derivedDataView}>
<WithLogFilterUrlState />
{children}
</LogFilterStateProvider>

View file

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

View file

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

View file

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

View file

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

View file

@ -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-*",

View file

@ -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> {

View file

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