foreman_remote_execution 13.1.0 → 13.2.0

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_remote_execution/locale/de/foreman_remote_execution.js +1000 -973
  3. data/app/assets/javascripts/foreman_remote_execution/locale/en/foreman_remote_execution.js +553 -526
  4. data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +584 -557
  5. data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +1043 -1016
  6. data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +1047 -1020
  7. data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +1041 -1014
  8. data/app/assets/javascripts/foreman_remote_execution/locale/ka/foreman_remote_execution.js +1015 -988
  9. data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +886 -859
  10. data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +1047 -1020
  11. data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +901 -874
  12. data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +1041 -1014
  13. data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +886 -859
  14. data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
  15. data/app/models/job_invocation.rb +23 -0
  16. data/app/models/job_invocation_composer.rb +2 -0
  17. data/app/views/api/v2/job_invocations/base.json.rabl +3 -2
  18. data/app/views/templates/script/package_action.erb +5 -2
  19. data/lib/foreman_remote_execution/version.rb +1 -1
  20. data/locale/de/foreman_remote_execution.po +27 -0
  21. data/locale/en/foreman_remote_execution.po +27 -0
  22. data/locale/en_GB/foreman_remote_execution.po +27 -0
  23. data/locale/es/foreman_remote_execution.po +27 -0
  24. data/locale/foreman_remote_execution.pot +177 -141
  25. data/locale/fr/foreman_remote_execution.po +27 -0
  26. data/locale/ja/foreman_remote_execution.po +27 -0
  27. data/locale/ka/foreman_remote_execution.po +27 -0
  28. data/locale/ko/foreman_remote_execution.po +27 -0
  29. data/locale/pt_BR/foreman_remote_execution.po +27 -0
  30. data/locale/ru/foreman_remote_execution.po +27 -0
  31. data/locale/zh_CN/foreman_remote_execution.po +27 -0
  32. data/locale/zh_TW/foreman_remote_execution.po +27 -0
  33. data/package.json +2 -1
  34. data/webpack/JobInvocationDetail/JobInvocationActions.js +134 -3
  35. data/webpack/JobInvocationDetail/JobInvocationConstants.js +14 -0
  36. data/webpack/JobInvocationDetail/JobInvocationDetail.scss +5 -2
  37. data/webpack/JobInvocationDetail/JobInvocationSelectors.js +5 -0
  38. data/webpack/JobInvocationDetail/JobInvocationSystemStatusChart.js +13 -9
  39. data/webpack/JobInvocationDetail/JobInvocationToolbarButtons.js +268 -0
  40. data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +259 -0
  41. data/webpack/JobInvocationDetail/__tests__/fixtures.js +117 -0
  42. data/webpack/JobInvocationDetail/index.js +58 -38
  43. data/webpack/JobWizard/JobWizardPageRerun.js +16 -11
  44. data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +8 -6
  45. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext/index.js +2 -0
  46. data/webpack/__mocks__/foremanReact/components/BreadcrumbBar/index.js +4 -0
  47. data/webpack/__mocks__/foremanReact/components/Head/index.js +10 -0
  48. data/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js +8 -0
  49. data/webpack/__mocks__/foremanReact/components/ToastsList/index.js +3 -0
  50. data/webpack/__mocks__/foremanReact/redux/API/APIActions.js +21 -0
  51. data/webpack/__mocks__/foremanReact/redux/API/APIConstants.js +7 -0
  52. data/webpack/__mocks__/foremanReact/redux/API/index.js +14 -0
  53. data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware/index.js +9 -0
  54. metadata +12 -2
@@ -1,9 +1,19 @@
1
- import { get } from 'foremanReact/redux/API';
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 { JOB_INVOCATION_KEY } from './JobInvocationConstants';
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-page-section {
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 pointerLength={0} constrainToVisibleArea />
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;