Katello/katello

View on GitHub
webpack/components/extensions/HostDetails/Tabs/RemoteExecutionHooks.js

Summary

Maintainability
C
1 day
Test Coverage
import { useState, useEffect, useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { propsToCamelCase } from 'foremanReact/common/helpers';
import { deleteToast } from 'foremanReact/components/ToastsList/slice';
import { startPollingJob, stopPollingJob } from './RemoteExecutionActions';
import { renderRexJobFailedToast, renderRexJobStartedToast, renderRexJobSucceededToast } from '../../../../scenes/Tasks/helpers';

export const useRexJobPolling = (initialAction, successAction = null, failureAction = null) => {
  const [isPolling, setIsPolling] = useState(null);
  const [succeeded, setSucceeded] = useState(null);
  const [rexJobId, setRexJobId] = useState(null);
  // A value that only changes when the job succeeds. Pass to TableWrapper as an additionalListener
  // to reload results.
  const [lastCompletedJob, setLastCompletedJob] = useState(null);
  const dispatch = useDispatch();

  const stopRexJobPolling = useCallback(({ jobId, statusLabel }) => {
    if (statusLabel) setIsPolling(false);
    if (statusLabel === 'succeeded') {
      setSucceeded(true);
      setLastCompletedJob(jobId);
    } else {
      setSucceeded(false);
    }
    dispatch(stopPollingJob({ key: `REX_JOB_POLLING_${jobId}` }));
    dispatch(deleteToast(`REX_TOAST_${jobId}`));
  }, [dispatch]);

  const tick = (resp) => {
    const data = resp?.data?.data || resp?.data;
    const { statusLabel, id, description } = propsToCamelCase(data);
    if (!id) setRexJobId(id);
    if (statusLabel && statusLabel !== 'running') {
      stopRexJobPolling({ jobId: id, statusLabel });
      if (statusLabel === 'succeeded') {
        renderRexJobSucceededToast({ id, description });
        if (successAction) dispatch(typeof successAction === 'function' ? successAction() : successAction);
      } else {
        renderRexJobFailedToast({ id, description });
        if (failureAction) dispatch(typeof failureAction === 'function' ? failureAction() : failureAction);
      }
    }
  };
  const startRexJobPolling = ({ jobId }) => {
    dispatch(startPollingJob({ key: `REX_JOB_POLLING_${jobId}`, jobId, handleSuccess: tick }));
  };
  const pollingStarted = !!(isPolling || succeeded);

  const dispatchInitialAction = (...args) => {
    const originalAction = typeof initialAction === 'function' ? initialAction(...args) : initialAction;
    const modifiedAction = {
      ...originalAction,
      payload: {
        ...originalAction.payload,
        handleSuccess: (resp) => {
          const jobId = resp?.data?.id;
          if (!jobId) return;
          renderRexJobStartedToast({ key: `REX_TOAST_${jobId}`, ...resp.data });
          startRexJobPolling({ jobId });
        },
      },
    };
    setIsPolling(true);
    dispatch(modifiedAction);
  };

  // eslint-disable-next-line arrow-body-style
  useEffect(() => {
    // clean up polling when component unmounts
    return function cleanupRexPolling() {
      if (rexJobId) stopRexJobPolling({ jobId: rexJobId });
    };
  }, [rexJobId, stopRexJobPolling]);
  return ({
    pollingStarted,
    isPolling,
    succeeded,
    rexJobId,
    lastCompletedJob,
    startRexJobPolling,
    triggerJobStart: dispatchInitialAction,
  });
};

export default useRexJobPolling;