foreman_remote_execution 16.0.4 → 16.0.5

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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/job_invocations_controller.rb +28 -1
  3. data/app/controllers/template_invocations_controller.rb +1 -1
  4. data/config/routes.rb +1 -0
  5. data/lib/foreman_remote_execution/engine.rb +9 -256
  6. data/lib/foreman_remote_execution/plugin.rb +246 -0
  7. data/lib/foreman_remote_execution/version.rb +1 -1
  8. data/test/functional/api/v2/job_invocations_controller_test.rb +1 -1
  9. data/test/unit/job_invocation_report_template_test.rb +6 -6
  10. data/webpack/JobInvocationDetail/CheckboxesActions.js +196 -0
  11. data/webpack/JobInvocationDetail/{JobInvocationHostTableToolbar.js → DropdownFilter.js} +3 -6
  12. data/webpack/JobInvocationDetail/JobInvocationConstants.js +6 -6
  13. data/webpack/JobInvocationDetail/JobInvocationDetail.scss +18 -0
  14. data/webpack/JobInvocationDetail/JobInvocationHostTable.js +205 -89
  15. data/webpack/JobInvocationDetail/JobInvocationSelectors.js +30 -3
  16. data/webpack/JobInvocationDetail/JobInvocationToolbarButtons.js +20 -27
  17. data/webpack/JobInvocationDetail/OpenAllInvocationsModal.js +118 -0
  18. data/webpack/JobInvocationDetail/TemplateInvocation.js +54 -24
  19. data/webpack/JobInvocationDetail/TemplateInvocationComponents/TemplateActionButtons.js +8 -10
  20. data/webpack/JobInvocationDetail/TemplateInvocationPage.js +1 -1
  21. data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +1 -1
  22. data/webpack/JobInvocationDetail/__tests__/TableToolbarActions.test.js +202 -0
  23. data/webpack/JobInvocationDetail/__tests__/TemplateInvocation.test.js +34 -28
  24. data/webpack/JobInvocationDetail/index.js +61 -30
  25. data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +26 -7
  26. data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.scss +1 -1
  27. metadata +8 -6
  28. data/webpack/JobInvocationDetail/OpenAlInvocations.js +0 -111
  29. data/webpack/JobInvocationDetail/__tests__/OpenAlInvocations.test.js +0 -110
@@ -1,48 +1,58 @@
1
1
  /* eslint-disable max-lines */
2
2
  /* eslint-disable camelcase */
3
- import PropTypes from 'prop-types';
4
- import React, { useMemo, useEffect, useState } from 'react';
5
- import { Icon } from 'patternfly-react';
6
- import { translate as __ } from 'foremanReact/common/I18n';
7
- import { FormattedMessage } from 'react-intl';
8
- import { Tr, Td, Tbody, ExpandableRowContent } from '@patternfly/react-table';
9
3
  import {
10
4
  EmptyState,
11
- EmptyStateVariant,
12
5
  EmptyStateBody,
13
6
  EmptyStateHeader,
7
+ EmptyStateVariant,
8
+ ToolbarItem,
14
9
  } from '@patternfly/react-core';
10
+ import { ExpandableRowContent, Tbody, Td, Tr } from '@patternfly/react-table';
11
+ import { translate as __ } from 'foremanReact/common/I18n';
15
12
  import { foremanUrl } from 'foremanReact/common/helpers';
16
13
  import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
14
+ import { RowSelectTd } from 'foremanReact/components/HostsIndex/RowSelectTd';
15
+ import SelectAllCheckbox from 'foremanReact/components/PF4/TableIndexPage/Table/SelectAllCheckbox';
17
16
  import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
18
- import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableIndexPage';
19
- import { useSetParamsAndApiAndSearch } from 'foremanReact/components/PF4/TableIndexPage/Table/TableIndexHooks';
20
17
  import {
21
18
  useBulkSelect,
22
19
  useUrlParams,
23
20
  } from 'foremanReact/components/PF4/TableIndexPage/Table/TableHooks';
21
+ import { getPageStats } from 'foremanReact/components/PF4/TableIndexPage/Table/helpers';
22
+ import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableIndexPage';
24
23
  import { getControllerSearchProps } from 'foremanReact/constants';
