foreman_remote_execution 8.1.2 → 8.2.1

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
  3. data/app/controllers/ui_job_wizard_controller.rb +6 -1
  4. data/app/models/job_invocation.rb +3 -2
  5. data/app/models/template_invocation.rb +1 -0
  6. data/app/views/api/v2/job_invocations/base.json.rabl +1 -1
  7. data/app/views/job_invocations/show.html.erb +1 -1
  8. data/app/views/job_invocations/welcome.html.erb +1 -1
  9. data/db/migrate/20210816100932_rex_setting_category_to_dsl.rb +1 -1
  10. data/lib/foreman_remote_execution/version.rb +1 -1
  11. data/locale/action_names.rb +2 -2
  12. data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  13. data/locale/de/foreman_remote_execution.po +266 -154
  14. data/locale/en/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  15. data/locale/en/foreman_remote_execution.po +132 -24
  16. data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  17. data/locale/en_GB/foreman_remote_execution.po +149 -41
  18. data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  19. data/locale/es/foreman_remote_execution.po +320 -210
  20. data/locale/foreman_remote_execution.pot +394 -211
  21. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  22. data/locale/fr/foreman_remote_execution.po +353 -241
  23. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  24. data/locale/ja/foreman_remote_execution.po +368 -261
  25. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  26. data/locale/ko/foreman_remote_execution.po +161 -53
  27. data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  28. data/locale/pt_BR/foreman_remote_execution.po +335 -225
  29. data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  30. data/locale/ru/foreman_remote_execution.po +161 -53
  31. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  32. data/locale/zh_CN/foreman_remote_execution.po +465 -359
  33. data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  34. data/locale/zh_TW/foreman_remote_execution.po +162 -54
  35. data/test/unit/job_invocation_test.rb +1 -0
  36. data/webpack/JobWizard/JobWizard.js +52 -10
  37. data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +8 -0
  38. data/webpack/JobWizard/__tests__/fixtures.js +5 -0
  39. data/webpack/JobWizard/__tests__/integration.test.js +15 -0
  40. data/webpack/JobWizard/__tests__/validation.test.js +27 -0
  41. data/webpack/JobWizard/autofill.js +1 -0
  42. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +19 -0
  43. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +8 -0
  44. data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +3 -0
  45. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +31 -1
  46. data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +16 -10
  47. data/webpack/JobWizard/steps/HostsAndInputs/index.js +51 -3
  48. data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +21 -1
  49. data/webpack/JobWizard/submit.js +13 -2
  50. data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.js +45 -0
  51. data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.scss +26 -0
  52. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +13 -1
  53. data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +5 -0
  54. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +3 -0
  55. data/webpack/react_app/components/TargetingHosts/index.js +23 -3
  56. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.js +25 -9
  57. data/webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js +6 -2
  58. data/webpack/react_app/components/jobInvocations/index.js +19 -2
  59. data/webpack/react_app/redux/actions/jobInvocations/index.js +8 -0
  60. data/webpack/react_app/redux/reducers/jobInvocations/index.js +2 -0
  61. metadata +4 -2
@@ -5,14 +5,18 @@ import { Grid } from 'patternfly-react';
5
5
  import SearchBar from 'foremanReact/components/SearchBar';
6
6
  import Pagination from 'foremanReact/components/Pagination';
7
7
  import { getControllerSearchProps } from 'foremanReact/constants';
8
+ import { noop } from 'foremanReact/common/helpers';
8
9
 
9
10
  import TargetingHosts from './TargetingHosts';
10
11
  import { TARGETING_HOSTS_AUTOCOMPLETE } from './TargetingHostsConsts';
11
12
  import './TargetingHostsPage.scss';
13
+ import TargetingHostsLabelsRow from './TargetingHostsLabelsRow';
12
14
 
