Skip to content

Commit

Permalink
[Security Solution] [Detections] Adds support for index patterns (Dat…
Browse files Browse the repository at this point in the history
…aViewBase) to be used for query bar filters (#166318)

## Summary

Ref: #164265
  • Loading branch information
dhurley14 authored Sep 18, 2023
1 parent be38c9d commit 8220c9b
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,45 @@ import { SearchBar } from '@kbn/unified-search-plugin/public';
import type { QueryBarComponentProps } from '.';
import { QueryBar } from '.';

import type { DataViewFieldMap } from '@kbn/data-views-plugin/common';
import { createStubDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { fields } from '@kbn/data-views-plugin/common/mocks';
import { useKibana } from '../../lib/kibana';

const getMockIndexPattern = () => ({
...createStubDataView({
spec: {
id: '1234',
title: 'logstash-*',
fields: ((): DataViewFieldMap => {
const fieldMap: DataViewFieldMap = Object.create(null);
for (const field of fields) {
fieldMap[field.name] = { ...field };
}
return fieldMap;
})(),
},
}),
});

const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings;
jest.mock('../../lib/kibana');

describe('QueryBar ', () => {
const mockClearInstanceCache = jest.fn().mockImplementation(({ id }: { id: string }) => {
return id;
});

(useKibana as jest.Mock).mockReturnValue({
services: {
data: {
dataViews: {
create: jest.fn().mockResolvedValue(getMockIndexPattern()),
clearInstanceCache: mockClearInstanceCache,
},
},
},
});
const mockOnChangeQuery = jest.fn();
const mockOnSubmitQuery = jest.fn();
const mockOnSavedQuery = jest.fn();
Expand Down Expand Up @@ -52,10 +88,10 @@ describe('QueryBar ', () => {
mockOnSavedQuery.mockClear();
});

test('check if we format the appropriate props to QueryBar', () => {
const wrapper = mount(
<TestProviders>
<QueryBar
test('check if we format the appropriate props to QueryBar', async () => {
await act(async () => {
const wrapper = await getWrapper(
<Proxy
dateRangeFrom={DEFAULT_FROM}
dateRangeTo={DEFAULT_TO}
hideSavedQuery={false}
Expand All @@ -68,143 +104,28 @@ describe('QueryBar ', () => {
onSubmitQuery={mockOnSubmitQuery}
onSavedQuery={mockOnSavedQuery}
/>
</TestProviders>
);
const {
customSubmitButton,
timeHistory,
onClearSavedQuery,
onFiltersUpdated,
onQueryChange,
onQuerySubmit,
onSaved,
onSavedQueryUpdated,
...searchBarProps
} = wrapper.find(SearchBar).props();
);

expect(searchBarProps).toEqual({
dataTestSubj: undefined,
dateRangeFrom: 'now/d',
dateRangeTo: 'now/d',
displayStyle: undefined,
filters: [],
indexPatterns: [
{
fields: [
{
aggregatable: true,
name: '@timestamp',
searchable: true,
type: 'date',
},
{
aggregatable: true,
name: '@version',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.ephemeral_id',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.hostname',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.id',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.test1',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.test2',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.test3',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.test4',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.test5',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.test6',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.test7',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'agent.test8',
searchable: true,
type: 'string',
},
{
aggregatable: true,
name: 'host.name',
searchable: true,
type: 'string',
},
{
aggregatable: false,
name: 'nestedField.firstAttributes',
searchable: true,
type: 'string',
},
{
aggregatable: false,
name: 'nestedField.secondAttributes',
searchable: true,
type: 'string',
},
],
title: 'filebeat-*,auditbeat-*,packetbeat-*',
},
],
isLoading: false,
isRefreshPaused: true,
query: {
language: 'kuery',
query: 'here: query',
},
refreshInterval: undefined,
savedQuery: undefined,
showAutoRefreshOnly: false,
showDatePicker: false,
showFilterBar: true,
showQueryInput: true,
showSaveQuery: true,
showSubmitButton: false,
await waitFor(() => {
wrapper.update();
const {
customSubmitButton,
timeHistory,
onClearSavedQuery,
onFiltersUpdated,
onQueryChange,
onQuerySubmit,
onSaved,
onSavedQueryUpdated,
...searchBarProps
} = wrapper.find(SearchBar).props();
expect((searchBarProps?.indexPatterns ?? [{ id: 'unknown' }])[0].id).toEqual(
getMockIndexPattern().id
);
});
// ensure useEffect cleanup is called correctly after component unmounts
wrapper.unmount();
expect(mockClearInstanceCache).toHaveBeenCalledWith(getMockIndexPattern().id);
});
});

Expand Down Expand Up @@ -294,7 +215,6 @@ describe('QueryBar ', () => {
const onSubmitQueryRef = searchBarProps.onQuerySubmit;
const onSavedQueryRef = searchBarProps.onSavedQueryUpdated;
wrapper.setProps({ onSavedQuery: jest.fn() });
wrapper.update();

expect(onSavedQueryRef).not.toEqual(wrapper.find(SearchBar).props().onSavedQueryUpdated);
expect(onChangedQueryRef).toEqual(wrapper.find(SearchBar).props().onQueryChange);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { memo, useMemo, useCallback } from 'react';
import React, { memo, useMemo, useCallback, useState, useEffect } from 'react';
import deepEqual from 'fast-deep-equal';

import type { DataViewBase, Filter, Query, TimeRange } from '@kbn/es-query';
Expand All @@ -16,6 +16,8 @@ import type { SearchBarProps } from '@kbn/unified-search-plugin/public';
import { SearchBar } from '@kbn/unified-search-plugin/public';
import { Storage } from '@kbn/kibana-utils-plugin/public';

import { useKibana } from '../../lib/kibana';

export interface QueryBarComponentProps {
dataTestSubj?: string;
dateRangeFrom?: string;
Expand All @@ -36,6 +38,9 @@ export interface QueryBarComponentProps {
isDisabled?: boolean;
}

export const isDataView = (obj: unknown): obj is DataView =>
obj != null && typeof obj === 'object' && Object.hasOwn(obj, 'getName');

export const QueryBar = memo<QueryBarComponentProps>(
({
dateRangeFrom,
Expand All @@ -56,6 +61,8 @@ export const QueryBar = memo<QueryBarComponentProps>(
displayStyle,
isDisabled,
}) => {
const { data } = useKibana().services;
const [dataView, setDataView] = useState<DataView>();
const onQuerySubmit = useCallback(
(payload: { dateRange: TimeRange; query?: Query }) => {
if (payload.query != null && !deepEqual(payload.query, filterQuery)) {
Expand Down Expand Up @@ -102,16 +109,33 @@ export const QueryBar = memo<QueryBarComponentProps>(
[filterManager]
);

const indexPatterns = useMemo(() => [indexPattern], [indexPattern]);
const timeHistory = useMemo(() => new TimeHistory(new Storage(localStorage)), []);
useEffect(() => {
let dv: DataView;
if (isDataView(indexPattern)) {
setDataView(indexPattern);
} else {
const createDataView = async () => {
dv = await data.dataViews.create({ title: indexPattern.title });
setDataView(dv);
};
createDataView();
}
return () => {
if (dv?.id) {
data.dataViews.clearInstanceCache(dv?.id);
}
};
}, [data.dataViews, indexPattern]);

const timeHistory = useMemo(() => new TimeHistory(new Storage(localStorage)), []);
const arrDataView = useMemo(() => (dataView != null ? [dataView] : []), [dataView]);
return (
<SearchBar
showSubmitButton={false}
dateRangeFrom={dateRangeFrom}
dateRangeTo={dateRangeTo}
filters={filters}
indexPatterns={indexPatterns as DataView[]}
indexPatterns={arrDataView}
isLoading={isLoading}
isRefreshPaused={isRefreshPaused}
query={filterQuery}
Expand Down

0 comments on commit 8220c9b

Please sign in to comment.