foreman_remote_execution 8.3.1 → 9.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby_ci.yml +3 -1
  3. data/app/controllers/api/v2/job_invocations_controller.rb +0 -1
  4. data/app/controllers/job_invocations_controller.rb +1 -20
  5. data/app/controllers/ui_job_wizard_controller.rb +1 -3
  6. data/app/helpers/remote_execution_helper.rb +1 -1
  7. data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
  8. data/app/views/api/v2/job_invocations/base.json.rabl +1 -1
  9. data/app/views/job_invocations/_form.html.erb +1 -1
  10. data/app/views/job_invocations/show.html.erb +1 -1
  11. data/app/views/job_invocations/welcome.html.erb +1 -1
  12. data/config/routes.rb +0 -1
  13. data/db/migrate/20210816100932_rex_setting_category_to_dsl.rb +1 -1
  14. data/extra/cockpit/foreman-cockpit-session +12 -29
  15. data/lib/foreman_remote_execution/engine.rb +2 -2
  16. data/lib/foreman_remote_execution/version.rb +1 -1
  17. data/locale/action_names.rb +2 -2
  18. data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  19. data/locale/de/foreman_remote_execution.po +154 -266
  20. data/locale/en/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  21. data/locale/en/foreman_remote_execution.po +24 -132
  22. data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  23. data/locale/en_GB/foreman_remote_execution.po +41 -149
  24. data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  25. data/locale/es/foreman_remote_execution.po +210 -320
  26. data/locale/foreman_remote_execution.pot +211 -394
  27. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  28. data/locale/fr/foreman_remote_execution.po +241 -353
  29. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  30. data/locale/ja/foreman_remote_execution.po +261 -368
  31. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  32. data/locale/ko/foreman_remote_execution.po +53 -161
  33. data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  34. data/locale/pt_BR/foreman_remote_execution.po +225 -335
  35. data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  36. data/locale/ru/foreman_remote_execution.po +53 -161
  37. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  38. data/locale/zh_CN/foreman_remote_execution.po +359 -465
  39. data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  40. data/locale/zh_TW/foreman_remote_execution.po +54 -162
  41. data/webpack/JobWizard/JobWizard.js +10 -52
  42. data/webpack/JobWizard/JobWizard.scss +1 -5
  43. data/webpack/JobWizard/JobWizardConstants.js +1 -1
  44. data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +0 -8
  45. data/webpack/JobWizard/__tests__/fixtures.js +0 -5
  46. data/webpack/JobWizard/__tests__/integration.test.js +0 -15
  47. data/webpack/JobWizard/__tests__/validation.test.js +0 -27
  48. data/webpack/JobWizard/autofill.js +2 -6
  49. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +0 -19
  50. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +1 -9
  51. data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +0 -3
  52. data/webpack/JobWizard/steps/HostsAndInputs/HostSearch.js +4 -28
  53. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +2 -32
  54. data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +10 -16
  55. data/webpack/JobWizard/steps/HostsAndInputs/index.js +3 -55
  56. data/webpack/JobWizard/steps/Schedule/QueryType.js +1 -1
  57. data/webpack/JobWizard/steps/Schedule/RepeatHour.js +1 -0
  58. data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +5 -25
  59. data/webpack/JobWizard/steps/form/DateTimePicker.js +1 -0
  60. data/webpack/JobWizard/steps/form/Formatter.js +8 -30
  61. data/webpack/JobWizard/steps/form/GroupedSelectField.js +1 -0
  62. data/webpack/JobWizard/steps/form/SelectField.js +1 -0
  63. data/webpack/JobWizard/submit.js +2 -13
  64. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +4 -4
  65. data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +2 -2
  66. data/webpack/react_app/components/RecentJobsCard/constants.js +2 -2
  67. metadata +5 -5
