foreman_remote_execution 16.2.0 → 16.2.2
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 +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ka/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +4 -1
- data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +4 -1
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +4 -1
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +3 -0
- data/config/routes.rb +2 -1
- data/extra/cockpit/foreman-cockpit-session +3 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/de/foreman_remote_execution.po +3 -0
- data/locale/en_GB/foreman_remote_execution.po +3 -0
- data/locale/es/foreman_remote_execution.po +3 -0
- data/locale/foreman_remote_execution.pot +17 -13
- data/locale/fr/foreman_remote_execution.po +3 -0
- data/locale/ja/foreman_remote_execution.po +3 -0
- data/locale/ka/foreman_remote_execution.po +3 -0
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +4 -1
- data/locale/pt_BR/foreman_remote_execution.po +3 -0
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +4 -1
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +4 -1
- data/locale/zh_TW/foreman_remote_execution.po +3 -0
- data/webpack/JobInvocationDetail/CheckboxesActions.js +39 -30
- data/webpack/JobInvocationDetail/JobInvocationConstants.js +0 -1
- data/webpack/JobInvocationDetail/JobInvocationHostTable.js +156 -170
- data/webpack/JobInvocationDetail/OpenAllInvocationsModal.js +27 -32
- data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +10 -2
- data/webpack/JobInvocationDetail/__tests__/TableToolbarActions.test.js +46 -30
- data/webpack/JobInvocationDetail/index.js +13 -2
- data/webpack/JobWizard/Footer.js +7 -1
- data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +1 -1
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +5 -6
- data/webpack/react_app/components/RegistrationExtension/RexInterface.js +5 -19
- data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +19 -3
- metadata +2 -3
- data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +0 -36
@@ -8,9 +8,10 @@ import {
|
|
8
8
|
ToolbarItem,
|
9
9
|
} from '@patternfly/react-core';
|
10
10
|
import { ExpandableRowContent, Tbody, Td, Tr } from '@patternfly/react-table';
|
11
|
+
import { useDispatch } from 'react-redux';
|
12
|
+
import { APIActions } from 'foremanReact/redux/API';
|
11
13
|
import { translate as __ } from 'foremanReact/common/I18n';
|
12
14
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
13
|
-
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
14
15
|
import { RowSelectTd } from 'foremanReact/components/HostsIndex/RowSelectTd';
|
15
16
|
import SelectAllCheckbox from 'foremanReact/components/PF4/TableIndexPage/Table/SelectAllCheckbox';
|
16
17
|
import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
|
@@ -26,22 +27,20 @@ import PropTypes from 'prop-types';
|
|
26
27
|
import React, { useEffect, useMemo, useState, useRef } from 'react';
|
27
28
|
import { FormattedMessage } from 'react-intl';
|
28
29
|
import { useHistory } from 'react-router-dom';
|
29
|
-
import
|
30
|
+
import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
|
30
31
|
import { CheckboxesActions } from './CheckboxesActions';
|
31
32
|
import DropdownFilter from './DropdownFilter';
|
32
33
|
import Columns, {
|
33
34
|
JOB_INVOCATION_HOSTS,
|
34
|
-
MAX_HOSTS_API_SIZE,
|
35
|
-
STATUS_UPPERCASE,
|
36
35
|
LIST_TEMPLATE_INVOCATIONS,
|
36
|
+
STATUS_UPPERCASE,
|
37
37
|
ALL_JOB_HOSTS,
|
38
38
|
} from './JobInvocationConstants';
|
39
|
-
import { PopupAlert } from './OpenAllInvocationsModal';
|
40
39
|
import { TemplateInvocation } from './TemplateInvocation';
|
41
40
|
import { RowActions } from './TemplateInvocationComponents/TemplateActionButtons';
|
41
|
+
import { PopupAlert } from './OpenAllInvocationsModal';
|
42
42
|
|
43
43
|
const JobInvocationHostTable = ({
|
44
|
-
failedCount,
|
45
44
|
id,
|
46
45
|
initialFilter,
|
47
46
|
onFilterUpdate,
|
@@ -50,19 +49,29 @@ const JobInvocationHostTable = ({
|
|
50
49
|
}) => {
|
51
50
|
const columns = Columns();
|
52
51
|
const columnNamesKeys = Object.keys(columns);
|
53
|
-
|
52
|
+
|
54
53
|
const history = useHistory();
|
55
|
-
const
|
54
|
+
const dispatch = useDispatch();
|
55
|
+
|
56
|
+
const [showAlert, setShowAlert] = useState(false);
|
57
|
+
|
58
|
+
const [apiResponse, setApiResponse] = useState([]);
|
59
|
+
const [status, setStatus] = useState(STATUS_UPPERCASE.PENDING);
|
60
|
+
const [allHostsIds, setAllHostsIds] = useState([]);
|
61
|
+
|
62
|
+
// Expansive items
|
56
63
|
const [expandedHost, setExpandedHost] = useState([]);
|
57
64
|
const prevStatusLabel = useRef(statusLabel);
|
58
65
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
66
|
+
const isHostExpanded = host => expandedHost.includes(host);
|
67
|
+
const setHostExpanded = (host, isExpanding = true) =>
|
68
|
+
setExpandedHost(prevExpanded => {
|
69
|
+
const otherExpandedHosts = prevExpanded.filter(h => h !== host);
|
70
|
+
return isExpanding ? [...otherExpandedHosts, host] : otherExpandedHosts;
|
71
|
+
});
|
65
72
|
|
73
|
+
// Page table params
|
74
|
+
// Parse URL
|
66
75
|
const {
|
67
76
|
searchParam: urlSearchQuery = '',
|
68
77
|
page: urlPage,
|
@@ -70,10 +79,27 @@ const JobInvocationHostTable = ({
|
|
70
79
|
order: urlOrder,
|
71
80
|
} = useUrlParams();
|
72
81
|
|
73
|
-
const
|
74
|
-
|
75
|
-
|
76
|
-
|
82
|
+
const { perPage: foremanPerPage } = useForemanSettings();
|
83
|
+
|
84
|
+
// default
|
85
|
+
const defaultParams = useMemo(
|
86
|
+
() => ({
|
87
|
+
page: urlPage ? Number(urlPage) : 1,
|
88
|
+
per_page: urlPerPage || Number(urlPerPage) || foremanPerPage,
|
89
|
+
order: urlOrder || '',
|
90
|
+
}),
|
91
|
+
[urlPage, urlPerPage, foremanPerPage, urlOrder]
|
92
|
+
);
|
93
|
+
|
94
|
+
// Page row for table
|
95
|
+
const { pageRowCount } = getPageStats({
|
96
|
+
total: apiResponse?.total || 0,
|
97
|
+
page: apiResponse?.page || urlPage || 1,
|
98
|
+
perPage: apiResponse?.per_page || urlPerPage || 0,
|
99
|
+
});
|
100
|
+
|
101
|
+
// Search filter
|
102
|
+
const constructFilter = (filter = initialFilter, search = urlSearchQuery) => {
|
77
103
|
const dropdownFilterClause =
|
78
104
|
filter && filter !== 'all_statuses'
|
79
105
|
? `job_invocation.result = ${filter}`
|
@@ -85,68 +111,93 @@ const JobInvocationHostTable = ({
|
|
85
111
|
.join(' AND ');
|
86
112
|
};
|
87
113
|
|
88
|
-
const
|
89
|
-
()
|
90
|
-
|
91
|
-
...(urlPerPage ? { per_page: Number(urlPerPage) } : {}),
|
92
|
-
...(urlOrder ? { order: urlOrder } : {}),
|
93
|
-
}),
|
94
|
-
[urlPage, urlPerPage, urlOrder]
|
95
|
-
);
|
114
|
+
const handleResponse = (data, key) => {
|
115
|
+
if (key === JOB_INVOCATION_HOSTS) {
|
116
|
+
const ids = data.data.results.map(i => i.id);
|
96
117
|
|
97
|
-
|
98
|
-
|
99
|
-
search: defaultParams.search,
|
100
|
-
},
|
101
|
-
key: LIST_TEMPLATE_INVOCATIONS,
|
102
|
-
});
|
103
|
-
const { response, status, setAPIOptions } = useAPI(
|
104
|
-
'get',
|
105
|
-
`/api/job_invocations/${id}/hosts`,
|
106
|
-
{
|
107
|
-
params: defaultParams,
|
118
|
+
setApiResponse(data.data);
|
119
|
+
setAllHostsIds(ids);
|
108
120
|
}
|
109
|
-
);
|
110
121
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
122
|
+
setStatus(STATUS_UPPERCASE.RESOLVED);
|
123
|
+
};
|
124
|
+
|
125
|
+
// Call hosts data with params
|
126
|
+
const makeApiCall = (requestParams, callParams = {}) => {
|
127
|
+
dispatch(
|
128
|
+
APIActions.get({
|
129
|
+
key: callParams.key ?? ALL_JOB_HOSTS,
|
130
|
+
url: callParams.url ?? `/api/job_invocations/${id}/hosts`,
|
131
|
+
params: requestParams,
|
132
|
+
handleSuccess: data => handleResponse(data, callParams.key),
|
133
|
+
handleError: () => setStatus(STATUS_UPPERCASE.ERROR),
|
134
|
+
errorToast: ({ response }) =>
|
135
|
+
response?.data?.error?.full_messages?.[0] || response,
|
136
|
+
})
|
137
|
+
);
|
116
138
|
};
|
117
139
|
|
118
|
-
const
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
140
|
+
const filterApiCall = newAPIOptions => {
|
141
|
+
const newParams = newAPIOptions?.params ?? newAPIOptions ?? {};
|
142
|
+
|
143
|
+
const filterSearch = constructFilter(
|
144
|
+
initialFilter,
|
145
|
+
newParams.search ?? urlSearchQuery
|
146
|
+
);
|
147
|
+
|
148
|
+
const finalParams = {
|
149
|
+
...defaultParams,
|
150
|
+
...newParams,
|
151
|
+
};
|
152
|
+
|
153
|
+
if (filterSearch !== '') {
|
154
|
+
finalParams.search = filterSearch;
|
124
155
|
}
|
125
|
-
);
|
126
156
|
|
157
|
+
makeApiCall(finalParams, { key: JOB_INVOCATION_HOSTS });
|
158
|
+
|
159
|
+
const urlSearchParams = new URLSearchParams(window.location.search);
|
160
|
+
|
161
|
+
['page', 'per_page', 'order'].forEach(key => {
|
162
|
+
if (finalParams[key]) urlSearchParams.set(key, finalParams[key]);
|
163
|
+
});
|
164
|
+
|
165
|
+
history.push({ search: urlSearchParams.toString() });
|
166
|
+
};
|
167
|
+
|
168
|
+
// Filter change
|
169
|
+
const handleFilterChange = newFilter => {
|
170
|
+
onFilterUpdate(newFilter);
|
171
|
+
};
|
172
|
+
|
173
|
+
// Effects
|
174
|
+
// run after mount
|
127
175
|
useEffect(() => {
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
176
|
+
// Job Invo template load
|
177
|
+
makeApiCall(
|
178
|
+
{},
|
179
|
+
{
|
180
|
+
url: `/job_invocations/${id}/hosts`,
|
181
|
+
key: LIST_TEMPLATE_INVOCATIONS,
|
182
|
+
}
|
183
|
+
);
|
184
|
+
|
185
|
+
if (initialFilter === '') {
|
186
|
+
onFilterUpdate('all_statuses');
|
133
187
|
}
|
188
|
+
|
134
189
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
135
|
-
}, [
|
190
|
+
}, []);
|
136
191
|
|
137
192
|
useEffect(() => {
|
138
|
-
if (
|
139
|
-
setAllPagesResponse(allResponse.results);
|
140
|
-
}
|
141
|
-
}, [allResponse]);
|
193
|
+
if (initialFilter !== '') filterApiCall();
|
142
194
|
|
143
|
-
useEffect(() => {
|
144
195
|
if (statusLabel !== prevStatusLabel.current) {
|
145
|
-
setAPIOptions(prevOptions => ({ ...prevOptions }));
|
146
196
|
prevStatusLabel.current = statusLabel;
|
197
|
+
filterApiCall();
|
147
198
|
}
|
148
199
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
149
|
-
}, [statusLabel]);
|
200
|
+
}, [initialFilter, statusLabel, id]);
|
150
201
|
|
151
202
|
const {
|
152
203
|
updateSearchQuery: updateSearchQueryBulk,
|
@@ -155,11 +206,11 @@ const JobInvocationHostTable = ({
|
|
155
206
|
exclusionSet,
|
156
207
|
...selectAllOptions
|
157
208
|
} = useBulkSelect({
|
158
|
-
results:
|
209
|
+
results: apiResponse?.results,
|
159
210
|
metadata: {
|
160
|
-
total:
|
161
|
-
page:
|
162
|
-
selectable:
|
211
|
+
total: apiResponse?.total,
|
212
|
+
page: apiResponse?.page,
|
213
|
+
selectable: apiResponse?.subtotal,
|
163
214
|
},
|
164
215
|
initialSearchQuery: urlSearchQuery,
|
165
216
|
});
|
@@ -175,35 +226,11 @@ const JobInvocationHostTable = ({
|
|
175
226
|
isSelected,
|
176
227
|
} = selectAllOptions;
|
177
228
|
|
178
|
-
const allHostIds = allPagesResponse?.map(item => item.id) || [];
|
179
229
|
const selectedIds =
|
180
230
|
areAllRowsSelected() || exclusionSet.size > 0
|
181
|
-
?
|
231
|
+
? allHostsIds.filter(hostId => !exclusionSet.has(hostId))
|
182
232
|
: Array.from(inclusionSet);
|
183
233
|
|
184
|
-
const { pageRowCount } = getPageStats({
|
185
|
-
total: response?.total || 0,
|
186
|
-
page: response?.page || urlPage || 1,
|
187
|
-
perPage: response?.per_page || urlPerPage || 0,
|
188
|
-
});
|
189
|
-
|
190
|
-
const selectionToolbar = (
|
191
|
-
<ToolbarItem key="selectAll">
|
192
|
-
<SelectAllCheckbox
|
193
|
-
{...{
|
194
|
-
selectAll,
|
195
|
-
selectPage,
|
196
|
-
selectNone,
|
197
|
-
selectedCount,
|
198
|
-
pageRowCount,
|
199
|
-
}}
|
200
|
-
totalCount={response?.total}
|
201
|
-
areAllRowsOnPageSelected={areAllRowsOnPageSelected()}
|
202
|
-
areAllRowsSelected={areAllRowsSelected()}
|
203
|
-
/>
|
204
|
-
</ToolbarItem>
|
205
|
-
);
|
206
|
-
|
207
234
|
const controller = 'hosts';
|
208
235
|
const memoDefaultSearchProps = useMemo(
|
209
236
|
() => getControllerSearchProps(controller),
|
@@ -213,70 +240,40 @@ const JobInvocationHostTable = ({
|
|
213
240
|
`/${controller}/auto_complete_search`
|
214
241
|
);
|
215
242
|
|
216
|
-
const wrapSetSelectedFilter = newFilter => {
|
217
|
-
setSelectedFilter(newFilter);
|
218
|
-
onFilterUpdate(newFilter);
|
219
|
-
|
220
|
-
const filterSearch = constructFilter(newFilter, urlSearchQuery);
|
221
|
-
|
222
|
-
const newParams = {
|
223
|
-
...defaultParams,
|
224
|
-
page: 1,
|
225
|
-
};
|
226
|
-
|
227
|
-
if (filterSearch !== '') {
|
228
|
-
newParams.search = filterSearch;
|
229
|
-
}
|
230
|
-
|
231
|
-
setAPIOptions(prev => ({ ...prev, params: newParams }));
|
232
|
-
|
233
|
-
const urlSearchParams = new URLSearchParams(window.location.search);
|
234
|
-
urlSearchParams.set('page', '1');
|
235
|
-
history.push({ search: urlSearchParams.toString() });
|
236
|
-
};
|
237
|
-
|
238
|
-
const wrapSetAPIOptions = newAPIOptions => {
|
239
|
-
const newParams = newAPIOptions?.params ?? newAPIOptions ?? {};
|
240
|
-
|
241
|
-
const filterSearch = constructFilter(
|
242
|
-
selectedFilter,
|
243
|
-
newParams.search ?? urlSearchQuery
|
244
|
-
);
|
245
|
-
|
246
|
-
const mergedParams = {
|
247
|
-
...defaultParams,
|
248
|
-
...newParams,
|
249
|
-
};
|
250
|
-
|
251
|
-
if (filterSearch !== '') {
|
252
|
-
mergedParams.search = filterSearch;
|
253
|
-
} else if ('search' in mergedParams) {
|
254
|
-
delete mergedParams.search;
|
255
|
-
}
|
256
|
-
|
257
|
-
setAPIOptions(prev => ({ ...prev, params: mergedParams }));
|
258
|
-
|
259
|
-
const { search: _search, ...paramsForUrl } = mergedParams;
|
260
|
-
const uri = new URI();
|
261
|
-
uri.setSearch(paramsForUrl);
|
262
|
-
history.push({ search: uri.search() });
|
263
|
-
};
|
264
|
-
|
265
243
|
const combinedResponse = {
|
266
244
|
response: {
|
267
245
|
search: urlSearchQuery,
|
268
246
|
can_create: false,
|
269
|
-
results:
|
270
|
-
total:
|
247
|
+
results: apiResponse?.results || [],
|
248
|
+
total: apiResponse?.total || 0,
|
271
249
|
per_page: defaultParams?.perPage,
|
272
250
|
page: defaultParams?.page,
|
273
|
-
subtotal:
|
274
|
-
message:
|
251
|
+
subtotal: apiResponse?.subtotal || 0,
|
252
|
+
message: apiResponse?.message || 'error',
|
275
253
|
},
|
276
254
|
status,
|
277
|
-
setAPIOptions:
|
255
|
+
setAPIOptions: filterApiCall,
|
278
256
|
};
|
279
257
|
|
258
|
+
const results = apiResponse.results ?? [];
|
259
|
+
|
260
|
+
const selectionToolbar = (
|
261
|
+
<ToolbarItem key="selectAll">
|
262
|
+
<SelectAllCheckbox
|
263
|
+
{...{
|
264
|
+
selectAll,
|
265
|
+
selectPage,
|
266
|
+
selectNone,
|
267
|
+
selectedCount,
|
268
|
+
pageRowCount,
|
269
|
+
}}
|
270
|
+
totalCount={apiResponse?.total}
|
271
|
+
areAllRowsOnPageSelected={areAllRowsOnPageSelected()}
|
272
|
+
areAllRowsSelected={areAllRowsSelected()}
|
273
|
+
/>
|
274
|
+
</ToolbarItem>
|
275
|
+
);
|
276
|
+
|
280
277
|
const customEmptyState = (
|
281
278
|
<Tr ouiaId="table-empty">
|
282
279
|
<Td colSpan={100}>
|
@@ -314,22 +311,11 @@ const JobInvocationHostTable = ({
|
|
314
311
|
</Tr>
|
315
312
|
);
|
316
313
|
|
317
|
-
const { results = [] } = response;
|
318
|
-
|
319
|
-
const isHostExpanded = host => expandedHost.includes(host);
|
320
|
-
const setHostExpanded = (host, isExpanding = true) =>
|
321
|
-
setExpandedHost(prevExpanded => {
|
322
|
-
const otherExpandedHosts = prevExpanded.filter(h => h !== host);
|
323
|
-
return isExpanding ? [...otherExpandedHosts, host] : otherExpandedHosts;
|
324
|
-
});
|
325
|
-
const [showAlert, setShowAlert] = useState(false);
|
326
|
-
|
327
314
|
return (
|
328
315
|
<>
|
329
316
|
{showAlert && <PopupAlert setShowAlert={setShowAlert} />}
|
330
317
|
<TableIndexPage
|
331
318
|
apiUrl=""
|
332
|
-
apiOptions={apiOptions}
|
333
319
|
customSearchProps={memoDefaultSearchProps}
|
334
320
|
controller="hosts"
|
335
321
|
creatable={false}
|
@@ -338,16 +324,17 @@ const JobInvocationHostTable = ({
|
|
338
324
|
customToolbarItems={[
|
339
325
|
<DropdownFilter
|
340
326
|
key="dropdown-filter"
|
341
|
-
dropdownFilter={
|
342
|
-
setDropdownFilter={
|
327
|
+
dropdownFilter={initialFilter}
|
328
|
+
setDropdownFilter={handleFilterChange}
|
343
329
|
/>,
|
344
330
|
<CheckboxesActions
|
345
331
|
bulkParams={selectedCount > 0 ? fetchBulkParams() : null}
|
346
332
|
selectedIds={selectedIds}
|
347
|
-
|
333
|
+
allJobs={results}
|
348
334
|
jobID={id}
|
349
335
|
key="checkboxes-actions"
|
350
|
-
filter={
|
336
|
+
filter={initialFilter}
|
337
|
+
setShowAlert={setShowAlert}
|
351
338
|
/>,
|
352
339
|
]}
|
353
340
|
selectionToolbar={selectionToolbar}
|
@@ -361,21 +348,21 @@ const JobInvocationHostTable = ({
|
|
361
348
|
: null
|
362
349
|
}
|
363
350
|
params={{
|
364
|
-
page:
|
365
|
-
per_page:
|
351
|
+
page: defaultParams.page || Number(urlPage),
|
352
|
+
per_page: defaultParams.per_page || Number(urlPerPage),
|
366
353
|
order: urlOrder,
|
367
354
|
}}
|
368
|
-
page={
|
369
|
-
perPage={
|
370
|
-
setParams={
|
371
|
-
itemCount={
|
355
|
+
page={defaultParams.page || Number(urlPage)}
|
356
|
+
perPage={defaultParams.per_page || Number(urlPerPage)}
|
357
|
+
setParams={filterApiCall}
|
358
|
+
itemCount={apiResponse?.subtotal}
|
372
359
|
results={results}
|
373
360
|
url=""
|
374
361
|
showCheckboxes
|
375
362
|
refreshData={() => {}}
|
376
363
|
errorMessage={
|
377
|
-
status === STATUS_UPPERCASE.ERROR &&
|
378
|
-
?
|
364
|
+
status === STATUS_UPPERCASE.ERROR && apiResponse?.message
|
365
|
+
? apiResponse.message
|
379
366
|
: null
|
380
367
|
}
|
381
368
|
isPending={status === STATUS_UPPERCASE.PENDING}
|
@@ -439,7 +426,6 @@ const JobInvocationHostTable = ({
|
|
439
426
|
JobInvocationHostTable.propTypes = {
|
440
427
|
id: PropTypes.string.isRequired,
|
441
428
|
targeting: PropTypes.object.isRequired,
|
442
|
-
failedCount: PropTypes.number.isRequired,
|
443
429
|
initialFilter: PropTypes.string.isRequired,
|
444
430
|
statusLabel: PropTypes.string,
|
445
431
|
onFilterUpdate: PropTypes.func,
|
@@ -7,10 +7,6 @@ import {
|
|
7
7
|
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
|
8
8
|
import PropTypes from 'prop-types';
|
9
9
|
import React from 'react';
|
10
|
-
import {
|
11
|
-
templateInvocationPageUrl,
|
12
|
-
MAX_HOSTS_API_SIZE,
|
13
|
-
} from './JobInvocationConstants';
|
14
10
|
|
15
11
|
export const PopupAlert = ({ setShowAlert }) => (
|
16
12
|
<Alert
|
@@ -25,21 +21,35 @@ export const PopupAlert = ({ setShowAlert }) => (
|
|
25
21
|
|
26
22
|
const OpenAllInvocationsModal = ({
|
27
23
|
failedCount,
|
28
|
-
failedHosts,
|
29
24
|
isOpen,
|
30
25
|
isOpenFailed,
|
31
|
-
jobID,
|
32
26
|
onClose,
|
33
|
-
setShowAlert,
|
34
27
|
selectedIds,
|
28
|
+
confirmCallback,
|
35
29
|
}) => {
|
36
|
-
const modalText =
|
30
|
+
const modalText = () => {
|
31
|
+
if (isOpenFailed) return 'failed';
|
32
|
+
if (selectedIds.length > 0) return 'selected';
|
33
|
+
return 'current page';
|
34
|
+
};
|
37
35
|
|
38
|
-
const
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
const selectedText = () => {
|
37
|
+
if (isOpenFailed) {
|
38
|
+
return (
|
39
|
+
<>
|
40
|
+
{__('The number of failed invocations is:')} <b>{failedCount}</b>
|
41
|
+
</>
|
42
|
+
);
|
42
43
|
}
|
44
|
+
if (selectedIds.length > 0) {
|
45
|
+
return (
|
46
|
+
<>
|
47
|
+
{__('The number of selected invocations is:')}{' '}
|
48
|
+
<b>{selectedIds.length}</b>
|
49
|
+
</>
|
50
|
+
);
|
51
|
+
}
|
52
|
+
return <></>;
|
43
53
|
};
|
44
54
|
|
45
55
|
return (
|
@@ -48,7 +58,7 @@ const OpenAllInvocationsModal = ({
|
|
48
58
|
isOpen={isOpen}
|
49
59
|
onClose={onClose}
|
50
60
|
ouiaId="template-invocation-new-tab-modal"
|
51
|
-
title={sprintf(__('Open all %s invocations in new tabs'), modalText)}
|
61
|
+
title={sprintf(__('Open all %s invocations in new tabs'), modalText())}
|
52
62
|
titleIconVariant="warning"
|
53
63
|
width={590}
|
54
64
|
actions={[
|
@@ -57,16 +67,7 @@ const OpenAllInvocationsModal = ({
|
|
57
67
|
key="confirm"
|
58
68
|
variant="primary"
|
59
69
|
onClick={() => {
|
60
|
-
|
61
|
-
? failedHosts
|
62
|
-
: selectedIds.map(id => ({ id }));
|
63
|
-
|
64
|
-
hostsToOpen
|
65
|
-
.slice(0, MAX_HOSTS_API_SIZE)
|
66
|
-
.forEach(({ id }) =>
|
67
|
-
openLink(templateInvocationPageUrl(id, jobID), '_blank')
|
68
|
-
);
|
69
|
-
|
70
|
+
confirmCallback();
|
70
71
|
onClose();
|
71
72
|
}}
|
72
73
|
>
|
@@ -84,30 +85,24 @@ const OpenAllInvocationsModal = ({
|
|
84
85
|
>
|
85
86
|
{sprintf(
|
86
87
|
__('Are you sure you want to open all %s invocations in new tabs?'),
|
87
|
-
modalText
|
88
|
+
modalText()
|
88
89
|
)}
|
89
90
|
<br />
|
90
|
-
{
|
91
|
-
<br />
|
92
|
-
{sprintf(__('The number of %s invocations is:'), modalText)}{' '}
|
93
|
-
<b>{isOpenFailed ? failedCount : selectedIds.length}</b>
|
91
|
+
{selectedText()}
|
94
92
|
</Modal>
|
95
93
|
);
|
96
94
|
};
|
97
95
|
|
98
96
|
OpenAllInvocationsModal.propTypes = {
|
99
97
|
failedCount: PropTypes.number.isRequired,
|
100
|
-
failedHosts: PropTypes.array,
|
101
98
|
isOpen: PropTypes.bool.isRequired,
|
102
99
|
isOpenFailed: PropTypes.bool,
|
103
|
-
jobID: PropTypes.string.isRequired,
|
104
100
|
onClose: PropTypes.func.isRequired,
|
105
|
-
setShowAlert: PropTypes.func.isRequired,
|
106
101
|
selectedIds: PropTypes.array.isRequired,
|
102
|
+
confirmCallback: PropTypes.func.isRequired,
|
107
103
|
};
|
108
104
|
|
109
105
|
OpenAllInvocationsModal.defaultProps = {
|
110
|
-
failedHosts: [],
|
111
106
|
isOpenFailed: false,
|
112
107
|
};
|
113
108
|
|
@@ -83,6 +83,7 @@ jest.mock('../JobInvocationHostTable.js', () => () => (
|
|
83
83
|
const reportTemplateJobId = mockReportTemplatesResponse.results[0].id;
|
84
84
|
|
85
85
|
const mockStore = configureMockStore([thunk]);
|
86
|
+
const props = { history: { push: jest.fn() } };
|
86
87
|
|
87
88
|
describe('JobInvocationDetailPage', () => {
|
88
89
|
it('renders main information', async () => {
|
@@ -91,7 +92,10 @@ describe('JobInvocationDetailPage', () => {
|
|
91
92
|
|
92
93
|
const { container } = render(
|
93
94
|
<Provider store={store}>
|
94
|
-
<JobInvocationDetailPage
|
95
|
+
<JobInvocationDetailPage
|
96
|
+
match={{ params: { id: `${jobId}` } }}
|
97
|
+
{...props}
|
98
|
+
/>
|
95
99
|
</Provider>
|
96
100
|
);
|
97
101
|
|
@@ -185,6 +189,7 @@ describe('JobInvocationDetailPage', () => {
|
|
185
189
|
<Provider store={store}>
|
186
190
|
<JobInvocationDetailPage
|
187
191
|
match={{ params: { id: `${jobInvocationDataScheduled.id}` } }}
|
192
|
+
{...props}
|
188
193
|
/>
|
189
194
|
</Provider>
|
190
195
|
);
|
@@ -201,7 +206,10 @@ describe('JobInvocationDetailPage', () => {
|
|
201
206
|
const store = mockStore(jobInvocationDataRecurring);
|
202
207
|
render(
|
203
208
|
<Provider store={store}>
|
204
|
-
<JobInvocationDetailPage
|
209
|
+
<JobInvocationDetailPage
|
210
|
+
match={{ params: { id: `${jobId}` } }}
|
211
|
+
{...props}
|
212
|
+
/>
|
205
213
|
</Provider>
|
206
214
|
);
|
207
215
|
|