foreman_remote_execution 16.0.5 → 16.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/foreman_remote_execution/locale/de/foreman_remote_execution.js +54 -30
- data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +52 -28
- data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +55 -31
- data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +61 -37
- data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +62 -38
- data/app/assets/javascripts/foreman_remote_execution/locale/ka/foreman_remote_execution.js +53 -29
- data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +61 -37
- data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +55 -31
- data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +52 -28
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +61 -37
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +52 -28
- data/app/controllers/api/v2/job_invocations_controller.rb +33 -6
- data/app/helpers/remote_execution_helper.rb +1 -1
- data/app/views/api/v2/job_templates/main.json.rabl +1 -1
- data/config/routes.rb +3 -2
- data/lib/foreman_remote_execution/plugin.rb +1 -7
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +54 -30
- data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en_GB/foreman_remote_execution.po +52 -28
- data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/es/foreman_remote_execution.po +55 -31
- data/locale/foreman_remote_execution.pot +193 -156
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +61 -37
- data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ja/foreman_remote_execution.po +62 -38
- data/locale/ka/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ka/foreman_remote_execution.po +53 -29
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +61 -37
- data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/pt_BR/foreman_remote_execution.po +55 -31
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +52 -28
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +61 -37
- data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_TW/foreman_remote_execution.po +52 -28
- data/webpack/JobInvocationDetail/CheckboxesActions.js +176 -46
- data/webpack/JobInvocationDetail/JobInvocationConstants.js +2 -3
- data/webpack/JobInvocationDetail/JobInvocationHostTable.js +14 -1
- data/webpack/JobInvocationDetail/JobInvocationToolbarButtons.js +3 -1
- data/webpack/JobInvocationDetail/TemplateInvocation.js +63 -50
- data/webpack/JobInvocationDetail/TemplateInvocationPage.js +1 -0
- data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +2 -4
- data/webpack/JobInvocationDetail/__tests__/TableToolbarActions.test.js +28 -26
- data/webpack/JobInvocationDetail/__tests__/TemplateInvocation.test.js +4 -0
- data/webpack/JobInvocationDetail/index.js +12 -13
- data/webpack/Routes/routes.js +1 -1
- data/webpack/react_app/components/HostKebab/KebabItems.js +2 -3
- metadata +2 -2
@@ -1,6 +1,7 @@
|
|
1
|
-
/* eslint-disable
|
1
|
+
/* eslint-disable max-lines */
|
2
2
|
import {
|
3
3
|
Button,
|
4
|
+
Divider,
|
4
5
|
Dropdown,
|
5
6
|
DropdownItem,
|
6
7
|
DropdownList,
|
@@ -11,20 +12,96 @@ import {
|
|
11
12
|
EllipsisVIcon,
|
12
13
|
OutlinedWindowRestoreIcon,
|
13
14
|
} from '@patternfly/react-icons';
|
14
|
-
import
|
15
|
+
import axios from 'axios';
|
15
16
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
16
17
|
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
18
|
+
import { translate as __, sprintf } from 'foremanReact/common/I18n';
|
19
|
+
import { addToast } from 'foremanReact/components/ToastsList';
|
17
20
|
import PropTypes from 'prop-types';
|
18
21
|
import React, { useState } from 'react';
|
19
|
-
import { useSelector } from 'react-redux';
|
22
|
+
import { useDispatch, useSelector } from 'react-redux';
|
23
|
+
|
20
24
|
import {
|
21
|
-
templateInvocationPageUrl,
|
22
|
-
MAX_HOSTS_API_SIZE,
|
23
25
|
DIRECT_OPEN_HOST_LIMIT,
|
26
|
+
MAX_HOSTS_API_SIZE,
|
27
|
+
templateInvocationPageUrl,
|
24
28
|
} from './JobInvocationConstants';
|
25
|
-
import {
|
29
|
+
import {
|
30
|
+
selectHasPermission,
|
31
|
+
selectTaskCancelable,
|
32
|
+
} from './JobInvocationSelectors';
|
26
33
|
import OpenAllInvocationsModal, { PopupAlert } from './OpenAllInvocationsModal';
|
27
34
|
|
35
|
+
/* eslint-disable camelcase */
|
36
|
+
const ActionsKebab = ({
|
37
|
+
selectedIds,
|
38
|
+
failedCount,
|
39
|
+
isTaskCancelable,
|
40
|
+
hasCancelPermission,
|
41
|
+
handleTaskAction,
|
42
|
+
handleOpenHosts,
|
43
|
+
isDropdownOpen,
|
44
|
+
setIsDropdownOpen,
|
45
|
+
}) => {
|
46
|
+
const dropdownItems = [
|
47
|
+
<DropdownItem
|
48
|
+
ouiaId="cancel-host-dropdown-item"
|
49
|
+
onClick={() => handleTaskAction('cancel')}
|
50
|
+
key="cancel"
|
51
|
+
component="button"
|
52
|
+
isDisabled={
|
53
|
+
selectedIds.length === 0 || !isTaskCancelable || !hasCancelPermission
|
54
|
+
}
|
55
|
+
>
|
56
|
+
{__('Cancel selected')}
|
57
|
+
</DropdownItem>,
|
58
|
+
<DropdownItem
|
59
|
+
ouiaId="abort-host-dropdown-item"
|
60
|
+
onClick={() => handleTaskAction('abort')}
|
61
|
+
key="abort"
|
62
|
+
component="button"
|
63
|
+
isDisabled={
|
64
|
+
selectedIds.length === 0 || !isTaskCancelable || !hasCancelPermission
|
65
|
+
}
|
66
|
+
>
|
67
|
+
{__('Abort selected')}
|
68
|
+
</DropdownItem>,
|
69
|
+
<Divider component="li" key="separator" />,
|
70
|
+
<DropdownItem
|
71
|
+
ouiaId="open-failed-dropdown-item"
|
72
|
+
key="open-failed"
|
73
|
+
onClick={() => handleOpenHosts('failed')}
|
74
|
+
isDisabled={failedCount === 0}
|
75
|
+
>
|
76
|
+
{sprintf(__('Open all failed runs (%s)'), failedCount)}
|
77
|
+
</DropdownItem>,
|
78
|
+
];
|
79
|
+
|
80
|
+
return (
|
81
|
+
<Dropdown
|
82
|
+
isOpen={isDropdownOpen}
|
83
|
+
onOpenChange={setIsDropdownOpen}
|
84
|
+
onSelect={() => setIsDropdownOpen(false)}
|
85
|
+
ouiaId="actions-kebab"
|
86
|
+
shouldFocusToggleOnSelect
|
87
|
+
toggle={toggleRef => (
|
88
|
+
<MenuToggle
|
89
|
+
aria-label="actions dropdown toggle"
|
90
|
+
id="toggle-kebab"
|
91
|
+
isExpanded={isDropdownOpen}
|
92
|
+
onClick={() => setIsDropdownOpen(prev => !prev)}
|
93
|
+
ref={toggleRef}
|
94
|
+
variant="plain"
|
95
|
+
>
|
96
|
+
<EllipsisVIcon />
|
97
|
+
</MenuToggle>
|
98
|
+
)}
|
99
|
+
>
|
100
|
+
<DropdownList>{dropdownItems}</DropdownList>
|
101
|
+
</Dropdown>
|
102
|
+
);
|
103
|
+
};
|
104
|
+
|
28
105
|
export const CheckboxesActions = ({
|
29
106
|
selectedIds,
|
30
107
|
failedCount,
|
@@ -35,10 +112,17 @@ export const CheckboxesActions = ({
|
|
35
112
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
36
113
|
const [showAlert, setShowAlert] = useState(false);
|
37
114
|
const [isOpenFailed, setIsOpenFailed] = useState(false);
|
38
|
-
const
|
115
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
116
|
+
const isTaskCancelable = useSelector(selectTaskCancelable);
|
117
|
+
const dispatch = useDispatch();
|
118
|
+
|
119
|
+
const hasCreatePermission = useSelector(
|
39
120
|
selectHasPermission('create_job_invocations')
|
40
121
|
);
|
41
|
-
const
|
122
|
+
const hasCancelPermission = useSelector(
|
123
|
+
selectHasPermission('cancel_job_invocations')
|
124
|
+
);
|
125
|
+
const jobSearchQuery = `job_invocation.id = ${jobID}`;
|
42
126
|
const filterQuery =
|
43
127
|
filter && filter !== 'all_statuses'
|
44
128
|
? ` and job_invocation.result = ${filter}`
|
@@ -89,43 +173,64 @@ export const CheckboxesActions = ({
|
|
89
173
|
setIsModalOpen(true);
|
90
174
|
};
|
91
175
|
|
92
|
-
const
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
onClick={() => handleOpenHosts('failed')}
|
100
|
-
isDisabled={failedCount === 0}
|
101
|
-
>
|
102
|
-
{sprintf(__('Open all failed runs (%s)'), failedCount)}
|
103
|
-
</DropdownItem>,
|
104
|
-
];
|
105
|
-
|
106
|
-
return (
|
107
|
-
<Dropdown
|
108
|
-
isOpen={isDropdownOpen}
|
109
|
-
onOpenChange={setIsDropdownOpen}
|
110
|
-
onSelect={() => setIsDropdownOpen(false)}
|
111
|
-
ouiaId="actions-kebab"
|
112
|
-
shouldFocusToggleOnSelect
|
113
|
-
toggle={toggleRef => (
|
114
|
-
<MenuToggle
|
115
|
-
aria-label="actions dropdown toggle"
|
116
|
-
id="toggle-kebab"
|
117
|
-
isExpanded={isDropdownOpen}
|
118
|
-
onClick={() => setIsDropdownOpen(prev => !prev)}
|
119
|
-
ref={toggleRef}
|
120
|
-
variant="plain"
|
121
|
-
>
|
122
|
-
<EllipsisVIcon />
|
123
|
-
</MenuToggle>
|
124
|
-
)}
|
125
|
-
>
|
126
|
-
<DropdownList>{dropdownItems}</DropdownList>
|
127
|
-
</Dropdown>
|
176
|
+
const cancelJobTasks = (search, action) => async () => {
|
177
|
+
dispatch(
|
178
|
+
addToast({
|
179
|
+
key: `cancel-job-info`,
|
180
|
+
type: 'info',
|
181
|
+
message: sprintf(__('Trying to %s the task'), action),
|
182
|
+
})
|
128
183
|
);
|
184
|
+
|
185
|
+
try {
|
186
|
+
const response = await axios.post(
|
187
|
+
`/api/v2/job_invocations/${jobID}/cancel`,
|
188
|
+
{
|
189
|
+
search,
|
190
|
+
force: action !== 'cancel',
|
191
|
+
}
|
192
|
+
);
|
193
|
+
|
194
|
+
const cancelledTasks = response.data?.cancelled;
|
195
|
+
const pastTenseAction =
|
196
|
+
action === 'cancel' ? __('cancelled') : __('aborted');
|
197
|
+
|
198
|
+
if (cancelledTasks && cancelledTasks.length > 0) {
|
199
|
+
const idList = cancelledTasks.join(', ');
|
200
|
+
dispatch(
|
201
|
+
addToast({
|
202
|
+
key: `success-tasks-cancelled`,
|
203
|
+
type: 'success',
|
204
|
+
message: sprintf(
|
205
|
+
__('%s task(s) successfully %s: %s'),
|
206
|
+
cancelledTasks.length,
|
207
|
+
pastTenseAction,
|
208
|
+
idList
|
209
|
+
),
|
210
|
+
})
|
211
|
+
);
|
212
|
+
} else {
|
213
|
+
dispatch(
|
214
|
+
addToast({
|
215
|
+
key: `warn-no-tasks-cancelled-${Date.now()}`,
|
216
|
+
type: 'warning',
|
217
|
+
message: sprintf(__('Task(s) were not %s'), pastTenseAction),
|
218
|
+
})
|
219
|
+
);
|
220
|
+
}
|
221
|
+
} catch (error) {
|
222
|
+
dispatch(
|
223
|
+
addToast({
|
224
|
+
key: `error-cancelling-tasks`,
|
225
|
+
type: 'danger',
|
226
|
+
message: error.response?.data?.error || __('An error occurred.'),
|
227
|
+
})
|
228
|
+
);
|
229
|
+
}
|
230
|
+
};
|
231
|
+
|
232
|
+
const handleTaskAction = action => {
|
233
|
+
dispatch(cancelJobTasks(combinedQuery, action));
|
129
234
|
};
|
130
235
|
|
131
236
|
const OpenAllButton = () => (
|
@@ -153,7 +258,7 @@ export const CheckboxesActions = ({
|
|
153
258
|
`/job_invocations/${jobID}/rerun?search=(${jobSearchQuery}) AND (${combinedQuery})`
|
154
259
|
)}
|
155
260
|
// eslint-disable-next-line camelcase
|
156
|
-
isDisabled={selectedIds.length === 0 || !
|
261
|
+
isDisabled={selectedIds.length === 0 || !hasCreatePermission}
|
157
262
|
isInline
|
158
263
|
ouiaId="template-invocation-rerun-selected-button"
|
159
264
|
variant="secondary"
|
@@ -166,7 +271,16 @@ export const CheckboxesActions = ({
|
|
166
271
|
<>
|
167
272
|
<OpenAllButton />
|
168
273
|
<RerunSelectedButton />
|
169
|
-
<ActionsKebab
|
274
|
+
<ActionsKebab
|
275
|
+
selectedIds={selectedIds}
|
276
|
+
failedCount={failedCount}
|
277
|
+
isTaskCancelable={isTaskCancelable}
|
278
|
+
hasCancelPermission={hasCancelPermission}
|
279
|
+
handleTaskAction={handleTaskAction}
|
280
|
+
handleOpenHosts={handleOpenHosts}
|
281
|
+
isDropdownOpen={isDropdownOpen}
|
282
|
+
setIsDropdownOpen={setIsDropdownOpen}
|
283
|
+
/>
|
170
284
|
{showAlert && <PopupAlert setShowAlert={setShowAlert} />}
|
171
285
|
<OpenAllInvocationsModal
|
172
286
|
isOpen={isModalOpen}
|
@@ -182,6 +296,22 @@ export const CheckboxesActions = ({
|
|
182
296
|
);
|
183
297
|
};
|
184
298
|
|
299
|
+
ActionsKebab.propTypes = {
|
300
|
+
selectedIds: PropTypes.array.isRequired,
|
301
|
+
failedCount: PropTypes.number.isRequired,
|
302
|
+
isTaskCancelable: PropTypes.bool,
|
303
|
+
hasCancelPermission: PropTypes.bool,
|
304
|
+
handleTaskAction: PropTypes.func.isRequired,
|
305
|
+
handleOpenHosts: PropTypes.func.isRequired,
|
306
|
+
isDropdownOpen: PropTypes.bool.isRequired,
|
307
|
+
setIsDropdownOpen: PropTypes.func.isRequired,
|
308
|
+
};
|
309
|
+
|
310
|
+
ActionsKebab.defaultProps = {
|
311
|
+
isTaskCancelable: false,
|
312
|
+
hasCancelPermission: false,
|
313
|
+
};
|
314
|
+
|
185
315
|
CheckboxesActions.propTypes = {
|
186
316
|
selectedIds: PropTypes.array.isRequired,
|
187
317
|
failedCount: PropTypes.number.isRequired,
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/* eslint-disable camelcase */
|
2
2
|
import React from 'react';
|
3
|
-
import { foremanUrl } from 'foremanReact/common/helpers';
|
4
3
|
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
5
5
|
import { useForemanHostDetailsPageUrl } from 'foremanReact/Root/Context/ForemanContext';
|
6
6
|
import JobStatusIcon from '../react_app/components/RecentJobsCard/JobStatusIcon';
|
7
7
|
|
@@ -31,8 +31,7 @@ export const LIST_TEMPLATE_INVOCATIONS = 'LIST_TEMPLATE_INVOCATIONS';
|
|
31
31
|
export const templateInvocationPageUrl = (hostID, jobID) =>
|
32
32
|
`/job_invocations_detail/${jobID}/host_invocation/${hostID}`;
|
33
33
|
|
34
|
-
export const jobInvocationDetailsUrl = id =>
|
35
|
-
`/experimental/job_invocations_detail/${id}`;
|
34
|
+
export const jobInvocationDetailsUrl = id => `/job_invocations/${id}`;
|
36
35
|
|
37
36
|
export const STATUS = {
|
38
37
|
PENDING: 'pending',
|
@@ -23,7 +23,7 @@ import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableInde
|
|
23
23
|
import { getControllerSearchProps } from 'foremanReact/constants';
|
24
24
|
import { Icon } from 'patternfly-react';
|
25
25
|
import PropTypes from 'prop-types';
|
26
|
-
import React, { useEffect, useMemo, useState } from 'react';
|
26
|
+
import React, { useEffect, useMemo, useState, useRef } from 'react';
|
27
27
|
import { FormattedMessage } from 'react-intl';
|
28
28
|
import { useHistory } from 'react-router-dom';
|
29
29
|
import URI from 'urijs';
|
@@ -45,6 +45,7 @@ const JobInvocationHostTable = ({
|
|
45
45
|
id,
|
46
46
|
initialFilter,
|
47
47
|
onFilterUpdate,
|
48
|
+
statusLabel,
|
48
49
|
targeting,
|
49
50
|
}) => {
|
50
51
|
const columns = Columns();
|
@@ -53,6 +54,7 @@ const JobInvocationHostTable = ({
|
|
53
54
|
const history = useHistory();
|
54
55
|
const [selectedFilter, setSelectedFilter] = useState(initialFilter);
|
55
56
|
const [expandedHost, setExpandedHost] = useState([]);
|
57
|
+
const prevStatusLabel = useRef(statusLabel);
|
56
58
|
|
57
59
|
useEffect(() => {
|
58
60
|
if (initialFilter !== selectedFilter) {
|
@@ -138,6 +140,14 @@ const JobInvocationHostTable = ({
|
|
138
140
|
}
|
139
141
|
}, [allResponse]);
|
140
142
|
|
143
|
+
useEffect(() => {
|
144
|
+
if (statusLabel !== prevStatusLabel.current) {
|
145
|
+
setAPIOptions(prevOptions => ({ ...prevOptions }));
|
146
|
+
prevStatusLabel.current = statusLabel;
|
147
|
+
}
|
148
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
149
|
+
}, [statusLabel]);
|
150
|
+
|
141
151
|
const {
|
142
152
|
updateSearchQuery: updateSearchQueryBulk,
|
143
153
|
fetchBulkParams,
|
@@ -408,6 +418,7 @@ const JobInvocationHostTable = ({
|
|
408
418
|
</div>
|
409
419
|
) : (
|
410
420
|
<TemplateInvocation
|
421
|
+
key={`${result.id}-${result.job_status}`}
|
411
422
|
hostID={result.id}
|
412
423
|
jobID={id}
|
413
424
|
isInTableView
|
@@ -430,11 +441,13 @@ JobInvocationHostTable.propTypes = {
|
|
430
441
|
targeting: PropTypes.object.isRequired,
|
431
442
|
failedCount: PropTypes.number.isRequired,
|
432
443
|
initialFilter: PropTypes.string.isRequired,
|
444
|
+
statusLabel: PropTypes.string,
|
433
445
|
onFilterUpdate: PropTypes.func,
|
434
446
|
};
|
435
447
|
|
436
448
|
JobInvocationHostTable.defaultProps = {
|
437
449
|
onFilterUpdate: () => {},
|
450
|
+
statusLabel: undefined,
|
438
451
|
};
|
439
452
|
|
440
453
|
export default JobInvocationHostTable;
|
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|
2
2
|
import React, { useEffect, useState } from 'react';
|
3
3
|
import { useDispatch, useSelector } from 'react-redux';
|
4
4
|
import { Button, Split, SplitItem } from '@patternfly/react-core';
|
5
|
+
import { UndoIcon } from '@patternfly/react-icons';
|
5
6
|
import {
|
6
7
|
Dropdown,
|
7
8
|
DropdownItem,
|
@@ -189,7 +190,8 @@ const JobInvocationToolbarButtons = ({ jobId, data }) => {
|
|
189
190
|
<DropdownSeparator ouiaId="dropdown-separator-2" key="separator-2" />,
|
190
191
|
<DropdownItem
|
191
192
|
ouiaId="legacy-ui-dropdown-item"
|
192
|
-
|
193
|
+
icon={<UndoIcon />}
|
194
|
+
href={`/legacy/job_invocations/${jobId}`}
|
193
195
|
key="legacy-ui"
|
194
196
|
>
|
195
197
|
{__('Legacy UI')}
|
@@ -57,9 +57,9 @@ export const TemplateInvocation = ({
|
|
57
57
|
hostID,
|
58
58
|
jobID,
|
59
59
|
isInTableView,
|
60
|
+
isExpanded,
|
60
61
|
hostName,
|
61
62
|
hostProxy,
|
62
|
-
isExpanded,
|
63
63
|
}) => {
|
64
64
|
const intervalRef = useRef(null);
|
65
65
|
const templateURL = showTemplateInvocationUrl(hostID, jobID);
|
@@ -67,72 +67,76 @@ export const TemplateInvocation = ({
|
|
67
67
|
|
68
68
|
const status = useSelector(selectTemplateInvocationStatus(hostID));
|
69
69
|
const response = useSelector(selectTemplateInvocation(hostID));
|
70
|
-
const finished = response.finished ?? true;
|
71
|
-
const autoRefresh = response.auto_refresh || false;
|
72
70
|
const dispatch = useDispatch();
|
73
71
|
|
72
|
+
const responseRef = useRef(response);
|
74
73
|
useEffect(() => {
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
74
|
+
responseRef.current = response;
|
75
|
+
}, [response]);
|
76
|
+
|
77
|
+
const [showOutputType, setShowOutputType] = useState({
|
78
|
+
stderr: true,
|
79
|
+
stdout: true,
|
80
|
+
debug: true,
|
81
|
+
});
|
82
|
+
const [showTemplatePreview, setShowTemplatePreview] = useState(false);
|
83
|
+
const [showCommand, setShowCommand] = useState(false);
|
84
|
+
|
85
|
+
useEffect(() => {
|
86
|
+
const dispatchFetch = () => {
|
87
|
+
dispatch(
|
88
|
+
APIActions.get({
|
89
|
+
url: templateURL,
|
90
|
+
key: `${GET_TEMPLATE_INVOCATION}_${hostID}`,
|
91
|
+
})
|
92
|
+
);
|
90
93
|
};
|
91
|
-
|
92
|
-
if (
|
94
|
+
|
95
|
+
if (intervalRef.current) {
|
96
|
+
clearInterval(intervalRef.current);
|
97
|
+
intervalRef.current = null;
|
98
|
+
}
|
99
|
+
|
100
|
+
if (isExpanded) {
|
101
|
+
if (isEmpty(responseRef.current)) {
|
102
|
+
dispatchFetch();
|
103
|
+
}
|
104
|
+
|
93
105
|
intervalRef.current = setInterval(() => {
|
94
|
-
|
106
|
+
const latestResponse = responseRef.current;
|
107
|
+
const finished = latestResponse?.finished ?? true;
|
108
|
+
// eslint-disable-next-line camelcase
|
109
|
+
const autoRefresh = latestResponse?.auto_refresh || false;
|
110
|
+
|
111
|
+
if (!finished && autoRefresh) {
|
112
|
+
dispatchFetch();
|
113
|
+
} else if (intervalRef.current) {
|
114
|
+
clearInterval(intervalRef.current);
|
115
|
+
}
|
95
116
|
}, 5000);
|
96
117
|
}
|
97
118
|
|
98
119
|
return () => {
|
99
120
|
if (intervalRef.current) {
|
100
121
|
clearInterval(intervalRef.current);
|
101
|
-
intervalRef.current = null;
|
102
122
|
}
|
103
123
|
};
|
104
|
-
}, [
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
124
|
+
}, [isExpanded, dispatch, templateURL, hostID]);
|
125
|
+
|
126
|
+
if (!isExpanded) {
|
127
|
+
return null;
|
128
|
+
}
|
129
|
+
|
130
|
+
if ((status === STATUS.PENDING && isEmpty(response)) || !response) {
|
131
|
+
return <Skeleton />;
|
132
|
+
}
|
113
133
|
|
114
134
|
const errorMessage =
|
115
135
|
response?.response?.data?.error?.message ||
|
116
136
|
response?.response?.data?.error ||
|
117
137
|
JSON.stringify(response);
|
118
|
-
|
119
|
-
|
120
|
-
output,
|
121
|
-
input_values: inputValues,
|
122
|
-
task,
|
123
|
-
permissions,
|
124
|
-
} = response;
|
125
|
-
const { id: taskID, cancellable: taskCancellable } = task || {};
|
126
|
-
const [showOutputType, setShowOutputType] = useState({
|
127
|
-
stderr: true,
|
128
|
-
stdout: true,
|
129
|
-
debug: true,
|
130
|
-
});
|
131
|
-
const [showTemplatePreview, setShowTemplatePreview] = useState(false);
|
132
|
-
const [showCommand, setCommand] = useState(false);
|
133
|
-
if (status === STATUS.PENDING && isEmpty(response)) {
|
134
|
-
return <Skeleton />;
|
135
|
-
} else if (status === STATUS.ERROR) {
|
138
|
+
|
139
|
+
if (status === STATUS.ERROR) {
|
136
140
|
return (
|
137
141
|
<Alert
|
138
142
|
ouiaId="template-invocation-error-alert"
|
@@ -146,6 +150,15 @@ export const TemplateInvocation = ({
|
|
146
150
|
);
|
147
151
|
}
|
148
152
|
|
153
|
+
const {
|
154
|
+
preview,
|
155
|
+
output,
|
156
|
+
input_values: inputValues,
|
157
|
+
task,
|
158
|
+
permissions,
|
159
|
+
} = response;
|
160
|
+
const { id: taskID, cancellable: taskCancellable } = task || {};
|
161
|
+
|
149
162
|
return (
|
150
163
|
<div
|
151
164
|
id={`template-invocation-${hostID}`}
|
@@ -159,7 +172,7 @@ export const TemplateInvocation = ({
|
|
159
172
|
setShowTemplatePreview={setShowTemplatePreview}
|
160
173
|
showTemplatePreview={showTemplatePreview}
|
161
174
|
showCommand={showCommand}
|
162
|
-
setShowCommand={
|
175
|
+
setShowCommand={setShowCommand}
|
163
176
|
newTabUrl={templateInvocationPageUrl(hostID, jobID)}
|
164
177
|
isInTableView={isInTableView}
|
165
178
|
copyToClipboard={
|
@@ -96,9 +96,7 @@ describe('JobInvocationDetailPage', () => {
|
|
96
96
|
);
|
97
97
|
|
98
98
|
expect(screen.getByText('Description')).toBeInTheDocument();
|
99
|
-
expect(
|
100
|
-
container.querySelector('.chart-donut .pf-v5-c-chart')
|
101
|
-
).toBeInTheDocument();
|
99
|
+
expect(container.querySelector('.chart-donut')).toBeInTheDocument();
|
102
100
|
expect(screen.getByText('2/6')).toBeInTheDocument();
|
103
101
|
expect(screen.getByText('Systems')).toBeInTheDocument();
|
104
102
|
expect(screen.getByText('System status')).toBeInTheDocument();
|
@@ -178,7 +176,7 @@ describe('JobInvocationDetailPage', () => {
|
|
178
176
|
.getByText('Legacy UI')
|
179
177
|
.closest('a')
|
180
178
|
.getAttribute('href')
|
181
|
-
).toEqual(`/job_invocations/${jobId}`);
|
179
|
+
).toEqual(`/legacy/job_invocations/${jobId}`);
|
182
180
|
});
|
183
181
|
|
184
182
|
it('shows scheduled date', async () => {
|