From 99814518ab5e63042edb2431905c32a152ecd320 Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado Date: Mon, 24 Apr 2023 14:17:44 +0200 Subject: [PATCH] Refactor useEnableDataFeed to split disablement and enablement logic --- .../hooks/use_enable_data_feed.test.tsx | 366 +++++++++--------- .../ml_popover/hooks/use_enable_data_feed.ts | 152 ++++---- .../components/ml_popover/ml_popover.tsx | 15 +- .../logic/use_start_ml_jobs.tsx | 2 +- ...admin_job_description.integration.test.tsx | 3 +- .../admin/ml_admin_job_description.tsx | 15 +- .../anomalies/components/enable_job.test.tsx | 2 +- .../anomalies/components/enable_job.tsx | 2 +- 8 files changed, 288 insertions(+), 269 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.test.tsx index 5b46fa3cb6fda..52d955414a099 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.test.tsx @@ -82,247 +82,253 @@ describe('useSecurityJobsHelpers', () => { mockSetupMlJob.mockReturnValue(Promise.resolve()); }); - it('renders isLoading=true when installing job', async () => { - let resolvePromiseCb: (value: unknown) => void; - mockSetupMlJob.mockReturnValue( - new Promise((resolve) => { - resolvePromiseCb = resolve; - }) - ); - const { result, waitForNextUpdate } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - expect(result.current.isLoading).toBe(false); - - await act(async () => { - const enableDataFeedPromise = result.current.enableDatafeed(JOB, TIMESTAMP, false); - - await waitForNextUpdate(); - expect(result.current.isLoading).toBe(true); - - resolvePromiseCb({}); - await enableDataFeedPromise; + describe('enableDatafeed', () => { + it('renders isLoading=true when installing job', async () => { + let resolvePromiseCb: (value: unknown) => void; + mockSetupMlJob.mockReturnValue( + new Promise((resolve) => { + resolvePromiseCb = resolve; + }) + ); + const { result, waitForNextUpdate } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); expect(result.current.isLoading).toBe(false); - }); - }); - - it('does not call setupMlJob if job is already installed', async () => { - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - - await act(async () => { - await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP, false); - }); - expect(mockSetupMlJob).not.toBeCalled(); - }); + await act(async () => { + const enableDataFeedPromise = result.current.enableDatafeed(JOB, TIMESTAMP); - it('calls setupMlJob if job is uninstalled', async () => { - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - await result.current.enableDatafeed({ ...JOB, isInstalled: false }, TIMESTAMP, false); - }); - expect(mockSetupMlJob).toBeCalled(); - }); + await waitForNextUpdate(); + expect(result.current.isLoading).toBe(true); - it('calls startDatafeeds if enable param is true', async () => { - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - await result.current.enableDatafeed(JOB, TIMESTAMP, true); + resolvePromiseCb({}); + await enableDataFeedPromise; + expect(result.current.isLoading).toBe(false); + }); }); - expect(mockStartDatafeeds).toBeCalled(); - expect(mockStopDatafeeds).not.toBeCalled(); - }); - it('calls stopDatafeeds if enable param is false', async () => { - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - await result.current.enableDatafeed(JOB, TIMESTAMP, false); - }); - expect(mockStartDatafeeds).not.toBeCalled(); - expect(mockStopDatafeeds).toBeCalled(); - }); + it('does not call setupMlJob if job is already installed', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); - it('calls startDatafeeds with 2 weeks old start date', async () => { - jest.useFakeTimers().setSystemTime(new Date('1989-03-07')); + await act(async () => { + await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP); + }); - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - await result.current.enableDatafeed(JOB, TIMESTAMP, true); + expect(mockSetupMlJob).not.toBeCalled(); }); - expect(mockStartDatafeeds).toBeCalledWith({ - datafeedIds: [`datafeed-test_job_id`], - start: new Date('1989-02-21').getTime(), - }); - }); - it('return enabled:true when startDataFeed successfully installed the job', async () => { - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - const response = await result.current.enableDatafeed(JOB, TIMESTAMP, true); - expect(response.enabled).toBeTruthy(); + it('calls setupMlJob if job is uninstalled', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed({ ...JOB, isInstalled: false }, TIMESTAMP); + }); + expect(mockSetupMlJob).toBeCalled(); }); - }); - it('return enabled:false when startDataFeed promise is rejected while installing a job', async () => { - mockStartDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - const response = await result.current.enableDatafeed(JOB, TIMESTAMP, true); - expect(response.enabled).toBeFalsy(); + it('calls startDatafeeds when enableDatafeed is called', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed(JOB, TIMESTAMP); + }); + expect(mockStartDatafeeds).toBeCalled(); + expect(mockStopDatafeeds).not.toBeCalled(); }); - }); - it('return enabled:false when startDataFeed failed to install the job', async () => { - mockStartDatafeeds.mockReturnValue( - Promise.resolve({ [`datafeed-${jobId}`]: { started: false, error: 'test_error' } }) - ); + it('calls startDatafeeds with 2 weeks old start date', async () => { + jest.useFakeTimers().setSystemTime(new Date('1989-03-07')); - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - const response = await result.current.enableDatafeed(JOB, TIMESTAMP, true); - expect(response.enabled).toBeFalsy(); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed(JOB, TIMESTAMP); + }); + expect(mockStartDatafeeds).toBeCalledWith({ + datafeedIds: [`datafeed-test_job_id`], + start: new Date('1989-02-21').getTime(), + }); }); - }); - it('return enabled:false when enableDatafeed successfully uninstalled the job', async () => { - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - const response = await result.current.enableDatafeed(JOB, TIMESTAMP, false); - expect(response.enabled).toBeFalsy(); + it('return enabled:true when startDataFeed successfully installed the job', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + const response = await result.current.enableDatafeed(JOB, TIMESTAMP); + expect(response.enabled).toBeTruthy(); + }); }); - }); - it('return enabled:true when promise is rejected while uninstalling the job', async () => { - mockStopDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - const response = await result.current.enableDatafeed(JOB, TIMESTAMP, false); - expect(response.enabled).toBeTruthy(); + it('return enabled:false when startDataFeed promise is rejected while installing a job', async () => { + mockStartDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + const response = await result.current.enableDatafeed(JOB, TIMESTAMP); + expect(response.enabled).toBeFalsy(); + }); }); - }); - - it('return enabled:true when enableDatafeed fails to uninstall the job', async () => { - mockStopDatafeeds.mockReturnValue( - Promise.resolve([{ [`datafeed-${jobId}`]: { stopped: false, error: 'test_error' } }]) - ); - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, - }); - await act(async () => { - const response = await result.current.enableDatafeed(JOB, TIMESTAMP, false); - expect(response.enabled).toBeTruthy(); - }); - }); + it('return enabled:false when startDataFeed failed to install the job', async () => { + mockStartDatafeeds.mockReturnValue( + Promise.resolve({ [`datafeed-${jobId}`]: { started: false, error: 'test_error' } }) + ); - describe('telemetry', () => { - it('reports telemetry when installing and enabling a job', async () => { const { result } = renderHook(() => useEnableDataFeed(), { wrapper, }); - await act(async () => { - await result.current.enableDatafeed(JOB, TIMESTAMP, true); + const response = await result.current.enableDatafeed(JOB, TIMESTAMP); + expect(response.enabled).toBeFalsy(); + }); + }); + + describe('telemetry', () => { + it('reports telemetry when installing and enabling a job', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + + await act(async () => { + await result.current.enableDatafeed(JOB, TIMESTAMP); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.moduleInstalled, + isElasticJob: true, + jobId, + moduleId, + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.started, + isElasticJob: true, + jobId, + }); }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ - status: ML_JOB_TELEMETRY_STATUS.moduleInstalled, - isElasticJob: true, - jobId, - moduleId, + it('reports telemetry when starting a job fails', async () => { + mockStartDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.startError, + errorMessage: 'Start job failure - test_error', + isElasticJob: true, + jobId, + }); }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ - status: ML_JOB_TELEMETRY_STATUS.started, - isElasticJob: true, - jobId, + it('reports telemetry when installing a module fails', async () => { + mockSetupMlJob.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed(JOB, TIMESTAMP); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.installationError, + errorMessage: 'Create job failure - test_error', + isElasticJob: true, + jobId, + moduleId, + }); }); }); + }); - it('reports telemetry when stopping a job', async () => { + describe('disableDatafeed', () => { + it('return enabled:false when disableDatafeed successfully uninstalled the job', async () => { const { result } = renderHook(() => useEnableDataFeed(), { wrapper, }); await act(async () => { - await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP, false); - }); - - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ - status: ML_JOB_TELEMETRY_STATUS.stopped, - isElasticJob: true, - jobId, + const response = await result.current.disableDatafeed(JOB); + expect(response.enabled).toBeFalsy(); }); }); - it('reports telemetry when stopping a job fails', async () => { + it('return enabled:true when promise is rejected while uninstalling the job', async () => { mockStopDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); const { result } = renderHook(() => useEnableDataFeed(), { wrapper, }); await act(async () => { - await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP, false); - }); - - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ - status: ML_JOB_TELEMETRY_STATUS.stopError, - errorMessage: 'Stop job failure - test_error', - isElasticJob: true, - jobId, + const response = await result.current.disableDatafeed(JOB); + expect(response.enabled).toBeTruthy(); }); }); - it('reports telemetry when starting a job fails', async () => { - mockStartDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); + it('return enabled:true when disableDatafeed fails to uninstall the job', async () => { + mockStopDatafeeds.mockReturnValue( + Promise.resolve([{ [`datafeed-${jobId}`]: { stopped: false, error: 'test_error' } }]) + ); + const { result } = renderHook(() => useEnableDataFeed(), { wrapper, }); await act(async () => { - await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP, true); - }); - - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ - status: ML_JOB_TELEMETRY_STATUS.startError, - errorMessage: 'Start job failure - test_error', - isElasticJob: true, - jobId, + const response = await result.current.disableDatafeed(JOB); + expect(response.enabled).toBeTruthy(); }); }); - it('reports telemetry when installing a module fails', async () => { - mockSetupMlJob.mockReturnValue(Promise.reject(new Error('test_error'))); + it('calls stopDatafeeds when disableDatafeed is called', async () => { const { result } = renderHook(() => useEnableDataFeed(), { wrapper, }); await act(async () => { - await result.current.enableDatafeed(JOB, TIMESTAMP, true); + await result.current.disableDatafeed(JOB); + }); + expect(mockStartDatafeeds).not.toBeCalled(); + expect(mockStopDatafeeds).toBeCalled(); + }); + + describe('telemetry', () => { + it('reports telemetry when stopping a job', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.disableDatafeed({ ...JOB, isInstalled: true }); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.stopped, + isElasticJob: true, + jobId, + }); }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ - status: ML_JOB_TELEMETRY_STATUS.installationError, - errorMessage: 'Create job failure - test_error', - isElasticJob: true, - jobId, - moduleId, + it('reports telemetry when stopping a job fails', async () => { + mockStopDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.disableDatafeed({ ...JOB, isInstalled: true }); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.stopError, + errorMessage: 'Stop job failure - test_error', + isElasticJob: true, + jobId, + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts index 21625d4f11841..393e132436c38 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts @@ -20,7 +20,7 @@ import { setupMlJob, startDatafeeds, stopDatafeeds } from '../api'; import type { ErrorResponse, SecurityJob } from '../types'; import * as i18n from './translations'; -// Enable/Disable Job & Datafeed -- passed to JobsTable for use as callback on JobSwitch +// Enable/Disable Job & Datafeed export const useEnableDataFeed = () => { const { telemetry } = useKibana().services; @@ -28,9 +28,12 @@ export const useEnableDataFeed = () => { const [isLoading, setIsLoading] = useState(false); const enableDatafeed = useCallback( - async (job: SecurityJob, latestTimestampMs: number, enable: boolean) => { - submitTelemetry(job, enable); + async (job: SecurityJob, latestTimestampMs: number) => { setIsLoading(true); + track( + METRIC_TYPE.COUNT, + job.isElasticJob ? TELEMETRY_EVENT.SIEM_JOB_ENABLED : TELEMETRY_EVENT.CUSTOM_JOB_ENABLED + ); if (!job.isInstalled) { try { @@ -40,7 +43,6 @@ export const useEnableDataFeed = () => { jobIdErrorFilter: [job.id], groups: job.groups, }); - setIsLoading(false); telemetry.reportMLJobUpdate({ jobId: job.id, isElasticJob: job.isElasticJob, @@ -48,8 +50,8 @@ export const useEnableDataFeed = () => { status: ML_JOB_TELEMETRY_STATUS.moduleInstalled, }); } catch (error) { - addError(error, { title: i18n.CREATE_JOB_FAILURE }); setIsLoading(false); + addError(error, { title: i18n.CREATE_JOB_FAILURE }); telemetry.reportMLJobUpdate({ jobId: job.id, isElasticJob: job.isElasticJob, @@ -67,94 +69,88 @@ export const useEnableDataFeed = () => { const maxStartTime = date.setDate(date.getDate() - 14); const datafeedId = `datafeed-${job.id}`; - if (enable) { - const startTime = Math.max(latestTimestampMs, maxStartTime); - const reportEnableJobError = (error: Error) => { - track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_ENABLE_FAILURE); - addError(error, { title: i18n.START_JOB_FAILURE }); - telemetry.reportMLJobUpdate({ - jobId: job.id, - isElasticJob: job.isElasticJob, - status: ML_JOB_TELEMETRY_STATUS.startError, - errorMessage: `${i18n.START_JOB_FAILURE} - ${error.message}`, - }); - }; - - try { - const response = await startDatafeeds({ - datafeedIds: [datafeedId], - start: startTime, - }); - if (response[datafeedId]?.error) { - throw new Error(response[datafeedId].error); - } + const startTime = Math.max(latestTimestampMs, maxStartTime); - telemetry.reportMLJobUpdate({ - jobId: job.id, - isElasticJob: job.isElasticJob, - status: ML_JOB_TELEMETRY_STATUS.started, - }); + try { + const response = await startDatafeeds({ + datafeedIds: [datafeedId], + start: startTime, + }); - return { enabled: response[datafeedId] ? response[datafeedId].started : false }; - } catch (error) { - reportEnableJobError(error); - } finally { - setIsLoading(false); + if (response[datafeedId]?.error) { + throw new Error(response[datafeedId].error); } - } else { - const reportDisableError = (error: Error) => { - track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_DISABLE_FAILURE); - addError(error, { title: i18n.STOP_JOB_FAILURE }); - telemetry.reportMLJobUpdate({ - jobId: job.id, - isElasticJob: job.isElasticJob, - status: ML_JOB_TELEMETRY_STATUS.stopError, - errorMessage: `${i18n.STOP_JOB_FAILURE} - ${error.message}`, - }); - }; - try { - const [response] = await stopDatafeeds({ datafeedIds: [datafeedId] }); + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + status: ML_JOB_TELEMETRY_STATUS.started, + }); + + return { enabled: response[datafeedId] ? response[datafeedId].started : false }; + } catch (error) { + track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_ENABLE_FAILURE); + addError(error, { title: i18n.START_JOB_FAILURE }); + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + status: ML_JOB_TELEMETRY_STATUS.startError, + errorMessage: `${i18n.START_JOB_FAILURE} - ${error.message}`, + }); + } finally { + setIsLoading(false); + } - if (isErrorResponse(response)) { - throw new Error(response.error); - } + return { enabled: false }; + }, + [addError, telemetry] + ); - telemetry.reportMLJobUpdate({ - jobId: job.id, - isElasticJob: job.isElasticJob, - status: ML_JOB_TELEMETRY_STATUS.stopped, - }); + const disableDatafeed = useCallback( + async (job: SecurityJob) => { + track( + METRIC_TYPE.COUNT, + job.isElasticJob ? TELEMETRY_EVENT.SIEM_JOB_DISABLED : TELEMETRY_EVENT.CUSTOM_JOB_DISABLED + ); + setIsLoading(true); - return { enabled: response[datafeedId] ? !response[datafeedId].stopped : true }; - } catch (error) { - reportDisableError(error); - } finally { - setIsLoading(false); + const datafeedId = `datafeed-${job.id}`; + + try { + const [response] = await stopDatafeeds({ datafeedIds: [datafeedId] }); + + if (isErrorResponse(response)) { + throw new Error(response.error); } + + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + status: ML_JOB_TELEMETRY_STATUS.stopped, + }); + + return { enabled: response[datafeedId] ? !response[datafeedId].stopped : true }; + } catch (error) { + track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_DISABLE_FAILURE); + addError(error, { title: i18n.STOP_JOB_FAILURE }); + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + status: ML_JOB_TELEMETRY_STATUS.stopError, + errorMessage: `${i18n.STOP_JOB_FAILURE} - ${error.message}`, + }); + } finally { + setIsLoading(false); } - return { enabled: !enable }; + + return { enabled: true }; }, [addError, telemetry] ); - return { enableDatafeed, isLoading }; + return { enableDatafeed, disableDatafeed, isLoading }; }; const isErrorResponse = (response: ErrorResponse): response is ErrorResponse => !isEmpty(response.error); - -const submitTelemetry = (job: SecurityJob, enabled: boolean) => { - // Report type of job enabled/disabled - track( - METRIC_TYPE.COUNT, - job.isElasticJob - ? enabled - ? TELEMETRY_EVENT.SIEM_JOB_ENABLED - : TELEMETRY_EVENT.SIEM_JOB_DISABLED - : enabled - ? TELEMETRY_EVENT.CUSTOM_JOB_ENABLED - : TELEMETRY_EVENT.CUSTOM_JOB_DISABLED - ); -}; diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx index 93febe1a7e3a6..5f3b834d777b8 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx @@ -59,13 +59,22 @@ export const MlPopover = React.memo(() => { } = useSecurityJobs(); const docLinks = useKibana().services.docLinks; - const { enableDatafeed, isLoading: isLoadingEnableDataFeed } = useEnableDataFeed(); + const { + enableDatafeed, + disableDatafeed, + isLoading: isLoadingEnableDataFeed, + } = useEnableDataFeed(); const handleJobStateChange = useCallback( async (job: SecurityJob, latestTimestampMs: number, enable: boolean) => { - await enableDatafeed(job, latestTimestampMs, enable); + if (enable) { + await enableDatafeed(job, latestTimestampMs); + } else { + await disableDatafeed(job); + } + refreshJobs(); }, - [refreshJobs, enableDatafeed] + [refreshJobs, enableDatafeed, disableDatafeed] ); const filteredJobs = filterJobs({ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_start_ml_jobs.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_start_ml_jobs.tsx index a0afcad5901e4..cf12b8b0f1bf6 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_start_ml_jobs.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_start_ml_jobs.tsx @@ -43,7 +43,7 @@ export const useStartMlJobs = (): ReturnUseStartMlJobs => { } const latestTimestampMs = job.latestTimestampMs ?? 0; - await enableDatafeed(job, latestTimestampMs, true); + await enableDatafeed(job, latestTimestampMs); }) ); refetchJobs(); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.integration.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.integration.test.tsx index ad9b3c751f59f..104ea748f106c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.integration.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.integration.test.tsx @@ -56,8 +56,7 @@ describe('MlAdminJobDescription', () => { userEvent.click(screen.getByTestId('job-switch')); expect(enableDatafeedSpy).toHaveBeenCalledWith( securityJobNotStarted, - securityJobNotStarted.latestTimestampMs, - true + securityJobNotStarted.latestTimestampMs ); await waitFor(() => { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.tsx index 7d4616a004364..7f1236aef08cd 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.tsx @@ -25,14 +25,23 @@ const MlAdminJobDescriptionComponent: FC = ({ loading, refreshJob, }) => { - const { enableDatafeed, isLoading: isLoadingEnableDataFeed } = useEnableDataFeed(); + const { + enableDatafeed, + disableDatafeed, + isLoading: isLoadingEnableDataFeed, + } = useEnableDataFeed(); const handleJobStateChange = useCallback( async (_, latestTimestampMs: number, enable: boolean) => { - await enableDatafeed(job, latestTimestampMs, enable); + if (enable) { + await enableDatafeed(job, latestTimestampMs); + } else { + await disableDatafeed(job); + } + refreshJob(job); }, - [enableDatafeed, job, refreshJob] + [enableDatafeed, disableDatafeed, job, refreshJob] ); const switchComponent = useMemo( diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx index 20061d5dc0592..c5c199b79df09 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx @@ -45,7 +45,7 @@ describe('EnableJob', () => { fireEvent.click(getByText('Run job')); await waitFor(() => { - expect(enableDatafeedMock).toHaveBeenCalledWith(job, job.latestTimestampMs, true); + expect(enableDatafeedMock).toHaveBeenCalledWith(job, job.latestTimestampMs); expect(onJobEnabledMock).toHaveBeenCalledWith(job); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx index abd62bc73b920..533a0eddcbc1a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx @@ -24,7 +24,7 @@ export const EnableJob = ({ const { enableDatafeed, isLoading: isEnabling } = useEnableDataFeed(); const handleChange = useCallback(async () => { - const result = await enableDatafeed(job, job.latestTimestampMs || 0, true); + const result = await enableDatafeed(job, job.latestTimestampMs || 0); if (result.enabled) { onJobEnabled(job);