From bc444028a29898111a38a890aed66418aabcb0a2 Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Wed, 20 Dec 2023 09:02:27 -0300 Subject: [PATCH 1/8] Remove duplicated data_grid,search_bar,doc_viewer --- .../dashboards/inventory/inventory_service.ts | 194 ---------------- .../data_grid/use_data_grid.ts | 105 --------- .../vulnerabilities/doc_viewer/doc_viewer.tsx | 150 ------------ .../doc_viewer/use_doc_viewer.ts | 28 --- .../vulnerabilities/search_bar/index.ts | 1 - .../use_search_bar_configuration.test.ts | 219 ------------------ .../use_search_bar_configuration.tsx | 180 -------------- 7 files changed, 877 deletions(-) delete mode 100644 plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory_service.ts delete mode 100644 plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts delete mode 100644 plugins/main/public/components/overview/vulnerabilities/doc_viewer/doc_viewer.tsx delete mode 100644 plugins/main/public/components/overview/vulnerabilities/doc_viewer/use_doc_viewer.ts delete mode 100644 plugins/main/public/components/overview/vulnerabilities/search_bar/index.ts delete mode 100644 plugins/main/public/components/overview/vulnerabilities/search_bar/use_search_bar_configuration.test.ts delete mode 100644 plugins/main/public/components/overview/vulnerabilities/search_bar/use_search_bar_configuration.tsx diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory_service.ts b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory_service.ts deleted file mode 100644 index 6443c678b1..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory_service.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { SearchResponse } from "../../../../../../../../src/core/server"; -import { getPlugins } from '../../../../../kibana-services'; -import { IndexPattern, Filter, OpenSearchQuerySortValue } from "../../../../../../../../src/plugins/data/public"; -import * as FileSaver from '../../../../../services/file-saver'; -import { beautifyDate } from "../../../../agents/vuls/inventory/lib"; -import { MAX_ENTRIES_PER_QUERY } from "./config"; - - -interface SearchParams { - indexPattern: IndexPattern; - filters?: Filter[]; - query?: any; - pagination?: { - pageIndex?: number; - pageSize?: number; - }; - fields?: string[], - sorting?: { - columns: { - id: string; - direction: 'asc' | 'desc'; - }[]; - }; -} - -export const search = async (params: SearchParams): Promise => { - const { indexPattern, filters = [], query, pagination, sorting, fields } = params; - if(!indexPattern){ - return; - } - const data = getPlugins().data; - const searchSource = await data.search.searchSource.create(); - const fromField = (pagination?.pageIndex || 0) * (pagination?.pageSize || 100); - const sortOrder: OpenSearchQuerySortValue[] = sorting?.columns.map((column) => { - const sortDirection = column.direction === 'asc' ? 'asc' : 'desc'; - return { [column?.id || '']: sortDirection } as OpenSearchQuerySortValue; - }) || []; - - const searchParams = searchSource - .setParent(undefined) - .setField('filter', filters) - .setField('query', query) - .setField('sort', sortOrder) - .setField('size', pagination?.pageSize) - .setField('from', fromField) - .setField('index', indexPattern) - - // add fields - if (fields && Array.isArray(fields) && fields.length > 0){ - searchParams.setField('fields', fields); - } - try{ - return await searchParams.fetch(); - }catch(error){ - if(error.body){ - throw error.body; - } - throw error; - } - -}; - - -export const parseData = (resultsHits: SearchResponse['hits']['hits']): any[] => { - const data = resultsHits.map((hit) => { - if (!hit) { - return {} - } - const source = hit._source as object; - const data = { - ...source, - _id: hit._id, - _index: hit._index, - _type: hit._type, - _score: hit._score, - }; - return data; - }); - return data; -} - - -export const getFieldFormatted = (rowIndex, columnId, indexPattern, rowsParsed) => { - const field = indexPattern.fields.find((field) => field.name === columnId); - let fieldValue = null; - if (columnId.includes('.')) { - // when the column is a nested field. The column could have 2 to n levels - // get dinamically the value of the nested field - const nestedFields = columnId.split('.'); - fieldValue = rowsParsed[rowIndex]; - nestedFields.forEach((field) => { - if (fieldValue) { - fieldValue = fieldValue[field]; - } - }); - - } else { - fieldValue = rowsParsed[rowIndex][columnId].formatted - ? rowsParsed[rowIndex][columnId].formatted - : rowsParsed[rowIndex][columnId]; - } - - // if is date field - if (field?.type === 'date') { - // @ts-ignore - fieldValue = beautifyDate(fieldValue); - } - return fieldValue; -} - -export const exportSearchToCSV = async (params: SearchParams): Promise => { - const DEFAULT_MAX_SIZE_PER_CALL = 1000; - const { indexPattern, filters = [], query, sorting, fields, pagination } = params; - // when the pageSize is greater than the default max size per call (10000) - // then we need to paginate the search - const mustPaginateSearch = pagination?.pageSize && pagination?.pageSize > DEFAULT_MAX_SIZE_PER_CALL; - const pageSize = mustPaginateSearch ? DEFAULT_MAX_SIZE_PER_CALL : pagination?.pageSize; - const totalHits = pagination?.pageSize || DEFAULT_MAX_SIZE_PER_CALL; - let pageIndex = params.pagination?.pageIndex || 0; - let hitsCount = 0; - let allHits = []; - let searchResults; - if (mustPaginateSearch) { - // paginate the search - while (hitsCount < totalHits &&  hitsCount < MAX_ENTRIES_PER_QUERY) { - const searchParams = { - indexPattern, - filters, - query, - pagination: { - pageIndex, - pageSize, - }, - sorting, - fields, - }; - searchResults = await search(searchParams); - allHits = allHits.concat(searchResults.hits.hits); - hitsCount = allHits.length; - pageIndex++; - } - } else { - searchResults = await search(params); - allHits = searchResults.hits.hits; - } - - const resultsFields = fields; - const data = allHits.map((hit) => { - // check if the field type is a date - const dateFields = indexPattern.fields.getByType('date'); - const dateFieldsNames = dateFields.map((field) => field.name); - const flattenHit = indexPattern.flattenHit(hit); - // replace the date fields with the formatted date - dateFieldsNames.forEach((field) => { - if (flattenHit[field]) { - flattenHit[field] = beautifyDate(flattenHit[field]); - } - }); - return flattenHit; - }); - - if (!resultsFields || resultsFields.length === 0){ - return; - } - - if (!data || data.length === 0) - return; - - const parsedData = data.map((row) => { - const parsedRow = resultsFields?.map((field) => { - const value = row[field]; - if (value === undefined || value === null) { - return ''; - } - if (typeof value === 'object') { - return JSON.stringify(value); - } - return `"${value}"`; - }); - return parsedRow?.join(','); - }).join('\n'); - - // create a csv file using blob - const blobData = new Blob( - [ - `${resultsFields?.join(',')}\n${parsedData}` - ], - { type: 'text/csv' } - ); - - if (blobData) { - FileSaver?.saveAs(blobData, `vulnerabilities_inventory-${new Date().toISOString()}.csv`); - } -} \ No newline at end of file diff --git a/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts b/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts deleted file mode 100644 index a43810fdf9..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/data_grid/use_data_grid.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { EuiDataGridCellValueElementProps, EuiDataGridColumn, EuiDataGridProps, EuiDataGridSorting } from "@elastic/eui" -import { useEffect, useMemo, useState, Fragment } from "react"; -import { SearchResponse } from "@opensearch-project/opensearch/api/types"; -import { IFieldType, IndexPattern } from "../../../../../../../src/plugins/data/common"; -import { parseData, getFieldFormatted } from '../dashboards/inventory/inventory_service'; -import { MAX_ENTRIES_PER_QUERY } from "../dashboards/inventory/config"; - -type tDataGridProps = { - indexPattern: IndexPattern; - results: SearchResponse; - defaultColumns: EuiDataGridColumn[]; - DocViewInspectButton: ({ rowIndex }: EuiDataGridCellValueElementProps) => React.JSX.Element - ariaLabelledBy: string; -}; - -export const parseColumns = (fields: IFieldType[]): EuiDataGridColumn[] => { - // remove _source field becuase is a object field and is not supported - fields = fields.filter((field) => field.name !== '_source'); - return fields.map((field) => { - return { - ...field, - id: field.name, - display: field.name, - schema: field.type, - actions: { - showHide: true, - }, - }; - }) || []; -} - -export const useDataGrid = (props: tDataGridProps): EuiDataGridProps => { - const { indexPattern, DocViewInspectButton, results, defaultColumns } = props; - /** Columns **/ - const [columns, setColumns] = useState(defaultColumns); - const [columnVisibility, setVisibility] = useState(() => - columns.map(({ id }) => id) - ); - /** Rows */ - const [rows, setRows] = useState([]); - const rowCount = results ? results?.hits?.total as number : 0; - /** Sorting **/ - // get default sorting from default columns - const getDefaultSorting = () => { - const defaultSort = columns.find((column) => column.isSortable || column.defaultSortDirection); - return defaultSort ? [{ id: defaultSort.id, direction: defaultSort.defaultSortDirection || 'desc' }] : []; - } - const defaultSorting: EuiDataGridSorting['columns'] = getDefaultSorting(); - const [sortingColumns, setSortingColumns] = useState(defaultSorting); - const onSort = (sortingColumns) => {setSortingColumns(sortingColumns)}; - /** Pagination **/ - const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 20 }); - const onChangeItemsPerPage = useMemo(() => (pageSize) => - setPagination((pagination) => ({ - ...pagination, - pageSize, - pageIndex: 0, - })), [rows, rowCount]); - const onChangePage = (pageIndex) => setPagination((pagination) => ({ ...pagination, pageIndex })) - - useEffect(() => { - setRows(results?.hits?.hits || []) - }, [results, results?.hits, results?.hits?.total]) - - useEffect(() => { - setPagination((pagination) => ({ ...pagination, pageIndex: 0 })); - }, [rowCount]) - - const renderCellValue = ({ rowIndex, columnId, setCellProps }) => { - const rowsParsed = parseData(rows); - // On the context data always is stored the current page data (pagination) - // then the rowIndex is relative to the current page - const relativeRowIndex = rowIndex % pagination.pageSize; - return rowsParsed.hasOwnProperty(relativeRowIndex) - ? getFieldFormatted(relativeRowIndex, columnId, indexPattern, rowsParsed) - : null; - }; - - const leadingControlColumns = useMemo(() => { - return [ - { - id: 'inspectCollapseColumn', - headerCellRender: () => null, - rowCellRender: (props) => DocViewInspectButton({ ...props, rowIndex: props.rowIndex % pagination.pageSize }), - width: 40, - }, - ]; - }, [results]); - - return { - "aria-labelledby": props.ariaLabelledBy, - columns: parseColumns(indexPattern?.fields || []), - columnVisibility: { visibleColumns: columnVisibility, setVisibleColumns: setVisibility }, - renderCellValue: renderCellValue, - leadingControlColumns: leadingControlColumns, - rowCount: rowCount < MAX_ENTRIES_PER_QUERY ? rowCount : MAX_ENTRIES_PER_QUERY, - sorting: { columns: sortingColumns, onSort }, - pagination: { - ...pagination, - pageSizeOptions: [20, 50, 100], - onChangeItemsPerPage: onChangeItemsPerPage, - onChangePage: onChangePage, - } - } -} \ No newline at end of file diff --git a/plugins/main/public/components/overview/vulnerabilities/doc_viewer/doc_viewer.tsx b/plugins/main/public/components/overview/vulnerabilities/doc_viewer/doc_viewer.tsx deleted file mode 100644 index 08e170f9d5..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/doc_viewer/doc_viewer.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import React, { useState } from 'react'; -import classNames from 'classnames'; -import { escapeRegExp } from 'lodash'; -import { i18n } from '@osd/i18n'; -import { FieldIcon } from '../../../../../../../src/plugins/opensearch_dashboards_react/public'; -import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; - -const COLLAPSE_LINE_LENGTH = 350; -const DOT_PREFIX_RE = /(.).+?\./g; - -export type tDocViewerProps = { - flattened: any; - formatted: any; - mapping: any; - indexPattern: any; -} - -/** - * Convert a dot.notated.string into a short - * version (d.n.string) - */ -export const shortenDottedString = (input: string) => input.replace(DOT_PREFIX_RE, '$1.'); - -export function getFieldTypeName(type: string) { - switch (type) { - case 'boolean': - return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', { - defaultMessage: 'Boolean field', - }); - case 'conflict': - return i18n.translate('discover.fieldNameIcons.conflictFieldAriaLabel', { - defaultMessage: 'Conflicting field', - }); - case 'date': - return i18n.translate('discover.fieldNameIcons.dateFieldAriaLabel', { - defaultMessage: 'Date field', - }); - case 'geo_point': - return i18n.translate('discover.fieldNameIcons.geoPointFieldAriaLabel', { - defaultMessage: 'Geo point field', - }); - case 'geo_shape': - return i18n.translate('discover.fieldNameIcons.geoShapeFieldAriaLabel', { - defaultMessage: 'Geo shape field', - }); - case 'ip': - return i18n.translate('discover.fieldNameIcons.ipAddressFieldAriaLabel', { - defaultMessage: 'IP address field', - }); - case 'murmur3': - return i18n.translate('discover.fieldNameIcons.murmur3FieldAriaLabel', { - defaultMessage: 'Murmur3 field', - }); - case 'number': - return i18n.translate('discover.fieldNameIcons.numberFieldAriaLabel', { - defaultMessage: 'Number field', - }); - case 'source': - // Note that this type is currently not provided, type for _source is undefined - return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', { - defaultMessage: 'Source field', - }); - case 'string': - return i18n.translate('discover.fieldNameIcons.stringFieldAriaLabel', { - defaultMessage: 'String field', - }); - case 'nested': - return i18n.translate('discover.fieldNameIcons.nestedFieldAriaLabel', { - defaultMessage: 'Nested field', - }); - default: - return i18n.translate('discover.fieldNameIcons.unknownFieldAriaLabel', { - defaultMessage: 'Unknown field', - }); - } -} - -const DocViewer = (props: tDocViewerProps) => { - const [fieldRowOpen, setFieldRowOpen] = useState({} as Record); - const { flattened, formatted, mapping, indexPattern } = props; - - return (<> - {flattened && ( - - - {Object.keys(flattened) - .sort() - .map((field, index) => { - const value = String(formatted[field]); - const fieldMapping = mapping(field); - const isCollapsible = value.length > COLLAPSE_LINE_LENGTH; - const isCollapsed = isCollapsible && !fieldRowOpen[field]; - const valueClassName = classNames({ - // eslint-disable-next-line @typescript-eslint/naming-convention - osdDocViewer__value: true, - 'truncate-by-height': isCollapsible && isCollapsed, - }); - const isNestedField = - !indexPattern.fields.getByName(field) && - !!indexPattern.fields.getAll().find((patternField) => { - // We only want to match a full path segment - const nestedRootRegex = new RegExp(escapeRegExp(field) + '(\\.|$)'); - return nestedRootRegex.test(patternField.subType?.nested?.path ?? ''); - }); - const fieldType = isNestedField ? 'nested' : indexPattern.fields.getByName(field)?.type; - const typeName = getFieldTypeName(String(fieldType)); - const displayName = field; - const fieldIconProps = { fill: 'none', color: 'gray' } - const scripted = Boolean(fieldMapping?.scripted) - - return ( - - - - - ); - })} - -
- - - - - - - {displayName} - - - - -
-
- )}) -}; - -export default DocViewer; \ No newline at end of file diff --git a/plugins/main/public/components/overview/vulnerabilities/doc_viewer/use_doc_viewer.ts b/plugins/main/public/components/overview/vulnerabilities/doc_viewer/use_doc_viewer.ts deleted file mode 100644 index d38e58bc3a..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/doc_viewer/use_doc_viewer.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { tDocViewerProps } from "./doc_viewer" -import { IndexPattern } from "../../../../../../../src/plugins/data/common"; - -type tUseDocViewerInputs = { - indexPattern: IndexPattern; - doc: any; -} - -export const useDocViewer = (props: tUseDocViewerInputs): tDocViewerProps => { - const { indexPattern, doc } = props; - - if (!indexPattern || !doc) { - return { - flattened: {}, - formatted: {}, - indexPattern: undefined, - mapping: undefined - } - } - - const mapping = indexPattern?.fields.getByName; - return { - flattened: indexPattern?.flattenHit(doc), - formatted: indexPattern?.formatHit(doc, 'html'), - indexPattern, - mapping - } -} \ No newline at end of file diff --git a/plugins/main/public/components/overview/vulnerabilities/search_bar/index.ts b/plugins/main/public/components/overview/vulnerabilities/search_bar/index.ts deleted file mode 100644 index 22509f1244..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/search_bar/index.ts +++ /dev/null @@ -1 +0,0 @@ -// searchbar index \ No newline at end of file diff --git a/plugins/main/public/components/overview/vulnerabilities/search_bar/use_search_bar_configuration.test.ts b/plugins/main/public/components/overview/vulnerabilities/search_bar/use_search_bar_configuration.test.ts deleted file mode 100644 index 37685811d8..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/search_bar/use_search_bar_configuration.test.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; -import '@testing-library/jest-dom/extend-expect'; -// osd dependencies -import { Start, dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; -import { - Filter, - IndexPattern, - Query, - TimeRange, -} from '../../../../../../../src/plugins/data/public'; -// wazuh plugin dependencies -import useSearchBar from './use_search_bar_configuration'; -import { getDataPlugin } from '../../../../kibana-services'; -import * as timeFilterHook from '../../../common/hooks/use-time-filter'; -import * as queryManagerHook from '../../../common/hooks/use-query'; - -/** - * Mocking Data Plugin - **/ -jest.mock('../../../../kibana-services', () => { - return { - getDataPlugin: jest.fn(), - }; -}); -/* using osd mock utils */ -const mockDataPlugin = dataPluginMock.createStartContract(); -const mockedGetDataPlugin = getDataPlugin as jest.Mock; -mockedGetDataPlugin.mockImplementation( - () => - ({ - ...mockDataPlugin, - ...{ - query: { - ...mockDataPlugin.query, - queryString: { - ...mockDataPlugin.query.queryString, - getUpdates$: jest.fn(() => ({ - subscribe: jest.fn(), - unsubscribe: jest.fn(), - })), - }, - }, - }, - } as Start) -); -/////////////////////////////////////////////////////////// - -const mockedDefaultIndexPatternData: Partial = { - // used partial not avoid fill all the interface, it's only for testing purpose - id: 'default-index-pattern', - title: '', -}; - -describe('[hook] useSearchBarConfiguration', () => { - beforeAll(() => { - /***** mock use-time-filter hook *****/ - const spyUseTimeFilter = jest.spyOn(timeFilterHook, 'useTimeFilter'); - const mockTimeFilterResult: TimeRange = { - from: 'now/d', - to: 'now/d', - }; - spyUseTimeFilter.mockImplementation(() => ({ - timeFilter: mockTimeFilterResult, - setTimeFilter: jest.fn(), - timeHistory: [], - })); - /***** mock use-time-filter hook *****/ - const spyUseQueryManager = jest.spyOn(queryManagerHook, 'useQueryManager'); - const mockQueryResult: Query = { - language: 'kuery', - query: '', - }; - spyUseQueryManager.mockImplementation(() => [mockQueryResult, jest.fn()]); - }); - - it('should return default app index pattern when not receiving a default index pattern', async () => { - jest - .spyOn(mockDataPlugin.indexPatterns, 'getDefault') - .mockResolvedValue(mockedDefaultIndexPatternData); - jest.spyOn(mockDataPlugin.query.filterManager, 'getFilters').mockReturnValue([]); - const { result, waitForNextUpdate } = renderHook(() => useSearchBar({})); - await waitForNextUpdate(); - expect(mockDataPlugin.indexPatterns.getDefault).toBeCalled(); - expect(result.current.searchBarProps.indexPatterns).toMatchObject([ - mockedDefaultIndexPatternData, - ]); - }); - - it('should return the same index pattern when receiving a default index pattern', async () => { - const exampleIndexPatternId = 'wazuh-index-pattern'; - const mockedIndexPatternData: Partial = { - // used partial not avoid fill all the interface, it's only for testing purpose - id: exampleIndexPatternId, - title: '', - }; - jest.spyOn(mockDataPlugin.indexPatterns, 'get').mockResolvedValue(mockedIndexPatternData); - const { result, waitForNextUpdate } = renderHook(() => - useSearchBar({ - defaultIndexPatternID: 'wazuh-index-pattern', - }) - ); - await waitForNextUpdate(); - expect(mockDataPlugin.indexPatterns.get).toBeCalledWith(exampleIndexPatternId); - expect(result.current.searchBarProps.indexPatterns).toMatchObject([mockedIndexPatternData]); - }); - - it('should show an ERROR message and get the default app index pattern when not found the index pattern data by the ID received', async () => { - const INDEX_NOT_FOUND_ERROR = new Error('Index Pattern not found'); - jest.spyOn(mockDataPlugin.indexPatterns, 'get').mockImplementation(() => { - throw INDEX_NOT_FOUND_ERROR; - }); - jest - .spyOn(mockDataPlugin.indexPatterns, 'getDefault') - .mockResolvedValue(mockedDefaultIndexPatternData); - jest.spyOn(mockDataPlugin.query.filterManager, 'getFilters').mockReturnValue([]); - - // mocking console error to avoid logs in test and check if is called - const mockedConsoleError = jest.spyOn(console, 'error').mockImplementationOnce(() => {}); - const { result, waitForNextUpdate } = renderHook(() => - useSearchBar({ - defaultIndexPatternID: 'invalid-index-pattern-id', - }) - ); - - await waitForNextUpdate(); - expect(mockDataPlugin.indexPatterns.getDefault).toBeCalled(); - expect(mockDataPlugin.indexPatterns.get).toBeCalledWith('invalid-index-pattern-id'); - expect(result.current.searchBarProps.indexPatterns).toMatchObject([ - mockedDefaultIndexPatternData, - ]); - expect(mockedConsoleError).toBeCalledWith(INDEX_NOT_FOUND_ERROR); - }); - - it('should return the same filters and apply them to the filter manager when are received by props', async () => { - const defaultFilters: Filter[] = [ - { - query: 'something to filter', - meta: { - alias: 'filter-mocked', - disabled: false, - negate: true, - }, - }, - ]; - jest - .spyOn(mockDataPlugin.indexPatterns, 'getDefault') - .mockResolvedValue(mockedDefaultIndexPatternData); - jest.spyOn(mockDataPlugin.query.filterManager, 'getFilters').mockReturnValue(defaultFilters); - const { result, waitForNextUpdate } = renderHook(() => - useSearchBar({ - filters: defaultFilters, - }) - ); - - await waitForNextUpdate(); - - expect(result.current.searchBarProps.filters).toMatchObject(defaultFilters); - expect(mockDataPlugin.query.filterManager.setFilters).toBeCalledWith(defaultFilters); - expect(mockDataPlugin.query.filterManager.getFilters).toBeCalled(); - }); - - it('should return and preserve filters when the index pattern received is equal to the index pattern already selected in the app', async () => { - const defaultIndexFilters: Filter[] = [ - { - query: 'something to filter', - meta: { - alias: 'filter-mocked', - disabled: false, - negate: true, - }, - }, - ]; - jest - .spyOn(mockDataPlugin.indexPatterns, 'getDefault') - .mockResolvedValue(mockedDefaultIndexPatternData); - jest - .spyOn(mockDataPlugin.indexPatterns, 'get') - .mockResolvedValue(mockedDefaultIndexPatternData); - jest - .spyOn(mockDataPlugin.query.filterManager, 'getFilters') - .mockReturnValue(defaultIndexFilters); - const { result, waitForNextUpdate } = renderHook(() => - useSearchBar({ - defaultIndexPatternID: mockedDefaultIndexPatternData.id, - }) - ); - await waitForNextUpdate(); - expect(result.current.searchBarProps.indexPatterns).toMatchObject([ - mockedDefaultIndexPatternData, - ]); - expect(result.current.searchBarProps.filters).toMatchObject(defaultIndexFilters); - }); - - it('should return empty filters when the index pattern is NOT equal to the default app index pattern', async () => { - const exampleIndexPatternId = 'wazuh-index-pattern'; - const mockedExampleIndexPatternData: Partial = { - // used partial not avoid fill all the interface, it's only for testing purpose - id: exampleIndexPatternId, - title: '', - }; - jest - .spyOn(mockDataPlugin.indexPatterns, 'get') - .mockResolvedValue(mockedExampleIndexPatternData); - jest - .spyOn(mockDataPlugin.indexPatterns, 'getDefault') - .mockResolvedValue(mockedDefaultIndexPatternData); - jest.spyOn(mockDataPlugin.query.filterManager, 'getFilters').mockReturnValue([]); - const { result, waitForNextUpdate } = renderHook(() => - useSearchBar({ - defaultIndexPatternID: exampleIndexPatternId, - }) - ); - await waitForNextUpdate(); - expect(result.current.searchBarProps.indexPatterns).toMatchObject([ - mockedExampleIndexPatternData, - ]); - expect(result.current.searchBarProps.filters).toStrictEqual([]); - }); -}); diff --git a/plugins/main/public/components/overview/vulnerabilities/search_bar/use_search_bar_configuration.tsx b/plugins/main/public/components/overview/vulnerabilities/search_bar/use_search_bar_configuration.tsx deleted file mode 100644 index 377f24930c..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/search_bar/use_search_bar_configuration.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { - SearchBarProps, - FilterManager, - TimeRange, - Query, -} from '../../../../../../../src/plugins/data/public'; -import { - Filter, - IIndexPattern, - IndexPatternsContract, -} from '../../../../../../../src/plugins/data/public'; -import { getDataPlugin } from '../../../../kibana-services'; - -import { - useFilterManager, - useQueryManager, - useTimeFilter, -} from '../../../common/hooks'; -import { AUTHORIZED_AGENTS } from '../../../../../common/constants'; - -// Input - types -type tUseSearchBarCustomInputs = { - defaultIndexPatternID: IIndexPattern['id']; - onFiltersUpdated?: (filters: Filter[]) => void; - onQuerySubmitted?: ( - payload: { dateRange: TimeRange; query?: Query }, - isUpdate?: boolean, - ) => void; -}; -type tUseSearchBarProps = Partial & tUseSearchBarCustomInputs; - -// Output types -type tUserSearchBarResponse = { - searchBarProps: Partial; -}; - -/** - * Hook used to compose the searchbar configuration props - * @param props - * @returns - */ -const useSearchBarConfiguration = ( - props?: tUseSearchBarProps, -): tUserSearchBarResponse => { - // dependencies - const filterManager = useFilterManager().filterManager as FilterManager; - const { filters } = useFilterManager(); - const [query, setQuery] = props?.query - ? useState(props?.query) - : useQueryManager(); - const { timeFilter, timeHistory, setTimeFilter } = useTimeFilter(); - // states - const [isLoading, setIsLoading] = useState(false); - const [indexPatternSelected, setIndexPatternSelected] = - useState(); - - useEffect(() => { - initSearchBar(); - }, []); - - useEffect(() => { - const defaultIndex = props?.defaultIndexPatternID; - /* Filters that do not belong to the default index are filtered */ - const cleanedFilters = filters.filter( - filter => filter.meta.index === defaultIndex, - ); - if (cleanedFilters.length !== filters.length) { - filterManager.setFilters(cleanedFilters); - } - }, [filters]); - - /** - * Initialize the searchbar props with the corresponding index pattern and filters - */ - const initSearchBar = async () => { - setIsLoading(true); - const indexPattern = await getIndexPattern(props?.defaultIndexPatternID); - setIndexPatternSelected(indexPattern); - const filters = await getInitialFilters(indexPattern); - filterManager.setFilters(filters); - setIsLoading(false); - }; - - /** - * Return the index pattern data by ID. - * If not receive a ID return the default index from the index pattern service - * @returns - */ - const getIndexPattern = async (indexPatternID?: string) => { - const indexPatternService = getDataPlugin() - .indexPatterns as IndexPatternsContract; - if (indexPatternID) { - try { - return await indexPatternService.get(indexPatternID); - } catch (error) { - // when the index pattern id not exists will get the default - console.error(error); - return await indexPatternService.getDefault(); - } - } else { - return await indexPatternService.getDefault(); - } - }; - - /** - * Return the initial filters considering if hook receives initial filters - * When the default index pattern is the same like the received preserve the filters - * @param indexPattern - * @returns - */ - const getInitialFilters = async (indexPattern: IIndexPattern) => { - const indexPatternService = getDataPlugin() - .indexPatterns as IndexPatternsContract; - let initialFilters: Filter[] = []; - if (props?.filters) { - return props?.filters; - } - if (indexPattern) { - // get filtermanager and filters - // if the index is the same, get filters stored - // else clear filters - const defaultIndexPattern = - (await indexPatternService.getDefault()) as IIndexPattern; - initialFilters = - defaultIndexPattern.id === indexPattern.id - ? filterManager.getFilters() - : []; - } else { - initialFilters = []; - } - return initialFilters; - }; - - /** - * Return filters from filters manager. - * Additionally solve the known issue with the auto loaded agent.id filters from the searchbar - * @returns - */ - const getFilters = () => { - const filters = filterManager ? filterManager.getFilters() : []; - return filters.filter( - filter => filter.meta.controlledBy !== AUTHORIZED_AGENTS, - ); // remove auto loaded agent.id filters - }; - - /** - * Search bar properties necessary to render and initialize the osd search bar component - */ - const searchBarProps: Partial = { - isLoading, - ...(indexPatternSelected && { indexPatterns: [indexPatternSelected] }), // indexPattern cannot be empty or empty [] - filters: getFilters(), - query, - timeHistory, - dateRangeFrom: timeFilter.from, - dateRangeTo: timeFilter.to, - onFiltersUpdated: (filters: Filter[]) => { - // its necessary execute setter to apply filters - filterManager.setFilters(filters); - props?.onFiltersUpdated && props?.onFiltersUpdated(filters); - }, - onQuerySubmit: ( - payload: { dateRange: TimeRange; query?: Query }, - _isUpdate?: boolean, - ): void => { - const { dateRange, query } = payload; - // its necessary execute setter to apply query filters - setTimeFilter(dateRange); - setQuery(query); - props?.onQuerySubmitted && props?.onQuerySubmitted(payload); - }, - }; - - return { - searchBarProps, - }; -}; - -export default useSearchBarConfiguration; From 34003aaa6b4f1c31b4fd67db33847d8ea41fdfac Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Wed, 20 Dec 2023 09:02:44 -0300 Subject: [PATCH 2/8] Replace imports on inventory --- .../dashboards/inventory/inventory.tsx | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx index 4f7cdc1518..45a8616f8e 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx @@ -1,6 +1,4 @@ import React, { useEffect, useMemo, useState } from 'react'; -import { getPlugins } from '../../../../../kibana-services'; -import useSearchBarConfiguration from '../../search_bar/use_search_bar_configuration'; import { IntlProvider } from 'react-intl'; import { EuiDataGrid, @@ -15,34 +13,36 @@ import { EuiFlyoutHeader, EuiTitle, EuiButtonEmpty, - EuiCallOut, - EuiSpacer, } from '@elastic/eui'; import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; import { SearchResponse } from '../../../../../../../../src/core/server'; -import DocViewer from '../../doc_viewer/doc_viewer'; -import { DiscoverNoResults } from '../../common/components/no_results'; -import { LoadingSpinner } from '../../common/components/loading_spinner'; -import { useDataGrid } from '../../data_grid/use_data_grid'; -import { MAX_ENTRIES_PER_QUERY, inventoryTableDefaultColumns } from './config'; -import { useDocViewer } from '../../doc_viewer/use_doc_viewer'; -import './inventory.scss'; -import { search, exportSearchToCSV } from './inventory_service'; +import { HitsCounter } from '../../../../../kibana-integrations/discover/application/components/hits_counter/hits_counter'; +import { formatNumWithCommas } from '../../../../../kibana-integrations/discover/application/helpers'; +import { getPlugins } from '../../../../../kibana-services'; import { ErrorHandler, ErrorFactory, HttpError, } from '../../../../../react-services/error-management'; -import { withErrorBoundary } from '../../../../common/hocs'; -import { HitsCounter } from '../../../../../kibana-integrations/discover/application/components/hits_counter/hits_counter'; -import { formatNumWithCommas } from '../../../../../kibana-integrations/discover/application/helpers'; +import './inventory.scss'; +import { MAX_ENTRIES_PER_QUERY, inventoryTableDefaultColumns } from './config'; +import { DiscoverNoResults } from '../../common/components/no_results'; +import { LoadingSpinner } from '../../common/components/loading_spinner'; +// common components/hooks +import DocViewer from '../../../../common/doc-viewer/doc-viewer'; +import useSearchBar from '../../../../common/search-bar/use-search-bar'; import { useAppConfig } from '../../../../common/hooks'; +import { useDataGrid } from '../../../../common/data-grid/use-data-grid'; +import { useDocViewer } from '../../../../common/doc-viewer/use-doc-viewer'; +import { withErrorBoundary } from '../../../../common/hocs'; +import { search } from '../../../../common/search-bar/search-bar-service'; +import { exportSearchToCSV } from '../../../../common/data-grid/data-grid-service'; const InventoryVulsComponent = () => { const appConfig = useAppConfig(); const VULNERABILITIES_INDEX_PATTERN_ID = appConfig.data['vulnerabilities.pattern']; - const { searchBarProps } = useSearchBarConfiguration({ + const { searchBarProps } = useSearchBar({ defaultIndexPatternID: VULNERABILITIES_INDEX_PATTERN_ID, }); const { isLoading, filters, query, indexPatterns } = searchBarProps; From 57d2d999f43ee043b7a5de33fed4e08754caf2f2 Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Wed, 20 Dec 2023 09:02:55 -0300 Subject: [PATCH 3/8] Replace imports on dashboard --- .../vulnerabilities/dashboards/overview/dashboard.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx index 5328214a06..85190ce5e9 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx @@ -3,11 +3,11 @@ import { getPlugins } from '../../../../../kibana-services'; import { ViewMode } from '../../../../../../../../src/plugins/embeddable/public'; import { getDashboardPanels } from './dashboard_panels'; import { I18nProvider } from '@osd/i18n/react'; -import useSearchBarConfiguration from '../../search_bar/use_search_bar_configuration'; +import { useAppConfig } from '../../../../common/hooks'; +import useSearchBar from '../../../../common/search-bar/use-search-bar'; import { getDashboardFilters } from './dashboard_panels_filters'; import './vulnerability_detector_filters.scss'; import { getKPIsPanel } from './dashboard_panels_kpis'; -import { useAppConfig } from '../../../../common/hooks'; const plugins = getPlugins(); const SearchBar = getPlugins().data.ui.SearchBar; @@ -23,7 +23,7 @@ export const DashboardVuls: React.FC = () => { const VULNERABILITIES_INDEX_PATTERN_ID = appConfig.data['vulnerabilities.pattern']; - const { searchBarProps } = useSearchBarConfiguration({ + const { searchBarProps } = useSearchBar({ defaultIndexPatternID: VULNERABILITIES_INDEX_PATTERN_ID, filters: [], }); From 8d8436bb04ecb36e619a0e6a4381a4ce6ed002a3 Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Wed, 20 Dec 2023 10:43:18 -0300 Subject: [PATCH 4/8] Updated CHANGELOG --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02971f0fce..627caf2ae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,10 @@ All notable changes to the Wazuh app project will be documented in this file. - Support for Wazuh 4.9.0 - Added AngularJS dependencies [#6145](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6145) -- Remove embedded discover [#6120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6120) + +### Changed + +- Removed embedded discover [#6120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6120) [#6235](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6235) ## Wazuh v4.8.1 - OpenSearch Dashboards 2.10.0 - Revision 00 From 1fa4f4765cb6052fd9b7767a1a90e44a91e30a44 Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Tue, 26 Dec 2023 09:59:25 -0300 Subject: [PATCH 5/8] Add master changes --- .../common/search-bar/use-search-bar.test.ts | 84 ++++++------- .../common/search-bar/use-search-bar.ts | 111 ++++++++++-------- .../common/hooks/useCheckIndexFields.tsx | 4 +- .../dashboards/overview/dashboard.tsx | 15 ++- .../vulnerability_detector_filters.scss | 7 ++ 5 files changed, 118 insertions(+), 103 deletions(-) diff --git a/plugins/main/public/components/common/search-bar/use-search-bar.test.ts b/plugins/main/public/components/common/search-bar/use-search-bar.test.ts index 54b740dcd7..4cef6433b7 100644 --- a/plugins/main/public/components/common/search-bar/use-search-bar.test.ts +++ b/plugins/main/public/components/common/search-bar/use-search-bar.test.ts @@ -73,11 +73,13 @@ describe('[hook] useSearchBarConfiguration', () => { spyUseQueryManager.mockImplementation(() => [mockQueryResult, jest.fn()]); }); - it.only('should return default app index pattern when not receiving a default index pattern', async () => { + it('should return default app index pattern when not receiving a default index pattern', async () => { jest .spyOn(mockDataPlugin.indexPatterns, 'getDefault') .mockResolvedValue(mockedDefaultIndexPatternData); - jest.spyOn(mockDataPlugin.query.filterManager, 'getFilters').mockReturnValue([]); + jest + .spyOn(mockDataPlugin.query.filterManager, 'getFilters') + .mockReturnValue([]); const { result, waitForNextUpdate } = renderHook(() => useSearchBar({})); await waitForNextUpdate(); expect(mockDataPlugin.indexPatterns.getDefault).toBeCalled(); @@ -93,15 +95,21 @@ describe('[hook] useSearchBarConfiguration', () => { id: exampleIndexPatternId, title: '', }; - jest.spyOn(mockDataPlugin.indexPatterns, 'get').mockResolvedValue(mockedIndexPatternData); + jest + .spyOn(mockDataPlugin.indexPatterns, 'get') + .mockResolvedValue(mockedIndexPatternData); const { result, waitForNextUpdate } = renderHook(() => useSearchBar({ defaultIndexPatternID: 'wazuh-index-pattern', - }) + }), ); await waitForNextUpdate(); - expect(mockDataPlugin.indexPatterns.get).toBeCalledWith(exampleIndexPatternId); - expect(result.current.searchBarProps.indexPatterns).toMatchObject([mockedIndexPatternData]); + expect(mockDataPlugin.indexPatterns.get).toBeCalledWith( + exampleIndexPatternId, + ); + expect(result.current.searchBarProps.indexPatterns).toMatchObject([ + mockedIndexPatternData, + ]); }); it('should show an ERROR message and get the default app index pattern when not found the index pattern data by the ID received', async () => { @@ -112,19 +120,25 @@ describe('[hook] useSearchBarConfiguration', () => { jest .spyOn(mockDataPlugin.indexPatterns, 'getDefault') .mockResolvedValue(mockedDefaultIndexPatternData); - jest.spyOn(mockDataPlugin.query.filterManager, 'getFilters').mockReturnValue([]); + jest + .spyOn(mockDataPlugin.query.filterManager, 'getFilters') + .mockReturnValue([]); // mocking console error to avoid logs in test and check if is called - const mockedConsoleError = jest.spyOn(console, 'error').mockImplementationOnce(() => {}); + const mockedConsoleError = jest + .spyOn(console, 'error') + .mockImplementationOnce(() => {}); const { result, waitForNextUpdate } = renderHook(() => useSearchBar({ defaultIndexPatternID: 'invalid-index-pattern-id', - }) + }), ); await waitForNextUpdate(); expect(mockDataPlugin.indexPatterns.getDefault).toBeCalled(); - expect(mockDataPlugin.indexPatterns.get).toBeCalledWith('invalid-index-pattern-id'); + expect(mockDataPlugin.indexPatterns.get).toBeCalledWith( + 'invalid-index-pattern-id', + ); expect(result.current.searchBarProps.indexPatterns).toMatchObject([ mockedDefaultIndexPatternData, ]); @@ -145,50 +159,22 @@ describe('[hook] useSearchBarConfiguration', () => { jest .spyOn(mockDataPlugin.indexPatterns, 'getDefault') .mockResolvedValue(mockedDefaultIndexPatternData); - jest.spyOn(mockDataPlugin.query.filterManager, 'getFilters').mockReturnValue(defaultFilters); + jest + .spyOn(mockDataPlugin.query.filterManager, 'getFilters') + .mockReturnValue(defaultFilters); const { result, waitForNextUpdate } = renderHook(() => useSearchBar({ filters: defaultFilters, - }) + }), ); await waitForNextUpdate(); expect(result.current.searchBarProps.filters).toMatchObject(defaultFilters); - expect(mockDataPlugin.query.filterManager.setFilters).toBeCalledWith(defaultFilters); - expect(mockDataPlugin.query.filterManager.getFilters).toBeCalled(); - }); - - it('should return and preserve filters when the index pattern received is equal to the index pattern already selected in the app', async () => { - const defaultIndexFilters: Filter[] = [ - { - query: 'something to filter', - meta: { - alias: 'filter-mocked', - disabled: false, - negate: true, - }, - }, - ]; - jest - .spyOn(mockDataPlugin.indexPatterns, 'getDefault') - .mockResolvedValue(mockedDefaultIndexPatternData); - jest - .spyOn(mockDataPlugin.indexPatterns, 'get') - .mockResolvedValue(mockedDefaultIndexPatternData); - jest - .spyOn(mockDataPlugin.query.filterManager, 'getFilters') - .mockReturnValue(defaultIndexFilters); - const { result, waitForNextUpdate } = renderHook(() => - useSearchBar({ - defaultIndexPatternID: mockedDefaultIndexPatternData.id, - }) + expect(mockDataPlugin.query.filterManager.setFilters).toBeCalledWith( + defaultFilters, ); - await waitForNextUpdate(); - expect(result.current.searchBarProps.indexPatterns).toMatchObject([ - mockedDefaultIndexPatternData, - ]); - expect(result.current.searchBarProps.filters).toMatchObject(defaultIndexFilters); + expect(mockDataPlugin.query.filterManager.getFilters).toBeCalled(); }); it('should return empty filters when the index pattern is NOT equal to the default app index pattern', async () => { @@ -204,11 +190,13 @@ describe('[hook] useSearchBarConfiguration', () => { jest .spyOn(mockDataPlugin.indexPatterns, 'getDefault') .mockResolvedValue(mockedDefaultIndexPatternData); - jest.spyOn(mockDataPlugin.query.filterManager, 'getFilters').mockReturnValue([]); + jest + .spyOn(mockDataPlugin.query.filterManager, 'getFilters') + .mockReturnValue([]); const { result, waitForNextUpdate } = renderHook(() => useSearchBar({ defaultIndexPatternID: exampleIndexPatternId, - }) + }), ); await waitForNextUpdate(); expect(result.current.searchBarProps.indexPatterns).toMatchObject([ @@ -216,4 +204,4 @@ describe('[hook] useSearchBarConfiguration', () => { ]); expect(result.current.searchBarProps.filters).toStrictEqual([]); }); -}); +}); \ No newline at end of file diff --git a/plugins/main/public/components/common/search-bar/use-search-bar.ts b/plugins/main/public/components/common/search-bar/use-search-bar.ts index 30025f9676..4c5e9d1019 100644 --- a/plugins/main/public/components/common/search-bar/use-search-bar.ts +++ b/plugins/main/public/components/common/search-bar/use-search-bar.ts @@ -13,6 +13,7 @@ import { getDataPlugin } from '../../../kibana-services'; import { useFilterManager, useQueryManager, useTimeFilter } from '../hooks'; import { AUTHORIZED_AGENTS } from '../../../../common/constants'; +// Input - types type tUseSearchBarCustomInputs = { defaultIndexPatternID: IIndexPattern['id']; onFiltersUpdated?: (filters: Filter[]) => void; @@ -37,6 +38,7 @@ const useSearchBar = ( props?: tUseSearchBarProps, ): tUserSearchBarResponse => { // dependencies + const SESSION_STORAGE_FILTERS_NAME = 'wazuh_persistent_searchbar_filters'; const filterManager = useFilterManager().filterManager as FilterManager; const { filters } = useFilterManager(); const [query, setQuery] = props?.query @@ -49,20 +51,29 @@ const useSearchBar = ( useState(); useEffect(() => { + if (filters && filters.length > 0) { + sessionStorage.setItem( + SESSION_STORAGE_FILTERS_NAME, + JSON.stringify(filters), + ); + } initSearchBar(); + /** + * When the component is disassembled, the original filters that arrived + * when the component was assembled are added. + */ + return () => { + const storagePreviousFilters = sessionStorage.getItem( + SESSION_STORAGE_FILTERS_NAME, + ); + if (storagePreviousFilters) { + const previousFilters = JSON.parse(storagePreviousFilters); + const cleanedFilters = cleanFilters(previousFilters); + filterManager.setFilters(cleanedFilters); + } + }; }, []); - useEffect(() => { - const defaultIndex = props?.defaultIndexPatternID; - /* Filters that do not belong to the default index are filtered */ - const cleanedFilters = filters.filter( - filter => filter.meta.index === defaultIndex, - ); - if (cleanedFilters.length !== filters.length) { - filterManager.setFilters(cleanedFilters); - } - }, [filters]); - /** * Initialize the searchbar props with the corresponding index pattern and filters */ @@ -70,8 +81,8 @@ const useSearchBar = ( setIsLoading(true); const indexPattern = await getIndexPattern(props?.defaultIndexPatternID); setIndexPatternSelected(indexPattern); - const filters = await getInitialFilters(indexPattern); - filterManager.setFilters(filters); + const initialFilters = props?.filters ?? filters; + filterManager.setFilters(initialFilters); setIsLoading(false); }; @@ -87,7 +98,7 @@ const useSearchBar = ( try { return await indexPatternService.get(indexPatternID); } catch (error) { - // when the index pattern id not exists will get the default defined in the index pattern service + // when the index pattern id not exists will get the default console.error(error); return await indexPatternService.getDefault(); } @@ -97,44 +108,33 @@ const useSearchBar = ( }; /** - * Return the initial filters considering if hook receives initial filters - * When the default index pattern is the same like the received preserve the filters - * @param indexPattern + * Return filters from filters manager. + * Additionally solve the known issue with the auto loaded agent.id filters from the searchbar + * and filters those filters that are not related to the default index pattern * @returns */ - const getInitialFilters = async (indexPattern: IIndexPattern) => { - const indexPatternService = getDataPlugin() - .indexPatterns as IndexPatternsContract; - let initialFilters: Filter[] = []; - if (props?.filters) { - return props?.filters; - } - if (indexPattern) { - // get filtermanager and filters - // if the index is the same, get filters stored - // else clear filters - const defaultIndexPattern = - (await indexPatternService.getDefault()) as IIndexPattern; - initialFilters = - defaultIndexPattern.id === indexPattern.id - ? filterManager.getFilters() - : []; - } else { - initialFilters = []; - } - return initialFilters; + const getFilters = () => { + const originalFilters = filterManager ? filterManager.getFilters() : []; + return originalFilters.filter( + (filter: Filter) => + filter?.meta?.controlledBy !== AUTHORIZED_AGENTS && // remove auto loaded agent.id filters + filter?.meta?.index === props?.defaultIndexPatternID, + ); }; /** - * Return filters from filters manager. - * Additionally solve the known issue with the auto loaded agent.id filters from the searchbar + * Return cleaned filters. + * Clean the known issue with the auto loaded agent.id filters from the searchbar + * and filters those filters that are not related to the default index pattern + * @param previousFilters * @returns */ - const getFilters = () => { - const filters = filterManager ? filterManager.getFilters() : []; - return filters.filter( - filter => filter.meta.controlledBy !== AUTHORIZED_AGENTS, - ); // remove auto loaded agent.id filters + const cleanFilters = (previousFilters: Filter[]) => { + return previousFilters.filter( + (filter: Filter) => + filter?.meta?.controlledBy !== AUTHORIZED_AGENTS && + filter?.meta?.index !== props?.defaultIndexPatternID, + ); }; /** @@ -149,9 +149,24 @@ const useSearchBar = ( dateRangeFrom: timeFilter.from, dateRangeTo: timeFilter.to, onFiltersUpdated: (filters: Filter[]) => { - // its necessary execute setter to apply filters - filterManager.setFilters(filters); - props?.onFiltersUpdated && props?.onFiltersUpdated(filters); + const storagePreviousFilters = sessionStorage.getItem( + SESSION_STORAGE_FILTERS_NAME, + ); + /** + * If there are persisted filters, it is necessary to add them when + * updating the filters in the filterManager + */ + if (storagePreviousFilters) { + const previousFilters = JSON.parse(storagePreviousFilters); + const cleanedFilters = cleanFilters(previousFilters); + filterManager.setFilters([...cleanedFilters, ...filters]); + + props?.onFiltersUpdated && + props?.onFiltersUpdated([...cleanedFilters, ...filters]); + } else { + filterManager.setFilters(filters); + props?.onFiltersUpdated && props?.onFiltersUpdated(filters); + } }, onQuerySubmit: ( payload: { dateRange: TimeRange; query?: Query }, diff --git a/plugins/main/public/components/overview/vulnerabilities/common/hooks/useCheckIndexFields.tsx b/plugins/main/public/components/overview/vulnerabilities/common/hooks/useCheckIndexFields.tsx index 75112cbc85..3a3ce9aa7a 100644 --- a/plugins/main/public/components/overview/vulnerabilities/common/hooks/useCheckIndexFields.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/common/hooks/useCheckIndexFields.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { search } from '../../dashboards/inventory/inventory_service'; +import { search } from '../../../../common/search-bar/search-bar-service'; import { IIndexPattern, IndexPattern, @@ -69,7 +69,7 @@ const useCheckIndexFields = ( checkIndexFields(); } - }, [indexPatternId]); + }, [indexPatternId, query, indexPattern]); return { isError, diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx index 6c8a0350fe..c29bc6393b 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx @@ -27,7 +27,6 @@ const DashboardVulsComponent: React.FC = () => { const appConfig = useAppConfig(); const VULNERABILITIES_INDEX_PATTERN_ID = appConfig.data['vulnerabilities.pattern']; - const { searchBarProps } = useSearchBar({ defaultIndexPatternID: VULNERABILITIES_INDEX_PATTERN_ID, }); @@ -63,11 +62,17 @@ const DashboardVulsComponent: React.FC = () => { ) : null} {!isLoadingSearchbar && !isLoading && - (isError || resultIndexData?.hits?.total === 0) ? ( + (isError || + !resultIndexData || + resultIndexData?.hits?.total === 0) ? ( ) : null} - {!isLoadingSearchbar && !isLoading && isSuccess ? ( - <> + {!isLoadingSearchbar && + !isLoading && + isSuccess && + resultIndexData && + resultIndexData?.hits?.total !== 0 ? ( +
{ hidePanelTitles: false, }} /> - +
) : null} diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/vulnerability_detector_filters.scss b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/vulnerability_detector_filters.scss index 631158a73e..041fb3f19c 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/vulnerability_detector_filters.scss +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/vulnerability_detector_filters.scss @@ -4,3 +4,10 @@ } } +.vulnerability-dashboard-responsive { + @media (max-width: 767px) { + .react-grid-layout { + height: auto !important; + } + } +} From 07ddd3630f45a8e05c36625db1b8643c18aef379 Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Tue, 26 Dec 2023 12:37:26 -0300 Subject: [PATCH 6/8] Solve wrong import path --- .../components/overview/fim/inventory/inventory.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/main/public/components/overview/fim/inventory/inventory.tsx b/plugins/main/public/components/overview/fim/inventory/inventory.tsx index 39503b0e94..e9708c8522 100644 --- a/plugins/main/public/components/overview/fim/inventory/inventory.tsx +++ b/plugins/main/public/components/overview/fim/inventory/inventory.tsx @@ -18,20 +18,20 @@ import { } from '@elastic/eui'; import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; import { SearchResponse } from '../../../../../../../../src/core/server'; -import DocViewer from '../../vulnerabilities/doc_viewer/doc_viewer'; +import DocViewer from '../../../common/doc-viewer/doc-viewer'; import { DiscoverNoResults } from '../../vulnerabilities/common/components/no_results'; import { LoadingSpinner } from '../../vulnerabilities/common/components/loading_spinner'; -import { useDataGrid } from '../../vulnerabilities/data_grid/use_data_grid'; +import { useDataGrid } from '../../../common/data-grid/use-data-grid'; import { MAX_ENTRIES_PER_QUERY, inventoryTableDefaultColumns, } from '../../vulnerabilities/dashboards/inventory/config'; -import { useDocViewer } from '../../vulnerabilities/doc_viewer/use_doc_viewer'; +import { useDocViewer } from '../../../common/doc-viewer/use-doc-viewer'; import './inventory.scss'; import { search, exportSearchToCSV, -} from '../../vulnerabilities/dashboards/inventory/inventory_service'; +} from '../../../common/search-bar/search-bar-service'; import { ErrorHandler, ErrorFactory, From 86a4993f83d02b58ea56c994a8db8700daf3d20c Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Tue, 26 Dec 2023 12:39:35 -0300 Subject: [PATCH 7/8] Fix CHANGELOG --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3e98c6474..93351f1003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,6 @@ All notable changes to the Wazuh app project will be documented in this file. ### Changed - Removed embedded discover [#6120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6120) [#6235](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6235) -- Remove embedded discover [#6120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6120) - Develop logic of a new index for the fim module [#6227](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6227) ## Wazuh v4.8.1 - OpenSearch Dashboards 2.10.0 - Revision 00 From 83d20e88079fd638174a8224fc05fa878fdac9fb Mon Sep 17 00:00:00 2001 From: Maximiliano Date: Fri, 29 Dec 2023 01:30:01 -0300 Subject: [PATCH 8/8] Resolve requested changes --- .../main/public/components/common/doc-viewer/doc-viewer.tsx | 3 +-- .../vulnerabilities/dashboards/inventory/inventory.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/main/public/components/common/doc-viewer/doc-viewer.tsx b/plugins/main/public/components/common/doc-viewer/doc-viewer.tsx index 79ef91eda1..bd36df61c0 100644 --- a/plugins/main/public/components/common/doc-viewer/doc-viewer.tsx +++ b/plugins/main/public/components/common/doc-viewer/doc-viewer.tsx @@ -115,12 +115,11 @@ const DocViewer = (props: tDocViewerProps) => { - + {displayName} diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx index 7591e3a19f..b92b1d9ae9 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx @@ -221,7 +221,7 @@ const InventoryVulsComponent = () => { } />