foreman_remote_execution 7.2.1 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby_ci.yml +2 -2
  3. data/app/controllers/ui_job_wizard_controller.rb +15 -0
  4. data/app/helpers/remote_execution_helper.rb +1 -1
  5. data/app/models/job_invocation.rb +2 -4
  6. data/app/models/job_invocation_composer.rb +5 -2
  7. data/app/views/templates/script/package_action.erb +8 -3
  8. data/config/routes.rb +3 -1
  9. data/lib/foreman_remote_execution/engine.rb +5 -5
  10. data/lib/foreman_remote_execution/version.rb +1 -1
  11. data/test/functional/api/v2/job_invocations_controller_test.rb +8 -0
  12. data/test/helpers/remote_execution_helper_test.rb +4 -0
  13. data/test/unit/job_invocation_report_template_test.rb +1 -1
  14. data/test/unit/job_invocation_test.rb +1 -2
  15. data/webpack/JobWizard/JobWizard.js +154 -20
  16. data/webpack/JobWizard/JobWizard.scss +43 -1
  17. data/webpack/JobWizard/JobWizardConstants.js +11 -1
  18. data/webpack/JobWizard/JobWizardPageRerun.js +112 -0
  19. data/webpack/JobWizard/__tests__/JobWizardPageRerun.test.js +79 -0
  20. data/webpack/JobWizard/__tests__/fixtures.js +73 -0
  21. data/webpack/JobWizard/__tests__/integration.test.js +17 -3
  22. data/webpack/JobWizard/autofill.js +8 -1
  23. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +36 -17
  24. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +3 -3
  25. data/webpack/JobWizard/steps/ReviewDetails/index.js +1 -3
  26. data/webpack/JobWizard/steps/Schedule/PurposeField.js +1 -3
  27. data/webpack/JobWizard/steps/Schedule/QueryType.js +33 -40
  28. data/webpack/JobWizard/steps/Schedule/RepeatHour.js +55 -16
  29. data/webpack/JobWizard/steps/Schedule/RepeatOn.js +19 -56
  30. data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +1 -1
  31. data/webpack/JobWizard/steps/Schedule/ScheduleFuture.js +126 -0
  32. data/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js +287 -0
  33. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +88 -20
  34. data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +206 -186
  35. data/webpack/JobWizard/steps/form/DateTimePicker.js +23 -6
  36. data/webpack/JobWizard/steps/form/Formatter.js +7 -8
  37. data/webpack/JobWizard/submit.js +8 -3
  38. data/webpack/Routes/routes.js +8 -2
  39. data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +1 -0
  40. data/webpack/react_app/components/HostKebab/KebabItems.js +0 -1
  41. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +0 -5
  42. data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +59 -51
  43. data/webpack/react_app/extend/Fills.js +4 -4
  44. metadata +8 -6
  45. data/webpack/JobWizard/steps/Schedule/StartEndDates.js +0 -106
  46. data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +0 -32
  47. data/webpack/JobWizard/steps/Schedule/index.js +0 -178
