diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx
index 118c78e290759..934e923d5724d 100644
--- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx
@@ -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();
@@ -52,10 +88,10 @@ describe('QueryBar ', () => {
mockOnSavedQuery.mockClear();
});
- test('check if we format the appropriate props to QueryBar', () => {
- const wrapper = mount(
-
- {
+ await act(async () => {
+ const wrapper = await getWrapper(
+ {
onSubmitQuery={mockOnSubmitQuery}
onSavedQuery={mockOnSavedQuery}
/>
-
- );
- 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);
});
});
@@ -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);
diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx
index d86f3de10b549..9356956c23d56 100644
--- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx
@@ -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';
@@ -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;
@@ -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(
({
dateRangeFrom,
@@ -56,6 +61,8 @@ export const QueryBar = memo(
displayStyle,
isDisabled,
}) => {
+ const { data } = useKibana().services;
+ const [dataView, setDataView] = useState();
const onQuerySubmit = useCallback(
(payload: { dateRange: TimeRange; query?: Query }) => {
if (payload.query != null && !deepEqual(payload.query, filterQuery)) {
@@ -102,16 +109,33 @@ export const QueryBar = memo(
[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 (