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.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
- data/app/controllers/ui_job_wizard_controller.rb +6 -1
- data/app/models/job_invocation.rb +3 -2
- data/app/models/template_invocation.rb +1 -0
- data/app/views/api/v2/job_invocations/base.json.rabl +1 -1
- data/app/views/job_invocations/show.html.erb +1 -1
- data/app/views/job_invocations/welcome.html.erb +1 -1
- data/db/migrate/20210816100932_rex_setting_category_to_dsl.rb +1 -1
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +2 -2
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +266 -154
- data/locale/en/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en/foreman_remote_execution.po +132 -24
- data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en_GB/foreman_remote_execution.po +149 -41
- data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/es/foreman_remote_execution.po +320 -210
- data/locale/foreman_remote_execution.pot +394 -211
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +353 -241
- data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ja/foreman_remote_execution.po +368 -261
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +161 -53
- data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/pt_BR/foreman_remote_execution.po +335 -225
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +161 -53
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +465 -359
- data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_TW/foreman_remote_execution.po +162 -54
- data/test/unit/job_invocation_test.rb +1 -0
- data/webpack/JobWizard/JobWizard.js +52 -10
- data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +8 -0
- data/webpack/JobWizard/__tests__/fixtures.js +5 -0
- data/webpack/JobWizard/__tests__/integration.test.js +15 -0
- data/webpack/JobWizard/__tests__/validation.test.js +27 -0
- data/webpack/JobWizard/autofill.js +1 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +19 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +8 -0
- data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +3 -0
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +31 -1
- data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +16 -10
- data/webpack/JobWizard/steps/HostsAndInputs/index.js +51 -3
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +21 -1
- data/webpack/JobWizard/submit.js +13 -2
- data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.js +45 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.scss +26 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +13 -1
- data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +5 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +3 -0
- data/webpack/react_app/components/TargetingHosts/index.js +23 -3
- data/webpack/react_app/components/jobInvocations/AggregateStatus/index.js +25 -9
- data/webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js +6 -2
- data/webpack/react_app/components/jobInvocations/index.js +19 -2
- data/webpack/react_app/redux/actions/jobInvocations/index.js +8 -0
- data/webpack/react_app/redux/reducers/jobInvocations/index.js +2 -0
- 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;
|
@@ -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
|
-
<
|
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
|
-
</
|
13
|
-
|
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
|
-
</
|
19
|
-
|
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
|
-
</
|
25
|
-
|
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
|
-
</
|
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(
|
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(
|
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
|
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:
|
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
|