foreman_remote_execution 9.0.1 → 10.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +14 -0
- data/.tx/config +3 -1
- data/app/assets/javascripts/foreman_remote_execution/locale/de/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/en/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/template_invocation.js +10 -1
- data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
- data/app/controllers/job_invocations_controller.rb +30 -1
- data/app/controllers/ui_job_wizard_controller.rb +3 -1
- data/app/helpers/remote_execution_helper.rb +1 -1
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +28 -2
- data/app/models/remote_execution_feature.rb +11 -8
- data/app/views/api/v2/job_invocations/base.json.rabl +1 -1
- data/app/views/job_invocations/_form.html.erb +1 -1
- data/app/views/job_invocations/show.html.erb +1 -1
- data/app/views/job_invocations/welcome.html.erb +1 -1
- data/app/views/templates/script/run_command.erb +1 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20210816100932_rex_setting_category_to_dsl.rb +1 -1
- data/db/migrate/20220426145007_add_unique_feature_label_index.rb +14 -0
- data/lib/foreman_remote_execution/engine.rb +9 -9
- data/lib/foreman_remote_execution/tasks/explain_proxy_selection.rake +12 -3
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/Makefile +6 -3
- data/locale/action_names.rb +1 -1
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +67 -19
- data/locale/en/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en/foreman_remote_execution.po +56 -8
- data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en_GB/foreman_remote_execution.po +58 -10
- data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/es/foreman_remote_execution.po +71 -23
- data/locale/foreman_remote_execution.pot +216 -141
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +104 -56
- data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ja/foreman_remote_execution.po +72 -24
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +63 -15
- data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/pt_BR/foreman_remote_execution.po +68 -20
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +63 -15
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +71 -23
- data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_TW/foreman_remote_execution.po +63 -15
- data/package.json +6 -6
- data/webpack/JobWizard/Footer.js +104 -0
- data/webpack/JobWizard/JobWizard.js +105 -32
- data/webpack/JobWizard/JobWizard.scss +6 -17
- data/webpack/JobWizard/JobWizardConstants.js +1 -1
- data/webpack/JobWizard/JobWizardPageRerun.js +2 -0
- data/webpack/JobWizard/StartsBeforeErrorAlert.js +17 -0
- data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +8 -0
- data/webpack/JobWizard/__tests__/fixtures.js +13 -1
- data/webpack/JobWizard/__tests__/integration.test.js +15 -0
- data/webpack/JobWizard/__tests__/validation.test.js +36 -0
- data/webpack/JobWizard/autofill.js +1 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +29 -10
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +8 -0
- data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +5 -0
- data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +28 -14
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +41 -4
- data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +16 -10
- data/webpack/JobWizard/steps/HostsAndInputs/index.js +51 -3
- data/webpack/JobWizard/steps/ReviewDetails/ReviewDetails.test.js +117 -0
- data/webpack/JobWizard/steps/ReviewDetails/helpers.js +43 -0
- data/webpack/JobWizard/steps/ReviewDetails/index.js +169 -18
- data/webpack/JobWizard/steps/Schedule/QueryType.js +1 -1
- data/webpack/JobWizard/steps/Schedule/RepeatHour.js +0 -1
- data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +1 -1
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +52 -18
- data/webpack/JobWizard/steps/form/DateTimePicker.js +1 -2
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +0 -1
- data/webpack/JobWizard/steps/form/SelectField.js +0 -1
- data/webpack/JobWizard/submit.js +14 -3
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +4 -4
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +2 -2
- data/webpack/react_app/components/RecentJobsCard/constants.js +2 -2
- data/webpack/react_app/components/RegistrationExtension/RexPull.js +0 -2
- metadata +23 -6
@@ -1,3 +1,4 @@
|
|
1
|
+
/* eslint-disable max-lines */
|
1
2
|
import React, { useEffect, useState } from 'react';
|
2
3
|
import {
|
3
4
|
Button,
|
@@ -5,12 +6,12 @@ import {
|
|
5
6
|
DescriptionListTerm,
|
6
7
|
DescriptionListGroup,
|
7
8
|
DescriptionListDescription,
|
9
|
+
WizardContextConsumer,
|
8
10
|
} from '@patternfly/react-core';
|
9
11
|
import PropTypes from 'prop-types';
|
10
12
|
import { useDispatch, useSelector } from 'react-redux';
|
11
|
-
import EllipsisWithTooltip from 'react-ellipsis-with-tooltip';
|
12
13
|
import { get } from 'foremanReact/redux/API';
|
13
|
-
import { translate as __
|
14
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
14
15
|
import {
|
15
16
|
selectJobTemplates,
|
16
17
|
selectHosts,
|
@@ -22,9 +23,12 @@ import {
|
|
22
23
|
HOSTS_API,
|
23
24
|
HOSTS_TO_PREVIEW_AMOUNT,
|
24
25
|
WIZARD_TITLES,
|
26
|
+
SCHEDULE_TYPES,
|
25
27
|
} from '../../JobWizardConstants';
|
26
28
|
import { buildHostQuery } from '../HostsAndInputs/buildHostQuery';
|
27
29
|
import { WizardTitle } from '../form/WizardTitle';
|
30
|
+
import { parseEnd, parseRepeat } from './helpers';
|
31
|
+
import { HostPreviewModal } from '../HostsAndInputs/HostPreviewModal';
|
28
32
|
|
29
33
|
const ReviewDetails = ({
|
30
34
|
jobCategory,
|
@@ -34,7 +38,20 @@ const ReviewDetails = ({
|
|
34
38
|
templateValues,
|
35
39
|
selectedTargets,
|
36
40
|
hostsSearchQuery,
|
41
|
+
goToStepByName,
|
37
42
|
}) => {
|
43
|
+
// eslint-disable-next-line react/prop-types
|
44
|
+
const StepButton = ({ stepName, children }) => (
|
45
|
+
<Button
|
46
|
+
variant="link"
|
47
|
+
isInline
|
48
|
+
onClick={() => {
|
49
|
+
goToStepByName(stepName);
|
50
|
+
}}
|
51
|
+
>
|
52
|
+
{children}
|
53
|
+
</Button>
|
54
|
+
);
|
38
55
|
const dispatch = useDispatch();
|
39
56
|
useEffect(() => {
|
40
57
|
dispatch(
|
@@ -58,6 +75,7 @@ const ReviewDetails = ({
|
|
58
75
|
const hosts = useSelector(selectHosts);
|
59
76
|
|
60
77
|
const hostsCount = useSelector(selectHostCount);
|
78
|
+
const [hostPreviewOpen, setHostPreviewOpen] = useState(false);
|
61
79
|
const stringHosts = () => {
|
62
80
|
if (hosts.length === 0) {
|
63
81
|
return __('No Target Hosts');
|
@@ -65,22 +83,57 @@ const ReviewDetails = ({
|
|
65
83
|
if (hosts.length === 1 || hosts.length === 2) {
|
66
84
|
return hosts.join(', ');
|
67
85
|
}
|
68
|
-
return
|
69
|
-
|
70
|
-
|
71
|
-
|
86
|
+
return (
|
87
|
+
<div>
|
88
|
+
{hostsCount} {__('hosts')}{' '}
|
89
|
+
<Button
|
90
|
+
variant="link"
|
91
|
+
isInline
|
92
|
+
onClick={() => setHostPreviewOpen(true)}
|
93
|
+
>
|
94
|
+
{__('view host names')}
|
95
|
+
</Button>
|
96
|
+
</div>
|
97
|
+
);
|
72
98
|
};
|
73
99
|
const [isAdvancedShown, setIsAdvancedShown] = useState(false);
|
74
100
|
const detailsFirstHalf = [
|
75
|
-
{
|
76
|
-
|
77
|
-
|
101
|
+
{
|
102
|
+
label: (
|
103
|
+
<StepButton stepName={WIZARD_TITLES.categoryAndTemplate}>
|
104
|
+
{__('Job category')}
|
105
|
+
</StepButton>
|
106
|
+
),
|
107
|
+
value: jobCategory,
|
108
|
+
},
|
109
|
+
{
|
110
|
+
label: (
|
111
|
+
<StepButton stepName={WIZARD_TITLES.categoryAndTemplate}>
|
112
|
+
{__('Job template')}
|
113
|
+
</StepButton>
|
114
|
+
),
|
115
|
+
value: jobTemplate,
|
116
|
+
},
|
117
|
+
{
|
118
|
+
label: (
|
119
|
+
<StepButton stepName={WIZARD_TITLES.hostsAndInputs}>
|
120
|
+
{__('Target hosts')}
|
121
|
+
</StepButton>
|
122
|
+
),
|
123
|
+
value: stringHosts(),
|
124
|
+
},
|
78
125
|
...templateInputs.map(({ name }) => ({
|
79
|
-
label:
|
126
|
+
label: (
|
127
|
+
<StepButton stepName={WIZARD_TITLES.hostsAndInputs}>{name}</StepButton>
|
128
|
+
),
|
80
129
|
value: templateValues[name],
|
81
130
|
})),
|
82
131
|
{
|
83
|
-
label:
|
132
|
+
label: (
|
133
|
+
<StepButton stepName={WIZARD_TITLES.advanced}>
|
134
|
+
{__('Advanced fields')}
|
135
|
+
</StepButton>
|
136
|
+
),
|
84
137
|
value: isAdvancedShown ? (
|
85
138
|
<Button
|
86
139
|
variant="link"
|
@@ -107,15 +160,98 @@ const ReviewDetails = ({
|
|
107
160
|
|
108
161
|
const detailsSecondHalf = [
|
109
162
|
{
|
110
|
-
label:
|
163
|
+
label: (
|
164
|
+
<StepButton stepName={WIZARD_TITLES.typeOfExecution}>
|
165
|
+
{__('Schedule type')}
|
166
|
+
</StepButton>
|
167
|
+
),
|
111
168
|
value: scheduleValue.scheduleType,
|
112
169
|
},
|
113
170
|
{
|
114
|
-
label:
|
171
|
+
label: (
|
172
|
+
<StepButton
|
173
|
+
stepName={
|
174
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING
|
175
|
+
? SCHEDULE_TYPES.RECURRING
|
176
|
+
: WIZARD_TITLES.typeOfExecution
|
177
|
+
}
|
178
|
+
>
|
179
|
+
{__('Recurrence')}
|
180
|
+
</StepButton>
|
181
|
+
),
|
115
182
|
value: scheduleValue.repeatType,
|
116
183
|
},
|
184
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE &&
|
185
|
+
scheduleValue.startsAt && {
|
186
|
+
label: (
|
187
|
+
<StepButton
|
188
|
+
stepName={
|
189
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING
|
190
|
+
? SCHEDULE_TYPES.RECURRING
|
191
|
+
: SCHEDULE_TYPES.FUTURE
|
192
|
+
}
|
193
|
+
>
|
194
|
+
{__('Starts at')}
|
195
|
+
</StepButton>
|
196
|
+
),
|
197
|
+
value: new Date(scheduleValue.startsAt).toString(),
|
198
|
+
},
|
199
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE &&
|
200
|
+
scheduleValue.startsBefore && {
|
201
|
+
label: (
|
202
|
+
<StepButton stepName={SCHEDULE_TYPES.FUTURE}>
|
203
|
+
{__('Starts Before')}
|
204
|
+
</StepButton>
|
205
|
+
),
|
206
|
+
value: new Date(scheduleValue.startsBefore).toString(),
|
207
|
+
},
|
208
|
+
|
209
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING && {
|
210
|
+
label: (
|
211
|
+
<StepButton stepName={SCHEDULE_TYPES.RECURRING}>
|
212
|
+
{__('Starts')}
|
213
|
+
</StepButton>
|
214
|
+
),
|
215
|
+
value: scheduleValue.isFuture
|
216
|
+
? new Date(scheduleValue.startsAt).toString()
|
217
|
+
: __('Now'),
|
218
|
+
},
|
219
|
+
|
220
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING && {
|
221
|
+
label: (
|
222
|
+
<StepButton stepName={SCHEDULE_TYPES.RECURRING}>
|
223
|
+
{__('Repeats')}
|
224
|
+
</StepButton>
|
225
|
+
),
|
226
|
+
value: parseRepeat(scheduleValue.repeatType, scheduleValue.repeatData),
|
227
|
+
},
|
228
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING && {
|
229
|
+
label: (
|
230
|
+
<StepButton stepName={SCHEDULE_TYPES.RECURRING}>
|
231
|
+
{__('Ends')}
|
232
|
+
</StepButton>
|
233
|
+
),
|
234
|
+
value: parseEnd(
|
235
|
+
scheduleValue.ends,
|
236
|
+
scheduleValue.isNeverEnds,
|
237
|
+
scheduleValue.repeatAmount
|
238
|
+
),
|
239
|
+
},
|
240
|
+
|
241
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING && {
|
242
|
+
label: (
|
243
|
+
<StepButton stepName={SCHEDULE_TYPES.RECURRING}>
|
244
|
+
{__('Purpose')}
|
245
|
+
</StepButton>
|
246
|
+
),
|
247
|
+
value: scheduleValue.purpose,
|
248
|
+
},
|
117
249
|
{
|
118
|
-
label:
|
250
|
+
label: (
|
251
|
+
<StepButton stepName={WIZARD_TITLES.typeOfExecution}>
|
252
|
+
{__('Type of query')}
|
253
|
+
</StepButton>
|
254
|
+
),
|
119
255
|
value: scheduleValue.isTypeStatic
|
120
256
|
? __('Static query')
|
121
257
|
: __('Dynamic query'),
|
@@ -144,6 +280,11 @@ const ReviewDetails = ({
|
|
144
280
|
|
145
281
|
return (
|
146
282
|
<>
|
283
|
+
<HostPreviewModal
|
284
|
+
isOpen={hostPreviewOpen}
|
285
|
+
setIsOpen={setHostPreviewOpen}
|
286
|
+
searchQuery={buildHostQuery(selectedTargets, hostsSearchQuery)}
|
287
|
+
/>
|
147
288
|
<WizardTitle
|
148
289
|
title={WIZARD_TITLES.review}
|
149
290
|
className="advanced-fields-title"
|
@@ -153,7 +294,7 @@ const ReviewDetails = ({
|
|
153
294
|
<DescriptionListGroup key={index}>
|
154
295
|
<DescriptionListTerm>{label}</DescriptionListTerm>
|
155
296
|
<DescriptionListDescription>
|
156
|
-
|
297
|
+
{value || ''}
|
157
298
|
</DescriptionListDescription>
|
158
299
|
</DescriptionListGroup>
|
159
300
|
))}
|
@@ -162,7 +303,7 @@ const ReviewDetails = ({
|
|
162
303
|
<DescriptionListGroup key={index} className="advanced-fields">
|
163
304
|
<DescriptionListTerm>{label}</DescriptionListTerm>
|
164
305
|
<DescriptionListDescription>
|
165
|
-
|
306
|
+
{value || ''}
|
166
307
|
</DescriptionListDescription>
|
167
308
|
</DescriptionListGroup>
|
168
309
|
))}
|
@@ -170,7 +311,7 @@ const ReviewDetails = ({
|
|
170
311
|
<DescriptionListGroup key={index}>
|
171
312
|
<DescriptionListTerm>{label}</DescriptionListTerm>
|
172
313
|
<DescriptionListDescription>
|
173
|
-
|
314
|
+
{value || ''}
|
174
315
|
</DescriptionListDescription>
|
175
316
|
</DescriptionListGroup>
|
176
317
|
))}
|
@@ -187,7 +328,17 @@ ReviewDetails.propTypes = {
|
|
187
328
|
templateValues: PropTypes.object.isRequired,
|
188
329
|
selectedTargets: PropTypes.object.isRequired,
|
189
330
|
hostsSearchQuery: PropTypes.string.isRequired,
|
331
|
+
goToStepByName: PropTypes.func.isRequired,
|
190
332
|
};
|
191
333
|
|
192
334
|
ReviewDetails.defaultProps = { jobTemplateID: null };
|
193
|
-
|
335
|
+
|
336
|
+
const WrappedReviewDetails = props => (
|
337
|
+
<WizardContextConsumer>
|
338
|
+
{({ goToStepByName }) => (
|
339
|
+
<ReviewDetails goToStepByName={goToStepByName} {...props} />
|
340
|
+
)}
|
341
|
+
</WizardContextConsumer>
|
342
|
+
);
|
343
|
+
|
344
|
+
export default WrappedReviewDetails;
|
@@ -31,7 +31,7 @@ export const QueryType = ({ isTypeStatic, setIsTypeStatic }) => (
|
|
31
31
|
id="query-type-dynamic"
|
32
32
|
label={__('Dynamic query')}
|
33
33
|
body={__(
|
34
|
-
"evaluates just before the execution is started, so if it's
|
34
|
+
"evaluates just before the execution is started, so if it's planned in future, targeted hosts set may change before it"
|
35
35
|
)}
|
36
36
|
/>
|
37
37
|
</FormGroup>
|
@@ -47,7 +47,6 @@ export const RepeatHour = ({ repeatData, setRepeatData }) => {
|
|
47
47
|
}}
|
48
48
|
isOpen={minuteOpen}
|
49
49
|
width={125}
|
50
|
-
menuAppendTo={() => document.querySelector('.pf-c-form.schedule-tab')}
|
51
50
|
toggleAriaLabel="select minute toggle"
|
52
51
|
validated={
|
53
52
|
isValidMinute(minute)
|
@@ -5,7 +5,7 @@ import { translate as __, documentLocale } from 'foremanReact/common/I18n';
|
|
5
5
|
import { RepeatDaily } from './RepeatDaily';
|
6
6
|
import { noop } from '../../../helpers';
|
7
7
|
|
8
|
-
const getWeekDays = () => {
|
8
|
+
export const getWeekDays = () => {
|
9
9
|
const locale = documentLocale().replace(/-/g, '_');
|
10
10
|
const baseDate = new Date(Date.UTC(2017, 0, 1)); // just a Sunday
|
11
11
|
const weekDays = [];
|
@@ -39,15 +39,35 @@ api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
|
39
39
|
handleSuccess({
|
40
40
|
data: { results: [jobTemplateResponse.job_template] },
|
41
41
|
});
|
42
|
+
} else if (action.key === 'HOST_IDS') {
|
43
|
+
handleSuccess &&
|
44
|
+
handleSuccess({
|
45
|
+
data: { results: [{ name: 'host1' }, { name: 'host3' }] },
|
46
|
+
});
|
42
47
|
}
|
43
48
|
return { type: 'get', ...action };
|
44
49
|
});
|
45
50
|
|
46
51
|
const mockStore = configureMockStore([]);
|
47
|
-
const store = mockStore({
|
52
|
+
const store = mockStore({
|
53
|
+
HOSTS_API: {
|
54
|
+
response: {
|
55
|
+
subtotal: 3,
|
56
|
+
},
|
57
|
+
},
|
58
|
+
});
|
48
59
|
jest.useFakeTimers();
|
49
60
|
|
50
61
|
describe('Schedule', () => {
|
62
|
+
beforeEach(() => {
|
63
|
+
jest.spyOn(selectors, 'selectRouterSearch');
|
64
|
+
selectors.selectRouterSearch.mockImplementation(() => ({
|
65
|
+
'host_ids[]': ['105', '37'],
|
66
|
+
}));
|
67
|
+
});
|
68
|
+
afterEach(() => {
|
69
|
+
selectors.selectRouterSearch.mockRestore();
|
70
|
+
});
|
51
71
|
it('sub steps appear', () => {
|
52
72
|
render(
|
53
73
|
<Provider store={store}>
|
@@ -85,7 +105,7 @@ describe('Schedule', () => {
|
|
85
105
|
});
|
86
106
|
act(() => {
|
87
107
|
fireEvent.click(screen.getByRole('button', { name: 'Future execution' }));
|
88
|
-
jest.
|
108
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
89
109
|
});
|
90
110
|
|
91
111
|
const newStartAtDate = '2030/03/12';
|
@@ -115,15 +135,15 @@ describe('Schedule', () => {
|
|
115
135
|
fireEvent.change(startsBeforeTimeField(), {
|
116
136
|
target: { value: newStartBeforeTime },
|
117
137
|
});
|
118
|
-
jest.
|
138
|
+
jest.advanceTimersByTime(1000);
|
119
139
|
});
|
120
140
|
|
121
141
|
act(() => {
|
122
|
-
fireEvent.click(screen.getByText('Category and
|
142
|
+
fireEvent.click(screen.getByText('Category and template'));
|
123
143
|
});
|
124
144
|
act(() => {
|
125
145
|
fireEvent.click(screen.getByRole('button', { name: 'Future execution' }));
|
126
|
-
jest.
|
146
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
127
147
|
});
|
128
148
|
expect(startsAtDateField().value).toBe(newStartAtDate);
|
129
149
|
expect(startsAtTimeField().value).toBe(newStartAtTime);
|
@@ -140,7 +160,7 @@ describe('Schedule', () => {
|
|
140
160
|
target: { value: '2030/03/11' },
|
141
161
|
});
|
142
162
|
await fireEvent.click(startsBeforeTimeField());
|
143
|
-
await jest.
|
163
|
+
await jest.advanceTimersByTime(1000);
|
144
164
|
});
|
145
165
|
expect(startsBeforeDateField().value).toBe('2030/03/11');
|
146
166
|
expect(
|
@@ -157,13 +177,13 @@ describe('Schedule', () => {
|
|
157
177
|
await fireEvent.change(startsAtDateField(), {
|
158
178
|
target: { value: '' },
|
159
179
|
});
|
160
|
-
jest.
|
180
|
+
jest.advanceTimersByTime(1000);
|
161
181
|
});
|
162
182
|
|
163
183
|
expect(startsBeforeDateField().value).toBe('2019/03/11');
|
164
184
|
expect(
|
165
185
|
screen.getAllByText("'Starts before' date must in the future")
|
166
|
-
).toHaveLength(
|
186
|
+
).toHaveLength(2);
|
167
187
|
});
|
168
188
|
|
169
189
|
it('Recurring execution - date pickers', async () => {
|
@@ -182,7 +202,7 @@ describe('Schedule', () => {
|
|
182
202
|
fireEvent.click(
|
183
203
|
screen.getByRole('button', { name: 'Recurring execution' })
|
184
204
|
);
|
185
|
-
jest.
|
205
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
186
206
|
});
|
187
207
|
|
188
208
|
const newStartAtDate = '2030/03/12';
|
@@ -207,7 +227,7 @@ describe('Schedule', () => {
|
|
207
227
|
fireEvent.change(startsAtTimeField(), {
|
208
228
|
target: { value: newStartAtTime },
|
209
229
|
});
|
210
|
-
jest.
|
230
|
+
jest.advanceTimersByTime(1000);
|
211
231
|
});
|
212
232
|
|
213
233
|
expect(endsAtDateField().disabled).toBeTruthy();
|
@@ -222,17 +242,17 @@ describe('Schedule', () => {
|
|
222
242
|
fireEvent.change(endsAtTimeField(), {
|
223
243
|
target: { value: newStartAtTime },
|
224
244
|
});
|
225
|
-
jest.
|
245
|
+
jest.advanceTimersByTime(1000);
|
226
246
|
});
|
227
247
|
|
228
248
|
act(() => {
|
229
|
-
fireEvent.click(screen.getByText('Category and
|
249
|
+
fireEvent.click(screen.getByText('Category and template'));
|
230
250
|
});
|
231
251
|
act(() => {
|
232
252
|
fireEvent.click(
|
233
253
|
screen.getByRole('button', { name: 'Recurring execution' })
|
234
254
|
);
|
235
|
-
jest.
|
255
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
236
256
|
});
|
237
257
|
expect(startsAtDateField().value).toBe(newStartAtDate);
|
238
258
|
expect(startsAtTimeField().value).toBe(newStartAtTime);
|
@@ -266,7 +286,7 @@ describe('Schedule', () => {
|
|
266
286
|
fireEvent.click(
|
267
287
|
screen.getByRole('button', { name: 'Recurring execution' })
|
268
288
|
);
|
269
|
-
jest.
|
289
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
270
290
|
});
|
271
291
|
await act(async () => {
|
272
292
|
fireEvent.click(screen.getByLabelText('Daily', { selector: 'button' }));
|
@@ -274,7 +294,7 @@ describe('Schedule', () => {
|
|
274
294
|
await act(async () => {
|
275
295
|
fireEvent.click(screen.getByText('Cronline'));
|
276
296
|
});
|
277
|
-
const newCronline = '1 2';
|
297
|
+
const newCronline = '1 2 3 4 5';
|
278
298
|
const cronline = screen.getByLabelText('cronline');
|
279
299
|
expect(cronline.value).toBe('');
|
280
300
|
await act(async () => {
|
@@ -286,19 +306,33 @@ describe('Schedule', () => {
|
|
286
306
|
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
287
307
|
|
288
308
|
await act(async () => {
|
289
|
-
fireEvent.click(screen.getByText('Category and
|
309
|
+
fireEvent.click(screen.getByText('Category and template'));
|
290
310
|
});
|
291
|
-
expect(screen.getAllByText('Category and
|
311
|
+
expect(screen.getAllByText('Category and template')).toHaveLength(3);
|
292
312
|
|
293
313
|
await act(async () => {
|
294
314
|
fireEvent.click(
|
295
315
|
screen.getByRole('button', { name: 'Recurring execution' })
|
296
316
|
);
|
297
|
-
jest.
|
317
|
+
jest.advanceTimersByTime(1000);
|
298
318
|
});
|
299
319
|
expect(screen.queryAllByText('Recurring execution')).toHaveLength(3);
|
300
320
|
expect(cronline.value).toBe(newCronline);
|
301
321
|
|
322
|
+
await act(async () => {
|
323
|
+
fireEvent.click(screen.getByText('Review details'));
|
324
|
+
});
|
325
|
+
expect(screen.queryAllByText('Review details')).toHaveLength(3);
|
326
|
+
expect(screen.getAllByText('Cron line - 1 2 3 4 5')).toHaveLength(1);
|
327
|
+
|
328
|
+
await act(async () => {
|
329
|
+
fireEvent.click(
|
330
|
+
screen.getByRole('button', { name: 'Recurring execution' })
|
331
|
+
);
|
332
|
+
jest.runAllTimers();
|
333
|
+
});
|
334
|
+
expect(screen.queryAllByText('Recurring execution')).toHaveLength(3);
|
335
|
+
|
302
336
|
fireEvent.click(screen.getByText('Cronline'));
|
303
337
|
await act(async () => {
|
304
338
|
fireEvent.click(screen.getByText('Monthly'));
|
@@ -59,7 +59,7 @@ export const DateTimePicker = ({
|
|
59
59
|
return false;
|
60
60
|
};
|
61
61
|
|
62
|
-
const onDateChange = newDate => {
|
62
|
+
const onDateChange = (e, newDate) => {
|
63
63
|
const parsedNewDate = new Date(newDate);
|
64
64
|
if (!newDate.length && allowEmpty) {
|
65
65
|
setDateTime('');
|
@@ -119,7 +119,6 @@ export const DateTimePicker = ({
|
|
119
119
|
is24Hour
|
120
120
|
isDisabled={isDisabled || formattedDate.length === 0}
|
121
121
|
invalidFormatErrorMessage={__('Invalid time format')}
|
122
|
-
menuAppendTo={() => document.body}
|
123
122
|
includeSeconds={includeSeconds}
|
124
123
|
/>
|
125
124
|
</>
|
@@ -43,7 +43,6 @@ export const SelectField = ({
|
|
43
43
|
isOpen={isOpen}
|
44
44
|
className="without_select2"
|
45
45
|
maxHeight="45vh"
|
46
|
-
menuAppendTo={() => document.body}
|
47
46
|
placeholderText=" " // To prevent showing first option as selected
|
48
47
|
aria-labelledby={fieldId}
|
49
48
|
toggleAriaLabel={`${label} toggle`}
|
data/webpack/JobWizard/submit.js
CHANGED
@@ -12,6 +12,8 @@ export const submit = ({
|
|
12
12
|
location,
|
13
13
|
organization,
|
14
14
|
feature,
|
15
|
+
provider,
|
16
|
+
advancedInputs,
|
15
17
|
dispatch,
|
16
18
|
}) => {
|
17
19
|
const {
|
@@ -37,6 +39,13 @@ export const submit = ({
|
|
37
39
|
keyPassphrase,
|
38
40
|
timeToPickup,
|
39
41
|
} = advancedValues;
|
42
|
+
const providerInputs = advancedInputs.filter(v => v.provider_input);
|
43
|
+
const providerValues = {};
|
44
|
+
providerInputs.forEach(({ name }) => {
|
45
|
+
providerValues[name] = advancedTemplateValues[name];
|
46
|
+
delete advancedTemplateValues[name];
|
47
|
+
});
|
48
|
+
|
40
49
|
const getCronLine = () => {
|
41
50
|
const [hour, minute] = repeatData.at
|
42
51
|
? repeatData.at.split(':')
|
@@ -104,14 +113,16 @@ export const submit = ({
|
|
104
113
|
concurrency_level: concurrencyLevel,
|
105
114
|
},
|
106
115
|
bookmark_id: null,
|
107
|
-
search_query:
|
108
|
-
buildHostQuery(selectedTargets, hostsSearchQuery) || 'name ~ *',
|
116
|
+
search_query: buildHostQuery(selectedTargets, hostsSearchQuery),
|
109
117
|
description_format: description,
|
110
118
|
execution_timeout_interval: timeoutToKill,
|
111
119
|
feature,
|
112
120
|
time_to_pickup: timeToPickup,
|
113
121
|
},
|
114
122
|
};
|
123
|
+
if (Object.keys(providerValues).length) {
|
124
|
+
api.job_invocation[provider] = providerValues;
|
125
|
+
}
|
115
126
|
|
116
127
|
dispatch(
|
117
128
|
post({
|
@@ -122,7 +133,7 @@ export const submit = ({
|
|
122
133
|
window.location.href = `/job_invocations/${id}`;
|
123
134
|
},
|
124
135
|
errorToast: ({ response }) =>
|
125
|
-
response?.
|
136
|
+
response?.data?.error?.message ||
|
126
137
|
response?.message ||
|
127
138
|
response?.statusText,
|
128
139
|
})
|
@@ -25,7 +25,7 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
25
25
|
header={__('Recent jobs')}
|
26
26
|
dropdownItems={[
|
27
27
|
<DropdownItem
|
28
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
28
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}`)}
|
29
29
|
key="link-to-all"
|
30
30
|
ouiaId="link-to-all-dropdown-item"
|
31
31
|
>
|
@@ -33,7 +33,7 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
33
33
|
</DropdownItem>,
|
34
34
|
<DropdownItem
|
35
35
|
href={foremanUrl(
|
36
|
-
`${JOB_BASE_URL}${
|
36
|
+
`${JOB_BASE_URL}${id}+and+status+%3D+failed+or+status%3D+succeeded`
|
37
37
|
)}
|
38
38
|
key="link-to-finished"
|
39
39
|
ouiaId="link-to-finished-dropdown-item"
|
@@ -41,14 +41,14 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
41
41
|
{__('View finished jobs')}
|
42
42
|
</DropdownItem>,
|
43
43
|
<DropdownItem
|
44
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
44
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}+and+status+%3D+running`)}
|
45
45
|
key="link-to-running"
|
46
46
|
ouiaId="link-to-running-dropdown-item"
|
47
47
|
>
|
48
48
|
{__('View running jobs')}
|
49
49
|
</DropdownItem>,
|
50
50
|
<DropdownItem
|
51
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
51
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}+and+status+%3D+queued`)}
|
52
52
|
key="link-to-scheduled"
|
53
53
|
ouiaId="link-to-scheduled-dropdown-item"
|
54
54
|
>
|
@@ -17,10 +17,10 @@ const RecentJobsTable = ({ status, hostId }) => {
|
|
17
17
|
const jobsUrl =
|
18
18
|
hostId &&
|
19
19
|
foremanUrl(
|
20
|
-
`${JOB_API_URL}${hostId}
|
20
|
+
`${JOB_API_URL}${hostId}&status=${status}&limit=${JOBS_IN_CARD}`
|
21
21
|
);
|
22
22
|
const {
|
23
|
-
response: {
|
23
|
+
response: { job_invocations: jobs },
|
24
24
|
status: responseStatus,
|
25
25
|
} = useAPI('get', jobsUrl, RECENT_JOBS_KEY);
|
26
26
|
|
@@ -6,8 +6,8 @@ export const SCHEDULED_TAB = 2;
|
|
6
6
|
export const JOB_SUCCESS_STATUS = 0;
|
7
7
|
export const JOB_ERROR_STATUS = 1;
|
8
8
|
|
9
|
-
export const JOB_BASE_URL = '/job_invocations?search=
|
9
|
+
export const JOB_BASE_URL = '/job_invocations?search=targeted_host_id+%3D+';
|
10
10
|
export const JOB_API_URL =
|
11
|
-
'/
|
11
|
+
'/job_invocations/preview_job_invocations_per_host?host_id=';
|
12
12
|
export const JOBS_IN_CARD = 3;
|
13
13
|
export const RECENT_JOBS_KEY = { key: 'RECENT_JOBS_KEY' };
|
@@ -26,7 +26,6 @@ const options = (value = '') => {
|
|
26
26
|
const RexPull = ({ isLoading, onChange, pluginValues, configParams }) => (
|
27
27
|
<FormGroup
|
28
28
|
label={__('REX pull mode')}
|
29
|
-
isRequired
|
30
29
|
labelIcon={
|
31
30
|
<LabelIcon
|
32
31
|
text={__(
|
@@ -44,7 +43,6 @@ const RexPull = ({ isLoading, onChange, pluginValues, configParams }) => (
|
|
44
43
|
className="without_select2"
|
45
44
|
id="registration_setup_remote_execution_pull"
|
46
45
|
isDisabled={isLoading}
|
47
|
-
isRequired
|
48
46
|
>
|
49
47
|
{/* eslint-disable-next-line camelcase */
|
50
48
|
options(configParams?.host_registration_remote_execution_pull)}
|