@@ -0,0 +1,112 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import URI from 'urijs';
4
+ import { Alert, Title, Divider, Skeleton } from '@patternfly/react-core';
5
+ import { sprintf, translate as __ } from 'foremanReact/common/I18n';
6
+ import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
7
+ import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
8
+ import { STATUS } from 'foremanReact/constants';
9
+ import {
10
+ useForemanOrganization,
11
+ useForemanLocation,
12
+ } from 'foremanReact/Root/Context/ForemanContext';
13
+ import { JobWizard } from './JobWizard';
14
+ import { JOB_API_KEY } from './JobWizardConstants';
15
+
16
+ const JobWizardPageRerun = ({
17
+ match: {
18
+ params: { id },
19
+ },
20
+ location: { search },
21
+ }) => {
22
+ const uri = new URI(search);
23
+ const { failed_only: failedOnly } = uri.search(true);
24
+ const { response, status } = useAPI(
25
+ 'get',
26
+ `/ui_job_wizard/job_invocation?id=${id}${
27
+ failedOnly ? '&failed_only=1' : ''
28
+ }`,
29
+ JOB_API_KEY
30
+ );
31
+ const title = __('Run job');
32
+ const breadcrumbOptions = {
33
+ breadcrumbItems: [
34
+ { caption: __('Jobs'), url: `/jobs` },
35
+ { caption: title },
36
+ ],
37
+ };
38
+
39
+ const jobOrganization = response.job_organization;
40
+ const jobLocation = response.job_location;
41
+ const currentOrganization = useForemanOrganization();
42
+ const currentLocation = useForemanLocation();
43
+
44
+ return (
45
+ <PageLayout
46
+ header={title}
47
+ breadcrumbOptions={breadcrumbOptions}
48
+ searchable={false}
49
+ >
50
+ <React.Fragment>
51
+ <Title headingLevel="h2" size="2xl">
52
+ {title}
53
+ </Title>
54
+ {!status || status === STATUS.PENDING ? (
55
+ <div style={{ height: '400px' }}>
56
+ <Skeleton
57
+ height="100%"
58
+ screenreaderText="Loading large rectangle contents"
59
+ />
60
+ </div>
61
+ ) : (
62
+ <React.Fragment>
63
+ {jobOrganization?.id !== currentOrganization?.id && (
64
+ <Alert
65
+ className="job-wizard-alert"
66
+ variant="warning"
67
+ title={sprintf(
68
+ __(
69
+ "Current organization %s is different from job's organization %s. This job may run on different hosts than before.",
70
+ currentOrganization,
71
+ jobOrganization
72
+ )
73
+ )}
74
+ />
75
+ )}
76
+ {jobLocation?.id !== currentLocation?.id && (
77
+ <Alert
78
+ className="job-wizard-alert"
79
+ variant="warning"
80
+ title={sprintf(
81
+ __(
82
+ "Current location %s is different from job's location %s. This job may run on different hosts than before.",
83
+ currentLocation,
84
+ jobLocation
85
+ )
86
+ )}
87
+ />
88
+ )}
89
+ <Divider component="div" />
90
+ <JobWizard
91
+ rerunData={{ ...response?.job, inputs: response?.inputs } || null}
92
+ />
93
+ </React.Fragment>
94
+ )}
95
+ </React.Fragment>
96
+ </PageLayout>
97
+ );
98
+ };
99
+ JobWizardPageRerun.propTypes = {
100
+ match: PropTypes.shape({
101
+ params: PropTypes.shape({
102
+ id: PropTypes.string.isRequired,
103
+ }),
104
+ }).isRequired,
105
+ location: PropTypes.shape({
106
+ search: PropTypes.string,
107
+ }),
108
+ };
109
+ JobWizardPageRerun.defaultProps = {
110
+ location: { search: '' },
111
+ };
112
+ export default JobWizardPageRerun;
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import { Provider } from 'react-redux';
3
+ import { render, fireEvent, screen, act } from '@testing-library/react';
4
+ import { MockedProvider } from '@apollo/client/testing';
5
+
6
+ import * as APIHooks from 'foremanReact/common/hooks/API/APIHooks';
7
+ import * as api from 'foremanReact/redux/API';
8
+ import JobWizardPageRerun from '../JobWizardPageRerun';
9
+ import * as selectors from '../JobWizardSelectors';
10
+ import { testSetup, mockApi, gqlMock, jobInvocation } from './fixtures';
11
+
12
+ const store = testSetup(selectors, api);
13
+ mockApi(api);
14
+ jest.spyOn(APIHooks, 'useAPI');
15
+ APIHooks.useAPI.mockImplementation((action, url) => {
16
+ if (url === '/ui_job_wizard/job_invocation?id=57') {
17
+ return { response: jobInvocation, status: 'RESOLVED' };
18
+ }
19
+ return {};
20
+ });
21
+
22
+ describe('Job wizard fill', () => {
23
+ it('fill defaults into fields', async () => {
24
+ render(
25
+ <MockedProvider mocks={gqlMock} addTypename={false}>
26
+ <Provider store={store}>
27
+ <JobWizardPageRerun
28
+ match={{
29
+ params: { id: '57' },
30
+ }}
31
+ />
32
+ </Provider>
33
+ </MockedProvider>
34
+ );
35
+ await act(async () => {
36
+ fireEvent.click(screen.getByText('Target hosts and inputs'));
37
+ });
38
+ await screen.findByLabelText('plain hidden', {
39
+ selector: 'textarea',
40
+ });
41
+
42
+ expect(
43
+ screen.getByLabelText('plain hidden', {
44
+ selector: 'textarea',
45
+ }).value
46
+ ).toBe('test command');
47
+
48
+ await act(async () => {
49
+ fireEvent.click(screen.getByText('Advanced fields'));
50
+ });
51
+
52
+ expect(
53
+ screen.getByLabelText('ssh user', {
54
+ selector: 'input',
55
+ }).value
56
+ ).toBe('ssh user');
57
+ expect(
58
+ screen.getByLabelText('effective user', {
59
+ selector: 'input',
60
+ }).value
61
+ ).toBe('Effective user');
62
+ expect(
63
+ screen.getByLabelText('timeout to kill', {
64
+ selector: 'input',
65
+ }).value
66
+ ).toBe('1');
67
+
68
+ expect(
69
+ screen.getByLabelText('Concurrency level', {
70
+ selector: 'input',
71
+ }).value
72
+ ).toBe('6');
73
+ expect(
74
+ screen.getByLabelText('Time span', {
75
+ selector: 'input',
76
+ }).value
77
+ ).toBe('4');
78
+ });
79
+ });
@@ -106,6 +106,7 @@ export const testSetup = (selectors, api) => {
106
106
  jest.spyOn(selectors, 'selectJobCategories');
107
107
  jest.spyOn(selectors, 'selectJobCategoriesStatus');
108
108
  jest.spyOn(selectors, 'selectWithKatello');
109
+ jest.spyOn(selectors, 'selectEffectiveUser');
109
110
 
110
111
  jest.spyOn(selectors, 'selectTemplateInputs');
111
112
  jest.spyOn(selectors, 'selectAdvancedTemplateInputs');
@@ -122,6 +123,10 @@ export const testSetup = (selectors, api) => {
122
123
  { ...jobTemplate, id: 2, name: 'template2' },
123
124
  ]);
