mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
# Backport This will backport the following commits from `main` to `8.6`: - [[TIP] Fix broken refresh button on the indicators page (#146526)](https://github.com/elastic/kibana/pull/146526) <!--- Backport version: 8.9.7 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Luke Gmys","email":"lgmys@users.noreply.github.com"},"sourceCommit":{"committedDate":"2022-12-05T15:12:53Z","message":"[TIP] Fix broken refresh button on the indicators page (#146526)\n\n## Summary\r\n\r\nThis PR fixes the broken refresh button noticed during the 8.6 demo.\r\n\r\nIncludes an e2e test to verify if it is working or not.\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"e89ba22953eba604fb61fe1d95ec5cbc7d22a6b8","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","backport:prev-minor","Team: Protections Experience","v8.7.0"],"number":146526,"url":"https://github.com/elastic/kibana/pull/146526","mergeCommit":{"message":"[TIP] Fix broken refresh button on the indicators page (#146526)\n\n## Summary\r\n\r\nThis PR fixes the broken refresh button noticed during the 8.6 demo.\r\n\r\nIncludes an e2e test to verify if it is working or not.\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"e89ba22953eba604fb61fe1d95ec5cbc7d22a6b8"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/146526","number":146526,"mergeCommit":{"message":"[TIP] Fix broken refresh button on the indicators page (#146526)\n\n## Summary\r\n\r\nThis PR fixes the broken refresh button noticed during the 8.6 demo.\r\n\r\nIncludes an e2e test to verify if it is working or not.\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"e89ba22953eba604fb61fe1d95ec5cbc7d22a6b8"}}]}] BACKPORT--> Co-authored-by: Luke Gmys <lgmys@users.noreply.github.com>
This commit is contained in:
parent
f3c36f206b
commit
de3b26d832
13 changed files with 135 additions and 21 deletions
|
@ -24,6 +24,8 @@ import { useSourcererDataView } from '../common/containers/sourcerer';
|
|||
import { SecuritySolutionPageWrapper } from '../common/components/page_wrapper';
|
||||
import { SiemSearchBar } from '../common/components/search_bar';
|
||||
import { useGlobalTime } from '../common/containers/use_global_time';
|
||||
import { deleteOneQuery, setQuery } from '../common/store/inputs/actions';
|
||||
import { InputsModelId } from '../common/store/inputs/constants';
|
||||
|
||||
const ThreatIntelligence = memo(() => {
|
||||
const { threatIntelligence } = useKibana().services;
|
||||
|
@ -34,17 +36,36 @@ const ThreatIntelligence = memo(() => {
|
|||
const securitySolutionStore = getStore() as Store;
|
||||
|
||||
const securitySolutionContext: SecuritySolutionPluginContext = {
|
||||
securitySolutionStore,
|
||||
|
||||
getFiltersGlobalComponent: () => FiltersGlobal,
|
||||
getPageWrapper: () => SecuritySolutionPageWrapper,
|
||||
licenseService,
|
||||
sourcererDataView: sourcererDataView as unknown as SourcererDataView,
|
||||
getSecuritySolutionStore: securitySolutionStore,
|
||||
getUseInvestigateInTimeline: useInvestigateInTimeline,
|
||||
|
||||
useQuery: () => useSelector(inputsSelectors.globalQuerySelector()),
|
||||
useFilters: () => useSelector(inputsSelectors.globalFiltersQuerySelector()),
|
||||
useGlobalTime,
|
||||
|
||||
registerQuery: (query) =>
|
||||
securitySolutionStore.dispatch(
|
||||
setQuery({
|
||||
inputId: InputsModelId.global,
|
||||
id: query.id,
|
||||
refetch: query.refetch,
|
||||
inspect: null,
|
||||
loading: query.loading,
|
||||
})
|
||||
),
|
||||
deregisterQuery: (query) =>
|
||||
securitySolutionStore.dispatch(
|
||||
deleteOneQuery({
|
||||
inputId: InputsModelId.global,
|
||||
id: query.id,
|
||||
})
|
||||
),
|
||||
|
||||
SiemSearchBar,
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ import {
|
|||
TABLE_CONTROLS,
|
||||
TIME_RANGE_PICKER,
|
||||
TOGGLE_FLYOUT_BUTTON,
|
||||
REFRESH_BUTTON,
|
||||
} from '../screens/indicators';
|
||||
import { login } from '../tasks/login';
|
||||
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
|
||||
|
@ -232,6 +233,18 @@ describe('Indicators', () => {
|
|||
cy.get(TABLE_CONTROLS).should('contain.text', 'Showing 1-25 of');
|
||||
});
|
||||
|
||||
it('should reload the data when refresh button is pressed', () => {
|
||||
cy.intercept(/bsearch/).as('search');
|
||||
|
||||
cy.get(REFRESH_BUTTON).should('exist').click();
|
||||
|
||||
cy.wait('@search');
|
||||
|
||||
cy.get(REFRESH_BUTTON).should('exist').click();
|
||||
|
||||
cy.wait('@search');
|
||||
});
|
||||
|
||||
describe('No items match search criteria', () => {
|
||||
before(() => {
|
||||
// Contradictory filter set
|
||||
|
|
|
@ -198,3 +198,5 @@ export const INSPECTOR_BUTTON = '[data-test-subj="tiIndicatorsGridInspect"]';
|
|||
export const INSPECTOR_PANEL = '[data-test-subj="inspectorPanel"]';
|
||||
|
||||
export const ADD_INTEGRATIONS_BUTTON = '[data-test-subj="add-data"]';
|
||||
|
||||
export const REFRESH_BUTTON = '[data-test-subj="querySubmitButton"]';
|
||||
|
|
|
@ -28,7 +28,7 @@ export const getSecuritySolutionContextMock = (): SecuritySolutionPluginContext
|
|||
indexPattern: { fields: [], title: '' },
|
||||
loading: false,
|
||||
},
|
||||
getSecuritySolutionStore: {
|
||||
securitySolutionStore: {
|
||||
// @ts-ignore
|
||||
dispatch: () => jest.fn(),
|
||||
},
|
||||
|
@ -44,4 +44,8 @@ export const getSecuritySolutionContextMock = (): SecuritySolutionPluginContext
|
|||
useGlobalTime: () => ({ from: '', to: '' }),
|
||||
|
||||
useQuery: () => ({ language: 'kuery', query: '' }),
|
||||
|
||||
registerQuery: () => {},
|
||||
|
||||
deregisterQuery: () => {},
|
||||
});
|
||||
|
|
|
@ -95,6 +95,11 @@ describe('useAggregatedIndicators()', () => {
|
|||
"isFetching": false,
|
||||
"isLoading": false,
|
||||
"onFieldChange": [Function],
|
||||
"query": Object {
|
||||
"id": "indicatorsBarchart",
|
||||
"loading": false,
|
||||
"refetch": [Function],
|
||||
},
|
||||
"selectedField": "threat.feed.name",
|
||||
"series": Array [],
|
||||
}
|
||||
|
|
|
@ -56,10 +56,14 @@ export interface UseAggregatedIndicatorsValue {
|
|||
|
||||
/** Is data update in progress? */
|
||||
isFetching?: boolean;
|
||||
|
||||
query: { refetch: VoidFunction; id: string; loading: boolean };
|
||||
}
|
||||
|
||||
const DEFAULT_FIELD = RawIndicatorFieldId.Feed;
|
||||
|
||||
const QUERY_ID = 'indicatorsBarchart';
|
||||
|
||||
export const useAggregatedIndicators = ({
|
||||
timeRange,
|
||||
filters,
|
||||
|
@ -87,9 +91,9 @@ export const useAggregatedIndicators = ({
|
|||
[inspectorAdapters, queryService, searchService]
|
||||
);
|
||||
|
||||
const { data, isLoading, isFetching } = useQuery(
|
||||
const { data, isLoading, isFetching, refetch } = useQuery(
|
||||
[
|
||||
'indicatorsBarchart',
|
||||
QUERY_ID,
|
||||
{
|
||||
filters,
|
||||
field,
|
||||
|
@ -113,6 +117,11 @@ export const useAggregatedIndicators = ({
|
|||
[queryService.timefilter.timefilter, timeRange]
|
||||
);
|
||||
|
||||
const query = useMemo(
|
||||
() => ({ refetch, id: QUERY_ID, loading: isLoading }),
|
||||
[isLoading, refetch]
|
||||
);
|
||||
|
||||
return {
|
||||
dateRange,
|
||||
series: data || [],
|
||||
|
@ -120,5 +129,6 @@ export const useAggregatedIndicators = ({
|
|||
selectedField: field,
|
||||
isLoading,
|
||||
isFetching,
|
||||
query,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -106,7 +106,6 @@ describe('useIndicators()', () => {
|
|||
expect(hookResult.result.current).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"dataUpdatedAt": 0,
|
||||
"handleRefresh": [Function],
|
||||
"indicatorCount": 0,
|
||||
"indicators": Array [],
|
||||
"isFetching": false,
|
||||
|
@ -122,6 +121,11 @@ describe('useIndicators()', () => {
|
|||
50,
|
||||
],
|
||||
},
|
||||
"query": Object {
|
||||
"id": "indicatorsTable",
|
||||
"loading": false,
|
||||
"refetch": [Function],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -26,8 +26,6 @@ export interface UseIndicatorsParams {
|
|||
}
|
||||
|
||||
export interface UseIndicatorsValue {
|
||||
handleRefresh: () => void;
|
||||
|
||||
/**
|
||||
* Array of {@link Indicator} ready to render inside the IndicatorTable component
|
||||
*/
|
||||
|
@ -48,8 +46,12 @@ export interface UseIndicatorsValue {
|
|||
isFetching: boolean;
|
||||
|
||||
dataUpdatedAt: number;
|
||||
|
||||
query: { refetch: VoidFunction; id: string; loading: boolean };
|
||||
}
|
||||
|
||||
const QUERY_ID = 'indicatorsTable';
|
||||
|
||||
export const useIndicators = ({
|
||||
filters,
|
||||
filterQuery,
|
||||
|
@ -98,7 +100,7 @@ export const useIndicators = ({
|
|||
|
||||
const { isLoading, isFetching, data, refetch, dataUpdatedAt } = useQuery(
|
||||
[
|
||||
'indicatorsTable',
|
||||
QUERY_ID,
|
||||
{
|
||||
timeRange,
|
||||
filterQuery,
|
||||
|
@ -119,10 +121,10 @@ export const useIndicators = ({
|
|||
}
|
||||
);
|
||||
|
||||
const handleRefresh = useCallback(() => {
|
||||
onChangePage(0);
|
||||
refetch();
|
||||
}, [onChangePage, refetch]);
|
||||
const query = useMemo(
|
||||
() => ({ refetch, id: QUERY_ID, loading: isLoading }),
|
||||
[isLoading, refetch]
|
||||
);
|
||||
|
||||
return {
|
||||
indicators: data?.indicators || [],
|
||||
|
@ -132,7 +134,7 @@ export const useIndicators = ({
|
|||
onChangeItemsPerPage,
|
||||
isLoading,
|
||||
isFetching,
|
||||
handleRefresh,
|
||||
dataUpdatedAt,
|
||||
query,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -30,6 +30,11 @@ describe('<IndicatorsPage />', () => {
|
|||
series: [],
|
||||
selectedField: '',
|
||||
onFieldChange: () => {},
|
||||
query: {
|
||||
id: 'chart',
|
||||
loading: false,
|
||||
refetch: stub,
|
||||
},
|
||||
});
|
||||
|
||||
(useIndicators as jest.MockedFunction<typeof useIndicators>).mockReturnValue({
|
||||
|
@ -40,8 +45,12 @@ describe('<IndicatorsPage />', () => {
|
|||
pagination: { pageIndex: 0, pageSize: 10, pageSizeOptions: [10] },
|
||||
onChangeItemsPerPage: stub,
|
||||
onChangePage: stub,
|
||||
handleRefresh: stub,
|
||||
dataUpdatedAt: Date.now(),
|
||||
query: {
|
||||
id: 'list',
|
||||
loading: false,
|
||||
refetch: stub,
|
||||
},
|
||||
});
|
||||
|
||||
(useFilters as jest.MockedFunction<typeof useFilters>).mockReturnValue({
|
||||
|
|
|
@ -16,8 +16,8 @@ import { FieldTypesProvider } from '../../../containers/field_types_provider';
|
|||
import { InspectorProvider } from '../../../containers/inspector';
|
||||
import { useColumnSettings } from '../components/table/hooks';
|
||||
import { IndicatorsFilters } from '../containers/filters';
|
||||
import { useSecurityContext } from '../../../hooks';
|
||||
import { UpdateStatus } from '../../../components/update_status';
|
||||
import { QueryBar } from '../../query_bar/query_bar';
|
||||
|
||||
const IndicatorsPageProviders: FC = ({ children }) => (
|
||||
<IndicatorsFilters>
|
||||
|
@ -43,6 +43,7 @@ const IndicatorsPageContent: VFC = () => {
|
|||
isLoading: isLoadingIndicators,
|
||||
isFetching: isFetchingIndicators,
|
||||
dataUpdatedAt,
|
||||
query: indicatorListQuery,
|
||||
} = useIndicators({
|
||||
filters,
|
||||
filterQuery,
|
||||
|
@ -57,14 +58,13 @@ const IndicatorsPageContent: VFC = () => {
|
|||
onFieldChange,
|
||||
isLoading: isLoadingAggregatedIndicators,
|
||||
isFetching: isFetchingAggregatedIndicators,
|
||||
query: indicatorChartQuery,
|
||||
} = useAggregatedIndicators({
|
||||
timeRange,
|
||||
filters,
|
||||
filterQuery,
|
||||
});
|
||||
|
||||
const { SiemSearchBar } = useSecurityContext();
|
||||
|
||||
return (
|
||||
<FieldTypesProvider>
|
||||
<DefaultPageLayout
|
||||
|
@ -72,7 +72,10 @@ const IndicatorsPageContent: VFC = () => {
|
|||
subHeader={<UpdateStatus isUpdating={isFetchingIndicators} updatedAt={dataUpdatedAt} />}
|
||||
>
|
||||
<FiltersGlobal>
|
||||
<SiemSearchBar indexPattern={indexPattern} id="global" />
|
||||
<QueryBar
|
||||
queries={[indicatorChartQuery, indicatorListQuery]}
|
||||
indexPattern={indexPattern}
|
||||
/>
|
||||
</FiltersGlobal>
|
||||
|
||||
<IndicatorsBarChartWrapper
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, VFC } from 'react';
|
||||
import { useSecurityContext } from '../../hooks/use_security_context';
|
||||
import { SecuritySolutionDataViewBase } from '../../types';
|
||||
|
||||
interface QueryBarProps {
|
||||
indexPattern: SecuritySolutionDataViewBase;
|
||||
queries: Array<{
|
||||
id: string;
|
||||
refetch: VoidFunction;
|
||||
loading: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const QueryBar: VFC<QueryBarProps> = ({ indexPattern, queries }) => {
|
||||
const { SiemSearchBar, registerQuery, deregisterQuery } = useSecurityContext();
|
||||
|
||||
useEffect(() => {
|
||||
queries.forEach(registerQuery);
|
||||
|
||||
return () => queries.forEach(deregisterQuery);
|
||||
}, [queries, deregisterQuery, registerQuery]);
|
||||
|
||||
return <SiemSearchBar id="global" indexPattern={indexPattern} />;
|
||||
};
|
|
@ -32,7 +32,7 @@ const LazyIndicatorsPageWrapper = React.lazy(() => import('./containers/indicato
|
|||
|
||||
/**
|
||||
* This is used here:
|
||||
* x-pack/plugins/security_solution/public/threat_intelligence/pages/threat_intelligence.tsx
|
||||
* x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx
|
||||
*/
|
||||
export const createApp =
|
||||
(services: Services) =>
|
||||
|
@ -40,7 +40,7 @@ export const createApp =
|
|||
({ securitySolutionContext }: AppProps) =>
|
||||
(
|
||||
<IntlProvider>
|
||||
<ReduxStoreProvider store={securitySolutionContext.getSecuritySolutionStore}>
|
||||
<ReduxStoreProvider store={securitySolutionContext.securitySolutionStore}>
|
||||
<SecuritySolutionContext.Provider value={securitySolutionContext}>
|
||||
<KibanaContextProvider services={services}>
|
||||
<EnterpriseGuard>
|
||||
|
|
|
@ -99,7 +99,7 @@ export interface SecuritySolutionPluginContext {
|
|||
/**
|
||||
* Security Solution store
|
||||
*/
|
||||
getSecuritySolutionStore: Store;
|
||||
securitySolutionStore: Store;
|
||||
/**
|
||||
* Pass UseInvestigateInTimeline functionality to TI plugin
|
||||
*/
|
||||
|
@ -114,4 +114,14 @@ export interface SecuritySolutionPluginContext {
|
|||
useGlobalTime: () => TimeRange;
|
||||
|
||||
SiemSearchBar: VFC<any>;
|
||||
|
||||
/**
|
||||
* Register query in security solution store for tracking and centralized refresh support
|
||||
*/
|
||||
registerQuery: (query: { id: string; loading: boolean; refetch: VoidFunction }) => void;
|
||||
|
||||
/**
|
||||
* Deregister stale query
|
||||
*/
|
||||
deregisterQuery: (query: { id: string }) => void;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue