foreman_remote_execution 13.1.1 → 13.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
 - data/app/models/job_invocation.rb +23 -0
 - data/app/models/job_invocation_composer.rb +2 -0
 - data/app/views/api/v2/job_invocations/base.json.rabl +3 -2
 - data/app/views/templates/script/package_action.erb +18 -10
 - data/lib/foreman_remote_execution/version.rb +1 -1
 - data/package.json +2 -1
 - data/webpack/JobInvocationDetail/JobInvocationActions.js +134 -3
 - data/webpack/JobInvocationDetail/JobInvocationConstants.js +14 -0
 - data/webpack/JobInvocationDetail/JobInvocationDetail.scss +5 -2
 - data/webpack/JobInvocationDetail/JobInvocationSelectors.js +5 -0
 - data/webpack/JobInvocationDetail/JobInvocationSystemStatusChart.js +13 -9
 - data/webpack/JobInvocationDetail/JobInvocationToolbarButtons.js +268 -0
 - data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +259 -0
 - data/webpack/JobInvocationDetail/__tests__/fixtures.js +117 -0
 - data/webpack/JobInvocationDetail/index.js +58 -38
 - data/webpack/JobWizard/JobWizard.scss +14 -5
 - data/webpack/JobWizard/JobWizardPageRerun.js +15 -10
 - data/webpack/JobWizard/steps/HostsAndInputs/index.js +4 -1
 - data/webpack/__mocks__/foremanReact/components/BreadcrumbBar/index.js +4 -0
 - data/webpack/__mocks__/foremanReact/components/Head/index.js +10 -0
 - data/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js +8 -0
 - data/webpack/__mocks__/foremanReact/components/ToastsList/index.js +3 -0
 - data/webpack/__mocks__/foremanReact/redux/API/APIActions.js +21 -0
 - data/webpack/__mocks__/foremanReact/redux/API/APIConstants.js +7 -0
 - data/webpack/__mocks__/foremanReact/redux/API/index.js +14 -0
 - data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware/index.js +9 -0
 - metadata +12 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 473900c99e054678a879651060b7596221f32c7f2713d28efb472957b7ac73ed
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 14e55ba559f96def212e56aad5087b3e171fceb0cae2209b2ba1ef0566bda620
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 715de4ec99b4f17450ec02cca4db12e7778001098f49c1a733fae18c84d72855dd59e8dc86c1a6edddd72856a26c345c1397b2e2c1a66f14b0e605497f59e747
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 7dd731683f6f68d5c9f32c14b9411cbde8a65e0de38de249f691cd70447b2ff936bac8f7413b4edad8bb776ae0612ccbcb487650a20a16c35e37fc2ded3b4af2
         
     | 
| 
         @@ -139,6 +139,7 @@ module Api 
     | 
|
| 
       139 
139 
     | 
    
         
             
                  api :POST, '/job_invocations/:id/rerun', N_('Rerun job on failed hosts')
         
     | 
| 
       140 
140 
     | 
    
         
             
                  param :id, :identifier, :required => true
         
     | 
| 
       141 
141 
     | 
    
         
             
                  param :failed_only, :bool
         
     | 
| 
      
 142 
     | 
    
         
            +
                  param :succeeded_only, :bool
         
     | 
| 
       142 
143 
     | 
    
         
             
                  def rerun
         
     | 
| 
       143 
144 
     | 
    
         
             
                    composer = JobInvocationComposer.from_job_invocation(@job_invocation, params)
         
     | 
| 
       144 
145 
     | 
    
         
             
                    if composer.rerun_possible?
         
     | 
| 
         @@ -194,6 +194,10 @@ class JobInvocation < ApplicationRecord 
     | 
|
| 
       194 
194 
     | 
    
         
             
                failed_hosts.pluck(:id)
         
     | 
| 
       195 
195 
     | 
    
         
             
              end
         
     | 
| 
       196 
196 
     | 
    
         | 
| 
      
 197 
     | 
    
         
            +
              def succeeded_host_ids
         
     | 
| 
      
 198 
     | 
    
         
            +
                succeeded_hosts.pluck(:id)
         
     | 
| 
      
 199 
     | 
    
         
            +
              end
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
       197 
201 
     | 
    
         
             
              def failed_hosts
         
     | 
| 
       198 
202 
     | 
    
         
             
                base = targeting.hosts
         
     | 
| 
       199 
203 
     | 
    
         
             
                if finished?
         
     | 
| 
         @@ -203,6 +207,15 @@ class JobInvocation < ApplicationRecord 
     | 
|
| 
       203 
207 
     | 
    
         
             
                end
         
     | 
| 
       204 
208 
     | 
    
         
             
              end
         
     | 
| 
       205 
209 
     | 
    
         | 
| 
      
 210 
     | 
    
         
            +
              def succeeded_hosts
         
     | 
| 
      
 211 
     | 
    
         
            +
                base = targeting.hosts
         
     | 
| 
      
 212 
     | 
    
         
            +
                if finished?
         
     | 
| 
      
 213 
     | 
    
         
            +
                  base.where.not(:id => not_succeeded_template_invocations.select(:host_id))
         
     | 
| 
      
 214 
     | 
    
         
            +
                else
         
     | 
| 
      
 215 
     | 
    
         
            +
                  base.where(:id => succeeded_template_invocations.select(:host_id))
         
     | 
| 
      
 216 
     | 
    
         
            +
                end
         
     | 
| 
      
 217 
     | 
    
         
            +
              end
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
       206 
219 
     | 
    
         
             
              def total_hosts_count
         
     | 
| 
       207 
220 
     | 
    
         
             
                count = _('N/A')
         
     | 
| 
       208 
221 
     | 
    
         | 
| 
         @@ -290,4 +303,14 @@ class JobInvocation < ApplicationRecord 
     | 
|
| 
       290 
303 
     | 
    
         
             
                results = [:cancelled, :failed].map { |state| TemplateInvocation::TaskResultMap.status_to_task_result(state) }.flatten
         
     | 
| 
       291 
304 
     | 
    
         
             
                template_invocations.joins(:run_host_job_task).where.not(ForemanTasks::Task.table_name => { :result => results })
         
     | 
| 
       292 
305 
     | 
    
         
             
              end
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
              def succeeded_template_invocations
         
     | 
| 
      
 308 
     | 
    
         
            +
                result = TemplateInvocation::TaskResultMap.status_to_task_result(:success)
         
     | 
| 
      
 309 
     | 
    
         
            +
                template_invocations.joins(:run_host_job_task).where(ForemanTasks::Task.table_name => { :result => result })
         
     | 
| 
      
 310 
     | 
    
         
            +
              end
         
     | 
| 
      
 311 
     | 
    
         
            +
             
     | 
| 
      
 312 
     | 
    
         
            +
              def not_succeeded_template_invocations
         
     | 
| 
      
 313 
     | 
    
         
            +
                result = TemplateInvocation::TaskResultMap.status_to_task_result(:success)
         
     | 
| 
      
 314 
     | 
    
         
            +
                template_invocations.joins(:run_host_job_task).where.not(ForemanTasks::Task.table_name => { :result => result })
         
     | 
| 
      
 315 
     | 
    
         
            +
              end
         
     | 
| 
       293 
316 
     | 
    
         
             
            end
         
     | 
| 
         @@ -25,9 +25,10 @@ end 
     | 
|
| 
       25 
25 
     | 
    
         
             
            if params.key?(:include_permissions)
         
     | 
| 
       26 
26 
     | 
    
         
             
              node :permissions do |invocation|
         
     | 
| 
       27 
27 
     | 
    
         
             
                authorizer = Authorizer.new(User.current)
         
     | 
| 
       28 
     | 
    
         
            -
                edit_job_templates_permission = Permission.where(name: "edit_job_templates", resource_type: "JobTemplate").first
         
     | 
| 
       29 
28 
     | 
    
         
             
                {
         
     | 
| 
       30 
     | 
    
         
            -
                  "edit_job_templates" =>  
     | 
| 
      
 29 
     | 
    
         
            +
                  "edit_job_templates" => authorizer.can?("edit_job_templates", invocation, false),
         
     | 
| 
      
 30 
     | 
    
         
            +
                  "view_foreman_tasks" => authorizer.can?("view_foreman_tasks", invocation.task, false),
         
     | 
| 
      
 31 
     | 
    
         
            +
                  "edit_recurring_logics" => authorizer.can?("edit_recurring_logics", invocation.recurring_logic, false),
         
     | 
| 
       31 
32 
     | 
    
         
             
                }
         
     | 
| 
       32 
33 
     | 
    
         
             
              end
         
     | 
| 
       33 
34 
     | 
    
         
             
            end
         
     | 
| 
         @@ -118,19 +118,27 @@ handle_zypp_res_codes () { 
     | 
|
| 
       118 
118 
     | 
    
         
             
                  end
         
     | 
| 
       119 
119 
     | 
    
         
             
                end
         
     | 
| 
       120 
120 
     | 
    
         
             
              -%>
         
     | 
| 
       121 
     | 
    
         
            -
               
     | 
| 
       122 
     | 
    
         
            -
               
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
      
 121 
     | 
    
         
            +
              run_cmd()
         
     | 
| 
      
 122 
     | 
    
         
            +
              {
         
     | 
| 
      
 123 
     | 
    
         
            +
                LANG=C apt-get -o APT::Get::Upgrade-Allow-New="true" -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y <%= input("options") %> <%= action %> <%= input("package") %> 2>&1
         
     | 
| 
      
 124 
     | 
    
         
            +
              }
         
     | 
| 
       124 
125 
     | 
    
         
             
              export DEBIAN_FRONTEND=noninteractive
         
     | 
| 
       125 
126 
     | 
    
         
             
              apt-get -y update
         
     | 
| 
       126 
     | 
    
         
            -
               
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
                 
     | 
| 
      
 127 
     | 
    
         
            +
              if [ "<%= action %>" = "remove" ]; then
         
     | 
| 
      
 128 
     | 
    
         
            +
                OUTFILE=$(mktemp)
         
     | 
| 
      
 129 
     | 
    
         
            +
                trap "rm -f $OUTFILE" EXIT
         
     | 
| 
      
 130 
     | 
    
         
            +
                run_cmd > $OUTFILE
         
     | 
| 
      
 131 
     | 
    
         
            +
                RETVAL=$?
         
     | 
| 
      
 132 
     | 
    
         
            +
                cat $OUTFILE
         
     | 
| 
      
 133 
     | 
    
         
            +
                if grep -q "Unable to locate" $OUTFILE; then
         
     | 
| 
      
 134 
     | 
    
         
            +
                  RETVAL=0
         
     | 
| 
      
 135 
     | 
    
         
            +
                fi
         
     | 
| 
       130 
136 
     | 
    
         
             
              else
         
     | 
| 
       131 
     | 
    
         
            -
                 
     | 
| 
       132 
     | 
    
         
            -
                 
     | 
| 
      
 137 
     | 
    
         
            +
                run_cmd
         
     | 
| 
      
 138 
     | 
    
         
            +
                RETVAL=$?
         
     | 
| 
       133 
139 
     | 
    
         
             
              fi
         
     | 
| 
      
 140 
     | 
    
         
            +
              setReturnValue() { RETVAL=$1; return $RETVAL; }
         
     | 
| 
      
 141 
     | 
    
         
            +
              setReturnValue $RETVAL
         
     | 
| 
       134 
142 
     | 
    
         
             
            <% elsif package_manager == 'zypper' -%>
         
     | 
| 
       135 
143 
     | 
    
         
             
              <%-
         
     | 
| 
       136 
144 
     | 
    
         
             
                if action == "group install"
         
     | 
| 
         @@ -141,7 +149,7 @@ handle_zypp_res_codes () { 
     | 
|
| 
       141 
149 
     | 
    
         
             
              -%>
         
     | 
| 
       142 
150 
     | 
    
         
             
              zypper refresh
         
     | 
| 
       143 
151 
     | 
    
         
             
              zypper -n <%= action %> <%= input("options") %> <%= input("package") %>
         
     | 
| 
       144 
     | 
    
         
            -
              handle_zypp_res_codes $?  
     | 
| 
      
 152 
     | 
    
         
            +
              handle_zypp_res_codes $? "<%= action %>"
         
     | 
| 
       145 
153 
     | 
    
         
             
            <% end -%>
         
     | 
| 
       146 
154 
     | 
    
         
             
            RETVAL=$?
         
     | 
| 
       147 
155 
     | 
    
         
             
            [ $RETVAL -eq 0 ] || exit_with_message "Package action failed" $RETVAL
         
     | 
    
        data/package.json
    CHANGED
    
    
| 
         @@ -1,9 +1,19 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import {  
     | 
| 
      
 1 
     | 
    
         
            +
            import { translate as __, sprintf } from 'foremanReact/common/I18n';
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { foremanUrl } from 'foremanReact/common/helpers';
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { addToast } from 'foremanReact/components/ToastsList';
         
     | 
| 
      
 4 
     | 
    
         
            +
            import { APIActions, get } from 'foremanReact/redux/API';
         
     | 
| 
       2 
5 
     | 
    
         
             
            import {
         
     | 
| 
       3 
     | 
    
         
            -
              withInterval,
         
     | 
| 
       4 
6 
     | 
    
         
             
              stopInterval,
         
     | 
| 
      
 7 
     | 
    
         
            +
              withInterval,
         
     | 
| 
       5 
8 
     | 
    
         
             
            } from 'foremanReact/redux/middlewares/IntervalMiddleware';
         
     | 
| 
       6 
     | 
    
         
            -
            import { 
     | 
| 
      
 9 
     | 
    
         
            +
            import {
         
     | 
| 
      
 10 
     | 
    
         
            +
              CANCEL_JOB,
         
     | 
| 
      
 11 
     | 
    
         
            +
              CANCEL_RECURRING_LOGIC,
         
     | 
| 
      
 12 
     | 
    
         
            +
              CHANGE_ENABLED_RECURRING_LOGIC,
         
     | 
| 
      
 13 
     | 
    
         
            +
              GET_TASK,
         
     | 
| 
      
 14 
     | 
    
         
            +
              JOB_INVOCATION_KEY,
         
     | 
| 
      
 15 
     | 
    
         
            +
              UPDATE_JOB,
         
     | 
| 
      
 16 
     | 
    
         
            +
            } from './JobInvocationConstants';
         
     | 
| 
       7 
17 
     | 
    
         | 
| 
       8 
18 
     | 
    
         
             
            export const getData = url => dispatch => {
         
     | 
| 
       9 
19 
     | 
    
         
             
              const fetchData = withInterval(
         
     | 
| 
         @@ -20,3 +30,124 @@ export const getData = url => dispatch => { 
     | 
|
| 
       20 
30 
     | 
    
         | 
| 
       21 
31 
     | 
    
         
             
              dispatch(fetchData);
         
     | 
| 
       22 
32 
     | 
    
         
             
            };
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            export const updateJob = jobId => dispatch => {
         
     | 
| 
      
 35 
     | 
    
         
            +
              const url = foremanUrl(`/api/job_invocations/${jobId}`);
         
     | 
| 
      
 36 
     | 
    
         
            +
              dispatch(
         
     | 
| 
      
 37 
     | 
    
         
            +
                APIActions.get({
         
     | 
| 
      
 38 
     | 
    
         
            +
                  url,
         
     | 
| 
      
 39 
     | 
    
         
            +
                  key: UPDATE_JOB,
         
     | 
| 
      
 40 
     | 
    
         
            +
                })
         
     | 
| 
      
 41 
     | 
    
         
            +
              );
         
     | 
| 
      
 42 
     | 
    
         
            +
            };
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            export const cancelJob = (jobId, force) => dispatch => {
         
     | 
| 
      
 45 
     | 
    
         
            +
              const infoToast = () =>
         
     | 
| 
      
 46 
     | 
    
         
            +
                force
         
     | 
| 
      
 47 
     | 
    
         
            +
                  ? sprintf(__('Trying to abort the job %s.'), jobId)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  : sprintf(__('Trying to cancel the job %s.'), jobId);
         
     | 
| 
      
 49 
     | 
    
         
            +
              const errorToast = response =>
         
     | 
| 
      
 50 
     | 
    
         
            +
                force
         
     | 
| 
      
 51 
     | 
    
         
            +
                  ? sprintf(__(`Could not abort the job %s: ${response}`), jobId)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  : sprintf(__(`Could not cancel the job %s: ${response}`), jobId);
         
     | 
| 
      
 53 
     | 
    
         
            +
              const url = force
         
     | 
| 
      
 54 
     | 
    
         
            +
                ? `/job_invocations/${jobId}/cancel?force=true`
         
     | 
| 
      
 55 
     | 
    
         
            +
                : `/job_invocations/${jobId}/cancel`;
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              dispatch(
         
     | 
| 
      
 58 
     | 
    
         
            +
                APIActions.post({
         
     | 
| 
      
 59 
     | 
    
         
            +
                  url,
         
     | 
| 
      
 60 
     | 
    
         
            +
                  key: CANCEL_JOB,
         
     | 
| 
      
 61 
     | 
    
         
            +
                  errorToast: ({ response }) =>
         
     | 
| 
      
 62 
     | 
    
         
            +
                    errorToast(
         
     | 
| 
      
 63 
     | 
    
         
            +
                      // eslint-disable-next-line camelcase
         
     | 
| 
      
 64 
     | 
    
         
            +
                      response?.data?.error?.full_messages ||
         
     | 
| 
      
 65 
     | 
    
         
            +
                        response?.data?.error?.message ||
         
     | 
| 
      
 66 
     | 
    
         
            +
                        'Unknown error.'
         
     | 
| 
      
 67 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 68 
     | 
    
         
            +
                  handleSuccess: () => {
         
     | 
| 
      
 69 
     | 
    
         
            +
                    dispatch(
         
     | 
| 
      
 70 
     | 
    
         
            +
                      addToast({
         
     | 
| 
      
 71 
     | 
    
         
            +
                        key: `cancel-job-error`,
         
     | 
| 
      
 72 
     | 
    
         
            +
                        type: 'info',
         
     | 
| 
      
 73 
     | 
    
         
            +
                        message: infoToast(),
         
     | 
| 
      
 74 
     | 
    
         
            +
                      })
         
     | 
| 
      
 75 
     | 
    
         
            +
                    );
         
     | 
| 
      
 76 
     | 
    
         
            +
                    dispatch(updateJob(jobId));
         
     | 
| 
      
 77 
     | 
    
         
            +
                  },
         
     | 
| 
      
 78 
     | 
    
         
            +
                })
         
     | 
| 
      
 79 
     | 
    
         
            +
              );
         
     | 
| 
      
 80 
     | 
    
         
            +
            };
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
            export const getTask = taskId => dispatch => {
         
     | 
| 
      
 83 
     | 
    
         
            +
              dispatch(
         
     | 
| 
      
 84 
     | 
    
         
            +
                get({
         
     | 
| 
      
 85 
     | 
    
         
            +
                  key: GET_TASK,
         
     | 
| 
      
 86 
     | 
    
         
            +
                  url: `/foreman_tasks/api/tasks/${taskId}`,
         
     | 
| 
      
 87 
     | 
    
         
            +
                })
         
     | 
| 
      
 88 
     | 
    
         
            +
              );
         
     | 
| 
      
 89 
     | 
    
         
            +
            };
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
            export const enableRecurringLogic = (
         
     | 
| 
      
 92 
     | 
    
         
            +
              recurrenceId,
         
     | 
| 
      
 93 
     | 
    
         
            +
              enabled,
         
     | 
| 
      
 94 
     | 
    
         
            +
              jobId
         
     | 
| 
      
 95 
     | 
    
         
            +
            ) => dispatch => {
         
     | 
| 
      
 96 
     | 
    
         
            +
              const successToast = () =>
         
     | 
| 
      
 97 
     | 
    
         
            +
                enabled
         
     | 
| 
      
 98 
     | 
    
         
            +
                  ? sprintf(__('Recurring logic %s disabled successfully.'), recurrenceId)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  : sprintf(__('Recurring logic %s enabled successfully.'), recurrenceId);
         
     | 
| 
      
 100 
     | 
    
         
            +
              const errorToast = response =>
         
     | 
| 
      
 101 
     | 
    
         
            +
                enabled
         
     | 
| 
      
 102 
     | 
    
         
            +
                  ? sprintf(
         
     | 
| 
      
 103 
     | 
    
         
            +
                      __(`Could not disable recurring logic %s: ${response}`),
         
     | 
| 
      
 104 
     | 
    
         
            +
                      recurrenceId
         
     | 
| 
      
 105 
     | 
    
         
            +
                    )
         
     | 
| 
      
 106 
     | 
    
         
            +
                  : sprintf(
         
     | 
| 
      
 107 
     | 
    
         
            +
                      __(`Could not enable recurring logic %s: ${response}`),
         
     | 
| 
      
 108 
     | 
    
         
            +
                      recurrenceId
         
     | 
| 
      
 109 
     | 
    
         
            +
                    );
         
     | 
| 
      
 110 
     | 
    
         
            +
              const url = `/foreman_tasks/api/recurring_logics/${recurrenceId}`;
         
     | 
| 
      
 111 
     | 
    
         
            +
              dispatch(
         
     | 
| 
      
 112 
     | 
    
         
            +
                APIActions.put({
         
     | 
| 
      
 113 
     | 
    
         
            +
                  url,
         
     | 
| 
      
 114 
     | 
    
         
            +
                  key: CHANGE_ENABLED_RECURRING_LOGIC,
         
     | 
| 
      
 115 
     | 
    
         
            +
                  params: { recurring_logic: { enabled: !enabled } },
         
     | 
| 
      
 116 
     | 
    
         
            +
                  successToast,
         
     | 
| 
      
 117 
     | 
    
         
            +
                  errorToast: ({ response }) =>
         
     | 
| 
      
 118 
     | 
    
         
            +
                    errorToast(
         
     | 
| 
      
 119 
     | 
    
         
            +
                      // eslint-disable-next-line camelcase
         
     | 
| 
      
 120 
     | 
    
         
            +
                      response?.data?.error?.full_messages ||
         
     | 
| 
      
 121 
     | 
    
         
            +
                        response?.data?.error?.message ||
         
     | 
| 
      
 122 
     | 
    
         
            +
                        'Unknown error.'
         
     | 
| 
      
 123 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 124 
     | 
    
         
            +
                  handleSuccess: () => dispatch(updateJob(jobId)),
         
     | 
| 
      
 125 
     | 
    
         
            +
                })
         
     | 
| 
      
 126 
     | 
    
         
            +
              );
         
     | 
| 
      
 127 
     | 
    
         
            +
            };
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
            export const cancelRecurringLogic = (recurrenceId, jobId) => dispatch => {
         
     | 
| 
      
 130 
     | 
    
         
            +
              const successToast = () =>
         
     | 
| 
      
 131 
     | 
    
         
            +
                sprintf(__('Recurring logic %s cancelled successfully.'), recurrenceId);
         
     | 
| 
      
 132 
     | 
    
         
            +
              const errorToast = response =>
         
     | 
| 
      
 133 
     | 
    
         
            +
                sprintf(
         
     | 
| 
      
 134 
     | 
    
         
            +
                  __(`Could not cancel recurring logic %s: ${response}`),
         
     | 
| 
      
 135 
     | 
    
         
            +
                  recurrenceId
         
     | 
| 
      
 136 
     | 
    
         
            +
                );
         
     | 
| 
      
 137 
     | 
    
         
            +
              const url = `/foreman_tasks/recurring_logics/${recurrenceId}/cancel`;
         
     | 
| 
      
 138 
     | 
    
         
            +
              dispatch(
         
     | 
| 
      
 139 
     | 
    
         
            +
                APIActions.post({
         
     | 
| 
      
 140 
     | 
    
         
            +
                  url,
         
     | 
| 
      
 141 
     | 
    
         
            +
                  key: CANCEL_RECURRING_LOGIC,
         
     | 
| 
      
 142 
     | 
    
         
            +
                  successToast,
         
     | 
| 
      
 143 
     | 
    
         
            +
                  errorToast: ({ response }) =>
         
     | 
| 
      
 144 
     | 
    
         
            +
                    errorToast(
         
     | 
| 
      
 145 
     | 
    
         
            +
                      // eslint-disable-next-line camelcase
         
     | 
| 
      
 146 
     | 
    
         
            +
                      response?.data?.error?.full_messages ||
         
     | 
| 
      
 147 
     | 
    
         
            +
                        response?.data?.error?.message ||
         
     | 
| 
      
 148 
     | 
    
         
            +
                        'Unknown error.'
         
     | 
| 
      
 149 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 150 
     | 
    
         
            +
                  handleSuccess: () => dispatch(updateJob(jobId)),
         
     | 
| 
      
 151 
     | 
    
         
            +
                })
         
     | 
| 
      
 152 
     | 
    
         
            +
              );
         
     | 
| 
      
 153 
     | 
    
         
            +
            };
         
     | 
| 
         @@ -1,9 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { foremanUrl } from 'foremanReact/common/helpers';
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            export const JOB_INVOCATION_KEY = 'JOB_INVOCATION_KEY';
         
     | 
| 
      
 4 
     | 
    
         
            +
            export const CURRENT_PERMISSIONS = 'CURRENT_PERMISSIONS';
         
     | 
| 
      
 5 
     | 
    
         
            +
            export const UPDATE_JOB = 'UPDATE_JOB';
         
     | 
| 
      
 6 
     | 
    
         
            +
            export const CANCEL_JOB = 'CANCEL_JOB';
         
     | 
| 
      
 7 
     | 
    
         
            +
            export const GET_TASK = 'GET_TASK';
         
     | 
| 
      
 8 
     | 
    
         
            +
            export const CHANGE_ENABLED_RECURRING_LOGIC = 'CHANGE_ENABLED_RECURRING_LOGIC';
         
     | 
| 
      
 9 
     | 
    
         
            +
            export const CANCEL_RECURRING_LOGIC = 'CANCEL_RECURRING_LOGIC';
         
     | 
| 
      
 10 
     | 
    
         
            +
            export const GET_REPORT_TEMPLATES = 'GET_REPORT_TEMPLATES';
         
     | 
| 
      
 11 
     | 
    
         
            +
            export const GET_REPORT_TEMPLATE_INPUTS = 'GET_REPORT_TEMPLATE_INPUTS';
         
     | 
| 
      
 12 
     | 
    
         
            +
            export const currentPermissionsUrl = foremanUrl(
         
     | 
| 
      
 13 
     | 
    
         
            +
              '/api/v2/permissions/current_permissions'
         
     | 
| 
      
 14 
     | 
    
         
            +
            );
         
     | 
| 
       2 
15 
     | 
    
         | 
| 
       3 
16 
     | 
    
         
             
            export const STATUS = {
         
     | 
| 
       4 
17 
     | 
    
         
             
              PENDING: 'pending',
         
     | 
| 
       5 
18 
     | 
    
         
             
              SUCCEEDED: 'succeeded',
         
     | 
| 
       6 
19 
     | 
    
         
             
              FAILED: 'failed',
         
     | 
| 
      
 20 
     | 
    
         
            +
              CANCELLED: 'cancelled',
         
     | 
| 
       7 
21 
     | 
    
         
             
            };
         
     | 
| 
       8 
22 
     | 
    
         | 
| 
       9 
23 
     | 
    
         
             
            export const DATE_OPTIONS = {
         
     | 
| 
         @@ -1,6 +1,8 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            .job-invocation-detail- 
     | 
| 
      
 1 
     | 
    
         
            +
            .job-invocation-detail-flex {
         
     | 
| 
       2 
2 
     | 
    
         
             
              $chart_size: 105px;
         
     | 
| 
       3 
     | 
    
         
            -
              
         
     | 
| 
      
 3 
     | 
    
         
            +
              padding-top: 0px;
         
     | 
| 
      
 4 
     | 
    
         
            +
              padding-left: 10px;
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       4 
6 
     | 
    
         
             
              .chart-donut {
         
     | 
| 
       5 
7 
     | 
    
         
             
                height: $chart_size;
         
     | 
| 
       6 
8 
     | 
    
         
             
                width: $chart_size;
         
     | 
| 
         @@ -36,3 +38,4 @@ 
     | 
|
| 
       36 
38 
     | 
    
         
             
                height: $chart_size;
         
     | 
| 
       37 
39 
     | 
    
         
             
              }
         
     | 
| 
       38 
40 
     | 
    
         
             
            }
         
     | 
| 
      
 41 
     | 
    
         
            +
              
         
     | 
| 
         @@ -3,3 +3,8 @@ import { JOB_INVOCATION_KEY } from './JobInvocationConstants'; 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            export const selectItems = state =>
         
     | 
| 
       5 
5 
     | 
    
         
             
              selectAPIResponse(state, JOB_INVOCATION_KEY);
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            export const selectTask = state => selectAPIResponse(state, 'GET_TASK');
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            export const selectTaskCancelable = state =>
         
     | 
| 
      
 10 
     | 
    
         
            +
              selectTask(state).available_actions?.cancellable || false;
         
     | 
| 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import React, { useEffect, useState } from 'react';
         
     | 
| 
       2 
1 
     | 
    
         
             
            import PropTypes from 'prop-types';
         
     | 
| 
      
 2 
     | 
    
         
            +
            import React, { useEffect, useState } from 'react';
         
     | 
| 
       3 
3 
     | 
    
         
             
            import { translate as __, sprintf } from 'foremanReact/common/I18n';
         
     | 
| 
      
 4 
     | 
    
         
            +
            import DefaultLoaderEmptyState from 'foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState';
         
     | 
| 
       4 
5 
     | 
    
         
             
            import {
         
     | 
| 
       5 
6 
     | 
    
         
             
              ChartDonut,
         
     | 
| 
       6 
7 
     | 
    
         
             
              ChartLabel,
         
     | 
| 
         @@ -9,20 +10,19 @@ import { 
     | 
|
| 
       9 
10 
     | 
    
         
             
            } from '@patternfly/react-charts';
         
     | 
| 
       10 
11 
     | 
    
         
             
            import {
         
     | 
| 
       11 
12 
     | 
    
         
             
              DescriptionList,
         
     | 
| 
       12 
     | 
    
         
            -
              DescriptionListTerm,
         
     | 
| 
       13 
     | 
    
         
            -
              DescriptionListGroup,
         
     | 
| 
       14 
13 
     | 
    
         
             
              DescriptionListDescription,
         
     | 
| 
      
 14 
     | 
    
         
            +
              DescriptionListGroup,
         
     | 
| 
      
 15 
     | 
    
         
            +
              DescriptionListTerm,
         
     | 
| 
       15 
16 
     | 
    
         
             
              FlexItem,
         
     | 
| 
       16 
17 
     | 
    
         
             
              Text,
         
     | 
| 
       17 
18 
     | 
    
         
             
            } from '@patternfly/react-core';
         
     | 
| 
       18 
19 
     | 
    
         
             
            import {
         
     | 
| 
       19 
     | 
    
         
            -
              global_palette_green_500 as successedColor,
         
     | 
| 
       20 
     | 
    
         
            -
              global_palette_red_100 as failedColor,
         
     | 
| 
       21 
     | 
    
         
            -
              global_palette_blue_300 as inProgressColor,
         
     | 
| 
       22 
20 
     | 
    
         
             
              global_palette_black_600 as canceledColor,
         
     | 
| 
       23 
21 
     | 
    
         
             
              global_palette_black_500 as emptyChartDonut,
         
     | 
| 
      
 22 
     | 
    
         
            +
              global_palette_red_100 as failedColor,
         
     | 
| 
      
 23 
     | 
    
         
            +
              global_palette_blue_300 as inProgressColor,
         
     | 
| 
      
 24 
     | 
    
         
            +
              global_palette_green_500 as successedColor,
         
     | 
| 
       24 
25 
     | 
    
         
             
            } from '@patternfly/react-tokens';
         
     | 
| 
       25 
     | 
    
         
            -
            import DefaultLoaderEmptyState from 'foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState';
         
     | 
| 
       26 
26 
     | 
    
         
             
            import './JobInvocationDetail.scss';
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
            const JobInvocationSystemStatusChart = ({
         
     | 
| 
         @@ -35,9 +35,9 @@ const JobInvocationSystemStatusChart = ({ 
     | 
|
| 
       35 
35 
     | 
    
         
             
                failed,
         
     | 
| 
       36 
36 
     | 
    
         
             
                pending,
         
     | 
| 
       37 
37 
     | 
    
         
             
                cancelled,
         
     | 
| 
       38 
     | 
    
         
            -
                total,
         
     | 
| 
       39 
38 
     | 
    
         
             
                total_hosts: totalHosts, // includes scheduled
         
     | 
| 
       40 
39 
     | 
    
         
             
              } = data;
         
     | 
| 
      
 40 
     | 
    
         
            +
              const total = succeeded + failed + pending + cancelled;
         
     | 
| 
       41 
41 
     | 
    
         
             
              const chartData = [
         
     | 
| 
       42 
42 
     | 
    
         
             
                { title: __('Succeeded:'), count: succeeded, color: successedColor.value },
         
     | 
| 
       43 
43 
     | 
    
         
             
                { title: __('Failed:'), count: failed, color: failedColor.value },
         
     | 
| 
         @@ -82,7 +82,11 @@ const JobInvocationSystemStatusChart = ({ 
     | 
|
| 
       82 
82 
     | 
    
         
             
                        total > 0 ? chartData.map(d => d.color) : [emptyChartDonut.value]
         
     | 
| 
       83 
83 
     | 
    
         
             
                      }
         
     | 
| 
       84 
84 
     | 
    
         
             
                      labelComponent={
         
     | 
| 
       85 
     | 
    
         
            -
                        <ChartTooltip 
     | 
| 
      
 85 
     | 
    
         
            +
                        <ChartTooltip
         
     | 
| 
      
 86 
     | 
    
         
            +
                          pointerLength={0}
         
     | 
| 
      
 87 
     | 
    
         
            +
                          constrainToVisibleArea
         
     | 
| 
      
 88 
     | 
    
         
            +
                          renderInPortal={false}
         
     | 
| 
      
 89 
     | 
    
         
            +
                        />
         
     | 
| 
       86 
90 
     | 
    
         
             
                      }
         
     | 
| 
       87 
91 
     | 
    
         
             
                      title={chartDonutTitle}
         
     | 
| 
       88 
92 
     | 
    
         
             
                      titleComponent={
         
     | 
| 
         @@ -0,0 +1,268 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import PropTypes from 'prop-types';
         
     | 
| 
      
 2 
     | 
    
         
            +
            import React, { useEffect, useState } from 'react';
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { useDispatch, useSelector } from 'react-redux';
         
     | 
| 
      
 4 
     | 
    
         
            +
            import {
         
     | 
| 
      
 5 
     | 
    
         
            +
              Button,
         
     | 
| 
      
 6 
     | 
    
         
            +
              Dropdown,
         
     | 
| 
      
 7 
     | 
    
         
            +
              DropdownItem,
         
     | 
| 
      
 8 
     | 
    
         
            +
              DropdownPosition,
         
     | 
| 
      
 9 
     | 
    
         
            +
              DropdownSeparator,
         
     | 
| 
      
 10 
     | 
    
         
            +
              DropdownToggle,
         
     | 
| 
      
 11 
     | 
    
         
            +
              Split,
         
     | 
| 
      
 12 
     | 
    
         
            +
              SplitItem,
         
     | 
| 
      
 13 
     | 
    
         
            +
            } from '@patternfly/react-core';
         
     | 
| 
      
 14 
     | 
    
         
            +
            import { translate as __ } from 'foremanReact/common/I18n';
         
     | 
| 
      
 15 
     | 
    
         
            +
            import { foremanUrl } from 'foremanReact/common/helpers';
         
     | 
| 
      
 16 
     | 
    
         
            +
            import { STATUS as APIStatus } from 'foremanReact/constants';
         
     | 
| 
      
 17 
     | 
    
         
            +
            import { get } from 'foremanReact/redux/API';
         
     | 
| 
      
 18 
     | 
    
         
            +
            import {
         
     | 
| 
      
 19 
     | 
    
         
            +
              cancelJob,
         
     | 
| 
      
 20 
     | 
    
         
            +
              cancelRecurringLogic,
         
     | 
| 
      
 21 
     | 
    
         
            +
              enableRecurringLogic,
         
     | 
| 
      
 22 
     | 
    
         
            +
            } from './JobInvocationActions';
         
     | 
| 
      
 23 
     | 
    
         
            +
            import {
         
     | 
| 
      
 24 
     | 
    
         
            +
              STATUS,
         
     | 
| 
      
 25 
     | 
    
         
            +
              GET_REPORT_TEMPLATES,
         
     | 
| 
      
 26 
     | 
    
         
            +
              GET_REPORT_TEMPLATE_INPUTS,
         
     | 
| 
      
 27 
     | 
    
         
            +
            } from './JobInvocationConstants';
         
     | 
| 
      
 28 
     | 
    
         
            +
            import { selectTaskCancelable } from './JobInvocationSelectors';
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            const JobInvocationToolbarButtons = ({
         
     | 
| 
      
 31 
     | 
    
         
            +
              jobId,
         
     | 
| 
      
 32 
     | 
    
         
            +
              data,
         
     | 
| 
      
 33 
     | 
    
         
            +
              currentPermissions,
         
     | 
| 
      
 34 
     | 
    
         
            +
              permissionsStatus,
         
     | 
| 
      
 35 
     | 
    
         
            +
            }) => {
         
     | 
| 
      
 36 
     | 
    
         
            +
              const { succeeded, failed, task, recurrence, permissions } = data;
         
     | 
| 
      
 37 
     | 
    
         
            +
              const recurringEnabled = recurrence?.state === 'active';
         
     | 
| 
      
 38 
     | 
    
         
            +
              const canViewForemanTasks = permissions
         
     | 
| 
      
 39 
     | 
    
         
            +
                ? permissions.view_foreman_tasks
         
     | 
| 
      
 40 
     | 
    
         
            +
                : false;
         
     | 
| 
      
 41 
     | 
    
         
            +
              const canEditRecurringLogic = permissions
         
     | 
| 
      
 42 
     | 
    
         
            +
                ? permissions.edit_recurring_logics
         
     | 
| 
      
 43 
     | 
    
         
            +
                : false;
         
     | 
| 
      
 44 
     | 
    
         
            +
              const isTaskCancelable = useSelector(selectTaskCancelable);
         
     | 
| 
      
 45 
     | 
    
         
            +
              const [isActionOpen, setIsActionOpen] = useState(false);
         
     | 
| 
      
 46 
     | 
    
         
            +
              const [reportTemplateJobId, setReportTemplateJobId] = useState(undefined);
         
     | 
| 
      
 47 
     | 
    
         
            +
              const [templateInputId, setTemplateInputId] = useState(undefined);
         
     | 
| 
      
 48 
     | 
    
         
            +
              const queryParams = new URLSearchParams({
         
     | 
| 
      
 49 
     | 
    
         
            +
                [`report_template_report[input_values][${templateInputId}][value]`]: jobId,
         
     | 
| 
      
 50 
     | 
    
         
            +
              });
         
     | 
| 
      
 51 
     | 
    
         
            +
              const dispatch = useDispatch();
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              const onActionFocus = () => {
         
     | 
| 
      
 54 
     | 
    
         
            +
                const element = document.getElementById(
         
     | 
| 
      
 55 
     | 
    
         
            +
                  `toggle-split-button-action-primary-${jobId}`
         
     | 
| 
      
 56 
     | 
    
         
            +
                );
         
     | 
| 
      
 57 
     | 
    
         
            +
                element.focus();
         
     | 
| 
      
 58 
     | 
    
         
            +
              };
         
     | 
| 
      
 59 
     | 
    
         
            +
              const onActionSelect = () => {
         
     | 
| 
      
 60 
     | 
    
         
            +
                setIsActionOpen(false);
         
     | 
| 
      
 61 
     | 
    
         
            +
                onActionFocus();
         
     | 
| 
      
 62 
     | 
    
         
            +
              };
         
     | 
| 
      
 63 
     | 
    
         
            +
              const hasPermission = permissionRequired =>
         
     | 
| 
      
 64 
     | 
    
         
            +
                permissionsStatus === APIStatus.RESOLVED
         
     | 
| 
      
 65 
     | 
    
         
            +
                  ? currentPermissions?.some(
         
     | 
| 
      
 66 
     | 
    
         
            +
                      permission => permission.name === permissionRequired
         
     | 
| 
      
 67 
     | 
    
         
            +
                    )
         
     | 
| 
      
 68 
     | 
    
         
            +
                  : false;
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
              useEffect(() => {
         
     | 
| 
      
 71 
     | 
    
         
            +
                dispatch(
         
     | 
| 
      
 72 
     | 
    
         
            +
                  get({
         
     | 
| 
      
 73 
     | 
    
         
            +
                    key: GET_REPORT_TEMPLATES,
         
     | 
| 
      
 74 
     | 
    
         
            +
                    url: '/api/report_templates',
         
     | 
| 
      
 75 
     | 
    
         
            +
                    handleSuccess: ({ data: { results } }) => {
         
     | 
| 
      
 76 
     | 
    
         
            +
                      setReportTemplateJobId(
         
     | 
| 
      
 77 
     | 
    
         
            +
                        results.find(result => result.name === 'Job - Invocation Report')
         
     | 
| 
      
 78 
     | 
    
         
            +
                          ?.id
         
     | 
| 
      
 79 
     | 
    
         
            +
                      );
         
     | 
| 
      
 80 
     | 
    
         
            +
                    },
         
     | 
| 
      
 81 
     | 
    
         
            +
                    handleError: () => {
         
     | 
| 
      
 82 
     | 
    
         
            +
                      setReportTemplateJobId(undefined);
         
     | 
| 
      
 83 
     | 
    
         
            +
                    },
         
     | 
| 
      
 84 
     | 
    
         
            +
                  })
         
     | 
| 
      
 85 
     | 
    
         
            +
                );
         
     | 
| 
      
 86 
     | 
    
         
            +
              }, [dispatch]);
         
     | 
| 
      
 87 
     | 
    
         
            +
              useEffect(() => {
         
     | 
| 
      
 88 
     | 
    
         
            +
                if (reportTemplateJobId !== undefined) {
         
     | 
| 
      
 89 
     | 
    
         
            +
                  dispatch(
         
     | 
| 
      
 90 
     | 
    
         
            +
                    get({
         
     | 
| 
      
 91 
     | 
    
         
            +
                      key: GET_REPORT_TEMPLATE_INPUTS,
         
     | 
| 
      
 92 
     | 
    
         
            +
                      url: `/api/templates/${reportTemplateJobId}/template_inputs`,
         
     | 
| 
      
 93 
     | 
    
         
            +
                      handleSuccess: ({ data: { results } }) => {
         
     | 
| 
      
 94 
     | 
    
         
            +
                        setTemplateInputId(
         
     | 
| 
      
 95 
     | 
    
         
            +
                          results.find(result => result.name === 'job_id')?.id
         
     | 
| 
      
 96 
     | 
    
         
            +
                        );
         
     | 
| 
      
 97 
     | 
    
         
            +
                      },
         
     | 
| 
      
 98 
     | 
    
         
            +
                      handleError: () => {
         
     | 
| 
      
 99 
     | 
    
         
            +
                        setTemplateInputId(undefined);
         
     | 
| 
      
 100 
     | 
    
         
            +
                      },
         
     | 
| 
      
 101 
     | 
    
         
            +
                    })
         
     | 
| 
      
 102 
     | 
    
         
            +
                  );
         
     | 
| 
      
 103 
     | 
    
         
            +
                }
         
     | 
| 
      
 104 
     | 
    
         
            +
              }, [dispatch, reportTemplateJobId]);
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              const recurrenceDropdownItems = recurrence
         
     | 
| 
      
 107 
     | 
    
         
            +
                ? [
         
     | 
| 
      
 108 
     | 
    
         
            +
                    <DropdownSeparator ouiaId="dropdown-separator-1" key="separator-1" />,
         
     | 
| 
      
 109 
     | 
    
         
            +
                    <DropdownItem
         
     | 
| 
      
 110 
     | 
    
         
            +
                      ouiaId="change-enabled-recurring-dropdown-item"
         
     | 
| 
      
 111 
     | 
    
         
            +
                      onClick={() =>
         
     | 
| 
      
 112 
     | 
    
         
            +
                        dispatch(
         
     | 
| 
      
 113 
     | 
    
         
            +
                          enableRecurringLogic(recurrence?.id, recurringEnabled, jobId)
         
     | 
| 
      
 114 
     | 
    
         
            +
                        )
         
     | 
| 
      
 115 
     | 
    
         
            +
                      }
         
     | 
| 
      
 116 
     | 
    
         
            +
                      key="change-enabled-recurring"
         
     | 
| 
      
 117 
     | 
    
         
            +
                      component="button"
         
     | 
| 
      
 118 
     | 
    
         
            +
                      isDisabled={
         
     | 
| 
      
 119 
     | 
    
         
            +
                        recurrence?.id === undefined ||
         
     | 
| 
      
 120 
     | 
    
         
            +
                        recurrence?.state === 'cancelled' ||
         
     | 
| 
      
 121 
     | 
    
         
            +
                        !canEditRecurringLogic
         
     | 
| 
      
 122 
     | 
    
         
            +
                      }
         
     | 
| 
      
 123 
     | 
    
         
            +
                    >
         
     | 
| 
      
 124 
     | 
    
         
            +
                      {recurringEnabled ? __('Disable recurring') : __('Enable recurring')}
         
     | 
| 
      
 125 
     | 
    
         
            +
                    </DropdownItem>,
         
     | 
| 
      
 126 
     | 
    
         
            +
                    <DropdownItem
         
     | 
| 
      
 127 
     | 
    
         
            +
                      ouiaId="cancel-recurring-dropdown-item"
         
     | 
| 
      
 128 
     | 
    
         
            +
                      onClick={() => dispatch(cancelRecurringLogic(recurrence?.id, jobId))}
         
     | 
| 
      
 129 
     | 
    
         
            +
                      key="cancel-recurring"
         
     | 
| 
      
 130 
     | 
    
         
            +
                      component="button"
         
     | 
| 
      
 131 
     | 
    
         
            +
                      isDisabled={
         
     | 
| 
      
 132 
     | 
    
         
            +
                        recurrence?.id === undefined ||
         
     | 
| 
      
 133 
     | 
    
         
            +
                        recurrence?.state === 'cancelled' ||
         
     | 
| 
      
 134 
     | 
    
         
            +
                        !canEditRecurringLogic
         
     | 
| 
      
 135 
     | 
    
         
            +
                      }
         
     | 
| 
      
 136 
     | 
    
         
            +
                    >
         
     | 
| 
      
 137 
     | 
    
         
            +
                      {__('Cancel recurring')}
         
     | 
| 
      
 138 
     | 
    
         
            +
                    </DropdownItem>,
         
     | 
| 
      
 139 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 140 
     | 
    
         
            +
                : [];
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
              const dropdownItems = [
         
     | 
| 
      
 143 
     | 
    
         
            +
                <DropdownItem
         
     | 
| 
      
 144 
     | 
    
         
            +
                  ouiaId="rerun-succeeded-dropdown-item"
         
     | 
| 
      
 145 
     | 
    
         
            +
                  href={foremanUrl(`/job_invocations/${jobId}/rerun?succeeded_only=1`)}
         
     | 
| 
      
 146 
     | 
    
         
            +
                  key="rerun-succeeded"
         
     | 
| 
      
 147 
     | 
    
         
            +
                  isDisabled={!(succeeded > 0) || !hasPermission('create_job_invocations')}
         
     | 
| 
      
 148 
     | 
    
         
            +
                  description="Rerun job on successful hosts"
         
     | 
| 
      
 149 
     | 
    
         
            +
                >
         
     | 
| 
      
 150 
     | 
    
         
            +
                  {__('Rerun successful')}
         
     | 
| 
      
 151 
     | 
    
         
            +
                </DropdownItem>,
         
     | 
| 
      
 152 
     | 
    
         
            +
                <DropdownItem
         
     | 
| 
      
 153 
     | 
    
         
            +
                  ouiaId="rerun-failed-dropdown-item"
         
     | 
| 
      
 154 
     | 
    
         
            +
                  href={foremanUrl(`/job_invocations/${jobId}/rerun?failed_only=1`)}
         
     | 
| 
      
 155 
     | 
    
         
            +
                  key="rerun-failed"
         
     | 
| 
      
 156 
     | 
    
         
            +
                  isDisabled={!(failed > 0) || !hasPermission('create_job_invocations')}
         
     | 
| 
      
 157 
     | 
    
         
            +
                  description="Rerun job on failed hosts"
         
     | 
| 
      
 158 
     | 
    
         
            +
                >
         
     | 
| 
      
 159 
     | 
    
         
            +
                  {__('Rerun failed')}
         
     | 
| 
      
 160 
     | 
    
         
            +
                </DropdownItem>,
         
     | 
| 
      
 161 
     | 
    
         
            +
                <DropdownItem
         
     | 
| 
      
 162 
     | 
    
         
            +
                  ouiaId="view-task-dropdown-item"
         
     | 
| 
      
 163 
     | 
    
         
            +
                  href={foremanUrl(`/foreman_tasks/tasks/${task?.id}`)}
         
     | 
| 
      
 164 
     | 
    
         
            +
                  key="view-task"
         
     | 
| 
      
 165 
     | 
    
         
            +
                  isDisabled={!canViewForemanTasks || task === undefined}
         
     | 
| 
      
 166 
     | 
    
         
            +
                  description="See details of latest task"
         
     | 
| 
      
 167 
     | 
    
         
            +
                >
         
     | 
| 
      
 168 
     | 
    
         
            +
                  {__('View task')}
         
     | 
| 
      
 169 
     | 
    
         
            +
                </DropdownItem>,
         
     | 
| 
      
 170 
     | 
    
         
            +
                <DropdownSeparator ouiaId="dropdown-separator-0" key="separator-0" />,
         
     | 
| 
      
 171 
     | 
    
         
            +
                <DropdownItem
         
     | 
| 
      
 172 
     | 
    
         
            +
                  ouiaId="cancel-dropdown-item"
         
     | 
| 
      
 173 
     | 
    
         
            +
                  onClick={() => dispatch(cancelJob(jobId, false))}
         
     | 
| 
      
 174 
     | 
    
         
            +
                  key="cancel"
         
     | 
| 
      
 175 
     | 
    
         
            +
                  component="button"
         
     | 
| 
      
 176 
     | 
    
         
            +
                  isDisabled={!isTaskCancelable || !hasPermission('cancel_job_invocations')}
         
     | 
| 
      
 177 
     | 
    
         
            +
                  description="Cancel job gracefully"
         
     | 
| 
      
 178 
     | 
    
         
            +
                >
         
     | 
| 
      
 179 
     | 
    
         
            +
                  {__('Cancel')}
         
     | 
| 
      
 180 
     | 
    
         
            +
                </DropdownItem>,
         
     | 
| 
      
 181 
     | 
    
         
            +
                <DropdownItem
         
     | 
| 
      
 182 
     | 
    
         
            +
                  ouiaId="abort-dropdown-item"
         
     | 
| 
      
 183 
     | 
    
         
            +
                  onClick={() => dispatch(cancelJob(jobId, true))}
         
     | 
| 
      
 184 
     | 
    
         
            +
                  key="abort"
         
     | 
| 
      
 185 
     | 
    
         
            +
                  component="button"
         
     | 
| 
      
 186 
     | 
    
         
            +
                  isDisabled={!isTaskCancelable || !hasPermission('cancel_job_invocations')}
         
     | 
| 
      
 187 
     | 
    
         
            +
                  description="Cancel job immediately"
         
     | 
| 
      
 188 
     | 
    
         
            +
                >
         
     | 
| 
      
 189 
     | 
    
         
            +
                  {__('Abort')}
         
     | 
| 
      
 190 
     | 
    
         
            +
                </DropdownItem>,
         
     | 
| 
      
 191 
     | 
    
         
            +
                ...recurrenceDropdownItems,
         
     | 
| 
      
 192 
     | 
    
         
            +
                <DropdownSeparator ouiaId="dropdown-separator-2" key="separator-2" />,
         
     | 
| 
      
 193 
     | 
    
         
            +
                <DropdownItem
         
     | 
| 
      
 194 
     | 
    
         
            +
                  ouiaId="legacy-ui-dropdown-item"
         
     | 
| 
      
 195 
     | 
    
         
            +
                  href={`/job_invocations/${jobId}`}
         
     | 
| 
      
 196 
     | 
    
         
            +
                  key="legacy-ui"
         
     | 
| 
      
 197 
     | 
    
         
            +
                >
         
     | 
| 
      
 198 
     | 
    
         
            +
                  {__('Legacy UI')}
         
     | 
| 
      
 199 
     | 
    
         
            +
                </DropdownItem>,
         
     | 
| 
      
 200 
     | 
    
         
            +
              ];
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
              return (
         
     | 
| 
      
 203 
     | 
    
         
            +
                <>
         
     | 
| 
      
 204 
     | 
    
         
            +
                  <Split hasGutter>
         
     | 
| 
      
 205 
     | 
    
         
            +
                    <SplitItem>
         
     | 
| 
      
 206 
     | 
    
         
            +
                      <Button
         
     | 
| 
      
 207 
     | 
    
         
            +
                        component="a"
         
     | 
| 
      
 208 
     | 
    
         
            +
                        ouiaId="button-create-report"
         
     | 
| 
      
 209 
     | 
    
         
            +
                        className="button-create-report"
         
     | 
| 
      
 210 
     | 
    
         
            +
                        href={foremanUrl(
         
     | 
| 
      
 211 
     | 
    
         
            +
                          `/templates/report_templates/${reportTemplateJobId}/generate?${queryParams.toString()}`
         
     | 
| 
      
 212 
     | 
    
         
            +
                        )}
         
     | 
| 
      
 213 
     | 
    
         
            +
                        variant="secondary"
         
     | 
| 
      
 214 
     | 
    
         
            +
                        isDisabled={
         
     | 
| 
      
 215 
     | 
    
         
            +
                          task?.state === STATUS.PENDING ||
         
     | 
| 
      
 216 
     | 
    
         
            +
                          templateInputId === undefined ||
         
     | 
| 
      
 217 
     | 
    
         
            +
                          !hasPermission('generate_report_templates')
         
     | 
| 
      
 218 
     | 
    
         
            +
                        }
         
     | 
| 
      
 219 
     | 
    
         
            +
                      >
         
     | 
| 
      
 220 
     | 
    
         
            +
                        {__(`Create report`)}
         
     | 
| 
      
 221 
     | 
    
         
            +
                      </Button>
         
     | 
| 
      
 222 
     | 
    
         
            +
                    </SplitItem>
         
     | 
| 
      
 223 
     | 
    
         
            +
                    <SplitItem>
         
     | 
| 
      
 224 
     | 
    
         
            +
                      <Dropdown
         
     | 
| 
      
 225 
     | 
    
         
            +
                        ouiaId="job-invocation-global-actions-dropdown"
         
     | 
| 
      
 226 
     | 
    
         
            +
                        onSelect={onActionSelect}
         
     | 
| 
      
 227 
     | 
    
         
            +
                        position={DropdownPosition.right}
         
     | 
| 
      
 228 
     | 
    
         
            +
                        toggle={
         
     | 
| 
      
 229 
     | 
    
         
            +
                          <DropdownToggle
         
     | 
| 
      
 230 
     | 
    
         
            +
                            ouiaId="toggle-button-action-primary"
         
     | 
| 
      
 231 
     | 
    
         
            +
                            id={`toggle-split-button-action-primary-${jobId}`}
         
     | 
| 
      
 232 
     | 
    
         
            +
                            splitButtonItems={[
         
     | 
| 
      
 233 
     | 
    
         
            +
                              <Button
         
     | 
| 
      
 234 
     | 
    
         
            +
                                component="a"
         
     | 
| 
      
 235 
     | 
    
         
            +
                                ouiaId="button-rerun-all"
         
     | 
| 
      
 236 
     | 
    
         
            +
                                key="rerun"
         
     | 
| 
      
 237 
     | 
    
         
            +
                                href={foremanUrl(`/job_invocations/${jobId}/rerun`)}
         
     | 
| 
      
 238 
     | 
    
         
            +
                                variant="control"
         
     | 
| 
      
 239 
     | 
    
         
            +
                                isDisabled={!hasPermission('create_job_invocations')}
         
     | 
| 
      
 240 
     | 
    
         
            +
                              >
         
     | 
| 
      
 241 
     | 
    
         
            +
                                {__(`Rerun all`)}
         
     | 
| 
      
 242 
     | 
    
         
            +
                              </Button>,
         
     | 
| 
      
 243 
     | 
    
         
            +
                            ]}
         
     | 
| 
      
 244 
     | 
    
         
            +
                            splitButtonVariant="action"
         
     | 
| 
      
 245 
     | 
    
         
            +
                            onToggle={setIsActionOpen}
         
     | 
| 
      
 246 
     | 
    
         
            +
                          />
         
     | 
| 
      
 247 
     | 
    
         
            +
                        }
         
     | 
| 
      
 248 
     | 
    
         
            +
                        isOpen={isActionOpen}
         
     | 
| 
      
 249 
     | 
    
         
            +
                        dropdownItems={dropdownItems}
         
     | 
| 
      
 250 
     | 
    
         
            +
                      />
         
     | 
| 
      
 251 
     | 
    
         
            +
                    </SplitItem>
         
     | 
| 
      
 252 
     | 
    
         
            +
                  </Split>
         
     | 
| 
      
 253 
     | 
    
         
            +
                </>
         
     | 
| 
      
 254 
     | 
    
         
            +
              );
         
     | 
| 
      
 255 
     | 
    
         
            +
            };
         
     | 
| 
      
 256 
     | 
    
         
            +
             
     | 
| 
      
 257 
     | 
    
         
            +
            JobInvocationToolbarButtons.propTypes = {
         
     | 
| 
      
 258 
     | 
    
         
            +
              jobId: PropTypes.string.isRequired,
         
     | 
| 
      
 259 
     | 
    
         
            +
              data: PropTypes.object.isRequired,
         
     | 
| 
      
 260 
     | 
    
         
            +
              currentPermissions: PropTypes.array,
         
     | 
| 
      
 261 
     | 
    
         
            +
              permissionsStatus: PropTypes.string,
         
     | 
| 
      
 262 
     | 
    
         
            +
            };
         
     | 
| 
      
 263 
     | 
    
         
            +
            JobInvocationToolbarButtons.defaultProps = {
         
     | 
| 
      
 264 
     | 
    
         
            +
              currentPermissions: undefined,
         
     | 
| 
      
 265 
     | 
    
         
            +
              permissionsStatus: undefined,
         
     | 
| 
      
 266 
     | 
    
         
            +
            };
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
            export default JobInvocationToolbarButtons;
         
     |