124
125
  selectors.selectJobTemplate.mockImplementation(() => jobTemplateResponse);
126
+
127
+ selectors.selectEffectiveUser.mockImplementation(
128
+ () => jobTemplateResponse.effective_user
129
+ );
125
130
  const mockStore = configureMockStore([]);
126
131
  const store = mockStore({
127
132
  ForemanTasksTask: {
@@ -224,3 +229,71 @@ export const gqlMock = [
224
229
  },
225
230
  },
226
231
  ];
232
+
233
+ export const jobInvocation = {
234
+ job: {
235
+ job_category: 'Ansible Commands',
236
+ targeting: {
237
+ user_id: 4,
238
+ search_query: 'name ~ *',
239
+ bookmark_id: null,
240
+ targeting_type: 'static_query',
241
+ randomized_ordering: true,
242
+ },
243
+ triggering: {
244
+ mode: 'immediate',
245
+ start_at: null,
246
+ start_before: null,
247
+ },
248
+ ssh_user: 'ssh user',
249
+ description_format: null,
250
+ concurrency_control: {
251
+ level: 6,
252
+ time_span: 4,
253
+ },
254
+ execution_timeout_interval: 1,
255
+ remote_execution_feature_id: null,
256
+ template_invocations: [
257
+ {
258
+ template_id: 263,
259
+ effective_user: 'Effective user',
260
+ input_values: [
261
+ {
262
+ template_input_id: 162,
263
+ value: 'test command',
264
+ },
265
+ ],
266
+ },
267
+ ],
268
+ reruns: 57,
269
+ },
270
+ job_organization: {
271
+ id: 5,
272
+ name: 'ana-praley',
273
+ created_at: '2021-08-26T13:47:35.655+02:00',
274
+ updated_at: '2021-08-26T13:48:21.435+02:00',
275
+ ignore_types: [],
276
+ description: null,
277
+ label: 'ana-praley',
278
+ ancestry: null,
279
+ title: 'ana-praley',
280
+ manifest_refreshed_at: null,
281
+ created_in_katello: true,
282
+ },
283
+ job_location: {
284
+ id: 2,
285
+ name: 'Default Location',
286
+ created_at: '2021-08-24T15:32:18.830+02:00',
287
+ updated_at: '2021-08-24T15:32:18.830+02:00',
288
+ ignore_types: ['ProvisioningTemplate', 'Hostgroup'],
289
+ description: null,
290
+ label: null,
291
+ ancestry: null,
292
+ title: 'Default Location',
293
+ manifest_refreshed_at: null,
294
+ created_in_katello: false,
295
+ },
296
+ inputs: {
297
+ 'inputs[plain hidden]': 'test command',
298
+ },
299
+ };
@@ -45,7 +45,7 @@ describe('Job wizard fill', () => {
45
45
  </Provider>
46
46
  );
47
47
  expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-disabled')).toHaveLength(
48
- 4
48
+ 5
49
49
  );
50
50
  selectors.selectJobCategoriesStatus.mockImplementation(() => 'RESOLVED');
51
51
  expect(store.getActions()).toMatchSnapshot('initial');
@@ -79,8 +79,12 @@ describe('Job wizard fill', () => {
79
79
  </Provider>
80
80
  </MockedProvider>
81
81
  );
82
- const titles = Object.values(WIZARD_TITLES);
83
- const steps = [titles[1], titles[0], ...titles.slice(2)]; // the first title is selected at the beggining
82
+ const steps = [
83
+ WIZARD_TITLES.hostsAndInputs,
84
+ WIZARD_TITLES.categoryAndTemplate,
85
+ WIZARD_TITLES.advanced,
86
+ WIZARD_TITLES.review,
87
+ ];
84
88
  // eslint-disable-next-line no-unused-vars
85
89
  for await (const step of steps) {
86
90
  const stepSelector = screen.getByText(step);
@@ -92,5 +96,15 @@ describe('Job wizard fill', () => {
92
96
  const stepTitles = screen.getAllByText(step);
93
97
  expect(stepTitles).toHaveLength(3);
94
98
  }
99
+ const step = WIZARD_TITLES.typeOfExecution;
100
+ const stepTitle = screen.getAllByText(step);
101
+ expect(stepTitle).toHaveLength(1);
102
+ expect(screen.queryAllByText('Select the type of execution')).toHaveLength(
103
+ 0
104
+ );
105
+ await act(async () => {
106
+ await fireEvent.click(stepTitle[0]);
107
+ });
108
+ expect(screen.getAllByText('Select the type of execution')).toHaveLength(1);
95
109
  });
96
110
  });
