From 79837fe6718f60a809bdaa0fe386936dd32d7e8f Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Tue, 15 Oct 2024 16:13:24 +0200 Subject: [PATCH] Add custom saved queries and fix duplicate requests (#7090) * Add custom saved queries * Add changelog * Add setTimeFilter function to save and update saved queries * Remove absolute date and add fingerprint to lastReloadRequestTime * Add explanation to fingerprint state * Add support for setting and updating refresh interval * Update saveSavedQuery function to handle newSavedQuery * Refactor saved query functions for better organization * Refactor functions to accept options for first time use * Add absolute date range for data grids * Refactor saved query hooks for setQuery and setFilters * Remove saved query clearing functionality * Refactor useSavedQuery function for better readability * Delete unused test and function for saved queries * Add jest spy for setFilters in useSearchBar hook test * Fix search-bar autorefresh feature * Apply autoRefreshFingerprint in dashboards * Add fingerprint to Vulnerabilities module * Refactor useSearchBar test for readability * Fix unit test * Prettier * Hide save query button in flyout search bar * Fix absolute date string format * By default show the save query button in the saved queries popover --------- Co-authored-by: Guido Modarelli --- CHANGELOG.md | 1 + plugins/main/common/constants.ts | 3 + .../common/data-grid/use-data-grid.ts | 1 + .../data-source/hooks/use-data-source.ts | 1 - .../common/hooks/saved_query/constants.ts | 1 + .../hooks/saved_query/use_saved_query.ts | 185 +++++++++++++++ .../common/hooks/use-time-filter.ts | 17 +- .../components/common/permissions/button.tsx | 17 +- .../common/search-bar/search-bar.tsx | 10 +- .../common/search-bar/use-search-bar.test.ts | 45 +++- .../common/search-bar/use-search-bar.ts | 87 +++++-- .../wazuh-discover/config/histogram-chart.ts | 2 + .../common/wazuh-discover/wz-discover.tsx | 40 +++- .../wazuh-discover/wz-flyout-discover.tsx | 65 ++++-- .../components/configuration_cards.tsx | 3 + .../cluster/components/overview_cards.tsx | 8 +- .../cluster/dashboard/dashboard.tsx | 16 +- .../dashboards/dashboard.tsx | 16 +- .../compliance-table/compliance-table.tsx | 15 +- .../overview/docker/dashboards/dashboard.tsx | 16 +- .../overview/fim/dashboard/dashboard.tsx | 16 +- .../overview/gdpr/dashboards/dashboard.tsx | 16 +- .../overview/github/dashboards/dashboard.tsx | 16 +- .../github/panel/config/drilldown-action.tsx | 5 +- .../github/panel/config/drilldown-actor.tsx | 5 +- .../panel/config/drilldown-organization.tsx | 5 +- .../panel/config/drilldown-repository.tsx | 5 +- .../google-cloud/dashboards/dashboard.tsx | 16 +- .../overview/hipaa/dashboards/dashboard.tsx | 16 +- .../malware-detection/dashboard/dashboard.tsx | 16 +- .../overview/mitre/dashboard/dashboard.tsx | 16 +- .../overview/mitre/framework/mitre.tsx | 19 +- .../overview/nist/dashboards/dashboard.tsx | 16 +- .../overview/office/dashboard/dashboard.tsx | 19 +- .../public/components/overview/overview.tsx | 13 +- .../overview/pci/dashboards/dashboard.tsx | 16 +- .../dashboards/dashboardTabsPanels.tsx | 16 +- .../dashboards/dashboard_analysis_engine.tsx | 3 + .../dashboards/dashboard_listener_engine.tsx | 3 + .../threat-hunting/dashboard/dashboard.tsx | 19 +- .../overview/tsc/dashboards/dashboard.tsx | 16 +- .../virustotal/dashboard/dashboard.tsx | 19 +- .../dashboards/inventory/inventory.tsx | 3 +- .../dashboards/overview/dashboard.tsx | 212 +++++++++--------- .../management/common/resources-handler.ts | 3 +- plugins/main/public/kibana-services.ts | 2 +- plugins/main/public/react-services/index.ts | 1 + .../public/react-services/state-storage.ts | 42 ++++ 48 files changed, 809 insertions(+), 294 deletions(-) create mode 100644 plugins/main/public/components/common/hooks/saved_query/constants.ts create mode 100644 plugins/main/public/components/common/hooks/saved_query/use_saved_query.ts create mode 100644 plugins/main/public/react-services/state-storage.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4db2d778..8d55a1c879 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Fixed style when unnpinned an agent in endpoint summary section [#7015](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7015) - Fixed overflow style on a long value filter [#7021](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7021) - Fixed buttons enabled for a readonly user in `Endpoint groups` section [#7056](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7056) +- Fixed the automatic page refresh in dashboards and prevent duplicate requests [#7090](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7090) ### Changed diff --git a/plugins/main/common/constants.ts b/plugins/main/common/constants.ts index 62eb2fc7ec..8c1ba88829 100644 --- a/plugins/main/common/constants.ts +++ b/plugins/main/common/constants.ts @@ -528,3 +528,6 @@ export const SEARCH_BAR_DEBOUNCE_UPDATE_TIME = 400; // ID used to refer the createOsdUrlStateStorage state export const OSD_URL_STATE_STORAGE_ID = 'state:storeInSessionStorage'; + +export const APP_STATE_URL_KEY = '_a'; +export const GLOBAL_STATE_URL_KEY = '_g'; diff --git a/plugins/main/public/components/common/data-grid/use-data-grid.ts b/plugins/main/public/components/common/data-grid/use-data-grid.ts index a330fc5ad1..9caf231c31 100644 --- a/plugins/main/public/components/common/data-grid/use-data-grid.ts +++ b/plugins/main/public/components/common/data-grid/use-data-grid.ts @@ -236,5 +236,6 @@ export const useDataGrid = (props: tDataGridProps): EuiDataGridProps => { onChangeItemsPerPage: onChangeItemsPerPage, onChangePage: onChangePage, }, + setPagination, } as EuiDataGridProps; }; diff --git a/plugins/main/public/components/common/data-source/hooks/use-data-source.ts b/plugins/main/public/components/common/data-source/hooks/use-data-source.ts index 6a026e7b3a..0fec9b7db9 100644 --- a/plugins/main/public/components/common/data-source/hooks/use-data-source.ts +++ b/plugins/main/public/components/common/data-source/hooks/use-data-source.ts @@ -98,7 +98,6 @@ export function useDataSource< const { filters: initialFilters = [...defaultFilters], fetchFilters: initialFetchFilters = [], - fixedFilters: initialFixedFilters = [], DataSource: DataSourceConstructor, repository, factory: injectedFactory, diff --git a/plugins/main/public/components/common/hooks/saved_query/constants.ts b/plugins/main/public/components/common/hooks/saved_query/constants.ts new file mode 100644 index 0000000000..59d4f85684 --- /dev/null +++ b/plugins/main/public/components/common/hooks/saved_query/constants.ts @@ -0,0 +1 @@ +export const SAVED_QUERY = 'savedQuery'; diff --git a/plugins/main/public/components/common/hooks/saved_query/use_saved_query.ts b/plugins/main/public/components/common/hooks/saved_query/use_saved_query.ts new file mode 100644 index 0000000000..de1b8ec4f8 --- /dev/null +++ b/plugins/main/public/components/common/hooks/saved_query/use_saved_query.ts @@ -0,0 +1,185 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { useState, useEffect } from 'react'; +import { + DataPublicPluginStart, + RefreshInterval, + SavedQuery, + TimeRange, + Query, + Filter, +} from '../../../../../../../src/plugins/data/public'; +import NavigationService from '../../../../react-services/navigation-service'; +import { OSD_URL_STATE_STORAGE_ID } from '../../../../../common/constants'; +import { getDataPlugin, getUiSettings } from '../../../../kibana-services'; +import { createOsdUrlStateStorage } from '../../../../../../../src/plugins/opensearch_dashboards_utils/public'; +import OsdUrlStateStorage from '../../../../react-services/state-storage'; + +interface Options { + firstTime?: boolean; +} + +interface UseSavedQueriesProps { + queryService: DataPublicPluginStart['query']; + setTimeFilter: (timeFilter: TimeRange) => void; + setRefreshInterval: (refreshInterval: RefreshInterval) => void; + setQuery: (query?: Query) => void; + setFilters: (filters: Filter[]) => void; +} + +interface UseSavedQueriesReturn { + savedQuery?: SavedQuery; + setSavedQuery: (savedQuery: SavedQuery) => void; + clearSavedQuery: () => void; +} + +export const useSavedQuery = ( + props: UseSavedQueriesProps, +): UseSavedQueriesReturn => { + // Handle saved queries + const [savedQuery, setSavedQuery] = useState(); + const data = getDataPlugin(); + const config = getUiSettings(); + const history = NavigationService.getInstance().getHistory(); + const osdUrlStateStorage = createOsdUrlStateStorage({ + useHash: config.get(OSD_URL_STATE_STORAGE_ID), + history: history, + }); + + const getAppFilters = ( + newSavedQuery?: SavedQuery, + { firstTime }: Options = { firstTime: false }, + ) => { + const { filterManager } = props.queryService; + // When the page reloads, savedQuery starts as undefined, so retrieve the time and refreshInterval from the URL. + return firstTime + ? filterManager.getAppFilters() + : newSavedQuery?.attributes.filters ?? []; + }; + + const getQuery = ( + newSavedQuery?: SavedQuery, + { firstTime }: Options = { firstTime: false }, + ) => { + const { queryString } = props.queryService; + // When the page reloads, savedQuery starts as undefined, so retrieve the time and refreshInterval from the URL. + return firstTime + ? queryString.getQuery() + : newSavedQuery?.attributes.query ?? { query: '', language: 'kuery' }; + }; + + const getTimeFilter = ( + newSavedQuery?: SavedQuery, + { firstTime }: Options = { firstTime: false }, + ) => { + const { timefilter } = props.queryService; + // When the page reloads, savedQuery starts as undefined, so retrieve the time and refreshInterval from the URL. + return firstTime + ? timefilter.timefilter.getTime() + : newSavedQuery?.attributes.timefilter; + }; + + const getRefreshInterval = ( + newSavedQuery?: SavedQuery, + { firstTime }: Options = { firstTime: false }, + ) => { + const { timefilter } = props.queryService; + // When the page reloads, savedQuery starts as undefined, so retrieve the time and refreshInterval from the URL. + return firstTime + ? timefilter.timefilter.getRefreshInterval() + : newSavedQuery?.attributes.timefilter?.refreshInterval; + }; + + const setTimeFilter = (timeFilter: TimeRange) => { + props.setTimeFilter(timeFilter); + props.queryService.timefilter.timefilter.setTime(timeFilter); + }; + + const saveSavedQuery = async ( + newSavedQuery?: SavedQuery, + { firstTime }: Options = { firstTime: false }, + ) => { + setSavedQuery(newSavedQuery); + const filters = getAppFilters(newSavedQuery, { firstTime }); + const query = getQuery(newSavedQuery, { firstTime }); + await OsdUrlStateStorage(data, osdUrlStateStorage).replaceUrlAppState({ + savedQuery: newSavedQuery?.id, + }); + props.setFilters(filters); + props.setQuery(query); + if (newSavedQuery?.attributes.timefilter) { + setTimeFilter(getTimeFilter(newSavedQuery, { firstTime })); + props.setRefreshInterval( + getRefreshInterval(newSavedQuery, { firstTime }), + ); + } + }; + + const updateSavedQuery = async ( + savedQuery: SavedQuery, + { firstTime }: { firstTime?: boolean } = { firstTime: false }, + ) => { + saveSavedQuery(savedQuery, { firstTime }); + }; + + const clearSavedQuery = () => { + // remove saved query from url + saveSavedQuery(undefined); + }; + + // Effect is used to convert a saved query id into an object + useEffect(() => { + const fetchSavedQuery = async () => { + try { + const savedQueryId = OsdUrlStateStorage( + data, + osdUrlStateStorage, + ).getAppStateFromUrl().savedQuery as string; + if (!savedQueryId) return; + // fetch saved query + const savedQuery = await props.queryService.savedQueries.getSavedQuery( + savedQueryId, + ); + updateSavedQuery(savedQuery, { firstTime: true }); + } catch (error) { + clearSavedQuery(); + } + }; + + fetchSavedQuery(); + }, [props.queryService, props.queryService.savedQueries]); + + return { + savedQuery, + setSavedQuery: updateSavedQuery, + clearSavedQuery, + }; +}; diff --git a/plugins/main/public/components/common/hooks/use-time-filter.ts b/plugins/main/public/components/common/hooks/use-time-filter.ts index 384169f65f..f580cb38aa 100644 --- a/plugins/main/public/components/common/hooks/use-time-filter.ts +++ b/plugins/main/public/components/common/hooks/use-time-filter.ts @@ -30,15 +30,30 @@ export function useTimeFilter() { const [timeFilter, setTimeFilter] = useState( globalStateFromUrl?.time ?? timefilter.getTime(), ); + const [refreshInterval, setRefreshInterval] = useState( + globalStateFromUrl?.refreshInterval ?? timefilter.getRefreshInterval(), + ); const [timeHistory, setTimeHistory] = useState(timefilter._history); useEffect(() => { const subscription = timefilter.getTimeUpdate$().subscribe(() => { setTimeFilter(timefilter.getTime()); setTimeHistory(timefilter._history); }); + const subscriptionRefreshInterval = timefilter + .getRefreshIntervalUpdate$() + .subscribe(() => { + setRefreshInterval(timefilter.getRefreshInterval()); + }); return () => { subscription.unsubscribe(); + subscriptionRefreshInterval.unsubscribe(); }; }, []); - return { timeFilter, setTimeFilter: timefilter.setTime, timeHistory }; + return { + timeFilter, + setTimeFilter: timefilter.setTime, + refreshInterval, + setRefreshInterval: timefilter.setRefreshInterval, + timeHistory, + }; } diff --git a/plugins/main/public/components/common/permissions/button.tsx b/plugins/main/public/components/common/permissions/button.tsx index 6033960584..5f4d920e69 100644 --- a/plugins/main/public/components/common/permissions/button.tsx +++ b/plugins/main/public/components/common/permissions/button.tsx @@ -18,18 +18,19 @@ import { EuiButtonEmpty, EuiButtonIcon, EuiLink, + EuiButtonProps, } from '@elastic/eui'; import { IWzElementPermissionsProps, WzElementPermissions } from './element'; -interface IWzButtonPermissionsProps - extends Omit< - IWzElementPermissionsProps, - 'children' | 'additionalPropsFunction' - > { - buttonType?: 'default' | 'empty' | 'icon' | 'link' | 'switch'; - rest: any; -} +type IWzButtonPermissionsProps = Omit< + IWzElementPermissionsProps, + 'children' | 'additionalPropsFunction' +> & + React.ButtonHTMLAttributes & + EuiButtonProps & { + buttonType?: 'default' | 'empty' | 'icon' | 'link' | 'switch'; + }; export const WzButtonPermissions = ({ buttonType = 'default', diff --git a/plugins/main/public/components/common/search-bar/search-bar.tsx b/plugins/main/public/components/common/search-bar/search-bar.tsx index 3d713958af..50001fb25c 100644 --- a/plugins/main/public/components/common/search-bar/search-bar.tsx +++ b/plugins/main/public/components/common/search-bar/search-bar.tsx @@ -13,10 +13,14 @@ export interface WzSearchBarProps extends SearchBarProps { preQueryBar?: React.ReactElement; postFilters?: React.ReactElement; hideFixedFilters?: boolean; + showSaveQueryButton?: boolean; + showSaveQuery?: boolean; } export const WzSearchBar = ({ fixedFilters = [], + showSaveQueryButton = true, + showSaveQuery = true, preQueryBar, hideFixedFilters, postFilters, @@ -48,7 +52,11 @@ export const WzSearchBar = ({ > {preQueryBar ? {preQueryBar} : null} - + ) : null} 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 6b0fc60ab1..e0c04a6315 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 @@ -16,6 +16,8 @@ import { getDataPlugin } from '../../../kibana-services'; import * as timeFilterHook from '../hooks/use-time-filter'; import * as queryManagerHook from '../hooks/use-query'; import { AppState } from '../../../react-services/app-state'; +import NavigationService from '../../../react-services/navigation-service'; +import { createHashHistory, History } from 'history'; /** * Mocking Data Plugin @@ -23,8 +25,12 @@ import { AppState } from '../../../react-services/app-state'; jest.mock('../../../kibana-services', () => { return { getDataPlugin: jest.fn(), + getUiSettings: jest.fn().mockImplementation(() => ({ + get: () => true, + })), }; }); + /* using osd mock utils */ const mockDataPlugin = dataPluginMock.createStartContract(); const mockedGetDataPlugin = getDataPlugin as jest.Mock; @@ -42,10 +48,25 @@ mockedGetDataPlugin.mockImplementation( unsubscribe: jest.fn(), })), }, + timefilter: { + ...mockDataPlugin.query.queryString.timefilter, + timefilter: { + getRefreshInterval: jest.fn().mockImplementation(() => ({ + value: 0, + pause: false, + })), + getAutoRefreshFetch$: jest.fn().mockImplementation(() => ({ + subscribe: jest + .fn() + .mockImplementation(() => ({ unsubscribe: jest.fn() })), + })), + }, + }, }, }, } as Start), ); + /////////////////////////////////////////////////////////// const mockedDefaultIndexPatternData: Partial = { @@ -55,6 +76,9 @@ const mockedDefaultIndexPatternData: Partial = { }; describe('[hook] useSearchBarConfiguration', () => { + let history: History; + let navigationService: NavigationService; + beforeAll(() => { /***** mock use-time-filter hook *****/ const spyUseTimeFilter = jest.spyOn(timeFilterHook, 'useTimeFilter'); @@ -76,6 +100,11 @@ describe('[hook] useSearchBarConfiguration', () => { spyUseQueryManager.mockImplementation(() => [mockQueryResult, jest.fn()]); }); + beforeEach(() => { + history = createHashHistory(); + navigationService = NavigationService.getInstance(history); + }); + it('should return default app index pattern when not receiving a default index pattern', async () => { jest .spyOn(AppState, 'getCurrentPattern') @@ -87,7 +116,11 @@ describe('[hook] useSearchBarConfiguration', () => { .spyOn(mockDataPlugin.query.filterManager, 'getFilters') .mockReturnValue([]); // @ts-ignore - const { result, waitForNextUpdate } = renderHook(() => useSearchBar({})); + const { result, waitForNextUpdate } = renderHook(() => + useSearchBar({ + setFilters: jest.fn(), + }), + ); await waitForNextUpdate(); expect(mockDataPlugin.indexPatterns.getDefault).toBeCalled(); expect(result.current.searchBarProps.indexPatterns).toMatchObject([ @@ -143,7 +176,7 @@ describe('[hook] useSearchBarConfiguration', () => { const { result, waitForNextUpdate } = renderHook(() => useSearchBar({ indexPattern: mockedExampleIndexPatternData as IndexPattern, - setFilters: jest.fn() + setFilters: jest.fn(), }), ); expect(result.current.searchBarProps.indexPatterns).toMatchObject([ @@ -174,11 +207,11 @@ describe('[hook] useSearchBarConfiguration', () => { .mockReturnValue([]); const { result, waitForNextUpdate, rerender } = renderHook( // @ts-ignore - (props) => useSearchBar(props), + props => useSearchBar(props), { initialProps: { indexPattern: mockedExampleIndexPatternData as IndexPattern, - setFilters: jest.fn() + setFilters: jest.fn(), }, }, ); @@ -195,10 +228,10 @@ describe('[hook] useSearchBarConfiguration', () => { .mockResolvedValue(newExampleIndexPatternData); rerender({ indexPattern: newExampleIndexPatternData as IndexPattern, - setFilters: jest.fn() + setFilters: jest.fn(), }); expect(result.current.searchBarProps.indexPatterns).toMatchObject([ newExampleIndexPatternData, ]); - }) + }); }); 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 b11c0e5113..747527e217 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 @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; +import { isEqual } from 'lodash'; import { SearchBarProps, TimeRange, @@ -9,14 +10,14 @@ import { } from '../../../../../../src/plugins/data/public'; import { getDataPlugin } from '../../../kibana-services'; import { useQueryManager, useTimeFilter } from '../hooks'; -import { transformDateRange } from './search-bar-service'; +import { useSavedQuery } from '../hooks/saved_query/use_saved_query'; // Input - types type tUseSearchBarCustomInputs = { indexPattern: IndexPattern; setFilters: (filters: Filter[]) => void; setTimeFilter?: (timeRange: TimeRange) => void; - setQuery?: (query: Query) => void; + setQuery?: (query?: Query) => void; onFiltersUpdated?: (filters: Filter[]) => void; onQuerySubmitted?: ( payload: { dateRange: TimeRange; query?: Query }, @@ -31,9 +32,10 @@ type tUserSearchBarResponse = { searchBarProps: Partial< SearchBarProps & { useDefaultBehaviors: boolean; - absoluteDateRange: TimeRange; } >; + fingerprint: number; + autoRefreshFingerprint: number; }; /** @@ -44,25 +46,40 @@ type tUserSearchBarResponse = { const useSearchBarConfiguration = ( props: tUseSearchBarProps, ): tUserSearchBarResponse => { - const { indexPattern, filters: defaultFilters, setFilters } = props; + const { indexPattern, filters = [], setFilters } = props; // dependencies const { timeFilter: globalTimeFilter, timeHistory, setTimeFilter: setGlobalTimeFilter, + setRefreshInterval, } = useTimeFilter(); - const filters = defaultFilters ?? []; const [timeFilter, setTimeFilter] = useState(globalTimeFilter); const [query, setQuery] = props?.setQuery ? useState(props?.query || { query: '', language: 'kuery' }) : useQueryManager(); - // This absoluteDateRange is used to ensure that the date range is the same when we make the - // pagination request with relative dates like "Last 24 hours" - const [absoluteDateRange, setAbsoluteDateRange] = useState( - transformDateRange(globalTimeFilter), + /* The state of the fingerprint is meant to pass this value to "lastReloadRequestTime" in + * the Dashboards embeddables so they refresh when the user clicks on the Update button in the search bar. + */ + const [fingerprint, setFingerprint] = useState(Date.now()); + + /* + This fingerprint is used for auto refresh of time filter + */ + const [autoRefreshFingerprint, setAutoRefreshFingerprint] = useState( + Date.now(), ); + + const { query: queryService } = getDataPlugin(); + const { savedQuery, setSavedQuery, clearSavedQuery } = useSavedQuery({ + queryService, + setTimeFilter, + setRefreshInterval, + setFilters, + setQuery, + }); // states const [isLoading, setIsLoading] = useState(true); const [indexPatternSelected, setIndexPatternSelected] = @@ -74,10 +91,20 @@ const useSearchBarConfiguration = ( } }, [indexPattern]); + useEffect(() => setTimeFilter(globalTimeFilter), [globalTimeFilter]); + useEffect(() => { initSearchBar(); }, []); + // Subscribe to changes in the search bar auto refresh feature (every 5 seconds, etc.) + useEffect(() => { + const subscription = queryService.timefilter.timefilter + .getAutoRefreshFetch$() + .subscribe(() => setAutoRefreshFingerprint(Date.now())); + return () => subscription.unsubscribe(); + }, []); + /** * Initialize the searchbar props with the corresponding index pattern and filters */ @@ -106,10 +133,10 @@ const useSearchBarConfiguration = ( /** * Search bar properties necessary to render and initialize the osd search bar component */ + const searchBarProps: Partial< SearchBarProps & { useDefaultBehaviors: boolean; - absoluteDateRange: TimeRange; } > = { isLoading, @@ -117,7 +144,6 @@ const useSearchBarConfiguration = ( filters, query, timeHistory, - absoluteDateRange, dateRangeFrom: timeFilter.from, dateRangeTo: timeFilter.to, onFiltersUpdated: (userFilters: Filter[]) => { @@ -126,28 +152,51 @@ const useSearchBarConfiguration = ( : console.warn('setFilters function is not defined'); props?.onFiltersUpdated && props?.onFiltersUpdated(userFilters); }, + refreshInterval: + queryService.timefilter.timefilter.getRefreshInterval().value, + isRefreshPaused: + queryService.timefilter.timefilter.getRefreshInterval().pause, + onRefreshChange: (options: { + isPaused: boolean; + refreshInterval: number; + }) => { + const { timefilter } = queryService.timefilter; + timefilter.setRefreshInterval({ + value: options.refreshInterval, + pause: options.isPaused, + }); + }, onQuerySubmit: ( payload: { dateRange: TimeRange; query?: Query }, _isUpdate?: boolean, ): void => { - const { dateRange, query } = payload; + const { dateRange: newDateRange, query: newQuery } = payload; // its necessary execute setter to apply query filters // when the hook receives the dateRange use the setter instead the global setTimeFilter props?.setTimeFilter - ? props?.setTimeFilter(dateRange) - : setGlobalTimeFilter(dateRange); - props?.setQuery ? props?.setQuery(query) : setQuery(query); + ? props?.setTimeFilter(newDateRange) + : setGlobalTimeFilter(newDateRange); + props?.setQuery ? props?.setQuery(newQuery) : setQuery(newQuery); props?.onQuerySubmitted && props?.onQuerySubmitted(payload); - setTimeFilter(dateRange); - setAbsoluteDateRange(transformDateRange(dateRange)); - setQuery(query); + // Change the fingerprint only when the search parameter are all the same. This should happen only when the user clicks on Update button + if (isEqual(newDateRange, timeFilter) && isEqual(query, newQuery)) { + setFingerprint(Date.now()); + } + + setTimeFilter(newDateRange); + setQuery(newQuery); }, // its necessary to use saved queries. if not, the load saved query not work - useDefaultBehaviors: true, + onClearSavedQuery: clearSavedQuery, + onSaved: setSavedQuery, + onSavedQueryUpdated: setSavedQuery, + savedQuery, }; return { searchBarProps, + fingerprint, + autoRefreshFingerprint, }; }; diff --git a/plugins/main/public/components/common/wazuh-discover/config/histogram-chart.ts b/plugins/main/public/components/common/wazuh-discover/config/histogram-chart.ts index 5c7966dfbd..db8a791986 100644 --- a/plugins/main/public/components/common/wazuh-discover/config/histogram-chart.ts +++ b/plugins/main/public/components/common/wazuh-discover/config/histogram-chart.ts @@ -135,6 +135,7 @@ export const histogramChartInput = ( query, dateRangeFrom, dateRangeTo, + lastReloadRequestTime, ) => ({ viewMode: ViewMode.VIEW, panels: getDiscoverPanels(indexPatternId), @@ -154,4 +155,5 @@ export const histogramChartInput = ( value: 15, }, hidePanelTitles: true, + lastReloadRequestTime, }); diff --git a/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx b/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx index f679f0a142..cfd96a987b 100644 --- a/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx +++ b/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx @@ -12,6 +12,7 @@ import { EuiFlyoutHeader, EuiPanel, } from '@elastic/eui'; +import { TimeRange } from '../../../../../../src/plugins/data/public'; import { IntlProvider } from 'react-intl'; import { IndexPattern } from '../../../../../../src/plugins/data/common'; import { SearchResponse } from '../../../../../../src/core/server'; @@ -48,6 +49,7 @@ import { import DiscoverDataGridAdditionalControls from './components/data-grid-additional-controls'; import { wzDiscoverRenderColumns } from './render-columns'; import { WzSearchBar } from '../search-bar'; +import { transformDateRange } from '../search-bar/search-bar-service'; import DocDetailsHeader from './components/doc-details-header'; export const MAX_ENTRIES_PER_QUERY = 10000; @@ -110,13 +112,15 @@ const WazuhDiscoverComponent = (props: WazuhDiscoverProps) => { ); }; - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; - + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; + const [absoluteDateRange, setAbsoluteDateRange] = useState( + transformDateRange({ from: dateRangeFrom, to: dateRangeTo }), + ); const dataGridProps = useDataGrid({ ariaLabelledBy: 'Discover events table', defaultColumns: defaultTableColumns, @@ -129,13 +133,15 @@ const WazuhDiscoverComponent = (props: WazuhDiscoverProps) => { setFilters, }); - const { pagination, sorting, columnVisibility } = dataGridProps; + const { pagination, setPagination, sorting, columnVisibility } = + dataGridProps; useEffect(() => { if (isDataSourceLoading) { return; } setIndexPattern(dataSource?.indexPattern); + fetchData({ query, pagination, @@ -150,12 +156,27 @@ const WazuhDiscoverComponent = (props: WazuhDiscoverProps) => { }); ErrorHandler.handleError(searchError); }); + }, [absoluteDateRange, JSON.stringify(sorting), JSON.stringify(pagination)]); + + useEffect(() => { + if (isDataSourceLoading) { + return; + } + setIndexPattern(dataSource?.indexPattern); + setPagination(pagination => ({ + ...pagination, + pageIndex: 0, + })); + setAbsoluteDateRange( + transformDateRange({ from: dateRangeFrom, to: dateRangeTo }), + ); }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(sorting), - JSON.stringify(pagination), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); const timeField = indexPattern?.timeFieldName @@ -239,8 +260,9 @@ const WazuhDiscoverComponent = (props: WazuhDiscoverProps) => { AlertsRepository.getStoreIndexPatternId(), fetchFilters, query, - absoluteDateRange.from, - absoluteDateRange.to, + dateRangeFrom, + dateRangeTo, + fingerprint, )} /> diff --git a/plugins/main/public/components/common/wazuh-discover/wz-flyout-discover.tsx b/plugins/main/public/components/common/wazuh-discover/wz-flyout-discover.tsx index 93cb855a4f..f0e2e4199f 100644 --- a/plugins/main/public/components/common/wazuh-discover/wz-flyout-discover.tsx +++ b/plugins/main/public/components/common/wazuh-discover/wz-flyout-discover.tsx @@ -9,6 +9,7 @@ import { EuiPanel, EuiText, } from '@elastic/eui'; +import { TimeRange } from '../../../../../../src/plugins/data/public'; import { HitsCounter } from '../../../kibana-integrations/discover/application/components/hits_counter'; import { formatNumWithCommas } from '../../../kibana-integrations/discover/application/helpers'; import { IntlProvider } from 'react-intl'; @@ -36,6 +37,7 @@ import { } from '../data-source'; import DocDetails from './components/doc-details'; import { WzSearchBar } from '../search-bar/search-bar'; +import { transformDateRange } from '../search-bar/search-bar-service'; import { MAX_ENTRIES_PER_QUERY } from '../data-grid/data-grid-service'; export const DEFAULT_PAGE_SIZE_OPTIONS = [20, 50, 100]; export const DEFAULT_PAGE_SIZE = 20; @@ -116,7 +118,7 @@ const WazuhFlyoutDiscoverComponent = (props: WazuhDiscoverProps) => { to: timeFilter.to, }); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, @@ -124,7 +126,9 @@ const WazuhFlyoutDiscoverComponent = (props: WazuhDiscoverProps) => { setTimeFilter: setDateRange, } as tUseSearchBarProps); - const { absoluteDateRange } = searchBarProps; + const [absoluteDateRange, setAbsoluteDateRange] = useState( + transformDateRange({ from: dateRange.from, to: dateRange.to }), + ); const parseSorting = useMemo(() => { if (!sorting) { @@ -142,11 +146,12 @@ const WazuhFlyoutDiscoverComponent = (props: WazuhDiscoverProps) => { return; } setIndexPattern(dataSource?.indexPattern); + fetchData({ query, - dateRange: absoluteDateRange, pagination, sorting: parseSorting, + dateRange: absoluteDateRange, }) .then((response: SearchResponse) => { setPagination({ @@ -154,20 +159,35 @@ const WazuhFlyoutDiscoverComponent = (props: WazuhDiscoverProps) => { }); setResults(response); }) - .catch((error: HttpError) => { + .catch(error => { const searchError = ErrorFactory.create(HttpError, { error, message: 'Error fetching data', }); ErrorHandler.handleError(searchError); }); + }, [absoluteDateRange, JSON.stringify(sorting), JSON.stringify(pagination)]); + + useEffect(() => { + if (isDataSourceLoading) { + return; + } + + setPagination(pagination => ({ + ...pagination, + pageIndex: 0, + })); + setAbsoluteDateRange( + transformDateRange({ from: dateRange.from, to: dateRange.to }), + ); }, [ isDataSourceLoading, JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(sorting), - JSON.stringify(pagination), - JSON.stringify(absoluteDateRange), + dateRange.from, + dateRange.to, + fingerprint, + autoRefreshFingerprint, ]); const toggleDetails = item => { @@ -278,6 +298,7 @@ const WazuhFlyoutDiscoverComponent = (props: WazuhDiscoverProps) => { {...searchBarProps} useDefaultBehaviors={false} hideFixedFilters + showSaveQueryButton={false} /> {!isDataSourceLoading && results?.hits?.total === 0 && ( @@ -307,21 +328,21 @@ const WazuhFlyoutDiscoverComponent = (props: WazuhDiscoverProps) => { : undefined } /> - {absoluteDateRange ? ( - - - - {formatUIDate(absoluteDateRange?.from)} -{' '} - {formatUIDate(absoluteDateRange?.to)} - - - - ) : null} + + + + + {`${formatUIDate( + absoluteDateRange?.from, + )} - ${formatUIDate(absoluteDateRange?.to)}`} + + + { const configurationItemsList = [ { @@ -126,6 +128,7 @@ export const ConfigurationCards = ({ value: 15, }, hidePanelTitles: false, + lastReloadRequestTime, }} /> ) : ( diff --git a/plugins/main/public/components/management/cluster/components/overview_cards.tsx b/plugins/main/public/components/management/cluster/components/overview_cards.tsx index a68bfc0d89..5b6db7ac3f 100644 --- a/plugins/main/public/components/management/cluster/components/overview_cards.tsx +++ b/plugins/main/public/components/management/cluster/components/overview_cards.tsx @@ -31,6 +31,7 @@ interface OverviewCardsProps { indexPattern: tParsedIndexPattern; clusterName?: string; filters: tFilter[]; + lastReloadRequestTime: number; } const plugins = getPlugins(); @@ -52,6 +53,7 @@ export const OverviewCards = ({ indexPattern, clusterName, filters, + lastReloadRequestTime, }: OverviewCardsProps) => { return ( <> @@ -211,7 +213,10 @@ export const OverviewCards = ({ filters: filters, useMargins: true, id: 'ct-dashboard-tab', - timeRange: searchBarProps?.absoluteDateRange, + timeRange: { + from: searchBarProps?.dateRangeFrom, + to: searchBarProps?.dateRangeTo, + }, title: 'Cluster Timelions dashboard', description: 'Dashboard of the Cluster Timelions', query: searchBarProps.query, @@ -220,6 +225,7 @@ export const OverviewCards = ({ value: 15, }, hidePanelTitles: false, + lastReloadRequestTime, }} /> diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx index c4a8e64b9a..963a008e57 100644 --- a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx @@ -67,12 +67,12 @@ const DashboardCT: React.FC = ({ statusRunning }) => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useEffect(() => { if (isDataSourceLoading) { @@ -80,7 +80,10 @@ const DashboardCT: React.FC = ({ statusRunning }) => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { + from: dateRangeFrom, + to: dateRangeTo, + }, }) .then(results => { setResults(results); @@ -95,7 +98,10 @@ const DashboardCT: React.FC = ({ statusRunning }) => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); const setBooleans = (component: string | null) => { @@ -182,6 +188,7 @@ const DashboardCT: React.FC = ({ statusRunning }) => { results={results} indexPattern={dataSource?.indexPattern} filters={fetchFilters ?? []} + lastReloadRequestTime={fingerprint} /> ) : null} {state.showConfig ? ( @@ -192,6 +199,7 @@ const DashboardCT: React.FC = ({ statusRunning }) => { results={results} indexPatternId={dataSource?.id} filters={fetchFilters ?? []} + lastReloadRequestTime={fingerprint} /> ) : null} {state.showNodes ? : null} diff --git a/plugins/main/public/components/overview/amazon-web-services/dashboards/dashboard.tsx b/plugins/main/public/components/overview/amazon-web-services/dashboards/dashboard.tsx index 6692bdd095..c472f7bb18 100644 --- a/plugins/main/public/components/overview/amazon-web-services/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/amazon-web-services/dashboards/dashboard.tsx @@ -48,13 +48,13 @@ const DashboardAWSComponents: React.FC = ({}) => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -62,7 +62,7 @@ const DashboardAWSComponents: React.FC = ({}) => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -71,7 +71,7 @@ const DashboardAWSComponents: React.FC = ({}) => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => setResults(results)) .catch(error => { @@ -84,7 +84,10 @@ const DashboardAWSComponents: React.FC = ({}) => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( <> @@ -121,7 +124,7 @@ const DashboardAWSComponents: React.FC = ({}) => { filters: fetchFilters || [], useMargins: true, id: 'aws-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'AWS dashboard', description: 'Dashboard of the AWS', query: query, @@ -130,6 +133,7 @@ const DashboardAWSComponents: React.FC = ({}) => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/compliance-table/compliance-table.tsx b/plugins/main/public/components/overview/compliance-table/compliance-table.tsx index d186be87a9..045ad5a32c 100644 --- a/plugins/main/public/components/overview/compliance-table/compliance-table.tsx +++ b/plugins/main/public/components/overview/compliance-table/compliance-table.tsx @@ -156,13 +156,13 @@ export const ComplianceTable = withAgentSupportModule(props => { repository: new AlertsDataSourceRepository(), }); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { absoluteDateRange } = searchBarProps; + const { dateRangeFrom, dateRangeTo } = searchBarProps; const [complianceData, setComplianceData] = useState({ descriptions: {}, complianceObject: {}, @@ -232,7 +232,7 @@ export const ComplianceTable = withAgentSupportModule(props => { const data = await fetchData({ aggs, query, - dateRange: absoluteDateRange, + dateRange: dateRange, }); return data?.aggregations?.tactics?.buckets || []; @@ -257,7 +257,7 @@ export const ComplianceTable = withAgentSupportModule(props => { props.section, dataSource, searchBarProps.query, - absoluteDateRange, + { from: dateRangeFrom, to: dateRangeTo }, ]); useEffect(() => { @@ -274,14 +274,17 @@ export const ComplianceTable = withAgentSupportModule(props => { section: props.section, fetchData, query: searchBarProps.query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }); } }, [ dataSource, JSON.stringify(searchBarProps.query), JSON.stringify(fetchFilters), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( diff --git a/plugins/main/public/components/overview/docker/dashboards/dashboard.tsx b/plugins/main/public/components/overview/docker/dashboards/dashboard.tsx index e95a8a46d6..319b09994f 100644 --- a/plugins/main/public/components/overview/docker/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/docker/dashboards/dashboard.tsx @@ -48,13 +48,13 @@ const DashboardDockerComponent: React.FC = ({}) => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -62,7 +62,7 @@ const DashboardDockerComponent: React.FC = ({}) => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -71,7 +71,7 @@ const DashboardDockerComponent: React.FC = ({}) => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => setResults(results)) .catch(error => { @@ -84,7 +84,10 @@ const DashboardDockerComponent: React.FC = ({}) => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -123,7 +126,7 @@ const DashboardDockerComponent: React.FC = ({}) => { filters: fetchFilters ?? [], useMargins: true, id: 'docker-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'Docker dashboard', description: 'Dashboard of Docker', query: query, @@ -132,6 +135,7 @@ const DashboardDockerComponent: React.FC = ({}) => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/fim/dashboard/dashboard.tsx b/plugins/main/public/components/overview/fim/dashboard/dashboard.tsx index 371bfb503a..cbecbf7704 100644 --- a/plugins/main/public/components/overview/fim/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/fim/dashboard/dashboard.tsx @@ -48,13 +48,13 @@ const DashboardFIMComponent: React.FC = ({}) => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -62,7 +62,7 @@ const DashboardFIMComponent: React.FC = ({}) => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -71,7 +71,7 @@ const DashboardFIMComponent: React.FC = ({}) => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => setResults(results)) .catch(error => { @@ -84,7 +84,10 @@ const DashboardFIMComponent: React.FC = ({}) => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -125,7 +128,7 @@ const DashboardFIMComponent: React.FC = ({}) => { filters: fetchFilters ?? [], useMargins: true, id: 'fim-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'File Integrity Monitoring dashboard', description: 'Dashboard of the File Integrity Monitoring', query: query, @@ -134,6 +137,7 @@ const DashboardFIMComponent: React.FC = ({}) => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/gdpr/dashboards/dashboard.tsx b/plugins/main/public/components/overview/gdpr/dashboards/dashboard.tsx index 382ecb0af6..5d43106d47 100644 --- a/plugins/main/public/components/overview/gdpr/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/gdpr/dashboards/dashboard.tsx @@ -47,13 +47,13 @@ const DashboardGDPRComponent: React.FC = () => { }); const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -61,7 +61,7 @@ const DashboardGDPRComponent: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -70,7 +70,7 @@ const DashboardGDPRComponent: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -85,7 +85,10 @@ const DashboardGDPRComponent: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -124,7 +127,7 @@ const DashboardGDPRComponent: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'gdpr-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'GDPR dashboard', description: 'Dashboard of the GDPR', query: searchBarProps.query, @@ -133,6 +136,7 @@ const DashboardGDPRComponent: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/github/dashboards/dashboard.tsx b/plugins/main/public/components/overview/github/dashboards/dashboard.tsx index 393def651b..90a37518e6 100644 --- a/plugins/main/public/components/overview/github/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/github/dashboards/dashboard.tsx @@ -47,13 +47,13 @@ const DashboardGitHubComponent: React.FC = () => { }); const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -61,7 +61,7 @@ const DashboardGitHubComponent: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -70,7 +70,7 @@ const DashboardGitHubComponent: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -85,7 +85,10 @@ const DashboardGitHubComponent: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -124,7 +127,7 @@ const DashboardGitHubComponent: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'github-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'GitHub dashboard', description: 'Dashboard of the GitHub', query: searchBarProps.query, @@ -133,6 +136,7 @@ const DashboardGitHubComponent: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/github/panel/config/drilldown-action.tsx b/plugins/main/public/components/overview/github/panel/config/drilldown-action.tsx index e711a36fc6..d5e571faa5 100644 --- a/plugins/main/public/components/overview/github/panel/config/drilldown-action.tsx +++ b/plugins/main/public/components/overview/github/panel/config/drilldown-action.tsx @@ -129,7 +129,10 @@ export const DrilldownConfigAction = (drilldownProps: ModuleConfigProps) => { filters: fetchFilters ?? [], useMargins: true, id: 'github-drilldown-action-dashboard-tab', - timeRange: searchBarProps?.absoluteDateRange, + timeRange: { + from: searchBarProps.dateRangeFrom, + to: searchBarProps.dateRangeTo, + }, title: 'GitHub drilldown action dashboard', description: 'Dashboard of the GitHub drilldown action', query: searchBarProps.query, diff --git a/plugins/main/public/components/overview/github/panel/config/drilldown-actor.tsx b/plugins/main/public/components/overview/github/panel/config/drilldown-actor.tsx index 6bcede34dc..42cb7381b8 100644 --- a/plugins/main/public/components/overview/github/panel/config/drilldown-actor.tsx +++ b/plugins/main/public/components/overview/github/panel/config/drilldown-actor.tsx @@ -129,7 +129,10 @@ export const DrilldownConfigActor = (drilldownProps: ModuleConfigProps) => { filters: fetchFilters ?? [], useMargins: true, id: 'github-drilldown-action-dashboard-tab', - timeRange: searchBarProps?.absoluteDateRange, + timeRange: { + from: searchBarProps.dateRangeFrom, + to: searchBarProps.dateRangeTo, + }, title: 'GitHub drilldown action dashboard', description: 'Dashboard of the GitHub drilldown action', query: searchBarProps.query, diff --git a/plugins/main/public/components/overview/github/panel/config/drilldown-organization.tsx b/plugins/main/public/components/overview/github/panel/config/drilldown-organization.tsx index 6cdac16307..ece1caad00 100644 --- a/plugins/main/public/components/overview/github/panel/config/drilldown-organization.tsx +++ b/plugins/main/public/components/overview/github/panel/config/drilldown-organization.tsx @@ -131,7 +131,10 @@ export const DrilldownConfigOrganization = ( filters: fetchFilters ?? [], useMargins: true, id: 'github-drilldown-action-dashboard-tab', - timeRange: searchBarProps?.absoluteDateRange, + timeRange: { + from: searchBarProps.dateRangeFrom, + to: searchBarProps.dateRangeTo, + }, title: 'GitHub drilldown action dashboard', description: 'Dashboard of the GitHub drilldown action', query: searchBarProps.query, diff --git a/plugins/main/public/components/overview/github/panel/config/drilldown-repository.tsx b/plugins/main/public/components/overview/github/panel/config/drilldown-repository.tsx index c2a5e01a92..28bf58517e 100644 --- a/plugins/main/public/components/overview/github/panel/config/drilldown-repository.tsx +++ b/plugins/main/public/components/overview/github/panel/config/drilldown-repository.tsx @@ -131,7 +131,10 @@ export const DrilldownConfigRepository = ( filters: fetchFilters ?? [], useMargins: true, id: 'github-drilldown-action-dashboard-tab', - timeRange: searchBarProps?.absoluteDateRange, + timeRange: { + from: searchBarProps.dateRangeFrom, + to: searchBarProps.dateRangeTo, + }, title: 'GitHub drilldown action dashboard', description: 'Dashboard of the GitHub drilldown action', query: searchBarProps.query, diff --git a/plugins/main/public/components/overview/google-cloud/dashboards/dashboard.tsx b/plugins/main/public/components/overview/google-cloud/dashboards/dashboard.tsx index 1566f0d965..630bfb4c74 100644 --- a/plugins/main/public/components/overview/google-cloud/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/google-cloud/dashboards/dashboard.tsx @@ -47,13 +47,13 @@ const DashboardGoogleCloudComponent: React.FC = () => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -61,7 +61,7 @@ const DashboardGoogleCloudComponent: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -70,7 +70,7 @@ const DashboardGoogleCloudComponent: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => setResults(results)) .catch(error => { @@ -83,7 +83,10 @@ const DashboardGoogleCloudComponent: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -123,7 +126,7 @@ const DashboardGoogleCloudComponent: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'google-cloud-detector-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'Google Cloud detector dashboard', description: 'Dashboard of the Google Cloud', query: searchBarProps.query, @@ -132,6 +135,7 @@ const DashboardGoogleCloudComponent: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/hipaa/dashboards/dashboard.tsx b/plugins/main/public/components/overview/hipaa/dashboards/dashboard.tsx index 812b515537..7327d8bf44 100644 --- a/plugins/main/public/components/overview/hipaa/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/hipaa/dashboards/dashboard.tsx @@ -47,13 +47,13 @@ const DashboardHIPAAComponent: React.FC = () => { }); const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -61,7 +61,7 @@ const DashboardHIPAAComponent: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -70,7 +70,7 @@ const DashboardHIPAAComponent: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -85,7 +85,10 @@ const DashboardHIPAAComponent: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -124,7 +127,7 @@ const DashboardHIPAAComponent: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'hipaa-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'HIPAA dashboard', description: 'Dashboard of the HIPAA', query: searchBarProps.query, @@ -133,6 +136,7 @@ const DashboardHIPAAComponent: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/malware-detection/dashboard/dashboard.tsx b/plugins/main/public/components/overview/malware-detection/dashboard/dashboard.tsx index 8d5b4b12df..37a60ea493 100644 --- a/plugins/main/public/components/overview/malware-detection/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/malware-detection/dashboard/dashboard.tsx @@ -48,13 +48,13 @@ const DashboardMalwareDetectionComponent: React.FC = ({}) => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -62,7 +62,7 @@ const DashboardMalwareDetectionComponent: React.FC = ({}) => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -71,7 +71,7 @@ const DashboardMalwareDetectionComponent: React.FC = ({}) => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => setResults(results)) .catch(error => { @@ -84,7 +84,10 @@ const DashboardMalwareDetectionComponent: React.FC = ({}) => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -125,7 +128,7 @@ const DashboardMalwareDetectionComponent: React.FC = ({}) => { filters: fetchFilters ?? [], useMargins: true, id: 'malware-detection-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'Malware Detection dashboard', description: 'Dashboard of the Malware Detection', query: query, @@ -134,6 +137,7 @@ const DashboardMalwareDetectionComponent: React.FC = ({}) => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/mitre/dashboard/dashboard.tsx b/plugins/main/public/components/overview/mitre/dashboard/dashboard.tsx index deaf0f5056..3d4fb28912 100644 --- a/plugins/main/public/components/overview/mitre/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/mitre/dashboard/dashboard.tsx @@ -45,12 +45,12 @@ export const DashboardMITRE: React.FC = () => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -58,7 +58,7 @@ export const DashboardMITRE: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -67,7 +67,7 @@ export const DashboardMITRE: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -82,7 +82,10 @@ export const DashboardMITRE: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -121,7 +124,7 @@ export const DashboardMITRE: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'mitre-dashboard-tab-filters', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'MITRE dashboard filters', description: 'Dashboard of the MITRE filters', query: query, @@ -130,6 +133,7 @@ export const DashboardMITRE: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/mitre/framework/mitre.tsx b/plugins/main/public/components/overview/mitre/framework/mitre.tsx index 9a37658387..7ed5f4aee0 100644 --- a/plugins/main/public/components/overview/mitre/framework/mitre.tsx +++ b/plugins/main/public/components/overview/mitre/framework/mitre.tsx @@ -68,13 +68,13 @@ const MitreComponent = props => { repository: new AlertsDataSourceRepository(), }); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters: setFilters, }); - const { absoluteDateRange } = searchBarProps; + const { dateRangeFrom, dateRangeTo } = searchBarProps; const [mitreState, setMitreState] = useState({ tacticsObject: {}, selectedTactics: {}, @@ -83,7 +83,10 @@ const MitreComponent = props => { const [filterParams, setFilterParams] = useState({ filters: fetchFilters, query: searchBarProps?.query, - time: absoluteDateRange, + time: { + from: dateRangeFrom, + to: dateRangeTo, + }, }); const [indexPattern, setIndexPattern] = useState(); //Todo: Add correct type const [isLoading, setIsLoading] = useState(true); @@ -93,7 +96,10 @@ const MitreComponent = props => { let filterParams = { filters: fetchFilters, // pass the fetchFilters to use it as initial filters in the technique flyout query: searchBarProps?.query, - time: absoluteDateRange, + time: { + from: dateRangeFrom, + to: dateRangeTo, + }, }; setFilterParams(filterParams); setIsLoading(true); @@ -108,7 +114,10 @@ const MitreComponent = props => { dataSource, searchBarProps.query, JSON.stringify(filters), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); const buildTacticsObject = async () => { diff --git a/plugins/main/public/components/overview/nist/dashboards/dashboard.tsx b/plugins/main/public/components/overview/nist/dashboards/dashboard.tsx index 017efa65f3..8f22366375 100644 --- a/plugins/main/public/components/overview/nist/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/nist/dashboards/dashboard.tsx @@ -47,13 +47,13 @@ const DashboardNIST80053Component: React.FC = () => { }); const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -61,7 +61,7 @@ const DashboardNIST80053Component: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -70,7 +70,7 @@ const DashboardNIST80053Component: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -85,7 +85,10 @@ const DashboardNIST80053Component: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -124,7 +127,7 @@ const DashboardNIST80053Component: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'nist-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'NIST 800-53 dashboard', description: 'Dashboard of the NIST 800-53', query: searchBarProps.query, @@ -133,6 +136,7 @@ const DashboardNIST80053Component: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/office/dashboard/dashboard.tsx b/plugins/main/public/components/overview/office/dashboard/dashboard.tsx index b3b48d1e5b..c95b9bb26a 100644 --- a/plugins/main/public/components/overview/office/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/office/dashboard/dashboard.tsx @@ -47,12 +47,12 @@ const DashboardOffice365Component: React.FC = () => { }); const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -60,7 +60,7 @@ const DashboardOffice365Component: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -69,7 +69,7 @@ const DashboardOffice365Component: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -84,7 +84,10 @@ const DashboardOffice365Component: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -123,7 +126,7 @@ const DashboardOffice365Component: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'kpis-th-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'KPIs Office 365 dashboard', description: 'KPIs Dashboard of the Office 365', query: searchBarProps.query, @@ -132,6 +135,7 @@ const DashboardOffice365Component: React.FC = () => { value: 15, }, hidePanelTitles: true, + lastReloadRequestTime: fingerprint, }} /> { filters: fetchFilters ?? [], useMargins: true, id: 'office-365-detector-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'Office 365 detector dashboard', description: 'Dashboard of the Office 365', query: searchBarProps.query, @@ -153,6 +157,7 @@ const DashboardOffice365Component: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/overview.tsx b/plugins/main/public/components/overview/overview.tsx index 12476ad0a7..03f236f705 100644 --- a/plugins/main/public/components/overview/overview.tsx +++ b/plugins/main/public/components/overview/overview.tsx @@ -4,7 +4,10 @@ import { Stats } from '../../controllers/overview/components/stats'; import { AppState, WzRequest } from '../../react-services'; import { OverviewWelcome } from '../common/welcome/overview-welcome'; import { MainModule } from '../common/modules/main'; -import { OSD_URL_STATE_STORAGE_ID } from '../../../common/constants'; +import { + APP_STATE_URL_KEY, + OSD_URL_STATE_STORAGE_ID, +} from '../../../common/constants'; import { WzCurrentOverviewSectionWrapper } from '../common/modules/overview-current-section-wrapper'; import { connectToQueryState, @@ -56,7 +59,9 @@ export const Overview: React.FC = withRouteResolvers({ history: history, }); - const appStateFromUrl = osdUrlStateStorage.get('_a') as AppState; + const appStateFromUrl = osdUrlStateStorage.get( + APP_STATE_URL_KEY, + ) as AppState; let initialAppState = { query: migrateLegacyQuery(data.query.queryString.getDefaultQuery()), ...appStateFromUrl, @@ -73,11 +78,11 @@ export const Overview: React.FC = withRouteResolvers({ const replaceUrlAppState = async (newPartial: AppState = {}) => { const state = { ...appStateContainer.getState(), ...newPartial }; - await osdUrlStateStorage.set('_a', state, { replace: true }); + await osdUrlStateStorage.set(APP_STATE_URL_KEY, state, { replace: true }); }; const { start, stop } = syncState({ - storageKey: '_a', + storageKey: APP_STATE_URL_KEY, stateContainer: appStateContainerModified, stateStorage: osdUrlStateStorage, }); diff --git a/plugins/main/public/components/overview/pci/dashboards/dashboard.tsx b/plugins/main/public/components/overview/pci/dashboards/dashboard.tsx index 64305d0b5c..c13e878cc4 100644 --- a/plugins/main/public/components/overview/pci/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/pci/dashboards/dashboard.tsx @@ -48,13 +48,13 @@ const DashboardPCIDSSComponent: React.FC = () => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -62,7 +62,7 @@ const DashboardPCIDSSComponent: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -71,7 +71,7 @@ const DashboardPCIDSSComponent: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -86,7 +86,10 @@ const DashboardPCIDSSComponent: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -125,7 +128,7 @@ const DashboardPCIDSSComponent: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'pci-dss-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'PCI DSS dashboard', description: 'Dashboard of the PCI DSS', query: searchBarProps.query, @@ -134,6 +137,7 @@ const DashboardPCIDSSComponent: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboardTabsPanels.tsx b/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboardTabsPanels.tsx index b261d69619..ad78f306d1 100644 --- a/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboardTabsPanels.tsx +++ b/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboardTabsPanels.tsx @@ -72,13 +72,13 @@ export const DashboardTabsPanels = ({ "Analysisd statistics refer to the data stored from the period indicated in the variable 'analysisd.state_interval'.", }; - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useEffect(() => { if (isDataSourceLoading) { @@ -86,7 +86,10 @@ export const DashboardTabsPanels = ({ } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { + from: dateRangeFrom, + to: dateRangeTo, + }, }) .then(results => { setResults(results); @@ -101,7 +104,10 @@ export const DashboardTabsPanels = ({ }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); const selectedNodeFilter: tFilter = { @@ -168,6 +174,7 @@ export const DashboardTabsPanels = ({ = ({ @@ -22,6 +23,7 @@ const DashboardStatistics: React.FC = ({ indexPatternId, filters, searchBarProps, + lastReloadRequestTime, }) => { return (
@@ -48,6 +50,7 @@ const DashboardStatistics: React.FC = ({ value: 15, }, hidePanelTitles: false, + lastReloadRequestTime, }} />
diff --git a/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboard_listener_engine.tsx b/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboard_listener_engine.tsx index 8396e180f8..0ba50b68c7 100644 --- a/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboard_listener_engine.tsx +++ b/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboard_listener_engine.tsx @@ -14,12 +14,14 @@ interface DashboardStatisticsProps { indexPatternId: string; filters: tFilter[]; searchBarProps: any; + lastReloadRequestTime: number; } const DashboardStatistics: React.FC = ({ indexPatternId, filters, searchBarProps, + lastReloadRequestTime, }) => { return (
@@ -43,6 +45,7 @@ const DashboardStatistics: React.FC = ({ value: 15, }, hidePanelTitles: false, + lastReloadRequestTime, }} />
diff --git a/plugins/main/public/components/overview/threat-hunting/dashboard/dashboard.tsx b/plugins/main/public/components/overview/threat-hunting/dashboard/dashboard.tsx index bad298ebfa..6ab8a40b54 100644 --- a/plugins/main/public/components/overview/threat-hunting/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/threat-hunting/dashboard/dashboard.tsx @@ -48,12 +48,12 @@ const DashboardTH: React.FC = () => { }); const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; const pinnedAgent = PatternDataSourceFilterManager.getPinnedAgentFilter(dataSource?.id!) .length > 0; @@ -64,7 +64,7 @@ const DashboardTH: React.FC = () => { indexPattern: dataSource?.indexPattern as IndexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -73,7 +73,7 @@ const DashboardTH: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -88,7 +88,10 @@ const DashboardTH: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + autoRefreshFingerprint, + fingerprint, ]); return ( @@ -128,7 +131,7 @@ const DashboardTH: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'kpis-th-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'KPIs Threat Hunting dashboard', description: 'KPIs Dashboard of the Threat Hunting', query: query, @@ -137,6 +140,7 @@ const DashboardTH: React.FC = () => { value: 15, }, hidePanelTitles: true, + lastReloadRequestTime: fingerprint, }} /> { filters: fetchFilters ?? [], useMargins: true, id: 'th-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'Threat Hunting dashboard', description: 'Dashboard of the Threat Hunting', query: query, @@ -159,6 +163,7 @@ const DashboardTH: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/tsc/dashboards/dashboard.tsx b/plugins/main/public/components/overview/tsc/dashboards/dashboard.tsx index dfb3da8ee1..0d931901d6 100644 --- a/plugins/main/public/components/overview/tsc/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/tsc/dashboards/dashboard.tsx @@ -47,13 +47,13 @@ const DashboardTSCComponent: React.FC = () => { }); const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -61,7 +61,7 @@ const DashboardTSCComponent: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -70,7 +70,7 @@ const DashboardTSCComponent: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -85,7 +85,10 @@ const DashboardTSCComponent: React.FC = () => { }, [ JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -124,7 +127,7 @@ const DashboardTSCComponent: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'tsc-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'TSC dashboard', description: 'Dashboard of the TSC', query: searchBarProps.query, @@ -133,6 +136,7 @@ const DashboardTSCComponent: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> diff --git a/plugins/main/public/components/overview/virustotal/dashboard/dashboard.tsx b/plugins/main/public/components/overview/virustotal/dashboard/dashboard.tsx index 29cf0cff74..4d9953f7c7 100644 --- a/plugins/main/public/components/overview/virustotal/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/virustotal/dashboard/dashboard.tsx @@ -48,12 +48,12 @@ const DashboardVT: React.FC = () => { const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint, autoRefreshFingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, }); - const { query, absoluteDateRange } = searchBarProps; + const { query, dateRangeFrom, dateRangeTo } = searchBarProps; useReportingCommunicateSearchContext({ isSearching: isDataSourceLoading, @@ -61,7 +61,7 @@ const DashboardVT: React.FC = () => { indexPattern: dataSource?.indexPattern, filters: fetchFilters, query: query, - time: absoluteDateRange, + time: { from: dateRangeFrom, to: dateRangeTo }, }); useEffect(() => { @@ -70,7 +70,7 @@ const DashboardVT: React.FC = () => { } fetchData({ query, - dateRange: absoluteDateRange, + dateRange: { from: dateRangeFrom, to: dateRangeTo }, }) .then(results => { setResults(results); @@ -86,7 +86,10 @@ const DashboardVT: React.FC = () => { isDataSourceLoading, JSON.stringify(fetchFilters), JSON.stringify(query), - JSON.stringify(absoluteDateRange), + dateRangeFrom, + dateRangeTo, + fingerprint, + autoRefreshFingerprint, ]); return ( @@ -125,7 +128,7 @@ const DashboardVT: React.FC = () => { filters: fetchFilters ?? [], useMargins: true, id: 'kpis-virustotal-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'KPIs Virustotal dashboard', description: 'KPIs Dashboard of the Virustotal', query: query, @@ -134,6 +137,7 @@ const DashboardVT: React.FC = () => { value: 15, }, hidePanelTitles: true, + lastReloadRequestTime: fingerprint, }} /> { filters: fetchFilters ?? [], useMargins: true, id: 'virustotal-dashboard-tab', - timeRange: absoluteDateRange, + timeRange: { from: dateRangeFrom, to: dateRangeTo }, title: 'Virustotal dashboard', description: 'Dashboard of the Virustotal', query: query, @@ -156,6 +160,7 @@ const DashboardVT: React.FC = () => { value: 15, }, hidePanelTitles: false, + lastReloadRequestTime: fingerprint, }} /> 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 ecef54dd3a..130892fd14 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx @@ -65,7 +65,7 @@ const InventoryVulsComponent = () => { DataSource: VulnerabilitiesDataSource, repository: new VulnerabilitiesDataSourceRepository(), }); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, @@ -169,6 +169,7 @@ const InventoryVulsComponent = () => { JSON.stringify(query), JSON.stringify(pagination), JSON.stringify(sorting), + fingerprint, ]); return ( 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 2ea3d9846f..49733f23bb 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/overview/dashboard.tsx @@ -59,7 +59,7 @@ const DashboardVulsComponent: React.FC = ({ const [results, setResults] = useState({} as SearchResponse); - const { searchBarProps } = useSearchBar({ + const { searchBarProps, fingerprint } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, setFilters, @@ -91,108 +91,116 @@ const DashboardVulsComponent: React.FC = ({ {isDataSourceLoading && !dataSource ? ( ) : ( - - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - }`} - > - -
- + -
- -
+ + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + }`} + > + +
+ +
+ +
+ + )} diff --git a/plugins/main/public/controllers/management/components/management/common/resources-handler.ts b/plugins/main/public/controllers/management/components/management/common/resources-handler.ts index f0b57c0625..5832153eff 100644 --- a/plugins/main/public/controllers/management/components/management/common/resources-handler.ts +++ b/plugins/main/public/controllers/management/components/management/common/resources-handler.ts @@ -63,9 +63,8 @@ export class ResourcesHandler { /** * Get the content of any type of file Rules, Decoders, CDB lists... - * @param {String} fileName */ - async getFileContent(fileName, relativeDirname) { + async getFileContent(fileName: string, relativeDirname?: string) { try { const result: any = await WzRequest.apiReq( 'GET', diff --git a/plugins/main/public/kibana-services.ts b/plugins/main/public/kibana-services.ts index 1c536dc5e1..f8d96e0301 100644 --- a/plugins/main/public/kibana-services.ts +++ b/plugins/main/public/kibana-services.ts @@ -8,7 +8,7 @@ import { ScopedHistory, ToastsStart, AppMountParameters, -} from 'opensearch_dashboards/public'; +} from '../../../src/core/public'; import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/common'; import { DataPublicPluginStart } from '../../../src/plugins/data/public'; import { VisualizationsStart } from '../../../src/plugins/visualizations/public'; diff --git a/plugins/main/public/react-services/index.ts b/plugins/main/public/react-services/index.ts index b09a1645a0..e04aaa8d42 100644 --- a/plugins/main/public/react-services/index.ts +++ b/plugins/main/public/react-services/index.ts @@ -21,3 +21,4 @@ export * from './wz-security-opensearch-dashboards-security'; export * from './wz-user-permissions'; export * from './query-config'; export * from './elastic_helpers'; +export * from './state-storage'; diff --git a/plugins/main/public/react-services/state-storage.ts b/plugins/main/public/react-services/state-storage.ts new file mode 100644 index 0000000000..d0ca3d63dc --- /dev/null +++ b/plugins/main/public/react-services/state-storage.ts @@ -0,0 +1,42 @@ +import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; +import { + createStateContainer, + IOsdUrlStateStorage, + syncState as _syncState, +} from '../../../../src/plugins/opensearch_dashboards_utils/public'; +import { AppState } from './app-state'; +import { migrateLegacyQuery } from '../utils/migrate_legacy_query'; +import { APP_STATE_URL_KEY } from '../../common/constants'; + +const OsdUrlStateStorage = ( + data: DataPublicPluginStart, + osdUrlStateStorage: IOsdUrlStateStorage, +) => { + const getAppStateFromUrl = () => { + return osdUrlStateStorage.get(APP_STATE_URL_KEY) as AppState; + }; + + const getAppStateContainer = () => { + const defaultQuery = migrateLegacyQuery( + data.query.queryString.getDefaultQuery(), + ); + let initialAppState = { + query: defaultQuery, + ...getAppStateFromUrl(), + }; + return createStateContainer(initialAppState); + }; + + const replaceUrlAppState = async (newPartial: AppState = {}) => { + const state = { ...getAppStateContainer().getState(), ...newPartial }; + await osdUrlStateStorage.set(APP_STATE_URL_KEY, state, { replace: true }); + }; + + return { + getAppStateFromUrl, + getAppStateContainer, + replaceUrlAppState, + }; +}; + +export default OsdUrlStateStorage;