foreman_remote_execution 7.2.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) 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/models/remote_execution_provider.rb +1 -1
  8. data/app/views/templates/script/package_action.erb +8 -3
  9. data/config/routes.rb +3 -1
  10. data/lib/foreman_remote_execution/engine.rb +5 -5
  11. data/lib/foreman_remote_execution/version.rb +1 -1
  12. data/test/functional/api/v2/job_invocations_controller_test.rb +8 -0
  13. data/test/helpers/remote_execution_helper_test.rb +4 -0
  14. data/test/unit/job_invocation_report_template_test.rb +1 -1
  15. data/test/unit/job_invocation_test.rb +1 -2
  16. data/test/unit/remote_execution_provider_test.rb +0 -22
  17. data/webpack/JobWizard/JobWizard.js +154 -20
  18. data/webpack/JobWizard/JobWizard.scss +43 -1
  19. data/webpack/JobWizard/JobWizardConstants.js +11 -1
  20. data/webpack/JobWizard/JobWizardPageRerun.js +112 -0
  21. data/webpack/JobWizard/__tests__/JobWizardPageRerun.test.js +79 -0
  22. data/webpack/JobWizard/__tests__/fixtures.js +73 -0
  23. data/webpack/JobWizard/__tests__/integration.test.js +17 -3
  24. data/webpack/JobWizard/autofill.js +8 -1
  25. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +36 -17
  26. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +3 -3
  27. data/webpack/JobWizard/steps/ReviewDetails/index.js +1 -3
  28. data/webpack/JobWizard/steps/Schedule/PurposeField.js +1 -3
  29. data/webpack/JobWizard/steps/Schedule/QueryType.js +33 -40
  30. data/webpack/JobWizard/steps/Schedule/RepeatHour.js +55 -16
  31. data/webpack/JobWizard/steps/Schedule/RepeatOn.js +19 -56
  32. data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +1 -1
  33. data/webpack/JobWizard/steps/Schedule/ScheduleFuture.js +126 -0
  34. data/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js +287 -0
  35. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +88 -20
  36. data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +206 -186
  37. data/webpack/JobWizard/steps/form/DateTimePicker.js +23 -6
  38. data/webpack/JobWizard/steps/form/Formatter.js +7 -8
  39. data/webpack/JobWizard/submit.js +8 -3
  40. data/webpack/Routes/routes.js +8 -2
  41. data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +1 -0
  42. data/webpack/react_app/components/HostKebab/KebabItems.js +0 -1
  43. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +0 -5
  44. data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +59 -51
  45. data/webpack/react_app/extend/Fills.js +4 -4
  46. metadata +8 -6
  47. data/webpack/JobWizard/steps/Schedule/StartEndDates.js +0 -106
  48. data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +0 -32
  49. 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 = {