@@ -209,11 +209,6 @@ export const JobWizard = ({ rerunData }) => {
209
209
  !templateError &&
210
210
  !!jobTemplateID &&
211
211
  templateResponse.job_template;
212
- const areHostsSelected =
213
- selectedTargets.hosts.length > 0 ||
214
- selectedTargets.hostCollections.length > 0 ||
215
- selectedTargets.hostGroups.length > 0 ||
216
- hostsSearchQuery.length > 0;
217
212
  const steps = [
218
213
  {
219
214
  name: WIZARD_TITLES.categoryAndTemplate,
@@ -243,7 +238,7 @@ export const JobWizard = ({ rerunData }) => {
243
238
  />
244
239
  ),
245
240
  canJumpTo: isTemplate,
246
- enableNext: isTemplate && valid.hostsAndInputs && areHostsSelected,
241
+ enableNext: isTemplate && valid.hostsAndInputs,
247
242
  },
248
243
  {
249
244
  name: WIZARD_TITLES.advanced,
@@ -259,26 +254,14 @@ export const JobWizard = ({ rerunData }) => {
259
254
  templateValues={templateValues}
260
255
  />
261
256
  ),
262
- canJumpTo: isTemplate && valid.hostsAndInputs && areHostsSelected,
263
- enableNext:
264
- isTemplate &&
265
- valid.hostsAndInputs &&
266
- areHostsSelected &&
267
- valid.advanced,
257
+ canJumpTo: isTemplate && valid.hostsAndInputs,
258
+ enableNext: isTemplate && valid.hostsAndInputs && valid.advanced,
268
259
  },
269
260
  {
270
261
  name: WIZARD_TITLES.schedule,
271
- canJumpTo:
272
- isTemplate &&
273
- valid.hostsAndInputs &&
274
- areHostsSelected &&
275
- valid.advanced,
262
+ canJumpTo: isTemplate && valid.hostsAndInputs && valid.advanced,
276
263
  enableNext:
277
- isTemplate &&
278
- valid.hostsAndInputs &&
279
- areHostsSelected &&
280
- valid.advanced &&
281
- valid.schedule,
264
+ isTemplate && valid.hostsAndInputs && valid.advanced && valid.schedule,
282
265
  steps: [
283
266
  {
284
267
  name: WIZARD_TITLES.typeOfExecution,
@@ -295,17 +278,9 @@ export const JobWizard = ({ rerunData }) => {
295
278
  }}
296
279
  />
297
280
  ),
298
- canJumpTo:
299
- isTemplate &&
300
- valid.hostsAndInputs &&
301
- areHostsSelected &&
302
- valid.advanced,
281
+ canJumpTo: isTemplate && valid.hostsAndInputs && valid.advanced,
303
282
 
304
- enableNext:
305
- isTemplate &&
306
- valid.hostsAndInputs &&
307
- areHostsSelected &&
308
- valid.advanced,
283
+ enableNext: isTemplate && valid.hostsAndInputs && valid.advanced,
309
284
  },
310
285
  ...(scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE
311
286
  ? [
@@ -323,15 +298,10 @@ export const JobWizard = ({ rerunData }) => {
323
298
  }}
324
299
  />
325
300
  ),
326
- canJumpTo:
327
- isTemplate &&
328
- valid.hostsAndInputs &&
329
- areHostsSelected &&
330
- valid.advanced,
301
+ canJumpTo: isTemplate && valid.hostsAndInputs && valid.advanced,
331
302
  enableNext:
332
303
  isTemplate &&
333
304
  valid.hostsAndInputs &&
334
- areHostsSelected &&
335
305
  valid.advanced &&
336
306
  valid.schedule,
337
307
  },
@@ -353,15 +323,10 @@ export const JobWizard = ({ rerunData }) => {
353
323
  }}
354
324
  />
355
325
  ),
356
- canJumpTo:
357
- isTemplate &&
358
- valid.hostsAndInputs &&
359
- areHostsSelected &&
360
- valid.advanced,
326
+ canJumpTo: isTemplate && valid.hostsAndInputs && valid.advanced,
361
327
  enableNext:
362
328
  isTemplate &&
363
329
  valid.hostsAndInputs &&
364
- areHostsSelected &&
365
330
  valid.advanced &&
366
331
  valid.schedule,
367
332
  },
@@ -384,15 +349,10 @@ export const JobWizard = ({ rerunData }) => {
384
349
  ),
385
350
  nextButtonText: 'Run',
386
351
  canJumpTo:
387
- isTemplate &&
388
- valid.advanced &&
389
- valid.hostsAndInputs &&
390
- areHostsSelected &&
391
- valid.schedule,
352
+ isTemplate && valid.hostsAndInputs && valid.advanced && valid.schedule,
392
353
  enableNext:
