foreman_remote_execution 8.1.2 → 8.2.1

Sign up to get free protection for your applications and to get access to all the features.
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