From dbb0b46b7279ea1e5fac70860f3a5f5091aef44f Mon Sep 17 00:00:00 2001 From: Marshall Main Date: Wed, 7 Oct 2020 15:27:51 -0400 Subject: [PATCH 1/6] Modify create_index_route to update template in place if outdated --- .../alerts/use_signal_index.tsx | 2 +- .../routes/index/create_index_route.ts | 29 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx index 14fd9ffa50843..efd6ba5be5abc 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx @@ -43,7 +43,7 @@ export const useSignalIndex = (): ReturnSignalIndex => { const fetchData = async () => { try { setLoading(true); - const signal = await getSignalIndex({ signal: abortCtrl.signal }); + const signal = await createSignalIndex({ signal: abortCtrl.signal }); if (isSubscribed && signal != null) { setSignalIndex({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts index a09fd9e0c9bd9..cf198ea4adcb0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { get } from 'lodash'; import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; @@ -39,24 +40,32 @@ export const createIndexRoute = (router: IRouter) => { const index = siemClient.getSignalsIndex(); const indexExists = await getIndexExists(callCluster, index); - if (indexExists) { - return siemResponse.error({ - statusCode: 409, - body: `index: "${index}" already exists`, + const templateExists = await getTemplateExists(callCluster, index); + let existingTemplateVersion: number | undefined; + if (templateExists) { + const existingTemplate: unknown = await callCluster('indices.getTemplate', { + name: index, }); - } else { + existingTemplateVersion = get(existingTemplate, [index, 'version']); + } + const newTemplate = getSignalsTemplate(index); + if ( + existingTemplateVersion === undefined || + existingTemplateVersion < newTemplate.version + ) { const policyExists = await getPolicyExists(callCluster, index); if (!policyExists) { await setPolicy(callCluster, index, signalsPolicy); } - const templateExists = await getTemplateExists(callCluster, index); - if (!templateExists) { - const template = getSignalsTemplate(index); - await setTemplate(callCluster, index, template); + await setTemplate(callCluster, index, newTemplate); + if (indexExists) { + await callCluster('indices.rollover', { alias: index }); } + } + if (!indexExists) { await createBootstrapIndex(callCluster, index); - return response.ok({ body: { acknowledged: true } }); } + return response.ok({ body: { acknowledged: true } }); } catch (err) { const error = transformError(err); return siemResponse.error({ From 56ca51839cb398cc45840b0ce4e584a6055a67f8 Mon Sep 17 00:00:00 2001 From: Marshall Main Date: Thu, 8 Oct 2020 11:57:17 -0400 Subject: [PATCH 2/6] Update frontend to always call create_index_route --- .../public/detections/components/user_info/index.tsx | 9 +-------- .../detection_engine/alerts/use_signal_index.tsx | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index 00e108ffb89b6..7ef5479f8661c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -218,14 +218,7 @@ export const useUserInfo = (): State => { }, [dispatch, loading, signalIndexName, apiSignalIndexName]); useEffect(() => { - if ( - isAuthenticated && - hasEncryptionKey && - hasIndexManage && - isSignalIndexExists != null && - !isSignalIndexExists && - createSignalIndex != null - ) { + if (isAuthenticated && hasEncryptionKey && hasIndexManage && createSignalIndex != null) { createSignalIndex(); } }, [createSignalIndex, isAuthenticated, hasEncryptionKey, isSignalIndexExists, hasIndexManage]); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx index efd6ba5be5abc..14fd9ffa50843 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx @@ -43,7 +43,7 @@ export const useSignalIndex = (): ReturnSignalIndex => { const fetchData = async () => { try { setLoading(true); - const signal = await createSignalIndex({ signal: abortCtrl.signal }); + const signal = await getSignalIndex({ signal: abortCtrl.signal }); if (isSubscribed && signal != null) { setSignalIndex({ From 1b6907c52d51d7bf91891a3df8a89df0cc5e5069 Mon Sep 17 00:00:00 2001 From: Marshall Main Date: Thu, 8 Oct 2020 16:26:11 -0400 Subject: [PATCH 3/6] Add template status to GET route --- .../components/user_info/index.test.tsx | 1 + .../detections/components/user_info/index.tsx | 46 ++++++++++++++++++- .../detection_engine/alerts/types.ts | 1 + .../alerts/use_signal_index.tsx | 9 ++-- .../routes/index/check_template_version.ts | 35 ++++++++++++++ .../routes/index/create_index_route.ts | 19 ++------ .../routes/index/read_index_route.ts | 4 +- 7 files changed, 93 insertions(+), 22 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/check_template_version.ts diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx index b01edac2605eb..92058d260ea92 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx @@ -43,6 +43,7 @@ describe('useUserInfo', () => { isSignalIndexExists: null, loading: true, signalIndexName: null, + signalIndexTemplateOutdated: null, }, error: undefined, }); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index 7ef5479f8661c..21161ea61771e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -20,6 +20,7 @@ export interface State { hasEncryptionKey: boolean | null; loading: boolean; signalIndexName: string | null; + signalIndexTemplateOutdated: boolean | null; } const initialState: State = { @@ -31,6 +32,7 @@ const initialState: State = { hasEncryptionKey: null, loading: true, signalIndexName: null, + signalIndexTemplateOutdated: null, }; export type Action = @@ -62,6 +64,10 @@ export type Action = | { type: 'updateSignalIndexName'; signalIndexName: string | null; + } + | { + type: 'updateSignalIndexTemplateOutdated'; + signalIndexTemplateOutdated: boolean | null; }; export const userInfoReducer = (state: State, action: Action): State => { @@ -114,6 +120,12 @@ export const userInfoReducer = (state: State, action: Action): State => { signalIndexName: action.signalIndexName, }; } + case 'updateSignalIndexTemplateOutdated': { + return { + ...state, + signalIndexTemplateOutdated: action.signalIndexTemplateOutdated, + }; + } default: return state; } @@ -144,6 +156,7 @@ export const useUserInfo = (): State => { hasEncryptionKey, loading, signalIndexName, + signalIndexTemplateOutdated, }, dispatch, ] = useUserData(); @@ -158,6 +171,7 @@ export const useUserInfo = (): State => { loading: indexNameLoading, signalIndexExists: isApiSignalIndexExists, signalIndexName: apiSignalIndexName, + signalIndexTemplateOutdated: apiSignalIndexTemplateOutdated, createDeSignalIndex: createSignalIndex, } = useSignalIndex(); @@ -218,10 +232,37 @@ export const useUserInfo = (): State => { }, [dispatch, loading, signalIndexName, apiSignalIndexName]); useEffect(() => { - if (isAuthenticated && hasEncryptionKey && hasIndexManage && createSignalIndex != null) { + if ( + !loading && + signalIndexTemplateOutdated !== apiSignalIndexTemplateOutdated && + apiSignalIndexTemplateOutdated != null + ) { + dispatch({ + type: 'updateSignalIndexTemplateOutdated', + signalIndexTemplateOutdated: apiSignalIndexTemplateOutdated, + }); + } + }, [dispatch, loading, signalIndexTemplateOutdated, apiSignalIndexTemplateOutdated]); + + useEffect(() => { + if ( + isAuthenticated && + hasEncryptionKey && + hasIndexManage && + ((isSignalIndexExists != null && !isSignalIndexExists) || + (signalIndexTemplateOutdated != null && signalIndexTemplateOutdated)) && + createSignalIndex != null + ) { createSignalIndex(); } - }, [createSignalIndex, isAuthenticated, hasEncryptionKey, isSignalIndexExists, hasIndexManage]); + }, [ + createSignalIndex, + isAuthenticated, + hasEncryptionKey, + isSignalIndexExists, + hasIndexManage, + signalIndexTemplateOutdated, + ]); return { loading, @@ -232,5 +273,6 @@ export const useUserInfo = (): State => { hasIndexManage, hasIndexWrite, signalIndexName, + signalIndexTemplateOutdated, }; }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/types.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/types.ts index 2eb2145c6c34d..59ab416ecc824 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/types.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/types.ts @@ -44,6 +44,7 @@ export interface UpdateAlertStatusProps { export interface AlertsIndex { name: string; + template_outdated: boolean; } export interface Privilege { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx index 14fd9ffa50843..f7d2202736169 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.tsx @@ -17,6 +17,7 @@ export interface ReturnSignalIndex { loading: boolean; signalIndexExists: boolean | null; signalIndexName: string | null; + signalIndexTemplateOutdated: boolean | null; createDeSignalIndex: Func | null; } @@ -27,11 +28,10 @@ export interface ReturnSignalIndex { */ export const useSignalIndex = (): ReturnSignalIndex => { const [loading, setLoading] = useState(true); - const [signalIndex, setSignalIndex] = useState< - Pick - >({ + const [signalIndex, setSignalIndex] = useState>({ signalIndexExists: null, signalIndexName: null, + signalIndexTemplateOutdated: null, createDeSignalIndex: null, }); const [, dispatchToaster] = useStateToaster(); @@ -49,6 +49,7 @@ export const useSignalIndex = (): ReturnSignalIndex => { setSignalIndex({ signalIndexExists: true, signalIndexName: signal.name, + signalIndexTemplateOutdated: signal.template_outdated, createDeSignalIndex: createIndex, }); } @@ -57,6 +58,7 @@ export const useSignalIndex = (): ReturnSignalIndex => { setSignalIndex({ signalIndexExists: false, signalIndexName: null, + signalIndexTemplateOutdated: null, createDeSignalIndex: createIndex, }); if (isSecurityAppError(error) && error.body.status_code !== 404) { @@ -87,6 +89,7 @@ export const useSignalIndex = (): ReturnSignalIndex => { setSignalIndex({ signalIndexExists: false, signalIndexName: null, + signalIndexTemplateOutdated: null, createDeSignalIndex: createIndex, }); errorToToaster({ title: i18n.SIGNAL_POST_FAILURE, error, dispatchToaster }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/check_template_version.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/check_template_version.ts new file mode 100644 index 0000000000000..09265dfcebae3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/check_template_version.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { get } from 'lodash'; +import { LegacyCallAPIOptions } from '../../../../../../../../src/core/server'; +import { getSignalsTemplate } from './get_signals_template'; +import { getTemplateExists } from '../../index/get_template_exists'; + +export const templateNeedsUpdate = async ( + callCluster: ( + endpoint: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + clientParams: Record, + options?: LegacyCallAPIOptions + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) => Promise, + index: string +) => { + const templateExists = await getTemplateExists(callCluster, index); + let existingTemplateVersion: number | undefined; + if (templateExists) { + const existingTemplate: unknown = await callCluster('indices.getTemplate', { + name: index, + }); + existingTemplateVersion = get(existingTemplate, [index, 'version']); + } + const newTemplate = getSignalsTemplate(index); + if (existingTemplateVersion === undefined || existingTemplateVersion < newTemplate.version) { + return true; + } + return false; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts index cf198ea4adcb0..a801bc18db439 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; @@ -13,9 +12,9 @@ import { getPolicyExists } from '../../index/get_policy_exists'; import { setPolicy } from '../../index/set_policy'; import { setTemplate } from '../../index/set_template'; import { getSignalsTemplate } from './get_signals_template'; -import { getTemplateExists } from '../../index/get_template_exists'; import { createBootstrapIndex } from '../../index/create_bootstrap_index'; import signalsPolicy from './signals_policy.json'; +import { templateNeedsUpdate } from './check_template_version'; export const createIndexRoute = (router: IRouter) => { router.post( @@ -40,24 +39,12 @@ export const createIndexRoute = (router: IRouter) => { const index = siemClient.getSignalsIndex(); const indexExists = await getIndexExists(callCluster, index); - const templateExists = await getTemplateExists(callCluster, index); - let existingTemplateVersion: number | undefined; - if (templateExists) { - const existingTemplate: unknown = await callCluster('indices.getTemplate', { - name: index, - }); - existingTemplateVersion = get(existingTemplate, [index, 'version']); - } - const newTemplate = getSignalsTemplate(index); - if ( - existingTemplateVersion === undefined || - existingTemplateVersion < newTemplate.version - ) { + if (await templateNeedsUpdate(callCluster, index)) { const policyExists = await getPolicyExists(callCluster, index); if (!policyExists) { await setPolicy(callCluster, index, signalsPolicy); } - await setTemplate(callCluster, index, newTemplate); + await setTemplate(callCluster, index, getSignalsTemplate(index)); if (indexExists) { await callCluster('indices.rollover', { alias: index }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts index 7debe0931abd6..b9ae8b546b8bd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts @@ -8,6 +8,7 @@ import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; import { getIndexExists } from '../../index/get_index_exists'; +import { templateNeedsUpdate } from './check_template_version'; export const readIndexRoute = (router: IRouter) => { router.get( @@ -31,9 +32,10 @@ export const readIndexRoute = (router: IRouter) => { const index = siemClient.getSignalsIndex(); const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, index); + const templateOutdated = await templateNeedsUpdate(clusterClient.callAsCurrentUser, index); if (indexExists) { - return response.ok({ body: { name: index } }); + return response.ok({ body: { name: index, template_outdated: templateOutdated } }); } else { return siemResponse.error({ statusCode: 404, From 15e8d6d310e888bb4f4bce3e65d716de08bf22c9 Mon Sep 17 00:00:00 2001 From: Marshall Main Date: Thu, 8 Oct 2020 16:40:35 -0400 Subject: [PATCH 4/6] Clean up parameter type --- .../routes/index/check_template_version.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/check_template_version.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/check_template_version.ts index 09265dfcebae3..473a2dad37f19 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/check_template_version.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/check_template_version.ts @@ -5,20 +5,11 @@ */ import { get } from 'lodash'; -import { LegacyCallAPIOptions } from '../../../../../../../../src/core/server'; +import { LegacyAPICaller } from '../../../../../../../../src/core/server'; import { getSignalsTemplate } from './get_signals_template'; import { getTemplateExists } from '../../index/get_template_exists'; -export const templateNeedsUpdate = async ( - callCluster: ( - endpoint: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - clientParams: Record, - options?: LegacyCallAPIOptions - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Promise, - index: string -) => { +export const templateNeedsUpdate = async (callCluster: LegacyAPICaller, index: string) => { const templateExists = await getTemplateExists(callCluster, index); let existingTemplateVersion: number | undefined; if (templateExists) { From 0457c6900c75126326f28d070326702f5252dc93 Mon Sep 17 00:00:00 2001 From: Marshall Main Date: Thu, 8 Oct 2020 18:56:31 -0400 Subject: [PATCH 5/6] Fix tests and types --- .../detections/containers/detection_engine/alerts/mock.ts | 1 + .../detection_engine/alerts/use_signal_index.test.tsx | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts index cd2cc1fe390ba..4fd240348f0f3 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/mock.ts @@ -980,6 +980,7 @@ export const mockStatusAlertQuery: object = { export const mockSignalIndex: AlertsIndex = { name: 'mock-signal-index', + template_outdated: false, }; export const mockUserPrivilege: Privilege = { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx index d0571bfca5b2b..1db952526414a 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx @@ -26,6 +26,7 @@ describe('useSignalIndex', () => { loading: true, signalIndexExists: null, signalIndexName: null, + signalIndexTemplateOutdated: null, }); }); }); @@ -42,6 +43,7 @@ describe('useSignalIndex', () => { loading: false, signalIndexExists: true, signalIndexName: 'mock-signal-index', + signalIndexTemplateOutdated: false, }); }); }); @@ -62,6 +64,7 @@ describe('useSignalIndex', () => { loading: false, signalIndexExists: true, signalIndexName: 'mock-signal-index', + signalIndexTemplateOutdated: false, }); }); }); @@ -101,6 +104,7 @@ describe('useSignalIndex', () => { loading: false, signalIndexExists: false, signalIndexName: null, + signalIndexTemplateOutdated: null, }); }); }); @@ -121,6 +125,7 @@ describe('useSignalIndex', () => { loading: false, signalIndexExists: false, signalIndexName: null, + signalIndexTemplateOutdated: null, }); }); }); From 34901706b7e5f03d67d332f18fe8228e15533c19 Mon Sep 17 00:00:00 2001 From: Marshall Main Date: Wed, 14 Oct 2020 15:44:17 -0400 Subject: [PATCH 6/6] Add test --- .../components/user_info/index.test.tsx | 61 ++++++++++++------- .../detections/components/user_info/index.tsx | 2 +- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx index 92058d260ea92..9b15007136b2e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx @@ -4,20 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { renderHook } from '@testing-library/react-hooks'; -import { useUserInfo } from './index'; +import { renderHook, act } from '@testing-library/react-hooks'; +import { useUserInfo, ManageUserInfo } from './index'; -import { usePrivilegeUser } from '../../containers/detection_engine/alerts/use_privilege_user'; -import { useSignalIndex } from '../../containers/detection_engine/alerts/use_signal_index'; import { useKibana } from '../../../common/lib/kibana'; -jest.mock('../../containers/detection_engine/alerts/use_privilege_user'); -jest.mock('../../containers/detection_engine/alerts/use_signal_index'); +import * as api from '../../containers/detection_engine/alerts/api'; + jest.mock('../../../common/lib/kibana'); +jest.mock('../../containers/detection_engine/alerts/api'); describe('useUserInfo', () => { beforeAll(() => { - (usePrivilegeUser as jest.Mock).mockReturnValue({}); - (useSignalIndex as jest.Mock).mockReturnValue({}); (useKibana as jest.Mock).mockReturnValue({ services: { application: { @@ -30,22 +27,40 @@ describe('useUserInfo', () => { }, }); }); - it('returns default state', () => { - const { result } = renderHook(() => useUserInfo()); + it('returns default state', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => useUserInfo()); + await waitForNextUpdate(); - expect(result).toEqual({ - current: { - canUserCRUD: null, - hasEncryptionKey: null, - hasIndexManage: null, - hasIndexWrite: null, - isAuthenticated: null, - isSignalIndexExists: null, - loading: true, - signalIndexName: null, - signalIndexTemplateOutdated: null, - }, - error: undefined, + expect(result).toEqual({ + current: { + canUserCRUD: null, + hasEncryptionKey: null, + hasIndexManage: null, + hasIndexWrite: null, + isAuthenticated: null, + isSignalIndexExists: null, + loading: true, + signalIndexName: null, + signalIndexTemplateOutdated: null, + }, + error: undefined, + }); + }); + }); + + it('calls createSignalIndex if signal index template is outdated', async () => { + const spyOnCreateSignalIndex = jest.spyOn(api, 'createSignalIndex'); + const spyOnGetSignalIndex = jest.spyOn(api, 'getSignalIndex').mockResolvedValueOnce({ + name: 'mock-signal-index', + template_outdated: true, + }); + await act(async () => { + const { waitForNextUpdate } = renderHook(() => useUserInfo(), { wrapper: ManageUserInfo }); + await waitForNextUpdate(); + await waitForNextUpdate(); }); + expect(spyOnGetSignalIndex).toHaveBeenCalledTimes(2); + expect(spyOnCreateSignalIndex).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index 21161ea61771e..639059fdea594 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -180,7 +180,7 @@ export const useUserInfo = (): State => { typeof uiCapabilities.siem.crud === 'boolean' ? uiCapabilities.siem.crud : false; useEffect(() => { - if (loading !== privilegeLoading || indexNameLoading) { + if (loading !== (privilegeLoading || indexNameLoading)) { dispatch({ type: 'updateLoading', loading: privilegeLoading || indexNameLoading }); } }, [dispatch, loading, privilegeLoading, indexNameLoading]);