393
354
  isTemplate &&
394
355
  valid.hostsAndInputs &&
395
- areHostsSelected &&
396
356
  valid.advanced &&
397
357
  valid.schedule &&
398
358
  !isSubmitting,
@@ -419,8 +379,6 @@ export const JobWizard = ({ rerunData }) => {
419
379
  location,
420
380
  organization,
421
381
  feature: routerSearch?.feature,
422
- provider: templateResponse.provider_name,
423
- advancedInputs: templateResponse.advanced_template_inputs,
424
382
  });
425
383
  }}
426
384
  />
@@ -1,4 +1,5 @@
1
1
  .job-wizard {
2
+ font-size: var(--pf-global--FontSize--md);
2
3
  .wizard-title {
3
4
  margin-bottom: 25px;
4
5
  }
@@ -134,11 +135,6 @@
134
135
  margin-left: 10px;
135
136
  }
136
137
  }
137
- .foreman-search-field {
138
- .autocomplete-search-btn {
139
- display: none;
140
- }
141
- }
142
138
  .pf-c-radio__body {
143
139
  font-size: var(--pf-c-radio__label--FontSize);
144
140
  }
@@ -23,7 +23,7 @@ export const SCHEDULE_TYPES = {
23
23
  };
24
24
 
25
25
  export const WIZARD_TITLES = {
26
- categoryAndTemplate: __('Category and template'),
26
+ categoryAndTemplate: __('Category and Template'),
27
27
  hostsAndInputs: __('Target hosts and inputs'),
28
28
  advanced: __('Advanced fields'),
29
29
  schedule: __('Schedule'),
@@ -7,14 +7,6 @@ Array [
7
7
  "type": "get",
8
8
  "url": "/ui_job_wizard/categories",
9
9
  },
10
- Object {
11
- "key": "HOST_IDS",
12
- "params": Object {
13
- "search": "id = 105 or id = 37",
14
- },
15
- "type": "get",
16
- "url": "/api/hosts",
17
- },
18
10
  Object {
19
11
  "key": "JOB_TEMPLATES",
20
12
  "type": "get",
@@ -161,11 +161,6 @@ export const testSetup = (selectors, api) => {
161
161
  ],
162
162
  },
163
163
  },
164
- HOSTS_API: {
165
- response: {
166
- subtotal: 3,
167
- },
168
- },
169
164
  });
170
165
  return store;
171
166
  };