24
+ import { Icon } from 'patternfly-react';
25
+ import PropTypes from 'prop-types';
26
+ import React, { useEffect, useMemo, useState } from 'react';
27
+ import { FormattedMessage } from 'react-intl';
28
+ import { useHistory } from 'react-router-dom';
29
+ import URI from 'urijs';
30
+ import { CheckboxesActions } from './CheckboxesActions';
31
+ import DropdownFilter from './DropdownFilter';
25
32
  import Columns, {
26
33
  JOB_INVOCATION_HOSTS,
34
+ MAX_HOSTS_API_SIZE,
27
35
  STATUS_UPPERCASE,
36
+ LIST_TEMPLATE_INVOCATIONS,
37
+ ALL_JOB_HOSTS,
28
38
  } from './JobInvocationConstants';
39
+ import { PopupAlert } from './OpenAllInvocationsModal';
29
40
  import { TemplateInvocation } from './TemplateInvocation';
30
- import { OpenAlInvocations, PopupAlert } from './OpenAlInvocations';
31
41
  import { RowActions } from './TemplateInvocationComponents/TemplateActionButtons';
32
- import JobInvocationHostTableToolbar from './JobInvocationHostTableToolbar';
33
42
 
34
43
  const JobInvocationHostTable = ({
44
+ failedCount,
35
45
  id,
36
- targeting,
37
- finished,
38
- autoRefresh,
39
46
  initialFilter,
40
47
  onFilterUpdate,
48
+ targeting,
41
49
  }) => {
42
50
  const columns = Columns();
43
51
  const columnNamesKeys = Object.keys(columns);
44
52
  const apiOptions = { key: JOB_INVOCATION_HOSTS };
53
+ const history = useHistory();
45
54
  const [selectedFilter, setSelectedFilter] = useState(initialFilter);
55
+ const [expandedHost, setExpandedHost] = useState([]);
46
56
 
47
57
  useEffect(() => {
48
58
  if (initialFilter !== selectedFilter) {
@@ -55,7 +65,9 @@ const JobInvocationHostTable = ({
55
65
  searchParam: urlSearchQuery = '',
56
66
  page: urlPage,
57
67
  per_page: urlPerPage,
68
+ order: urlOrder,
58
69
  } = useUrlParams();
70
+
59
71
  const constructFilter = (
60
72
  filter = selectedFilter,
61
73
  search = urlSearchQuery
@@ -71,11 +83,21 @@ const JobInvocationHostTable = ({
71
83
  .join(' AND ');
72
84
  };
73
85
 
74
- const search = constructFilter();
75
- const defaultParams = search !== '' ? { search } : {};
76
- if (urlPage) defaultParams.page = Number(urlPage);
77
- if (urlPerPage) defaultParams.per_page = Number(urlPerPage);
78
- const [expandedHost, setExpandedHost] = useState([]);
86
+ const defaultParams = useMemo(
87
+ () => ({
88
+ ...(urlPage ? { page: Number(urlPage) } : {}),
89
+ ...(urlPerPage ? { per_page: Number(urlPerPage) } : {}),
90
+ ...(urlOrder ? { order: urlOrder } : {}),
91
+ }),
92
+ [urlPage, urlPerPage, urlOrder]
93
+ );
94
+
95
+ useAPI('get', `/job_invocations/${id}/hosts`, {
96
+ params: {
97
+ search: defaultParams.search,
98
+ },
99
+ key: LIST_TEMPLATE_INVOCATIONS,
100
+ });
79
101
  const { response, status, setAPIOptions } = useAPI(
80
102
  'get',
81
103
  `/api/job_invocations/${id}/hosts`,
@@ -84,18 +106,93 @@ const JobInvocationHostTable = ({
84
106
  }
85
107
  );
86
108
 
87
- const { params } = useSetParamsAndApiAndSearch({
88
- defaultParams,
89
- apiOptions,
90
- setAPIOptions,
91
- });
109
+ const [allPagesResponse, setAllPagesResponse] = useState([]);
110
+ const apiAllParams = {
111
+ page: 1,
112
+ per_page: Math.min(response?.subtotal || 1, MAX_HOSTS_API_SIZE),
113
+ search: constructFilter(selectedFilter, urlSearchQuery),
114
+ };
92
115
 
93
- const { updateSearchQuery: updateSearchQueryBulk } = useBulkSelect({
116
+ const { response: allResponse, setAPIOptions: setAllAPIOptions } = useAPI(
117
+ 'get',
118
+ `/api/job_invocations/${id}/hosts`,
119
+ {
120
+ params: apiAllParams,
121
+ key: ALL_JOB_HOSTS,
122
+ }
123
+ );
124
+
125
+ useEffect(() => {
126
+ if (response?.subtotal) {
127
+ setAllAPIOptions(prevOptions => ({
128
+ ...prevOptions,
129
+ params: apiAllParams,
130
+ }));
131
+ }
132
+ // eslint-disable-next-line react-hooks/exhaustive-deps
133
+ }, [response?.subtotal, selectedFilter, urlSearchQuery]);
134
+
135
+ useEffect(() => {
136
+ if (allResponse?.results) {
137
+ setAllPagesResponse(allResponse.results);
138
+ }
139
+ }, [allResponse]);
140
+
141
+ const {
142
+ updateSearchQuery: updateSearchQueryBulk,
143
+ fetchBulkParams,
144
+ inclusionSet,
145
+ exclusionSet,
146
+ ...selectAllOptions
147
+ } = useBulkSelect({
148
+ results: response?.results,
149
+ metadata: {
150
+ total: response?.total,
151
+ page: response?.page,
152
+ selectable: response?.subtotal,
153
+ },
94
154
  initialSearchQuery: urlSearchQuery,
95
155
  });
96
- const updateSearchQuery = searchQuery => {
97
- updateSearchQueryBulk(searchQuery);
98
- };
156
+
157
+ const {
158
+ selectAll,
159
+ selectPage,
160
+ selectNone,
161
+ selectedCount,
162
+ selectOne,
163
+ areAllRowsOnPageSelected,
164
+ areAllRowsSelected,
165
+ isSelected,
166
+ } = selectAllOptions;
167
+
168
+ const allHostIds = allPagesResponse?.map(item => item.id) || [];
169
+ const selectedIds =
170
+ areAllRowsSelected() || exclusionSet.size > 0
171
+ ? allHostIds.filter(hostId => !exclusionSet.has(hostId))
172
+ : Array.from(inclusionSet);
173
+
174
+ const { pageRowCount } = getPageStats({
175
+ total: response?.total || 0,
176
+ page: response?.page || urlPage || 1,
177
+ perPage: response?.per_page || urlPerPage || 0,
178
+ });
179
+
180
+ const selectionToolbar = (
181
+ <ToolbarItem key="selectAll">
182
+ <SelectAllCheckbox
183
+ {...{
184
+ selectAll,
185
+ selectPage,
186
+ selectNone,
187
+ selectedCount,
188
+ pageRowCount,
189
+ }}
190
+ totalCount={response?.total}
191
+ areAllRowsOnPageSelected={areAllRowsOnPageSelected()}
192
+ areAllRowsSelected={areAllRowsSelected()}
193
+ />
194
+ </ToolbarItem>
195
+ );
99
196
 
100
197
  const controller = 'hosts';
101
198
  const memoDefaultSearchProps = useMemo(
@@ -106,50 +203,53 @@ const JobInvocationHostTable = ({
106
203
  `/${controller}/auto_complete_search`
107
204
  );
108
205
 
109
- const wrapSetSelectedFilter = filter => {
110
- const filterSearch = constructFilter(filter);
111
- setAPIOptions(prevOptions => {
112
- if (prevOptions.params.search !== filterSearch) {
113
- return {
114
- ...prevOptions,
115
- params: {
116
- ...prevOptions.params,
117
- search: filterSearch,
118
- },
119
- };
120
- }
121
- return prevOptions;
122
- });
123
- setSelectedFilter(filter);
124
- onFilterUpdate(filter);
125
- };
206
+ const wrapSetSelectedFilter = newFilter => {
207
+ setSelectedFilter(newFilter);
208
+ onFilterUpdate(newFilter);
126
209
 
127
- useEffect(() => {
128
- const intervalId = setInterval(() => {
129
- if (!finished || autoRefresh) {
130
- setAPIOptions(prevOptions => ({
131
- ...prevOptions,
132
- params: {
133
- ...prevOptions.params,
134
- },
135
- }));
136
- }
137
- }, 5000);
138
-
139
- return () => {
140
- clearInterval(intervalId);
210
+ const filterSearch = constructFilter(newFilter, urlSearchQuery);
211
+
212
+ const newParams = {
213
+ ...defaultParams,
214
+ page: 1,
141
215
  };
142
- }, [finished, autoRefresh, setAPIOptions]);
216
+
217
+ if (filterSearch !== '') {
218
+ newParams.search = filterSearch;
219
+ }
220
+
221
+ setAPIOptions(prev => ({ ...prev, params: newParams }));
222
+
223
+ const urlSearchParams = new URLSearchParams(window.location.search);
224
+ urlSearchParams.set('page', '1');
225
+ history.push({ search: urlSearchParams.toString() });
226
+ };
143
227
 
144
228
  const wrapSetAPIOptions = newAPIOptions => {
145
- setAPIOptions(prevOptions => ({
146
- ...prevOptions,
147
- params: {
148
- ...prevOptions.params,
149
- ...newAPIOptions.params,
150
- search: constructFilter(undefined, newAPIOptions?.params?.search),
151
- },
152
- }));
229
+ const newParams = newAPIOptions?.params ?? newAPIOptions ?? {};
230
+
231
+ const filterSearch = constructFilter(
232
+ selectedFilter,
233
+ newParams.search ?? urlSearchQuery
234
+ );
235
+
236
+ const mergedParams = {
237
+ ...defaultParams,
238
+ ...newParams,
239
+ };
240
+
241
+ if (filterSearch !== '') {
242
+ mergedParams.search = filterSearch;
243
+ } else if ('search' in mergedParams) {
244
+ delete mergedParams.search;
245
+ }
246
+
247
+ setAPIOptions(prev => ({ ...prev, params: mergedParams }));
248
+
249
+ const { search: _search, ...paramsForUrl } = mergedParams;
250
+ const uri = new URI();
251
+ uri.setSearch(paramsForUrl);
252
+ history.push({ search: uri.search() });
153
253
  };
154
254
 
155
255
  const combinedResponse = {
@@ -158,8 +258,8 @@ const JobInvocationHostTable = ({
158
258
  can_create: false,
159
259
  results: response?.results || [],
160
260
  total: response?.total || 0,
161
- per_page: response?.perPage,
162
- page: response?.page,
261
+ per_page: defaultParams?.perPage,
262
+ page: defaultParams?.page,
163
263
  subtotal: response?.subtotal || 0,
164
264
  message: response?.message || 'error',
165
265
  },
@@ -213,6 +313,7 @@ const JobInvocationHostTable = ({
213
313
  return isExpanding ? [...otherExpandedHosts, host] : otherExpandedHosts;
214
314
  });
215
315
  const [showAlert, setShowAlert] = useState(false);
316
+
216
317
  return (
217
318
  <>
218
319
  {showAlert && <PopupAlert setShowAlert={setShowAlert} />}
@@ -223,34 +324,44 @@ const JobInvocationHostTable = ({
223
324
  controller="hosts"
224
325
  creatable={false}
225
326
  replacementResponse={combinedResponse}
226
- updateSearchQuery={updateSearchQuery}
327
+ updateSearchQuery={updateSearchQueryBulk}
227
328
  customToolbarItems={[
228
- <OpenAlInvocations
229
- setShowAlert={setShowAlert}
230
- results={results}
231
- id={id}
232
- key="OpenAlInvocations"
233
- />,
234
- <JobInvocationHostTableToolbar
329
+ <DropdownFilter
330
+ key="dropdown-filter"
235
331
  dropdownFilter={selectedFilter}
236
332
  setDropdownFilter={wrapSetSelectedFilter}
237
- key="JobInvocationHostTableToolbar"
333
+ />,
334
+ <CheckboxesActions
335
+ bulkParams={selectedCount > 0 ? fetchBulkParams() : null}
336
+ selectedIds={selectedIds}
337
+ failedCount={failedCount}
338
+ jobID={id}
339
+ key="checkboxes-actions"
340
+ filter={selectedFilter}
238
341
  />,
239
342
  ]}
343
+ selectionToolbar={selectionToolbar}
240
344
  >
241
345
  <Table
242
346
  ouiaId="job-invocation-hosts-table"
243
347
  columns={columns}
244
348
  customEmptyState={
245
- status === STATUS_UPPERCASE.RESOLVED && !response?.results?.length
349
+ status === STATUS_UPPERCASE.RESOLVED && !results.length
246
350
  ? customEmptyState
247
351
  : null
248
352
  }
249
- params={params}
353
+ params={{
354
+ page: response?.page || Number(urlPage),
355
+ per_page: response?.per_page || Number(urlPerPage),
356
+ order: urlOrder,
357
+ }}
358
+ page={response?.page || Number(urlPage)}
359
+ perPage={response?.per_page || Number(urlPerPage)}
250
360
  setParams={wrapSetAPIOptions}
251
361
  itemCount={response?.subtotal}
252
- results={response?.results}
362
+ results={results}
253
363
  url=""
364
+ showCheckboxes
254
365
  refreshData={() => {}}
255
366
  errorMessage={
256
367
  status === STATUS_UPPERCASE.ERROR && response?.message
@@ -261,9 +372,9 @@ const JobInvocationHostTable = ({
261
372
  isDeleteable={false}
262
373
  childrenOutsideTbody
263
374
  >
264
- {results?.map((result, rowIndex) => (
265
- <Tbody key={rowIndex}>
266
- <Tr ouiaId={`table-row-${rowIndex}`}>
375
+ {results.map((result, rowIndex) => (
376
+ <Tbody key={result.id}>
377
+ <Tr ouiaId={`table-row-${result.id}`}>
267
378
  <Td
268
379
  expand={{
269
380
  rowIndex,
@@ -273,6 +384,7 @@ const JobInvocationHostTable = ({
273
384
  expandId: 'host-expandable',
274
385
  }}
275
386
  />
387
+ <RowSelectTd rowData={result} {...{ selectOne, isSelected }} />
276
388
  {columnNamesKeys.map(k => (
277
389
  <Td key={k}>{columns[k].wrapper(result)}</Td>
278
390
  ))}
@@ -286,7 +398,7 @@ const JobInvocationHostTable = ({
286
398
  >
287
399
  <Td
288
400
  dataLabel={`${result.id}-expandable-content`}
289
- colSpan={columnNamesKeys.length}
401
+ colSpan={columnNamesKeys.length + 3}
290
402
  >
291
403
  <ExpandableRowContent>
292
404
  {result.job_status === 'cancelled' ||
@@ -295,7 +407,12 @@ const JobInvocationHostTable = ({
295
407
  {__('A task for this host has not been started')}
296
408
  </div>
297
409
  ) : (
298
- <TemplateInvocation hostID={result.id} jobID={id} />
410
+ <TemplateInvocation
411
+ hostID={result.id}
412
+ jobID={id}
413
+ isInTableView
414
+ isExpanded={isHostExpanded(result.id)}
415
+ />
299
416
  )}
300
417
  </ExpandableRowContent>
301
418
  </Td>
@@ -311,8 +428,7 @@ const JobInvocationHostTable = ({
311
428
  JobInvocationHostTable.propTypes = {
312
429
  id: PropTypes.string.isRequired,
313
430
  targeting: PropTypes.object.isRequired,
314
- finished: PropTypes.bool.isRequired,
315
- autoRefresh: PropTypes.bool.isRequired,
431
+ failedCount: PropTypes.number.isRequired,
316
432
  initialFilter: PropTypes.string.isRequired,
317
433
  onFilterUpdate: PropTypes.func,
318
434
  };
@@ -1,8 +1,15 @@
1
- import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
1
+ /* eslint-disable camelcase */
2
+ import {
3
+ selectAPIResponse,
4
+ selectAPIStatus,
5
+ } from 'foremanReact/redux/API/APISelectors';
6
+ import { STATUS as APIStatus } from 'foremanReact/constants';
2
7
  import {
3
8
  JOB_INVOCATION_KEY,
4
9
  GET_TASK,
5
10
  GET_TEMPLATE_INVOCATION,
11
+ LIST_TEMPLATE_INVOCATIONS,
12
+ CURRENT_PERMISSIONS,
6
13
  } from './JobInvocationConstants';
7
14
 
8
15
  export const selectItems = state =>
@@ -13,5 +20,25 @@ export const selectTask = state => selectAPIResponse(state, GET_TASK);
13
20
  export const selectTaskCancelable = state =>
14
21
  selectTask(state).available_actions?.cancellable || false;
15
22
 
16
- export const selectTemplateInvocation = state =>
17
- selectAPIResponse(state, GET_TEMPLATE_INVOCATION);
23
+ export const selectTemplateInvocation = hostID => state =>
24
+ selectAPIResponse(state, `${GET_TEMPLATE_INVOCATION}_${hostID}`);
25
+
26
+ export const selectTemplateInvocationStatus = hostID => state =>
27
+ selectAPIStatus(state, `${GET_TEMPLATE_INVOCATION}_${hostID}`);
28
+
29
+ export const selectTemplateInvocationList = state =>
30
+ selectAPIResponse(state, LIST_TEMPLATE_INVOCATIONS)
31
+ ?.template_invocations_task_by_hosts;
32
+
33
+ export const selectCurrentPermisions = state =>
34
+ selectAPIResponse(state, CURRENT_PERMISSIONS);
35
+
36
+ export const selectHasPermission = permissionRequired => state => {
37
+ const status = selectAPIStatus(state, CURRENT_PERMISSIONS);
38
+ const selectCurrentPermissions = selectCurrentPermisions(state)?.results;
39
+ return status === APIStatus.RESOLVED
40
+ ? selectCurrentPermissions?.some(
41
+ permission => permission.name === permissionRequired
42
+ )
43
+ : false;
44
+ };
@@ -11,7 +11,6 @@ import {
11
11
  } from '@patternfly/react-core/deprecated';
12
12
  import { translate as __ } from 'foremanReact/common/I18n';
13
13
  import { foremanUrl } from 'foremanReact/common/helpers';
14
- import { STATUS as APIStatus } from 'foremanReact/constants';
15
14
  import { get } from 'foremanReact/redux/API';
16
15
  import {
17
16
  cancelJob,
@@ -23,14 +22,12 @@ import {
23
22
  GET_REPORT_TEMPLATES,
24
23
  GET_REPORT_TEMPLATE_INPUTS,
25
24
  } from './JobInvocationConstants';
26
- import { selectTaskCancelable } from './JobInvocationSelectors';
25
+ import {
26
+ selectTaskCancelable,
27
+ selectHasPermission,
28
+ } from './JobInvocationSelectors';
27
29
 
28
- const JobInvocationToolbarButtons = ({
29
- jobId,
30
- data,
31
- currentPermissions,
32
- permissionsStatus,
33
- }) => {
30
+ const JobInvocationToolbarButtons = ({ jobId, data }) => {
34
31
  const { succeeded, failed, task, recurrence, permissions } = data;
35
32
  const recurringEnabled = recurrence?.state === 'active';
36
33
  const canViewForemanTasks = permissions
@@ -40,6 +37,8 @@ const JobInvocationToolbarButtons = ({
40
37
  ? permissions.edit_recurring_logics
41
38
  : false;
42
39
  const isTaskCancelable = useSelector(selectTaskCancelable);
40
+ const useHasPermission = permissionRequired =>
41
+ useSelector(selectHasPermission(permissionRequired));
43
42
  const [isActionOpen, setIsActionOpen] = useState(false);
44
43
  const [reportTemplateJobId, setReportTemplateJobId] = useState(undefined);
45
44
  const [templateInputId, setTemplateInputId] = useState(undefined);
@@ -58,12 +57,6 @@ const JobInvocationToolbarButtons = ({
58
57
  setIsActionOpen(false);
59
58
  onActionFocus();
60
59
  };
61
- const hasPermission = permissionRequired =>
62
- permissionsStatus === APIStatus.RESOLVED
63
- ? currentPermissions?.some(
64
- permission => permission.name === permissionRequired
65
- )
66
- : false;
67
60
 
68
61
  useEffect(() => {
69
62
  dispatch(
@@ -142,7 +135,9 @@ const JobInvocationToolbarButtons = ({
142
135
  ouiaId="rerun-succeeded-dropdown-item"
143
136
  href={foremanUrl(`/job_invocations/${jobId}/rerun?succeeded_only=1`)}
144
137
  key="rerun-succeeded"
145
- isDisabled={!(succeeded > 0) || !hasPermission('create_job_invocations')}
138
+ isDisabled={
139
+ !useHasPermission('create_job_invocations') || !(succeeded > 0)
140
+ }
146
141
  description="Rerun job on successful hosts"
147
142
  >
148
143
  {__('Rerun successful')}
@@ -151,7 +146,7 @@ const JobInvocationToolbarButtons = ({
151
146
  ouiaId="rerun-failed-dropdown-item"
152
147
  href={foremanUrl(`/job_invocations/${jobId}/rerun?failed_only=1`)}
153
148
  key="rerun-failed"
154
- isDisabled={!(failed > 0) || !hasPermission('create_job_invocations')}
149
+ isDisabled={!useHasPermission('create_job_invocations') || !(failed > 0)}
155
150
  description="Rerun job on failed hosts"
156
151
  >
157
152
  {__('Rerun failed')}
@@ -171,7 +166,9 @@ const JobInvocationToolbarButtons = ({
171
166
  onClick={() => dispatch(cancelJob(jobId, false))}
172
167
  key="cancel"
173
168
  component="button"
174
- isDisabled={!isTaskCancelable || !hasPermission('cancel_job_invocations')}
169
+ isDisabled={
170
+ !useHasPermission('cancel_job_invocations') || !isTaskCancelable
171
+ }
175
172
  description="Cancel job gracefully"
176
173
  >
177
174
  {__('Cancel')}
@@ -181,7 +178,9 @@ const JobInvocationToolbarButtons = ({
181
178
  onClick={() => dispatch(cancelJob(jobId, true))}
182
179
  key="abort"
183
180
  component="button"
184
- isDisabled={!isTaskCancelable || !hasPermission('cancel_job_invocations')}
181
+ isDisabled={
182
+ !useHasPermission('cancel_job_invocations') || !isTaskCancelable
183
+ }
185
184
  description="Cancel job immediately"
186
185
  >
187
186
  {__('Abort')}
@@ -210,9 +209,9 @@ const JobInvocationToolbarButtons = ({
210
209
  )}
211
210
  variant="secondary"
212
211
  isDisabled={
212
+ !useHasPermission('generate_report_templates') ||
213
213
  task?.state === STATUS.PENDING ||
214
- templateInputId === undefined ||
215
- !hasPermission('generate_report_templates')
214
+ templateInputId === undefined
216
215
  }
217
216
  >
218
217
  {__(`Create report`)}
@@ -234,7 +233,7 @@ const JobInvocationToolbarButtons = ({
234
233
  key="rerun"
235
234
  href={foremanUrl(`/job_invocations/${jobId}/rerun`)}
236
235
  variant="control"
237
- isDisabled={!hasPermission('create_job_invocations')}
236
+ isDisabled={!useHasPermission('create_job_invocations')}
238
237
  >
239
238
  {__(`Rerun all`)}
240
239
  </Button>,
@@ -255,12 +254,6 @@ const JobInvocationToolbarButtons = ({
255
254
  JobInvocationToolbarButtons.propTypes = {
256
255
  jobId: PropTypes.string.isRequired,
257
256
  data: PropTypes.object.isRequired,
258
- currentPermissions: PropTypes.array,
259
- permissionsStatus: PropTypes.string,
260
- };
261
- JobInvocationToolbarButtons.defaultProps = {
262
- currentPermissions: undefined,
263
- permissionsStatus: undefined,
264
257
  };
265
258
 
266
259
  export default JobInvocationToolbarButtons;