mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Entity Analytics] Prevent multiple duplicated calls to index_status API on alert details page (#176573)
## Summary When checking if we are using the new or old risk engine, we use `useIsNewRiskScoreModuleInstalled`, this uses the risk engine status API. We were previously defaulting to returning false while the request was loading, this would then cause us to make API calls for the old indices index status, then the new ones once the API request comes back. The fix is to add a loading state to the hook and wait for loading to finish before proceeding, this seems to also prevent a few re-renders which was triggering the hook multiple times, making it so we only call the APIs once for each index (hosts and users) **Steps to reproduce:** Open an alert, and expand details with network tab open, note how many calls there are to the index_status API and to which indices. ### **Before, 14 API calls, most of which to legacy indices 😢** <img width="968" alt="Screenshot 2024-02-09 at 09 39 20" src="e3943ce2
-5cf2-4034-a9fa-189015eccae8"> ### **After, 2 API calls to the new indices 😍** <img width="716" alt="Screenshot 2024-02-09 at 09 39 53" src="596ff183
-395b-434c-8cb0-a97a49fe067d"> --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
7e3a9f8fbe
commit
c7c7bc2a82
12 changed files with 102 additions and 26 deletions
|
@ -12,6 +12,12 @@ import { useSearchStrategy } from '../../use_search_strategy';
|
|||
jest.mock('../../use_search_strategy', () => ({
|
||||
useSearchStrategy: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../entity_analytics/api/hooks/use_risk_engine_status', () => ({
|
||||
useIsNewRiskScoreModuleInstalled: jest
|
||||
.fn()
|
||||
.mockReturnValue({ isLoading: false, installed: true }),
|
||||
}));
|
||||
const mockUseSearchStrategy = useSearchStrategy as jest.Mock;
|
||||
const mockSearch = jest.fn();
|
||||
|
||||
|
|
|
@ -51,7 +51,8 @@ export const useUserRelatedHosts = ({
|
|||
abort: skip,
|
||||
});
|
||||
|
||||
const isNewRiskScoreModuleInstalled = useIsNewRiskScoreModuleInstalled();
|
||||
const { installed: isNewRiskScoreModuleInstalled, isLoading: riskScoreStatusLoading } =
|
||||
useIsNewRiskScoreModuleInstalled();
|
||||
|
||||
const userRelatedHostsResponse = useMemo(
|
||||
() => ({
|
||||
|
@ -76,10 +77,10 @@ export const useUserRelatedHosts = ({
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!skip) {
|
||||
if (!skip && !riskScoreStatusLoading) {
|
||||
search(userRelatedHostsRequest);
|
||||
}
|
||||
}, [userRelatedHostsRequest, search, skip]);
|
||||
}, [userRelatedHostsRequest, search, skip, riskScoreStatusLoading]);
|
||||
|
||||
return userRelatedHostsResponse;
|
||||
};
|
||||
|
|
|
@ -12,6 +12,13 @@ import { useSearchStrategy } from '../../use_search_strategy';
|
|||
jest.mock('../../use_search_strategy', () => ({
|
||||
useSearchStrategy: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../entity_analytics/api/hooks/use_risk_engine_status', () => ({
|
||||
useIsNewRiskScoreModuleInstalled: jest
|
||||
.fn()
|
||||
.mockReturnValue({ isLoading: false, installed: true }),
|
||||
}));
|
||||
|
||||
const mockUseSearchStrategy = useSearchStrategy as jest.Mock;
|
||||
const mockSearch = jest.fn();
|
||||
|
||||
|
@ -30,7 +37,7 @@ const mockResult = {
|
|||
loading: false,
|
||||
};
|
||||
|
||||
describe('useUsersRelatedHosts', () => {
|
||||
describe('useHostRelatedUsers', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockUseSearchStrategy.mockReturnValue({
|
||||
|
|
|
@ -35,7 +35,8 @@ export const useHostRelatedUsers = ({
|
|||
from,
|
||||
skip = false,
|
||||
}: UseHostRelatedUsersParam): UseHostRelatedUsersResult => {
|
||||
const isNewRiskScoreModuleInstalled = useIsNewRiskScoreModuleInstalled();
|
||||
const { installed: isNewRiskScoreModuleInstalled, isLoading: riskScoreStatusLoading } =
|
||||
useIsNewRiskScoreModuleInstalled();
|
||||
const {
|
||||
loading,
|
||||
result: response,
|
||||
|
@ -75,10 +76,10 @@ export const useHostRelatedUsers = ({
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!skip) {
|
||||
if (!skip && !riskScoreStatusLoading) {
|
||||
search(hostRelatedUsersRequest);
|
||||
}
|
||||
}, [hostRelatedUsersRequest, search, skip]);
|
||||
}, [hostRelatedUsersRequest, riskScoreStatusLoading, search, skip]);
|
||||
|
||||
return hostRelatedUsersResponse;
|
||||
};
|
||||
|
|
|
@ -21,10 +21,19 @@ export const useInvalidateRiskEngineStatusQuery = () => {
|
|||
}, [queryClient]);
|
||||
};
|
||||
|
||||
export const useIsNewRiskScoreModuleInstalled = () => {
|
||||
const { data: riskEngineStatus } = useRiskEngineStatus();
|
||||
interface RiskScoreModuleStatus {
|
||||
isLoading: boolean;
|
||||
installed?: boolean;
|
||||
}
|
||||
|
||||
return riskEngineStatus?.isNewRiskScoreModuleInstalled ?? false;
|
||||
export const useIsNewRiskScoreModuleInstalled = (): RiskScoreModuleStatus => {
|
||||
const { data: riskEngineStatus, isLoading } = useRiskEngineStatus();
|
||||
|
||||
if (isLoading) {
|
||||
return { isLoading: true };
|
||||
}
|
||||
|
||||
return { isLoading: false, installed: !!riskEngineStatus?.isNewRiskScoreModuleInstalled };
|
||||
};
|
||||
|
||||
export const useRiskEngineStatus = () => {
|
||||
|
|
|
@ -12,8 +12,8 @@ import { useSearchStrategy } from '../../../common/containers/use_search_strateg
|
|||
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
|
||||
import { useAppToastsMock } from '../../../common/hooks/use_app_toasts.mock';
|
||||
import { useRiskScoreFeatureStatus } from './use_risk_score_feature_status';
|
||||
import { useIsNewRiskScoreModuleInstalled } from './use_risk_engine_status';
|
||||
import { RiskScoreEntity } from '../../../../common/search_strategy';
|
||||
|
||||
jest.mock('../../../common/containers/use_search_strategy', () => ({
|
||||
useSearchStrategy: jest.fn(),
|
||||
}));
|
||||
|
@ -25,12 +25,20 @@ jest.mock('../../../common/hooks/use_space_id', () => ({
|
|||
jest.mock('../../../common/hooks/use_app_toasts');
|
||||
jest.mock('./use_risk_score_feature_status');
|
||||
|
||||
jest.mock('./use_risk_engine_status');
|
||||
|
||||
const mockUseIsNewRiskScoreModuleInstalled = useIsNewRiskScoreModuleInstalled as jest.Mock;
|
||||
const mockUseRiskScoreFeatureStatus = useRiskScoreFeatureStatus as jest.Mock;
|
||||
const mockUseSearchStrategy = useSearchStrategy as jest.Mock;
|
||||
const mockSearch = jest.fn();
|
||||
|
||||
let appToastsMock: jest.Mocked<ReturnType<typeof useAppToastsMock.create>>;
|
||||
|
||||
const defaultRiskScoreModuleStatus = {
|
||||
isLoading: false,
|
||||
installed: false,
|
||||
};
|
||||
|
||||
const defaultFeatureStatus = {
|
||||
isLoading: false,
|
||||
isDeprecated: false,
|
||||
|
@ -67,6 +75,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])(
|
|||
(useAppToasts as jest.Mock).mockReturnValue(appToastsMock);
|
||||
mockUseRiskScoreFeatureStatus.mockReturnValue(defaultFeatureStatus);
|
||||
mockUseSearchStrategy.mockReturnValue(defaultSearchResponse);
|
||||
mockUseIsNewRiskScoreModuleInstalled.mockReturnValue(defaultRiskScoreModuleStatus);
|
||||
});
|
||||
|
||||
test('does not search if license is not valid', () => {
|
||||
|
@ -172,6 +181,8 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])(
|
|||
renderHook(() => useRiskScore({ riskEntity }), {
|
||||
wrapper: TestProviders,
|
||||
});
|
||||
|
||||
expect(mockSearch).toHaveBeenCalledTimes(1);
|
||||
expect(mockSearch).toHaveBeenCalledWith({
|
||||
defaultIndex: [`ml_${riskEntity}_risk_score_latest_default`],
|
||||
factoryQueryType: `${riskEntity}sRiskScore`,
|
||||
|
@ -180,6 +191,25 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])(
|
|||
});
|
||||
});
|
||||
|
||||
test('runs search with new index if feature is enabled and not deprecated and new module installed', () => {
|
||||
mockUseIsNewRiskScoreModuleInstalled.mockReturnValue({
|
||||
...defaultRiskScoreModuleStatus,
|
||||
installed: true,
|
||||
});
|
||||
|
||||
renderHook(() => useRiskScore({ riskEntity }), {
|
||||
wrapper: TestProviders,
|
||||
});
|
||||
|
||||
expect(mockSearch).toHaveBeenCalledTimes(1);
|
||||
expect(mockSearch).toHaveBeenCalledWith({
|
||||
defaultIndex: ['risk-score.risk-score-latest-default'],
|
||||
factoryQueryType: `${riskEntity}sRiskScore`,
|
||||
riskScoreEntity: riskEntity,
|
||||
includeAlertsCount: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('return result', async () => {
|
||||
mockUseSearchStrategy.mockReturnValue({
|
||||
...defaultSearchResponse,
|
||||
|
|
|
@ -82,12 +82,14 @@ export const useRiskScore = <T extends RiskScoreEntity.host | RiskScoreEntity.us
|
|||
includeAlertsCount = false,
|
||||
}: UseRiskScore<T>): RiskScoreState<T> => {
|
||||
const spaceId = useSpaceId();
|
||||
const isNewRiskScoreModuleInstalled = useIsNewRiskScoreModuleInstalled();
|
||||
const defaultIndex = spaceId
|
||||
? riskEntity === RiskScoreEntity.host
|
||||
? getHostRiskIndex(spaceId, onlyLatest, isNewRiskScoreModuleInstalled)
|
||||
: getUserRiskIndex(spaceId, onlyLatest, isNewRiskScoreModuleInstalled)
|
||||
: undefined;
|
||||
const { installed: isNewRiskScoreModuleInstalled, isLoading: riskScoreStatusLoading } =
|
||||
useIsNewRiskScoreModuleInstalled();
|
||||
const defaultIndex =
|
||||
spaceId && !riskScoreStatusLoading && isNewRiskScoreModuleInstalled !== undefined
|
||||
? riskEntity === RiskScoreEntity.host
|
||||
? getHostRiskIndex(spaceId, onlyLatest, isNewRiskScoreModuleInstalled)
|
||||
: getUserRiskIndex(spaceId, onlyLatest, isNewRiskScoreModuleInstalled)
|
||||
: undefined;
|
||||
const factoryQueryType =
|
||||
riskEntity === RiskScoreEntity.host ? RiskQueries.hostsRiskScore : RiskQueries.usersRiskScore;
|
||||
|
||||
|
|
|
@ -52,12 +52,14 @@ export const useRiskScoreKpi = ({
|
|||
}: UseRiskScoreKpiProps): RiskScoreKpi => {
|
||||
const { addError } = useAppToasts();
|
||||
const spaceId = useSpaceId();
|
||||
const isNewRiskScoreModuleInstalled = useIsNewRiskScoreModuleInstalled();
|
||||
const defaultIndex = spaceId
|
||||
? riskEntity === RiskScoreEntity.host
|
||||
? getHostRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled)
|
||||
: getUserRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled)
|
||||
: undefined;
|
||||
const { installed: isNewRiskScoreModuleInstalled, isLoading: riskScoreStatusLoading } =
|
||||
useIsNewRiskScoreModuleInstalled();
|
||||
const defaultIndex =
|
||||
spaceId && !riskScoreStatusLoading && isNewRiskScoreModuleInstalled !== undefined
|
||||
? riskEntity === RiskScoreEntity.host
|
||||
? getHostRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled)
|
||||
: getUserRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled)
|
||||
: undefined;
|
||||
|
||||
const {
|
||||
isDeprecated,
|
||||
|
|
|
@ -14,6 +14,12 @@ import { useSearchStrategy } from '../../../../common/containers/use_search_stra
|
|||
jest.mock('../../../../common/containers/use_search_strategy', () => ({
|
||||
useSearchStrategy: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../entity_analytics/api/hooks/use_risk_engine_status', () => ({
|
||||
useIsNewRiskScoreModuleInstalled: jest
|
||||
.fn()
|
||||
.mockReturnValue({ isLoading: false, installed: true }),
|
||||
}));
|
||||
const mockUseSearchStrategy = useSearchStrategy as jest.Mock;
|
||||
const mockSearch = jest.fn();
|
||||
|
||||
|
|
|
@ -62,7 +62,8 @@ export const useAllHost = ({
|
|||
getHostsSelector(state, type)
|
||||
);
|
||||
|
||||
const isNewRiskScoreModuleInstalled = useIsNewRiskScoreModuleInstalled();
|
||||
const { installed: isNewRiskScoreModuleInstalled, isLoading: riskScoreStatusLoading } =
|
||||
useIsNewRiskScoreModuleInstalled();
|
||||
|
||||
const [hostsRequest, setHostRequest] = useState<HostsRequestOptionsInput | null>(null);
|
||||
|
||||
|
@ -129,6 +130,9 @@ export const useAllHost = ({
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (riskScoreStatusLoading) {
|
||||
return;
|
||||
}
|
||||
setHostRequest((prevRequest) => {
|
||||
const myRequest: HostsRequestOptionsInput = {
|
||||
...(prevRequest ?? {}),
|
||||
|
@ -162,6 +166,7 @@ export const useAllHost = ({
|
|||
startDate,
|
||||
sortField,
|
||||
isNewRiskScoreModuleInstalled,
|
||||
riskScoreStatusLoading,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -15,6 +15,11 @@ import { UsersType } from '../../store/model';
|
|||
|
||||
jest.mock('../../../../common/containers/query_toggle');
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
jest.mock('../../../../entity_analytics/api/hooks/use_risk_engine_status', () => ({
|
||||
useIsNewRiskScoreModuleInstalled: jest
|
||||
.fn()
|
||||
.mockReturnValue({ isLoading: false, installed: true }),
|
||||
}));
|
||||
|
||||
const mockSearch = jest.fn();
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ export const AllUsersQueryTabBody = ({
|
|||
|
||||
const getUsersSelector = useMemo(() => usersSelectors.allUsersSelector(), []);
|
||||
const { activePage, limit, sort } = useDeepEqualSelector((state) => getUsersSelector(state));
|
||||
const isNewRiskScoreModuleInstalled = useIsNewRiskScoreModuleInstalled();
|
||||
const { installed: isNewRiskScoreModuleInstalled, isLoading: riskScoreStatusLoading } =
|
||||
useIsNewRiskScoreModuleInstalled();
|
||||
|
||||
const {
|
||||
loading,
|
||||
|
@ -67,7 +68,7 @@ export const AllUsersQueryTabBody = ({
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!querySkip) {
|
||||
if (!querySkip && !riskScoreStatusLoading) {
|
||||
search({
|
||||
filterQuery,
|
||||
defaultIndex: indexNames,
|
||||
|
@ -92,6 +93,7 @@ export const AllUsersQueryTabBody = ({
|
|||
limit,
|
||||
sort,
|
||||
isNewRiskScoreModuleInstalled,
|
||||
riskScoreStatusLoading,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue