Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] [Detections] Adds support for index patterns (DataViewBase) to be used for query bar filters #166318

Merged
merged 22 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
624cdab
fixes infosec issue
dhurley14 Aug 30, 2023
f55945c
updates tests, creates data view correctly
dhurley14 Aug 30, 2023
3fe87bf
cleanup
dhurley14 Aug 30, 2023
f821cb9
Merge branch 'main' into dataview-casting-error
dhurley14 Aug 30, 2023
7f8a42c
typeguard data view type
dhurley14 Aug 31, 2023
a625f58
utilizes in-memory data view creation, fixes test
dhurley14 Aug 31, 2023
1cd626f
Merge remote-tracking branch 'upstream/main' into dataview-casting-error
dhurley14 Sep 1, 2023
7a0d900
Merge remote-tracking branch 'upstream/main' into dataview-casting-error
dhurley14 Sep 1, 2023
bf95e23
Merge remote-tracking branch 'upstream/main' into dataview-casting-error
dhurley14 Sep 1, 2023
504be93
Merge remote-tracking branch 'upstream/main' into dataview-casting-error
dhurley14 Sep 1, 2023
8cc579f
Merge branch 'main' into dataview-casting-error
dhurley14 Sep 5, 2023
9ba848c
Merge branch 'main' into dataview-casting-error
dhurley14 Sep 5, 2023
b340368
Merge branch 'main' into dataview-casting-error
dhurley14 Sep 6, 2023
1834dcb
Merge branch 'main' into dataview-casting-error
dhurley14 Sep 6, 2023
5d7d16b
Merge branch 'main' into dataview-casting-error
dhurley14 Sep 7, 2023
6b6a1c4
Merge branch 'main' into dataview-casting-error
dhurley14 Sep 8, 2023
9e260fb
Merge remote-tracking branch 'upstream/main' into dataview-casting-error
dhurley14 Sep 12, 2023
ef0a548
Merge remote-tracking branch 'upstream/main' into dataview-casting-error
dhurley14 Sep 13, 2023
ce8ae6a
fixes useEffect
dhurley14 Sep 13, 2023
5380d78
Merge branch 'main' into dataview-casting-error
dhurley14 Sep 13, 2023
244c32b
Merge branch 'main' into dataview-casting-error
dhurley14 Sep 13, 2023
d7d959b
add expect in unit test for when search bar unmounts, clear cached da…
dhurley14 Sep 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed dataView?.id from the side effect's dependency list here.


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