@@ -11,6 +11,7 @@ export const useAutoFill = ({
11
11
  setHostsSearchQuery,
12
12
  setJobTemplateID,
13
13
  setTemplateValues,
14
+ setAdvancedValues,
14
15
  }) => {
15
16
  const dispatch = useDispatch();
16
17
 
@@ -49,15 +50,21 @@ export const useAutoFill = ({
49
50
  },
50
51
  })
51
52
  );
53
+ }
54
+ if (rest) {
52
55
  Object.keys(rest).forEach(key => {
53
56
  const re = /inputs\[(?<input>.*)\]/g;
54
57
  const input = re.exec(key)?.groups?.input;
55
58
  if (input) {
56
59
  setTemplateValues(prev => ({ ...prev, [input]: rest[key] }));
60
+ setAdvancedValues(prev => ({
61
+ ...prev,
62
+ templateValues: { ...prev.templateValues, [input]: rest[key] },
63
+ }));
57
64
  }
58
65
  });
59
66
  }
60
67
  }
61
68
  // eslint-disable-next-line react-hooks/exhaustive-deps
62
- }, []);
69
+ }, [fills]);
63
70
  };
@@ -17,14 +17,14 @@ import {
17
17
  } from '../../../__tests__/fixtures';
18
18
  import { WIZARD_TITLES } from '../../../JobWizardConstants';
19
19
 
20
+ const lodash = require('lodash');
21
+
22
+ lodash.debounce = fn => fn;
20
23
  const store = testSetup(selectors, api);
21
24
  mockApi(api);
22
25
 
23
- jest.spyOn(selectors, 'selectEffectiveUser');
26
+ jest.useFakeTimers();
24
27
 