13
15
  const TargetingHostsPage = ({
14
16
  handleSearch,
15
17
  searchQuery,
18
+ statusFilter,
19
+ statusFilterReset,
16
20
  apiStatus,
17
21
  items,
18
22
  totalHosts,
@@ -23,7 +27,7 @@ const TargetingHostsPage = ({
23
27
  <Grid.Row>
24
28
  <Grid.Col md={6} className="title_filter">
25
29
  <SearchBar
26
- onSearch={query => handleSearch(query)}
30
+ onSearch={query => handleSearch(query, statusFilter)}
27
31
  data={{
28
32
  ...getControllerSearchProps('hosts', TARGETING_HOSTS_AUTOCOMPLETE),
29
33
  autocomplete: {
@@ -37,6 +41,10 @@ const TargetingHostsPage = ({
37
41
  />
38
42
  </Grid.Col>
39
43
  </Grid.Row>
44
+ <TargetingHostsLabelsRow
45
+ query={statusFilter}
46
+ updateQuery={statusFilterReset}
47
+ />
40
48
  <br />
41
49
  <TargetingHosts apiStatus={apiStatus} items={items} />
42
50
  <Pagination
@@ -53,6 +61,8 @@ TargetingHostsPage.propTypes = {
53
61
  handleSearch: PropTypes.func.isRequired,
54
62
  searchQuery: PropTypes.string.isRequired,
55
63
  apiStatus: PropTypes.string,
64
+ statusFilter: PropTypes.object,
65
+ statusFilterReset: PropTypes.func,
56
66
  items: PropTypes.array.isRequired,
57
67
  totalHosts: PropTypes.number.isRequired,
58
68
  handlePagination: PropTypes.func.isRequired,
@@ -61,6 +71,8 @@ TargetingHostsPage.propTypes = {
61
71
 
62
72
  TargetingHostsPage.defaultProps = {
63
73
  apiStatus: null,
74
+ statusFilter: {},
75
+ statusFilterReset: noop,
64
76
  };
65
77
 
66
78
  export default TargetingHostsPage;
@@ -18,3 +18,8 @@ export const selectTotalHosts = state =>
18
18
 
19
19
  export const selectIntervalExists = state =>
20
20
  selectDoesIntervalExist(state, TARGETING_HOSTS);
21
+
22
+ const defaultStatusFilter = {};
23
+ export const selectStatusFilter = state =>
24
+ state.foremanRemoteExecutionReducers.jobInvocations
25
+ .jobInvocationStateFilter || defaultStatusFilter;
@@ -38,6 +38,9 @@ exports[`TargetingHostsPage renders 1`] = `
38
38
  />
39
39
  </Col>
40
40
  </Row>
41
+ <TargetingHostsLabelsRow
42
+ query={Object {}}
43
+ />
41
44
  <br />
42
45
  <TargetingHosts
43
46
  apiStatus="RESOLVED"
@@ -14,10 +14,23 @@ import {
14
14
  selectAutoRefresh,
15
15
  selectTotalHosts,
16
16
  selectIntervalExists,
17
+ selectStatusFilter,
17
18
  } from './TargetingHostsSelectors';
18
19
  import { getApiUrl } from './TargetingHostsHelpers';
19
20
  import { TARGETING_HOSTS } from './TargetingHostsConsts';
20
21
  import TargetingHostsPage from './TargetingHostsPage';
22
+ import { chartFilter } from '../../redux/actions/jobInvocations';
23
+
24
+ const buildSearchQuery = (query, stateFilter) => {
25
+ const filters = Object.entries(stateFilter).map(
26
+ ([key, value]) => `${key} = ${value}`
27
+ );
28
+ return [query, filters]
29
+ .flat()
30
+ .filter(x => x)
31
+ .map(x => `(${x})`)
32
+ .join(' AND ');
33
+ };
21
34
 
22
35
  const WrappedTargetingHosts = () => {
23
36
  const dispatch = useDispatch();
@@ -27,6 +40,7 @@ const WrappedTargetingHosts = () => {
27
40
  const items = useSelector(selectItems);
28
41
  const apiStatus = useSelector(selectApiStatus);
29
42
  const totalHosts = useSelector(selectTotalHosts);
43
+ const statusFilter = useSelector(selectStatusFilter);
30
44
  const [searchQuery, setSearchQuery] = useState('');
31
45
  const [pagination, setPagination] = useState({
32
46
  page: 1,
@@ -36,11 +50,11 @@ const WrappedTargetingHosts = () => {
36
50
  const [apiUrl, setApiUrl] = useState(getApiUrl(searchQuery, pagination));
37
51
  const intervalExists = useSelector(selectIntervalExists);
38
52
 
39
- const handleSearch = query => {
53
+ const handleSearch = (query, status) => {
40
54
  const defaultPagination = { page: 1, per_page: pagination.per_page };
41
55
  stopApiInterval();
42
56
 
43
- setApiUrl(getApiUrl(query, defaultPagination));
57
+ setApiUrl(getApiUrl(buildSearchQuery(query, status), defaultPagination));
44
58
  setSearchQuery(query);
45
59
  setPagination(defaultPagination);
46
60
  };
@@ -48,7 +62,7 @@ const WrappedTargetingHosts = () => {
48
62
  const handlePagination = args => {
49
63
  stopApiInterval();
50
64
  setPagination(args);
51
- setApiUrl(getApiUrl(searchQuery, args));
65
+ setApiUrl(getApiUrl(buildSearchQuery(searchQuery, statusFilter), args));
52
66
  };
53
67
 
54
68
  const stopApiInterval = () => {
@@ -81,10 +95,16 @@ const WrappedTargetingHosts = () => {
81
95
  };
82
96
  }, [dispatch, apiUrl, autoRefresh]);
83
97
 
98
+ useEffect(() => {
99
+ handleSearch(searchQuery, statusFilter);
100
+ }, [statusFilter, searchQuery]);
101
+
84
102
  return (
85
103
  <TargetingHostsPage
86
104
  handleSearch={handleSearch}
87
105
  searchQuery={searchQuery}
106
+ statusFilter={statusFilter}
107
+ statusFilterReset={_x => chartFilter(null)(dispatch, null)}
88
108
  apiStatus={apiStatus}
89
109
  items={items}
90
110
  totalHosts={totalHosts}
@@ -1,33 +1,48 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
- const AggregateStatus = ({ statuses }) => (
4
+ const AggregateStatus = ({ statuses, chartFilter }) => (
5
5
  <div id="aggregate_statuses">
6
6
  <p className="card-pf-aggregate-status-notifications">
7
- <span className="card-pf-aggregate-status-notification">
7
+ <a
8
+ className="card-pf-aggregate-status-notification"
9
+ onClick={() => chartFilter('success')}
10
+ >
8
11
  <span id="success_count">
9
12
  <span className="pficon pficon-ok" />
10
13
  {statuses.success}
11
14
  </span>
12
- </span>
13
- <span className="card-pf-aggregate-status-notification">
15
+ </a>
16
+
17
+ <a
18
+ className="card-pf-aggregate-status-notification"
19
+ onClick={() => chartFilter('failed')}
20
+ >
14
21
  <span id="failed_count">
15
22
  <span className="pficon pficon-error-circle-o" />
16
23
  {statuses.failed}
17
24
  </span>
18
- </span>
19
- <span className="card-pf-aggregate-status-notification">
25
+ </a>
26
+
27
+ <a
28
+ className="card-pf-aggregate-status-notification"
29
+ onClick={() => chartFilter('pending')}
30
+ >
20
31
  <span id="pending_count">
21
32
  <span className="pficon pficon-running" />
22
33
  {statuses.pending}
23
34
  </span>
24
- </span>
25
- <span className="card-pf-aggregate-status-notification">
35
+ </a>
36
+
37
+ <a
38
+ className="card-pf-aggregate-status-notification"
39
+ onClick={() => chartFilter('cancelled')}
40
+ >
26
41
  <span id="cancelled_count">
27
42
  <span className="pficon pficon-close" />
28
43
  {statuses.cancelled}
29
44
  </span>
30
- </span>
45
+ </a>
31
46
  </p>
32
47
  </div>
33
48
  );
@@ -39,6 +54,7 @@ AggregateStatus.propTypes = {
39
54
  pending: PropTypes.number,
40
55
  success: PropTypes.number,
41
56
  }).isRequired,
57
+ chartFilter: PropTypes.func.isRequired,
42
58
  };
43
59
 
44
60
  export default AggregateStatus;
@@ -7,7 +7,9 @@ jest.unmock('./index.js');
7
7
  describe('AggregateStatus', () => {
8
8
  describe('has no data', () => {
9
9
  it('renders cards with no data', () => {
10
- const chartNumbers = shallow(<AggregateStatus statuses={{}} />);
10
+ const chartNumbers = shallow(
11
+ <AggregateStatus statuses={{}} chartFilter={_x => {}} />
12
+ );
11
13
  const success = chartNumbers.find('#success_count').text();
12
14
  const failed = chartNumbers.find('#failed_count').text();
13
15
  const pending = chartNumbers.find('#pending_count').text();
@@ -25,7 +27,9 @@ describe('AggregateStatus', () => {
25
27
  cancelled: 31,
26
28
  pending: 3,
27
29
  };
28
- const chartNumbers = shallow(<AggregateStatus statuses={statuses} />);
30
+ const chartNumbers = shallow(
31
+ <AggregateStatus statuses={statuses} chartFilter={_x => {}} />
32
+ );
29
33
  const success = chartNumbers.find('#success_count').text();
30
34
  const failed = chartNumbers.find('#failed_count').text();
31
35
  const pending = chartNumbers.find('#pending_count').text();
@@ -10,6 +10,13 @@ import * as JobInvocationActions from '../../redux/actions/jobInvocations';
10
10
  const colIndexOfMaxValue = columns =>
11
11
  columns.reduce((iMax, x, i, arr) => (x[1] > arr[iMax][1] ? i : iMax), 0);
12
12
 
13
+ const colorMap = {
14
+ '#5CB85C': 'success',
15
+ '#D9534F': 'failed',
16
+ '#DEDEDE': 'pending',
17
+ '#B7312D': 'cancelled',
18
+ };
19
+
13
20
  class JobInvocationContainer extends React.Component {
14
21
  componentDidMount() {
15
22
  const {
@@ -21,9 +28,14 @@ class JobInvocationContainer extends React.Component {
21
28
  }
22
29
 
23
30
  render() {
24
- const { jobInvocations, statuses } = this.props;
31
+ const { jobInvocations, statuses, chartFilter } = this.props;
25
32
  const iMax = colIndexOfMaxValue(jobInvocations);
26
33
 
34
+ const map = jobInvocations.reduce(
35
+ (acc, [label, _count, color]) => ({ [label]: colorMap[color], ...acc }),
36
+ {}
37
+ );
38
+
27
39
  return (
28
40
  <div id="job_invocations_chart_container">
29
41
  <DonutChart
@@ -32,8 +44,11 @@ class JobInvocationContainer extends React.Component {
32
44
  type: 'percent',
33
45
  secondary: (jobInvocations[iMax] || [])[0],
34
46
  }}
47
+ onclick={(d, element) => {
48
+ chartFilter(map[d.name]);
49
+ }}
35
50
  />
36
- <AggregateStatus statuses={statuses} />
51
+ <AggregateStatus statuses={statuses} chartFilter={chartFilter} />
37
52
  </div>
38
53
  );
39
54
  }
@@ -63,10 +78,12 @@ JobInvocationContainer.propTypes = {
63
78
  pending: PropTypes.number,
64
79
  success: PropTypes.number,
65
80
  }),
81
+ chartFilter: PropTypes.func,
66
82
  };
67
83
 
68
84
  JobInvocationContainer.defaultProps = {
69
85
  startJobInvocationsPolling: JobInvocationActions.startJobInvocationsPolling,
86
+ chartFilter: JobInvocationActions.chartFilter,
70
87
  data: {},
71
88
  jobInvocations: [['property', 3, 'color']],
72
89
  statuses: {},
@@ -76,3 +76,11 @@ export const startJobInvocationsPolling = url => (dispatch, getState) => {
76
76
  });
77
77
  dispatch(getJobInvocations(url));
78
78
  };
79
+
80
+ export const chartFilter = state => (dispatch, getState) => {
81
+ const filter = state ? { 'job_invocation.result': state.toLowerCase() } : {};
82
+ dispatch({
83
+ type: 'JOB_INVOCATION_CHART_FILTER',
84
+ payload: { filter },
85
+ });
86
+ };
@@ -26,6 +26,8 @@ export default (state = initialState, action) => {
26
26
  return state
27
27
  .set('jobInvocations', payload.jobInvocations.job_invocations)
28
28
  .set('statuses', payload.jobInvocations.statuses);
29
+ case 'JOB_INVOCATION_CHART_FILTER':
30
+ return state.set('jobInvocationStateFilter', payload.filter);
29
31
  default:
30
32
  return state;
31
33
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_remote_execution
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.2
4
+ version: 8.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Remote Execution team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-13 00:00:00.000000000 Z
11
+ date: 2023-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deface
@@ -508,6 +508,8 @@ files:
508
508
  - webpack/react_app/components/TargetingHosts/TargetingHosts.js
509
509
  - webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js
510
510
  - webpack/react_app/components/TargetingHosts/TargetingHostsHelpers.js
511
+ - webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.js
512
+ - webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.scss
511
513
  - webpack/react_app/components/TargetingHosts/TargetingHostsPage.js
512
514
  - webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss
513
515
  - webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js