@@ -18,15 +18,6 @@ import {
18
18
  const store = testSetup(selectors, api);
19
19
 
20
20
  describe('Job wizard fill', () => {
21
- beforeEach(() => {
22
- jest.spyOn(selectors, 'selectRouterSearch');
23
- selectors.selectRouterSearch.mockImplementation(() => ({
24
- 'host_ids[]': ['105', '37'],
25
- }));
26
- });
27
- afterEach(() => {
28
- selectors.selectRouterSearch.mockRestore();
29
- });
30
21
  it('should select template', async () => {
31
22
  api.get.mockImplementation(({ handleSuccess, ...action }) => {
32
23
  if (action.key === 'JOB_CATEGORIES') {
@@ -42,13 +33,7 @@ describe('Job wizard fill', () => {
42
33
  handleSuccess({
43
34
  data: jobTemplate,
44
35
  });
45
- } else if (action.key === 'HOST_IDS') {
46
- handleSuccess &&
47
- handleSuccess({
48
- data: { results: [{ name: 'host1' }, { name: 'host3' }] },
49
- });
50
36
  }
51
-
52
37
  return { type: 'get', ...action };
53
38
  });
54
39
  selectors.selectJobTemplate.mockRestore();
@@ -41,26 +41,11 @@ describe('Job wizard validation', () => {
41
41
  expect(screen.getByText(WIZARD_TITLES.review)).toBeDisabled();
42
42
  await act(async () => {
43
43
  fireEvent.click(screen.getByText(WIZARD_TITLES.hostsAndInputs));
44
- await new Promise(resolve => setTimeout(resolve, 0)); // to resolve gql
45
44
  });
46
- const select = name =>
47
- screen.getByRole('button', { name: `${name} toggle` });
48
- fireEvent.click(select('hosts'));
49
- await act(async () => {
50
- fireEvent.click(screen.getByText('host1'));
51
- });
52
-
53
- expect(screen.getByText(WIZARD_TITLES.advanced)).toBeDisabled();
54
- expect(screen.getByText(WIZARD_TITLES.schedule)).toBeDisabled();
55
- expect(screen.getByText(WIZARD_TITLES.review)).toBeDisabled();
56
45
  const textField = screen.getByLabelText('plain hidden', {
57
46
  selector: 'textarea',
58
47
  });
59
48
  await act(async () => {
60
- fireEvent.click(
61
- // Close the select
62
- select('hosts')
63
- );
64
49
  await fireEvent.change(textField, {
65
50
  target: { value: 'text' },
66
51
  });
@@ -100,20 +85,8 @@ describe('Job wizard validation', () => {
100
85
  // setup
101
86
  await act(async () => {
102
87
  fireEvent.click(screen.getByText(WIZARD_TITLES.hostsAndInputs));
103
- await new Promise(resolve => setTimeout(resolve, 0)); // to resolve gql
104
88
  });
105
-
106
- const select = name =>
107
- screen.getByRole('button', { name: `${name} toggle` });
108
- fireEvent.click(select('hosts'));
109
89
  await act(async () => {
110
- fireEvent.click(screen.getByText('host1'));
111
- });
112
- await act(async () => {
113
- fireEvent.click(
114
- // Close the host select
115
- select('hosts')
116
- );
117
90
  await fireEvent.change(
118
91
  screen.getByLabelText('plain hidden', {
119
92
  selector: 'textarea',
@@ -48,11 +48,8 @@ export const useAutoFill = ({
48
48
  })
49
49
  );
50
50
  }
51
- if ((search || search === '') && !hostIds?.length) {
52
- // replace an empty string search with a dummy search query to match all hosts
53
- // but only if search query was entered (based on presence of :search parameter)
54
- const hostSearch = search === '' ? "name != ''" : search;
55
- setHostsSearchQuery(hostSearch);
51
+ if (search && !hostIds?.length) {
52
+ setHostsSearchQuery(search);
56
53
  }
57
54
  if (templateID) {
58
55
  setJobTemplateID(+templateID);
@@ -75,7 +72,6 @@ export const useAutoFill = ({
75
72
  if (input) {
76
73
  if (typeof rest[key] === 'string') {
77
74
  setTemplateValues(prev => ({ ...prev, [input]: rest[key] }));
78
- setAdvancedValues(prev => ({ ...prev, [input]: rest[key] }));
79
75
  } else {
80
76
  const { value, advanced } = rest[key];
81
77
  if (advanced) {
@@ -26,15 +26,6 @@ mockApi(api);
26
26
  jest.useFakeTimers();
27
27
 
28
28
  describe('AdvancedFields', () => {
29
- beforeEach(() => {
30
- jest.spyOn(selectors, 'selectRouterSearch');
31
- selectors.selectRouterSearch.mockImplementation(() => ({
32
- 'host_ids[]': ['105', '37'],
33
- }));
34
- });
35
- afterEach(() => {
36
- selectors.selectRouterSearch.mockRestore();
37
- });
38
29
  it('should save data between steps for advanced fields', async () => {
39
30
  const wrapper = mount(
40
31
  <MockedProvider mocks={gqlMock} addTypename={false}>
@@ -279,11 +270,6 @@ describe('AdvancedFields', () => {
279
270
  handleSuccess({
280
271
  data: { results: [jobTemplate] },
281
272
  });
282
- } else if (action.key === 'HOST_IDS') {
283
- handleSuccess &&
284
- handleSuccess({
285
- data: { results: [{ name: 'host1' }, { name: 'host3' }] },
286
- });
287
273
  }
288
274
  return { type: 'get', ...action };
289
275
  });
@@ -353,11 +339,6 @@ describe('AdvancedFields', () => {
353
339
  handleSuccess({
354
340
  data: { results: [jobTemplate] },
355
341
  });
356
- } else if (action.key === 'HOST_IDS') {
357
- handleSuccess &&
358
- handleSuccess({
359
- data: { results: [{ name: 'host1' }, { name: 'host3' }] },
360
- });
361
342
  }
362
343
  return { type: 'get', ...action };
363
344
  });
@@ -7,14 +7,6 @@ Array [
7
7
  "type": "get",
8
8
  "url": "/ui_job_wizard/categories",
9
9
  },
10
- Object {
11
- "key": "HOST_IDS",
12
- "params": Object {
13
- "search": "id = 105 or id = 37",
14
- },
15
- "type": "get",
16
- "url": "/api/hosts",
17
- },
18
10
  Object {
19
11
  "key": "JOB_TEMPLATES",
20
12
  "type": "get",
@@ -57,7 +49,7 @@ Array [
57
49
  "port": null,
58
50
  "preventInvalidHostname": false,
59
51
  "protocol": null,
60
- "query": "resource=ForemanTasks%3A%3ATask",
52
+ "query": "resource=ForemanTasks%3A%3ATask&name=some+search",
61
53
  "urn": null,
62
54
  "username": null,
63
55
  },
@@ -60,6 +60,3 @@ HostPreviewModal.propTypes = {
60
60
  setIsOpen: PropTypes.func.isRequired,
61
61
  searchQuery: PropTypes.string.isRequired,
62
62
  };
63
- HostPreviewModal.defaultPropTypes = {
64
- searchQuery: '',
65
- };
@@ -1,33 +1,10 @@
1
- import React, { useEffect } from 'react';
2
- import { useSelector, useDispatch } from 'react-redux';
1
+ import React from 'react';
3
2
  import PropTypes from 'prop-types';
4
3
  import SearchBar from 'foremanReact/components/SearchBar';
5
4
  import { getControllerSearchProps } from 'foremanReact/constants';
6
- import { getResults } from 'foremanReact/components/AutoComplete/AutoCompleteActions';
7
- import { TRIGGERS } from 'foremanReact/components/AutoComplete/AutoCompleteConstants';
8
5
  import { hostsController, hostQuerySearchID } from '../../JobWizardConstants';
9
- import { noop } from '../../../helpers';
10
6
 
11
7
  export const HostSearch = ({ value, setValue }) => {
12
- const searchQuery = useSelector(
13
- state => state.autocomplete?.[hostQuerySearchID]?.searchQuery
14
- );
15
- useEffect(() => {
16
- setValue(searchQuery || '');
17
- }, [setValue, searchQuery]);
18
- const dispatch = useDispatch();
19
- const setSearch = newSearchQuery => {
20
- dispatch(
21
- getResults({
22
- url: '/hosts/auto_complete_search',
23
- searchQuery: newSearchQuery,
24
- controller: 'hostsController',
25
- trigger: TRIGGERS.INPUT_CHANGE,
26
- id: hostQuerySearchID,
27
- })
28
- );
29
- };
30
-
31
8
  const props = getControllerSearchProps(hostsController, hostQuerySearchID);
32
9
  return (
33
10
  <div className="foreman-search-field">
@@ -37,12 +14,11 @@ export const HostSearch = ({ value, setValue }) => {
37
14
  autocomplete: {
38
15
  id: hostQuerySearchID,
39
16
  url: '/hosts/auto_complete_search',
40
- useKeyShortcuts: true,
17
+ searchQuery: value,
41
18
  },
42
19
  }}
43
- onSearch={noop}
44
- initialQuery={value}
45
- onBookmarkClick={search => setSearch(search)}
20
+ onSearch={null}
21
+ onSearchChange={search => setValue(search)}
46
22
  />
47
23
  </div>
48
24
  );
@@ -30,27 +30,6 @@ describe('Hosts', () => {
30
30
  const select = name =>
31
31
  screen.getByRole('button', { name: `${name} toggle` });
32
32
  fireEvent.click(select('hosts'));
33
- await act(async () => {
34
- fireEvent.click(screen.getByText('host1'));
35
- fireEvent.click(select('hosts'));
36
- });
37
- expect(
38
- screen.queryAllByText('Please select at least one host')
39
- ).toHaveLength(0);
40
- await act(async () => {
41
- fireEvent.click(select('hosts'));
42
- });
43
- await act(async () => {
44
- fireEvent.click(
45
- screen.getByText('host1', {
46
- selector: '.pf-c-select__menu-item',
47
- })
48
- );
49
- fireEvent.blur(select('hosts'));
50
- });
51
- expect(
52
- screen.queryAllByText('Please select at least one host')
53
- ).toHaveLength(1);
54
33
  await act(async () => {
55
34
  fireEvent.click(screen.getByText('host1'));
56
35
  fireEvent.click(screen.getByText('host2'));
@@ -91,7 +70,7 @@ describe('Hosts', () => {
91
70
  expect(screen.queryAllByText('host_collection1')).toHaveLength(1);
92
71
 
93
72
  await act(async () => {
94
- fireEvent.click(screen.getByText('Category and template'));
73
+ fireEvent.click(screen.getByText('Category and Template'));
95
74
  });
96
75
  await act(async () => {
97
76
  fireEvent.click(screen.getByText('Target hosts and inputs'));
@@ -172,9 +151,8 @@ describe('Hosts', () => {
172
151
 
173
152
  it('input fill from url', async () => {
174
153
  const inputText = 'test text';
175
- const advancedInputText = 'test adv text';
176
154
  routerSelectors.selectRouterLocation.mockImplementation(() => ({
177
- search: `host_ids%5B%5D=host1&host_ids%5B%5D=host3&feature=test_feature&inputs[plain hidden]=${inputText}&inputs[adv plain hidden]=${advancedInputText}`,
155
+ search: `feature=test_feature&inputs[plain hidden]=${inputText}`,
178
156
  }));
179
157
  render(
180
158
  <MockedProvider mocks={gqlMock} addTypename={false}>
@@ -197,13 +175,5 @@ describe('Hosts', () => {
197
175
  selector: 'textarea',
198
176
  });
199
177
  expect(textField.value).toBe(inputText);
200
-
201
- await act(async () => {
202
- fireEvent.click(screen.getByText('Advanced fields'));
203
- });
204
- const advancedTextField = screen.getByLabelText('adv plain hidden', {
205
- selector: 'textarea',
206
- });
207
- expect(advancedTextField.value).toBe(advancedInputText);
208
178
  });
209
179
  });
@@ -1,24 +1,18 @@
1
1
  export const buildHostQuery = (selected, search) => {
2
2
  const { hosts, hostCollections, hostGroups } = selected;
3
- const hostsSearch = `id ^ (${hosts.map(({ id }) => id).join(',')})`;
4
- const hostCollectionsSearch = `host_collection_id ^ (${hostCollections
3
+ const hostsSearch = `(id ^ (${hosts.map(({ id }) => id).join(',')}))`;
4
+ const hostCollectionsSearch = `(host_collection_id ^ (${hostCollections
5
5
  .map(({ id }) => id)
6
- .join(',')})`;
7
- const hostGroupsSearch = `hostgroup_id ^ (${hostGroups
6
+ .join(',')}))`;
7
+ const hostGroupsSearch = `(hostgroup_id ^ (${hostGroups
8
8
  .map(({ id }) => id)
9
- .join(',')})`;
10
- const queryParts = [
9
+ .join(',')}))`;
10
+ return [
11
11
  hosts.length ? hostsSearch : false,
12
12
  hostCollections.length ? hostCollectionsSearch : false,
13
13
  hostGroups.length ? hostGroupsSearch : false,
14
- search.length ? search : false,
15
- ].filter(Boolean);
16
-
17
- if (queryParts.length === 0) {
18
- return 'name=a AND name=b';
19
- }
20
- if (queryParts.length === 1) {
21
- return queryParts[0] || 'name=a AND name=b';
22
- }
23
- return queryParts.map(p => `(${p})`).join(' or ') || 'name=a AND name=b';
14
+ search.length ? `(${search})` : false,
15
+ ]
16
+ .filter(Boolean)
17
+ .join(' or ');
24
18
  };
@@ -13,7 +13,6 @@ import { FilterIcon } from '@patternfly/react-icons';
13
13
  import { debounce } from 'lodash';
14
14
  import { get } from 'foremanReact/redux/API';
15
15
  import { translate as __ } from 'foremanReact/common/I18n';
16
- import { resetData } from 'foremanReact/components/AutoComplete/AutoCompleteActions';
17
16
  import {
18
17
  selectTemplateInputs,
19
18
  selectWithKatello,
@@ -31,8 +30,6 @@ import {
31
30
  HOST_COLLECTIONS,
32
31
  HOST_GROUPS,
33
32
  hostMethods,
34
- hostsController,
35
- hostQuerySearchID,
36
33
  HOSTS_API,
37
34
  HOSTS_TO_PREVIEW_AMOUNT,
38
35
  DEBOUNCE_API,
@@ -54,30 +51,6 @@ const HostsAndInputs = ({
54
51
  const isLoading = useSelector(selectIsLoadingHosts);
55
52
  const templateInputs = useSelector(selectTemplateInputs);
56
53
  const [hostPreviewOpen, setHostPreviewOpen] = useState(false);
57
- const [wasFocus, setWasFocus] = useState(false);
58
- const [isError, setIsError] = useState(false);
59
- useEffect(() => {
60
- if (wasFocus) {
61
- if (
62
- selected.hosts.length === 0 &&
63
- selected.hostCollections.length === 0 &&
64
- selected.hostGroups.length === 0 &&
65
- hostsSearchQuery.length === 0
66
- ) {
67
- setIsError(true);
68
- } else {
69
- setIsError(false);
70
- }
71
- }
72
- }, [
73
- hostMethod,
74
- hostsSearchQuery.length,
75
- selected,
76
- selected.hostCollections.length,
77
- selected.hostGroups.length,
78
- selected.hosts.length,
79
- wasFocus,
80
- ]);
81
54
  useEffect(() => {
82
55
  debounce(() => {
83
56
  dispatch(
@@ -124,12 +97,8 @@ const HostsAndInputs = ({
124
97
  };
125
98
 
126
99
  const clearSearch = () => {
127
- dispatch(resetData(hostsController, hostQuerySearchID));
128
100
  setHostsSearchQuery('');
129
101
  };
130
- const [errorText, setErrorText] = useState(
131
- __('Please select at least one host')
132
- );
133
102
  return (
134
103
  <div className="target-hosts-and-inputs">
135
104
  <WizardTitle title={WIZARD_TITLES.hostsAndInputs} />
@@ -141,13 +110,8 @@ const HostsAndInputs = ({
141
110
  />
142
111
  )}
143
112
  <Form>
144
- <FormGroup
145
- fieldId="host_selection"
146
- id="host-selection"
147
- helperTextInvalid={errorText}
148
- validated={isError ? 'error' : 'default'}
149
- >
150
- <InputGroup onBlur={() => setWasFocus(true)}>
113
+ <FormGroup fieldId="host_selection" id="host-selection">
114
+ <InputGroup>
151
115
  <SelectField
152
116
  isRequired
153
117
  className="target-method-select"
@@ -159,23 +123,7 @@ const HostsAndInputs = ({
159
123
  }
160
124
  return true;
161
125
  })}
162
- setValue={val => {
163
- setHostMethod(val);
164
- if (val === hostMethods.searchQuery) {
165
- setErrorText(__('Please enter a search query'));
166
- }
167
- if (val === hostMethods.hosts) {
168
- setErrorText(__('Please select at least one host'));
169
- }
170
- if (val === hostMethods.hostCollections) {
171
- setErrorText(
172
- __('Please select at least one host collection')
173
- );
174
- }
175
- if (val === hostMethods.hostGroups) {
176
- setErrorText(__('Please select at least one host group'));
177
- }
178
- }}
126
+ setValue={setHostMethod}
179
127
  value={hostMethod}
180
128
  />
181
129
  {hostMethod === hostMethods.searchQuery && (
@@ -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 planned in future, targeted hosts set may change before it"
34
+ "evaluates just before the execution is started, so if it's planed in future, targeted hosts set may change before it"
35
35
  )}
36
36
  />
37
37
  </FormGroup>
@@ -47,6 +47,7 @@ export const RepeatHour = ({ repeatData, setRepeatData }) => {
47
47
  }}
48
48
  isOpen={minuteOpen}
49
49
  width={125}
50
+ menuAppendTo={() => document.querySelector('.pf-c-form.schedule-tab')}
50
51
  toggleAriaLabel="select minute toggle"
51
52
  validated={
52
53
  isValidMinute(minute)