25
- selectors.selectEffectiveUser.mockImplementation(
26
- () => jobTemplateResponse.effective_user
27
- );
28
28
  describe('AdvancedFields', () => {
29
29
  it('should save data between steps for advanced fields', async () => {
30
30
  const wrapper = mount(
@@ -49,6 +49,10 @@ describe('AdvancedFields', () => {
49
49
  .find('.pf-c-wizard__nav-link')
50
50
  .at(2)
51
51
  .simulate('click'); // Advanced step
52
+
53
+ await act(async () => {
54
+ jest.runAllTimers(); // to handle pf4 date picker popover
55
+ });
52
56
  const effectiveUserInput = () => wrapper.find('input#effective-user');
53
57
  const advancedTemplateInput = () =>
54
58
  wrapper.find('.pf-c-form__group-control textarea');
@@ -78,6 +82,9 @@ describe('AdvancedFields', () => {
78
82
  .at(2)
79
83
  .simulate('click'); // Advanced step
80
84
 
85
+ await act(async () => {
86
+ jest.runOnlyPendingTimers(); // to handle pf4 date picker popover
87
+ });
81
88
  expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
82
89
  expect(advancedTemplateInput().prop('value')).toEqual(
83
90
  advancedTemplateInputValue
@@ -93,10 +100,13 @@ describe('AdvancedFields', () => {
93
100
  );
94
101
  await act(async () => {
95
102
  fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
103
+ jest.runOnlyPendingTimers(); // to handle pf4 date picker popover
96
104
  });
105
+
97
106
  const searchValue = 'search test';
98
107
  const textValue = 'I am a text';
99
- const dateValue = '08/07/2021';
108
+ const dateValue = '2022/06/24';
109
+ const timeValue = '12:34:56';
100
110
  const textField = screen.getByLabelText('adv plain hidden', {
101
111
  selector: 'textarea',
102
112
  });
@@ -105,9 +115,8 @@ describe('AdvancedFields', () => {
105
115
  'adv resource select toggle'
106
116
  );
107
117
  const searchField = screen.getByPlaceholderText('Filter...');
108
- const dateField = screen.getByLabelText('adv date', {
109
- selector: 'input',
110
- });
118
+ const dateField = screen.getByLabelText('adv date datepicker');
119
+ const timeField = screen.getByLabelText('adv date timepicker');
111
120
 
112
121
  fireEvent.click(selectField);
113
122
  await act(async () => {
@@ -115,18 +124,20 @@ describe('AdvancedFields', () => {
115
124
  fireEvent.click(screen.getAllByText(WIZARD_TITLES.advanced)[0]); // to remove focus
116
125
 
117
126
  fireEvent.click(resourceSelectField);
118
- await fireEvent.click(screen.getByText('resource2'));
119
-
120
- await fireEvent.change(textField, {
127
+ fireEvent.click(screen.getByText('resource2'));
128
+ fireEvent.change(textField, {
121
129
  target: { value: textValue },
122
130
  });
123
-
124
- await fireEvent.change(searchField, {
131
+ fireEvent.change(searchField, {
125
132
  target: { value: searchValue },
126
133
  });
127
134
  await fireEvent.change(dateField, {
128
135
  target: { value: dateValue },
129
136
  });
137
+ fireEvent.change(timeField, {
138
+ target: { value: timeValue },
139
+ });
140
+ jest.runAllTimers(); // to handle pf4 date picker popover
130
141
  });
131
142
  expect(
132
143
  screen.getByLabelText('adv plain hidden', {
@@ -134,7 +145,8 @@ describe('AdvancedFields', () => {
134
145
  }).value
135
146
  ).toBe(textValue);
136
147
  expect(searchField.value).toBe(searchValue);
137
- expect(dateField.value).toBe(dateValue);
148
+ expect(screen.getByLabelText('adv date datepicker').value).toBe(dateValue);
149
+ expect(timeField.value).toBe(timeValue);
138
150
  await act(async () => {
139
151
  fireEvent.click(screen.getByText(WIZARD_TITLES.categoryAndTemplate));
140
152
  });
@@ -143,11 +155,13 @@ describe('AdvancedFields', () => {
143
155
  );
144
156
 
145
157
  await act(async () => {
146
- fireEvent.click(screen.getByText('Advanced fields'));
158
+ await fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
159
+ jest.runOnlyPendingTimers();
147
160
  });
148
161
  expect(textField.value).toBe(textValue);
149
162
  expect(searchField.value).toBe(searchValue);
150
163
  expect(dateField.value).toBe(dateValue);
164
+ expect(timeField.value).toBe(timeValue);
151
165
  expect(screen.queryAllByText('option 1')).toHaveLength(0);
152
166
  expect(screen.queryAllByText('option 2')).toHaveLength(1);
153
167
  expect(screen.queryAllByDisplayValue('resource1')).toHaveLength(0);
@@ -162,7 +176,8 @@ describe('AdvancedFields', () => {
162
176
  </MockedProvider>
163
177
  );
164
178
  await act(async () => {
165
- fireEvent.click(screen.getByText('Advanced fields'));
179
+ fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
180
+ jest.runAllTimers(); // to handle pf4 date picker popover
166
181
  });
167
182
 
168
183
  expect(
@@ -197,6 +212,7 @@ describe('AdvancedFields', () => {
197
212
  );
198
213
  await act(async () => {
199
214
  fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
215
+ jest.runAllTimers(); // to handle pf4 date picker popover
200
216
  });
201
217
 
202
218
  const textField = screen.getByLabelText('adv plain hidden', {
@@ -264,6 +280,7 @@ describe('AdvancedFields', () => {
264
280
  );
265
281
  await act(async () => {
266
282
  fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
283
+ jest.runAllTimers(); // to handle pf4 date picker popover
267
284
  });
268
285
  expect(
269
286
  screen.getByLabelText('description preview', {
@@ -332,6 +349,7 @@ describe('AdvancedFields', () => {
332
349
  );
333
350
  await act(async () => {
334
351
  fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
352
+ jest.runAllTimers(); // to handle pf4 date picker popover
335
353
  });
336
354
  expect(
337
355
  screen.getByLabelText('description preview', {
@@ -351,6 +369,7 @@ describe('AdvancedFields', () => {
351
369
  );
352
370
  await act(async () => {
353
371
  fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
372
+ jest.runAllTimers(); // to handle pf4 date picker popover
354
373
  });
355
374
  const resourceSelectField = screen.getByLabelText(
356
375
  'adv resource select typeahead input'
@@ -361,7 +380,7 @@ describe('AdvancedFields', () => {
361
380
  target: { value: 'some search' },
362
381
  });
363
382
 
364
- await jest.runAllTimers();
383
+ jest.runAllTimers();
365
384
  });
366
385
  expect(newStore.getActions()).toMatchSnapshot('resource search');
367
386
  });
@@ -25,7 +25,7 @@ const ConnectedCategoryAndTemplate = ({
25
25
  setJobTemplate,
26
26
  category,
27
27
  setCategory,
28
- isFeature,
28
+ isCategoryPreselected,
29
29
  }) => {
30
30
  const dispatch = useDispatch();
31
31
 
@@ -44,7 +44,7 @@ const ConnectedCategoryAndTemplate = ({
44
44
  default_template: defaultTemplate,
45
45
  },
46
46
  }) => {
47
- if (!isFeature) {
47
+ if (!isCategoryPreselected) {
48
48
  setCategory(defaultCategory || jobCategories[0] || '');
49
49
  if (defaultTemplate) setJobTemplate(defaultTemplate);
50
50
  }
@@ -106,7 +106,7 @@ ConnectedCategoryAndTemplate.propTypes = {
106
106
  setJobTemplate: PropTypes.func.isRequired,
107
107
  category: PropTypes.string.isRequired,
108
108
  setCategory: PropTypes.func.isRequired,
109
- isFeature: PropTypes.bool.isRequired,
109
+ isCategoryPreselected: PropTypes.bool.isRequired,
110
110
  };
111
111
  ConnectedCategoryAndTemplate.defaultProps = { jobTemplate: null };
112
112
 
@@ -108,9 +108,7 @@ const ReviewDetails = ({
108
108
  const detailsSecondHalf = [
109
109
  {
110
110
  label: __('Schedule type'),
111
- value: scheduleValue.isFuture
112
- ? __('Schedule for future execution')
113
- : __('Execute now'),
111
+ value: scheduleValue.scheduleType,
114
112
  },
115
113
  {
116
114
  label: __('Recurrence'),
@@ -4,7 +4,7 @@ import { TextInput, FormGroup } from '@patternfly/react-core';
4
4
  import { translate as __ } from 'foremanReact/common/I18n';
5
5
  import { helpLabel } from '../form/FormHelpers';
6
6
 
7
- export const PurposeField = ({ isDisabled, purpose, setPurpose }) => (
7
+ export const PurposeField = ({ purpose, setPurpose }) => (
8
8
  <FormGroup
9
9
  label={__('Purpose')}
10
10
  labelIcon={helpLabel(
@@ -14,7 +14,6 @@ export const PurposeField = ({ isDisabled, purpose, setPurpose }) => (
14
14
  )}
15
15
  >
16
16
  <TextInput
17
- isDisabled={isDisabled}
18
17
  aria-label="purpose"
19
18
  type="text"
20
19
  value={purpose}
@@ -25,7 +24,6 @@ export const PurposeField = ({ isDisabled, purpose, setPurpose }) => (
25
24
  </FormGroup>
26
25
  );
27
26
  PurposeField.propTypes = {
28
- isDisabled: PropTypes.bool.isRequired,
29
27
  purpose: PropTypes.string.isRequired,
30
28
  setPurpose: PropTypes.func.isRequired,
31
29
  };
@@ -1,48 +1,41 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
1
  import { FormGroup, Radio } from '@patternfly/react-core';
4
2
  import { translate as __ } from 'foremanReact/common/I18n';
3
+ import PropTypes from 'prop-types';
4
+ import React from 'react';
5
5
  import { helpLabel } from '../form/FormHelpers';
6
6
 
7
7
  export const QueryType = ({ isTypeStatic, setIsTypeStatic }) => (
8
- <FormGroup
9
- label={__('Query type')}
10
- fieldId="query-type"
11
- labelIcon={helpLabel(
12
- <p>
13
- {__('Type has impact on when is the query evaluated to hosts.')}
14
- <br />
15
- <ul>
16
- <li>
17
- <b>{__('Static')}</b> -{' '}
18
- {__('evaluates just after you submit this form')}
19
- </li>
20
- <li>
21
- <b>{__('Dynamic')}</b> -{' '}
22
- {__(
23
- "evaluates just before the execution is started, so if it's planed in future, targeted hosts set may change before it"
24
- )}
25
- </li>
26
- </ul>
27
- </p>,
28
- 'query-type'
29
- )}
30
- >
31
- <Radio
32
- isChecked={isTypeStatic}
33
- name="query-type"
34
- onChange={() => setIsTypeStatic(true)}
35
- id="query-type-static"
36
- label={__('Static query')}
37
- />
38
- <Radio
39
- isChecked={!isTypeStatic}
40
- name="query-type"
41
- onChange={() => setIsTypeStatic(false)}
42
- id="query-type-dynamic"
43
- label={__('Dynamic query')}
44
- />
45
- </FormGroup>
8
+ <>
9
+ <FormGroup
10
+ label={__('Query type')}
11
+ fieldId="query-type-static"
12
+ labelIcon={helpLabel(
13
+ __('Type has impact on when is the query evaluated to hosts.'),
14
+ 'query-type'
15
+ )}
16
+ >
17
+ <Radio
18
+ isChecked={isTypeStatic}
19
+ name="query-type"
20
+ onChange={() => setIsTypeStatic(true)}
21
+ id="query-type-static"
22
+ label={__('Static query')}
23
+ body={__('evaluates just after you submit this form')}
24
+ />
25
+ </FormGroup>
26
+ <FormGroup fieldId="query-type-dynamic">
27
+ <Radio
28
+ isChecked={!isTypeStatic}
29
+ name="query-type"
30
+ onChange={() => setIsTypeStatic(false)}
31
+ id="query-type-dynamic"
32
+ label={__('Dynamic query')}
33
+ body={__(
34
+ "evaluates just before the execution is started, so if it's planed in future, targeted hosts set may change before it"
35
+ )}
36
+ />
37
+ </FormGroup>
38
+ </>
46
39
  );
47
40
 
48
41
  QueryType.propTypes = {