katello 3.8.0.rc1 → 3.8.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of katello might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/assets/javascripts/katello/common/index.js +1 -0
- data/app/assets/javascripts/katello/sync_management/index.js +1 -0
- data/app/controllers/katello/api/registry/registry_proxies_controller.rb +1 -1
- data/app/controllers/katello/api/v2/environments_controller.rb +0 -1
- data/app/controllers/katello/api/v2/ostree_branches_controller.rb +1 -1
- data/app/controllers/katello/api/v2/repository_sets_controller.rb +10 -1
- data/app/controllers/katello/remote_execution_controller.rb +6 -6
- data/app/helpers/katello/hosts_and_hostgroups_helper.rb +37 -9
- data/app/lib/katello/resources/registry.rb +4 -4
- data/app/models/katello/authorization/repository.rb +2 -1
- data/app/models/katello/content_view.rb +12 -4
- data/app/models/katello/glue/candlepin/owner.rb +0 -8
- data/app/models/katello/glue/candlepin/pool.rb +11 -11
- data/app/models/katello/kt_environment.rb +0 -6
- data/app/models/katello/product_content.rb +4 -1
- data/app/models/katello/rpm.rb +13 -5
- data/app/services/katello/ui_notifications/pulp/proxy_disk_space.rb +3 -1
- data/app/views/overrides/activation_keys/_host_environment_select.html.erb +2 -3
- data/config/katello.yaml.example +5 -0
- data/config/routes.rb +1 -0
- data/db/seeds.d/75-job_templates.rb +5 -2
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-details.html +43 -8
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/discovery.controller.js +17 -2
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery-create.html +1 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery.html +1 -1
- data/engines/bastion_katello/app/assets/stylesheets/bastion_katello/bastion_katello.scss +5 -0
- data/lib/katello/version.rb +1 -1
- data/package.json +11 -7
- data/webpack/__mocks__/foremanReact/components/BreadcrumbBar.js +3 -0
- data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +8 -0
- data/webpack/__mocks__/foremanReact/redux.js +3 -0
- data/webpack/components/Search/Search.test.js +3 -1
- data/webpack/components/SelectOrg/SelectOrg.scss +3 -0
- data/webpack/components/SelectOrg/SelectOrgAction.js +41 -0
- data/webpack/components/SelectOrg/SelectOrgReducer.js +33 -0
- data/webpack/components/SelectOrg/SetOrganization.js +116 -0
- data/webpack/components/WithOrganization/withOrganization.js +28 -0
- data/webpack/containers/Application/config.js +9 -2
- data/webpack/containers/Application/index.js +4 -2
- data/webpack/global_test_setup.js +6 -0
- data/webpack/helpers/caret.js +6 -0
- data/webpack/mockRequest.js +3 -3
- data/webpack/move_to_foreman/common/helpers.js +45 -8
- data/webpack/move_to_foreman/components/common/{emptyState → EmptyState}/index.js +16 -3
- data/webpack/move_to_foreman/components/common/table/components/Table.js +1 -1
- data/webpack/move_to_foreman/components/common/table/components/__snapshots__/CollapseSubscriptionGroupButton.test.js.snap +2 -2
- data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionCell.test.js.snap +1 -1
- data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionHeaderCell.test.js.snap +1 -1
- data/webpack/move_to_pf/LoadingState/LoadingState.js +27 -14
- data/webpack/move_to_pf/LoadingState/LoadingState.test.js +8 -4
- data/webpack/move_to_pf/Select/Select.js +40 -0
- data/webpack/move_to_pf/react-bootstrap-select/index.js +12 -1
- data/webpack/redux/actions/RedHatRepositories/enabled.js +0 -1
- data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -5
- data/webpack/redux/actions/RedHatRepositories/sets.js +1 -1
- data/webpack/redux/consts.js +6 -0
- data/webpack/redux/reducers/index.js +2 -0
- data/webpack/scenes/RedHatRepositories/components/EnabledRepository.js +14 -23
- data/webpack/scenes/RedHatRepositories/components/EnabledRepositoryContent.js +34 -0
- data/webpack/scenes/RedHatRepositories/components/RepositorySetRepository.js +1 -1
- data/webpack/scenes/RedHatRepositories/components/SearchBar.js +1 -0
- data/webpack/scenes/RedHatRepositories/components/__tests__/EnabledRepository.test.js +36 -0
- data/webpack/scenes/RedHatRepositories/components/__tests__/EnabledRepositoryContent.test.js +27 -0
- data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/EnabledRepository.test.js.snap +25 -0
- data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/EnabledRepositoryContent.test.js.snap +47 -0
- data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/RecommendedRepositorySetsToggler.test.js.snap +3 -1
- data/webpack/scenes/RedHatRepositories/index.js +7 -3
- data/webpack/scenes/RedHatRepositories/index.scss +1 -0
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetailActions.js +3 -8
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetailProducts.js +5 -3
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetailReducer.js +1 -1
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.js +44 -6
- data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.scss +4 -0
- data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailReducer.test.js +3 -1
- data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetails.test.js +2 -1
- data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailProducts.test.js.snap +113 -23
- data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +23 -14
- data/webpack/scenes/Subscriptions/Details/__tests__/subscriptionDetails.fixtures.js +3 -4
- data/webpack/scenes/Subscriptions/Details/index.js +2 -2
- data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +78 -34
- data/webpack/scenes/Subscriptions/Manifest/ManifestActions.js +5 -24
- data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryReducer.js +9 -1
- data/webpack/scenes/Subscriptions/Manifest/__tests__/ManageManifestModal.test.js +3 -0
- data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestActions.test.js +20 -8
- data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestHistoryReducer.test.js +3 -1
- data/webpack/scenes/Subscriptions/Manifest/__tests__/__snapshots__/ManageManifestModal.test.js.snap +34 -7
- data/webpack/scenes/Subscriptions/Manifest/__tests__/manifest.fixtures.js +9 -16
- data/webpack/scenes/Subscriptions/Manifest/index.js +1 -0
- data/webpack/scenes/Subscriptions/SubscriptionActions.js +5 -26
- data/webpack/scenes/Subscriptions/SubscriptionConstants.js +1 -0
- data/webpack/scenes/Subscriptions/SubscriptionHelpers.js +3 -0
- data/webpack/scenes/Subscriptions/SubscriptionReducer.js +11 -4
- data/webpack/scenes/Subscriptions/SubscriptionsPage.js +31 -36
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsActions.js +3 -12
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +57 -27
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsReducer.js +2 -3
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsTableSchema.js +10 -5
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsActions.test.js +10 -5
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsPage.test.js +50 -5
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsReducer.test.js +8 -3
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/__snapshots__/UpstreamSubscriptionsPage.test.js.snap +21 -11
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/upstreamSubscriptions.fixtures.js +5 -8
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +2 -0
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsReducer.test.js +9 -3
- data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +14 -2
- data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +11 -17
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/EntitlementsInlineEditFormatter.js +8 -5
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTable.js +45 -58
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableHelpers.js +11 -4
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +2 -2
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/EntitlementsInlineEditFormatter.test.js +110 -0
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionsTable.test.js +16 -3
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/EntitlementsInlineEditFormatter.test.js.snap +228 -0
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +392 -365
- data/webpack/scenes/Subscriptions/index.js +1 -0
- data/webpack/scenes/Tasks/helpers.js +52 -0
- data/webpack/services/api/index.js +17 -1
- data/webpack/services/api/testHelpers.js +28 -0
- data/webpack/test_setup.js +2 -0
- metadata +24 -5
- data/config/katello.yaml +0 -89
- data/webpack/services/api/fixtures.js +0 -353
data/webpack/scenes/Subscriptions/Manifest/__tests__/__snapshots__/ManageManifestModal.test.js.snap
CHANGED
@@ -73,14 +73,19 @@ exports[`manage manifest modal should render 1`] = `
|
|
73
73
|
<FormGroup
|
74
74
|
bsClass="form-group"
|
75
75
|
>
|
76
|
-
<
|
77
|
-
bsClass="
|
78
|
-
|
79
|
-
|
80
|
-
srOnly={false}
|
76
|
+
<Col
|
77
|
+
bsClass="col"
|
78
|
+
componentClass="div"
|
79
|
+
sm={3}
|
81
80
|
>
|
82
|
-
|
83
|
-
|
81
|
+
<ControlLabel
|
82
|
+
bsClass="control-label"
|
83
|
+
htmlFor="cdnUrl"
|
84
|
+
srOnly={false}
|
85
|
+
>
|
86
|
+
Red Hat CDN URL
|
87
|
+
</ControlLabel>
|
88
|
+
</Col>
|
84
89
|
<Col
|
85
90
|
bsClass="col"
|
86
91
|
componentClass="div"
|
@@ -96,6 +101,27 @@ exports[`manage manifest modal should render 1`] = `
|
|
96
101
|
/>
|
97
102
|
</Col>
|
98
103
|
</FormGroup>
|
104
|
+
<FormGroup
|
105
|
+
bsClass="form-group"
|
106
|
+
>
|
107
|
+
<Col
|
108
|
+
bsClass="col"
|
109
|
+
componentClass="div"
|
110
|
+
sm={3}
|
111
|
+
smOffset={3}
|
112
|
+
>
|
113
|
+
<Button
|
114
|
+
active={false}
|
115
|
+
block={false}
|
116
|
+
bsClass="btn"
|
117
|
+
bsStyle="default"
|
118
|
+
disabled={false}
|
119
|
+
onClick={[Function]}
|
120
|
+
>
|
121
|
+
Update
|
122
|
+
</Button>
|
123
|
+
</Col>
|
124
|
+
</FormGroup>
|
99
125
|
<br />
|
100
126
|
<h5>
|
101
127
|
Subscription Manifest
|
@@ -215,6 +241,7 @@ exports[`manage manifest modal should render 1`] = `
|
|
215
241
|
<LoadingState
|
216
242
|
loading={false}
|
217
243
|
loadingText="Loading"
|
244
|
+
timeout={300}
|
218
245
|
>
|
219
246
|
<Table
|
220
247
|
columns={
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import Immutable from 'seamless-immutable';
|
2
|
+
import { toastErrorAction, failureAction } from '../../../../services/api/testHelpers';
|
2
3
|
|
3
4
|
export const manifestHistoryInitialState = Immutable({
|
4
5
|
loading: true,
|
@@ -149,10 +150,8 @@ export const manifestHistoryFailureActions = [
|
|
149
150
|
{
|
150
151
|
type: 'MANIFEST_HISTORY_REQUEST',
|
151
152
|
},
|
152
|
-
|
153
|
-
|
154
|
-
type: 'MANIFEST_HISTORY_FAILURE',
|
155
|
-
},
|
153
|
+
failureAction('MANIFEST_HISTORY_FAILURE'),
|
154
|
+
toastErrorAction(),
|
156
155
|
];
|
157
156
|
|
158
157
|
export const uploadManifestSuccessActions = [
|
@@ -169,10 +168,8 @@ export const uploadManifestFailureActions = [
|
|
169
168
|
{
|
170
169
|
type: 'UPLOAD_MANIFEST_REQUEST',
|
171
170
|
},
|
172
|
-
|
173
|
-
|
174
|
-
type: 'UPLOAD_MANIFEST_FAILURE',
|
175
|
-
},
|
171
|
+
failureAction('UPLOAD_MANIFEST_FAILURE'),
|
172
|
+
toastErrorAction(),
|
176
173
|
];
|
177
174
|
|
178
175
|
export const refreshManifestSuccessActions = [
|
@@ -189,10 +186,8 @@ export const refreshManifestFailureActions = [
|
|
189
186
|
{
|
190
187
|
type: 'REFRESH_MANIFEST_REQUEST',
|
191
188
|
},
|
192
|
-
|
193
|
-
|
194
|
-
type: 'REFRESH_MANIFEST_FAILURE',
|
195
|
-
},
|
189
|
+
failureAction('REFRESH_MANIFEST_FAILURE'),
|
190
|
+
toastErrorAction(),
|
196
191
|
];
|
197
192
|
|
198
193
|
export const deleteManifestSuccessActions = [
|
@@ -209,8 +204,6 @@ export const deleteManifestFailureActions = [
|
|
209
204
|
{
|
210
205
|
type: 'DELETE_MANIFEST_REQUEST',
|
211
206
|
},
|
212
|
-
|
213
|
-
|
214
|
-
type: 'DELETE_MANIFEST_FAILURE',
|
215
|
-
},
|
207
|
+
failureAction('DELETE_MANIFEST_FAILURE'),
|
208
|
+
toastErrorAction(),
|
216
209
|
];
|
@@ -15,6 +15,7 @@ import './Manifest.scss';
|
|
15
15
|
const mapStateToProps = state => ({
|
16
16
|
organization: state.katello.organization,
|
17
17
|
manifestHistory: state.katello.manifestHistory,
|
18
|
+
taskDetails: state.katello.manifestHistory.taskDetails,
|
18
19
|
});
|
19
20
|
|
20
21
|
// map action dispatchers to props
|
@@ -16,7 +16,7 @@ import {
|
|
16
16
|
DELETE_SUBSCRIPTIONS_FAILURE,
|
17
17
|
} from './SubscriptionConstants';
|
18
18
|
import { filterRHSubscriptions } from './SubscriptionHelpers.js';
|
19
|
-
import {
|
19
|
+
import { apiError } from '../../move_to_foreman/common/helpers.js';
|
20
20
|
|
21
21
|
export const createSubscriptionParams = (extendedParams = {}) => ({
|
22
22
|
...{ organization_id: orgId() },
|
@@ -35,12 +35,7 @@ export const loadAvailableQuantities = (extendedParams = {}) => (dispatch) => {
|
|
35
35
|
response: data,
|
36
36
|
});
|
37
37
|
})
|
38
|
-
.catch(
|
39
|
-
dispatch({
|
40
|
-
type: SUBSCRIPTIONS_QUANTITIES_FAILURE,
|
41
|
-
error: getResponseError(result.response),
|
42
|
-
});
|
43
|
-
});
|
38
|
+
.catch(result => dispatch(apiError(SUBSCRIPTIONS_QUANTITIES_FAILURE, result)));
|
44
39
|
};
|
45
40
|
|
46
41
|
export const loadSubscriptions = (extendedParams = {}) => (dispatch) => {
|
@@ -60,12 +55,7 @@ export const loadSubscriptions = (extendedParams = {}) => (dispatch) => {
|
|
60
55
|
dispatch(loadAvailableQuantities({ poolIds }));
|
61
56
|
}
|
62
57
|
})
|
63
|
-
.catch(
|
64
|
-
dispatch({
|
65
|
-
type: SUBSCRIPTIONS_FAILURE,
|
66
|
-
error: getResponseError(result.response),
|
67
|
-
});
|
68
|
-
});
|
58
|
+
.catch(result => dispatch(apiError(SUBSCRIPTIONS_FAILURE, result)));
|
69
59
|
};
|
70
60
|
|
71
61
|
export const updateQuantity = (quantities = {}) => (dispatch) => {
|
@@ -83,12 +73,7 @@ export const updateQuantity = (quantities = {}) => (dispatch) => {
|
|
83
73
|
response: data,
|
84
74
|
});
|
85
75
|
})
|
86
|
-
.catch(
|
87
|
-
dispatch({
|
88
|
-
type: UPDATE_QUANTITY_FAILURE,
|
89
|
-
error: getResponseError(result.response),
|
90
|
-
});
|
91
|
-
});
|
76
|
+
.catch(result => dispatch(apiError(UPDATE_QUANTITY_FAILURE, result)));
|
92
77
|
};
|
93
78
|
|
94
79
|
export const deleteSubscriptions = poolIds => (dispatch) => {
|
@@ -106,13 +91,7 @@ export const deleteSubscriptions = poolIds => (dispatch) => {
|
|
106
91
|
response: data,
|
107
92
|
});
|
108
93
|
})
|
109
|
-
.catch(
|
110
|
-
dispatch({
|
111
|
-
type: DELETE_SUBSCRIPTIONS_FAILURE,
|
112
|
-
result,
|
113
|
-
});
|
114
|
-
});
|
94
|
+
.catch(result => dispatch(apiError(DELETE_SUBSCRIPTIONS_FAILURE, result)));
|
115
95
|
};
|
116
96
|
|
117
|
-
|
118
97
|
export default loadSubscriptions;
|
@@ -21,6 +21,7 @@ export const BLOCKING_FOREMAN_TASK_TYPES = [
|
|
21
21
|
'Actions::Katello::UpstreamSubscriptions::BindEntitlements',
|
22
22
|
'Actions::Katello::UpstreamSubscriptions::UpdateEntitlement',
|
23
23
|
'Actions::Katello::UpstreamSubscriptions::RemoveEntitlements',
|
24
|
+
'Actions::Katello::UpstreamSubscriptions::UpdateEntitlements',
|
24
25
|
];
|
25
26
|
|
26
27
|
export const MANIFEST_TASKS_BULK_SEARCH_ID = 'activeManifestTasksSearch';
|
@@ -1,3 +1,6 @@
|
|
1
1
|
// eslint-disable-next-line import/prefer-default-export
|
2
2
|
export const filterRHSubscriptions = subscriptions =>
|
3
3
|
subscriptions.filter(sub => sub.available >= 0);
|
4
|
+
|
5
|
+
export const manifestExists = organization =>
|
6
|
+
organization.owner_details && organization.owner_details.upstreamConsumer;
|
@@ -23,7 +23,7 @@ import { GET_SETTING_SUCCESS } from '../../move_to_foreman/Settings/SettingsCons
|
|
23
23
|
const initialState = Immutable({
|
24
24
|
...initialApiState,
|
25
25
|
quantitiesLoading: false,
|
26
|
-
availableQuantities:
|
26
|
+
availableQuantities: null,
|
27
27
|
tasks: [],
|
28
28
|
});
|
29
29
|
|
@@ -73,15 +73,22 @@ export default (state = initialState, action) => {
|
|
73
73
|
return state.set('loading', false);
|
74
74
|
|
75
75
|
case SUBSCRIPTIONS_FAILURE:
|
76
|
+
return state
|
77
|
+
.set('loading', false)
|
78
|
+
.set('results', [])
|
79
|
+
.set('itemCount', 0);
|
80
|
+
|
76
81
|
case UPDATE_QUANTITY_FAILURE:
|
77
82
|
case DELETE_SUBSCRIPTIONS_FAILURE:
|
78
83
|
return state.merge({
|
79
|
-
error: action.error,
|
80
84
|
loading: false,
|
81
85
|
});
|
82
86
|
|
83
87
|
case SUBSCRIPTIONS_QUANTITIES_REQUEST:
|
84
|
-
return state.
|
88
|
+
return state.merge({
|
89
|
+
quantitiesLoading: true,
|
90
|
+
availableQuantities: null,
|
91
|
+
});
|
85
92
|
|
86
93
|
case SUBSCRIPTIONS_QUANTITIES_SUCCESS: {
|
87
94
|
return state.merge({
|
@@ -93,7 +100,7 @@ export default (state = initialState, action) => {
|
|
93
100
|
case SUBSCRIPTIONS_QUANTITIES_FAILURE: {
|
94
101
|
return state.merge({
|
95
102
|
quantitiesLoading: false,
|
96
|
-
|
103
|
+
availableQuantities: {},
|
97
104
|
});
|
98
105
|
}
|
99
106
|
|
@@ -1,15 +1,14 @@
|
|
1
1
|
import React, { Component } from 'react';
|
2
|
-
import ReactDOMServer from 'react-dom/server';
|
3
2
|
import PropTypes from 'prop-types';
|
4
3
|
import { LinkContainer } from 'react-router-bootstrap';
|
5
4
|
import { Grid, Row, Col, Form, FormGroup } from 'react-bootstrap';
|
6
5
|
import { Button } from 'patternfly-react';
|
7
6
|
import TooltipButton from 'react-bootstrap-tooltip-button';
|
8
|
-
import {
|
9
|
-
import helpers from '../../move_to_foreman/common/helpers';
|
7
|
+
import { renderTaskFinishedToast } from '../Tasks/helpers';
|
10
8
|
import ModalProgressBar from '../../move_to_foreman/components/common/ModalProgressBar';
|
11
9
|
import ManageManifestModal from './Manifest/';
|
12
10
|
import { SubscriptionsTable } from './components/SubscriptionsTable';
|
11
|
+
import { manifestExists } from './SubscriptionHelpers';
|
13
12
|
import Search from '../../components/Search/index';
|
14
13
|
import api, { orgId } from '../../services/api';
|
15
14
|
import { createSubscriptionParams } from './SubscriptionActions.js';
|
@@ -70,7 +69,7 @@ class SubscriptionsPage extends Component {
|
|
70
69
|
}
|
71
70
|
|
72
71
|
getDisabledReason(deleteButton) {
|
73
|
-
const { tasks = [], subscriptions } = this.props;
|
72
|
+
const { tasks = [], subscriptions, organization } = this.props;
|
74
73
|
const { disconnected } = subscriptions;
|
75
74
|
let disabledReason = null;
|
76
75
|
|
@@ -79,7 +78,9 @@ class SubscriptionsPage extends Component {
|
|
79
78
|
} else if (tasks.length > 0) {
|
80
79
|
disabledReason = __('This is disabled because a manifest related task is in progress.');
|
81
80
|
} else if (deleteButton && !disabledReason) {
|
82
|
-
disabledReason = __('This is disabled because no subscriptions are selected');
|
81
|
+
disabledReason = __('This is disabled because no subscriptions are selected.');
|
82
|
+
} else if (!manifestExists(organization)) {
|
83
|
+
disabledReason = __('This is disabled because no manifest has been uploaded.');
|
83
84
|
}
|
84
85
|
|
85
86
|
return disabledReason;
|
@@ -103,39 +104,14 @@ class SubscriptionsPage extends Component {
|
|
103
104
|
|
104
105
|
pollTaskUntilDone(taskToPoll.id, {}, POLL_TASK_INTERVAL)
|
105
106
|
.then((task) => {
|
106
|
-
|
107
|
-
return (
|
108
|
-
<ul>
|
109
|
-
{task.humanized.errors.map(error => (
|
110
|
-
<li key={error}> {error} </li>
|
111
|
-
))}
|
112
|
-
</ul>
|
113
|
-
);
|
114
|
-
}
|
115
|
-
|
116
|
-
const message = (
|
117
|
-
<span>
|
118
|
-
<span>
|
119
|
-
{`${__(`Task ${task.humanized.action} completed with a result of ${task.result}.`)} `}
|
120
|
-
</span>
|
121
|
-
{task.errors ? getErrors() : ''}
|
122
|
-
<a href={helpers.urlBuilder('foreman_tasks/tasks', '', task.id)}>
|
123
|
-
{__('Click here to go to the tasks page for the task.')}
|
124
|
-
</a>
|
125
|
-
</span>
|
126
|
-
);
|
127
|
-
|
128
|
-
notify({
|
129
|
-
message: ReactDOMServer.renderToStaticMarkup(message),
|
130
|
-
type: task.result,
|
131
|
-
});
|
132
|
-
|
107
|
+
renderTaskFinishedToast(task);
|
133
108
|
loadSubscriptions();
|
134
109
|
});
|
135
110
|
}
|
136
111
|
|
137
112
|
render() {
|
138
|
-
const { tasks = [], subscriptions } = this.props;
|
113
|
+
const { tasks = [], subscriptions, organization } = this.props;
|
114
|
+
const currentOrg = orgId();
|
139
115
|
const { disconnected } = subscriptions;
|
140
116
|
const taskInProgress = tasks.length > 0;
|
141
117
|
const disableManifestActions = taskInProgress || disconnected;
|
@@ -153,7 +129,7 @@ class SubscriptionsPage extends Component {
|
|
153
129
|
const getAutoCompleteParams = search => ({
|
154
130
|
endpoint: '/subscriptions/auto_complete_search',
|
155
131
|
params: {
|
156
|
-
organization_id:
|
132
|
+
organization_id: currentOrg,
|
157
133
|
search,
|
158
134
|
},
|
159
135
|
});
|
@@ -187,8 +163,18 @@ class SubscriptionsPage extends Component {
|
|
187
163
|
this.setState({ disableDeleteButton: !rowsSelected });
|
188
164
|
};
|
189
165
|
|
166
|
+
|
190
167
|
const csvParams = createSubscriptionParams({ search: this.state.searchQuery });
|
191
168
|
|
169
|
+
const emptyStateData = {
|
170
|
+
header: __('There are no Subscriptions to display'),
|
171
|
+
description: __('Import a Manifest to manage your Entitlements.'),
|
172
|
+
action: {
|
173
|
+
onClick: showManageManifestModal,
|
174
|
+
title: __('Import a Manifest'),
|
175
|
+
},
|
176
|
+
};
|
177
|
+
|
192
178
|
return (
|
193
179
|
<Grid bsClass="container-fluid">
|
194
180
|
<Row>
|
@@ -208,7 +194,10 @@ class SubscriptionsPage extends Component {
|
|
208
194
|
|
209
195
|
<div className="toolbar-pf-action-right">
|
210
196
|
<FormGroup>
|
211
|
-
<LinkContainer
|
197
|
+
<LinkContainer
|
198
|
+
to="subscriptions/add"
|
199
|
+
disabled={disableManifestActions || !manifestExists(organization)}
|
200
|
+
>
|
212
201
|
<TooltipButton
|
213
202
|
tooltipId="add-subscriptions-button-tooltip"
|
214
203
|
tooltipText={this.getDisabledReason()}
|
@@ -257,11 +246,14 @@ class SubscriptionsPage extends Component {
|
|
257
246
|
<SubscriptionsTable
|
258
247
|
loadSubscriptions={this.props.loadSubscriptions}
|
259
248
|
updateQuantity={this.props.updateQuantity}
|
249
|
+
emptyState={emptyStateData}
|
260
250
|
subscriptions={this.props.subscriptions}
|
261
251
|
subscriptionDeleteModalOpen={this.state.subscriptionDeleteModalOpen}
|
262
252
|
onSubscriptionDeleteModalClose={onSubscriptionDeleteModalClose}
|
263
253
|
onDeleteSubscriptions={onDeleteSubscriptions}
|
264
254
|
toggleDeleteButton={toggleDeleteButton}
|
255
|
+
task={task}
|
256
|
+
bulkSearch={this.props.bulkSearch}
|
265
257
|
/>
|
266
258
|
<ModalProgressBar
|
267
259
|
show={this.state.showTaskModal}
|
@@ -279,8 +271,10 @@ class SubscriptionsPage extends Component {
|
|
279
271
|
SubscriptionsPage.propTypes = {
|
280
272
|
loadSubscriptions: PropTypes.func.isRequired,
|
281
273
|
updateQuantity: PropTypes.func.isRequired,
|
282
|
-
subscriptions: PropTypes.shape().isRequired,
|
274
|
+
subscriptions: PropTypes.shape({}).isRequired,
|
275
|
+
organization: PropTypes.shape({}).isRequired,
|
283
276
|
pollBulkSearch: PropTypes.func.isRequired,
|
277
|
+
bulkSearch: PropTypes.func,
|
284
278
|
pollTaskUntilDone: PropTypes.func.isRequired,
|
285
279
|
loadSetting: PropTypes.func.isRequired,
|
286
280
|
tasks: PropTypes.arrayOf(PropTypes.shape({})),
|
@@ -289,6 +283,7 @@ SubscriptionsPage.propTypes = {
|
|
289
283
|
|
290
284
|
SubscriptionsPage.defaultProps = {
|
291
285
|
tasks: [],
|
286
|
+
bulkSearch: undefined,
|
292
287
|
};
|
293
288
|
|
294
289
|
export default SubscriptionsPage;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import api, { orgId } from '../../../services/api';
|
2
2
|
import { propsToSnakeCase } from '../../../services/index';
|
3
|
+
import { apiError } from '../../../move_to_foreman/common/helpers.js';
|
3
4
|
|
4
5
|
import {
|
5
6
|
UPSTREAM_SUBSCRIPTIONS_REQUEST,
|
@@ -27,12 +28,7 @@ export const loadUpstreamSubscriptions = (extendedParams = {}) => (dispatch) =>
|
|
27
28
|
search: extendedParams.search,
|
28
29
|
});
|
29
30
|
})
|
30
|
-
.catch(
|
31
|
-
dispatch({
|
32
|
-
type: UPSTREAM_SUBSCRIPTIONS_FAILURE,
|
33
|
-
result,
|
34
|
-
});
|
35
|
-
});
|
31
|
+
.catch(result => dispatch(apiError(UPSTREAM_SUBSCRIPTIONS_FAILURE, result)));
|
36
32
|
};
|
37
33
|
|
38
34
|
export const saveUpstreamSubscriptions = upstreamSubscriptions => (dispatch) => {
|
@@ -50,12 +46,7 @@ export const saveUpstreamSubscriptions = upstreamSubscriptions => (dispatch) =>
|
|
50
46
|
response: data,
|
51
47
|
});
|
52
48
|
})
|
53
|
-
.catch(
|
54
|
-
dispatch({
|
55
|
-
type: SAVE_UPSTREAM_SUBSCRIPTIONS_FAILURE,
|
56
|
-
result,
|
57
|
-
});
|
58
|
-
});
|
49
|
+
.catch(result => dispatch(apiError(SAVE_UPSTREAM_SUBSCRIPTIONS_FAILURE, result)));
|
59
50
|
};
|
60
51
|
|
61
52
|
export default loadUpstreamSubscriptions;
|
@@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
|
|
5
5
|
import { LinkContainer } from 'react-router-bootstrap';
|
6
6
|
import { Grid, Row, Col } from 'react-bootstrap';
|
7
7
|
import { bindMethods, Button } from 'patternfly-react';
|
8
|
+
import BreadcrumbsBar from 'foremanReact/components/BreadcrumbBar';
|
8
9
|
import { LoadingState } from '../../../move_to_pf/LoadingState';
|
9
10
|
import { notify } from '../../../move_to_foreman/foreman_toast_notifications';
|
10
11
|
import helpers from '../../../move_to_foreman/common/helpers';
|
@@ -22,6 +23,7 @@ class UpstreamSubscriptionsPage extends Component {
|
|
22
23
|
bindMethods(this, [
|
23
24
|
'onChange',
|
24
25
|
'saveUpstreamSubscriptions',
|
26
|
+
'quantityValidationInput',
|
25
27
|
]);
|
26
28
|
}
|
27
29
|
|
@@ -31,18 +33,17 @@ class UpstreamSubscriptionsPage extends Component {
|
|
31
33
|
|
32
34
|
onChange(value, rowData) {
|
33
35
|
const { selectedRows } = this.state;
|
34
|
-
const newValue = parseInt(value, 10);
|
35
36
|
const pool = {
|
36
37
|
...rowData,
|
37
38
|
id: rowData.id,
|
38
|
-
updatedQuantity:
|
39
|
+
updatedQuantity: value,
|
39
40
|
selected: true,
|
40
41
|
};
|
41
42
|
|
42
43
|
const match = this.poolInSelectedRows(pool);
|
43
44
|
const index = _.indexOf(selectedRows, match);
|
44
45
|
|
45
|
-
if (
|
46
|
+
if (value) {
|
46
47
|
if (match) {
|
47
48
|
selectedRows.splice(index, 1, pool);
|
48
49
|
} else {
|
@@ -55,6 +56,25 @@ class UpstreamSubscriptionsPage extends Component {
|
|
55
56
|
this.setState({ selectedRows });
|
56
57
|
}
|
57
58
|
|
59
|
+
// eslint-disable-next-line class-methods-use-this
|
60
|
+
quantityValidation(pool) {
|
61
|
+
const origQuantity = pool.updatedQuantity;
|
62
|
+
if (origQuantity && helpers.stringIsInteger(origQuantity)) {
|
63
|
+
const parsedQuantity = parseInt(origQuantity, 10);
|
64
|
+
const aboveZeroMsg = [false, __('Please enter a positive number above zero')];
|
65
|
+
|
66
|
+
if (parsedQuantity.toString().length > 10) return [false, __('Please limit number to 10 digits')];
|
67
|
+
if (!pool.available) return [false, __('No pools available')];
|
68
|
+
// handling unlimited subscriptions, they show as -1
|
69
|
+
if (pool.available === -1) return parsedQuantity ? [true, ''] : aboveZeroMsg;
|
70
|
+
if (parsedQuantity > pool.available) return [false, __(`Quantity must not be above ${pool.available}`)];
|
71
|
+
if (parsedQuantity <= 0) return aboveZeroMsg;
|
72
|
+
} else {
|
73
|
+
return [false, __('Please enter digits only')];
|
74
|
+
}
|
75
|
+
return [true, ''];
|
76
|
+
}
|
77
|
+
|
58
78
|
poolInSelectedRows(pool) {
|
59
79
|
const { selectedRows } = this.state;
|
60
80
|
|
@@ -64,16 +84,30 @@ class UpstreamSubscriptionsPage extends Component {
|
|
64
84
|
);
|
65
85
|
}
|
66
86
|
|
87
|
+
quantityValidationInput(pool) {
|
88
|
+
if (!pool || pool.updatedQuantity === undefined) return null;
|
89
|
+
if (this.quantityValidation(pool)[0]) {
|
90
|
+
return 'success';
|
91
|
+
}
|
92
|
+
return 'error';
|
93
|
+
}
|
94
|
+
|
95
|
+
validateSelectedRows() {
|
96
|
+
return Array.isArray(this.state.selectedRows) &&
|
97
|
+
this.state.selectedRows.length &&
|
98
|
+
this.state.selectedRows.every(pool => this.quantityValidation(pool)[0]);
|
99
|
+
}
|
100
|
+
|
67
101
|
saveUpstreamSubscriptions() {
|
68
102
|
const updatedPools = _.map(
|
69
103
|
this.state.selectedRows,
|
70
|
-
pool => ({ ...pool, quantity: pool.updatedQuantity }),
|
104
|
+
pool => ({ ...pool, quantity: parseInt(pool.updatedQuantity, 10) }),
|
71
105
|
);
|
72
106
|
|
73
107
|
const updatedSubscriptions = { pools: updatedPools };
|
74
108
|
|
75
109
|
this.props.saveUpstreamSubscriptions(updatedSubscriptions).then(() => {
|
76
|
-
const { task
|
110
|
+
const { task } = this.props.upstreamSubscriptions;
|
77
111
|
|
78
112
|
// TODO: could probably factor this out into a task response component
|
79
113
|
if (task) {
|
@@ -88,18 +122,6 @@ class UpstreamSubscriptionsPage extends Component {
|
|
88
122
|
|
89
123
|
notify({ message: ReactDOMServer.renderToStaticMarkup(message), type: 'success' });
|
90
124
|
this.props.history.push('/subscriptions');
|
91
|
-
} else {
|
92
|
-
let errorMessages = [];
|
93
|
-
|
94
|
-
if (error.errors) {
|
95
|
-
errorMessages = error.errors;
|
96
|
-
} else if (error.message) {
|
97
|
-
errorMessages.push(error.message);
|
98
|
-
}
|
99
|
-
|
100
|
-
for (let i = 0; i < errorMessages.length; i += 1) {
|
101
|
-
notify({ message: errorMessages[i], type: 'error' });
|
102
|
-
}
|
103
125
|
}
|
104
126
|
});
|
105
127
|
}
|
@@ -121,7 +143,8 @@ class UpstreamSubscriptionsPage extends Component {
|
|
121
143
|
<Button
|
122
144
|
bsStyle="primary"
|
123
145
|
type="submit"
|
124
|
-
disabled={upstreamSubscriptions.loading
|
146
|
+
disabled={upstreamSubscriptions.loading ||
|
147
|
+
!this.validateSelectedRows()}
|
125
148
|
onClick={this.saveUpstreamSubscriptions}
|
126
149
|
>
|
127
150
|
{__('Submit')}
|
@@ -170,13 +193,9 @@ class UpstreamSubscriptionsPage extends Component {
|
|
170
193
|
description: __('Subscription Allocations allow you to export subscriptions from the Red Hat Customer Portal to ' +
|
171
194
|
'an on-premise subscription management application such as Red Hat Satellite.'),
|
172
195
|
docUrl: 'http://redhat.com',
|
173
|
-
documentation: {
|
174
|
-
title: __('Learn more about Subscription Allocations'),
|
175
|
-
url: 'http://redhat.com',
|
176
|
-
},
|
177
196
|
action: {
|
178
|
-
title: __('
|
179
|
-
url: '
|
197
|
+
title: __('Import a Manifest to Begin'),
|
198
|
+
url: '/subscriptions',
|
180
199
|
},
|
181
200
|
});
|
182
201
|
|
@@ -212,7 +231,19 @@ class UpstreamSubscriptionsPage extends Component {
|
|
212
231
|
|
213
232
|
return (
|
214
233
|
<Grid bsClass="container-fluid">
|
215
|
-
<
|
234
|
+
<BreadcrumbsBar data={{
|
235
|
+
isSwitchable: false,
|
236
|
+
breadcrumbItems: [
|
237
|
+
{
|
238
|
+
caption: __('Subscriptions'),
|
239
|
+
onClick: () => this.props.history.push('/subscriptions'),
|
240
|
+
},
|
241
|
+
{
|
242
|
+
caption: __('Add Subscriptions'),
|
243
|
+
},
|
244
|
+
],
|
245
|
+
}}
|
246
|
+
/>
|
216
247
|
|
217
248
|
<LoadingState loading={upstreamSubscriptions.loading} loadingText={__('Loading')}>
|
218
249
|
<Row>
|
@@ -235,13 +266,12 @@ class UpstreamSubscriptionsPage extends Component {
|
|
235
266
|
}
|
236
267
|
|
237
268
|
UpstreamSubscriptionsPage.propTypes = {
|
238
|
-
history: PropTypes.shape({ push: PropTypes.func }).isRequired,
|
239
269
|
loadUpstreamSubscriptions: PropTypes.func.isRequired,
|
240
270
|
saveUpstreamSubscriptions: PropTypes.func.isRequired,
|
241
271
|
upstreamSubscriptions: PropTypes.shape({
|
242
272
|
task: PropTypes.shape({}),
|
243
|
-
error: PropTypes.shape({}),
|
244
273
|
}).isRequired,
|
274
|
+
history: PropTypes.shape({ push: PropTypes.func.isRequired }).isRequired,
|
245
275
|
};
|
246
276
|
|
247
277
|
export default UpstreamSubscriptionsPage;
|
@@ -38,7 +38,7 @@ export default (state = initialState, action) => {
|
|
38
38
|
|
39
39
|
case UPSTREAM_SUBSCRIPTIONS_FAILURE:
|
40
40
|
return state.merge({
|
41
|
-
error: action.
|
41
|
+
error: action.payload.message,
|
42
42
|
loading: false,
|
43
43
|
});
|
44
44
|
|
@@ -49,8 +49,7 @@ export default (state = initialState, action) => {
|
|
49
49
|
return state.set('task', action.response).set('loading', false);
|
50
50
|
|
51
51
|
case SAVE_UPSTREAM_SUBSCRIPTIONS_FAILURE: {
|
52
|
-
|
53
|
-
return state.set('error', error).set('loading', false);
|
52
|
+
return state.set('error', action.payload.message).set('loading', false);
|
54
53
|
}
|
55
54
|
|
56
55
|
default:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
2
|
+
import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap';
|
3
3
|
import helpers from '../../../move_to_foreman/common/helpers';
|
4
4
|
import {
|
5
5
|
headerFormatter,
|
@@ -71,7 +71,7 @@ export const columns = (controller, selectionController) => [
|
|
71
71
|
},
|
72
72
|
},
|
73
73
|
{
|
74
|
-
property: '
|
74
|
+
property: 'available',
|
75
75
|
header: {
|
76
76
|
label: __('Available Entitlements'),
|
77
77
|
formatters: [headerFormatter],
|
@@ -96,22 +96,27 @@ export const columns = (controller, selectionController) => [
|
|
96
96
|
formatters: [
|
97
97
|
(value, { rowData }) => (
|
98
98
|
<td>
|
99
|
-
<FormGroup
|
99
|
+
<FormGroup
|
100
|
+
validationState={controller.quantityValidationInput(rowData)}
|
101
|
+
>
|
100
102
|
<ControlLabel srOnly>{__('Number to Allocate')}</ControlLabel>
|
101
103
|
<FormControl
|
102
104
|
type="text"
|
103
105
|
onBlur={e => controller.onChange(e.target.value, rowData)}
|
104
106
|
defaultValue={rowData.updatedQuantity}
|
107
|
+
onChange={(e) => {
|
108
|
+
controller.onChange(e.target.value, rowData);
|
109
|
+
}}
|
105
110
|
onKeyDown={(e) => {
|
106
111
|
const key = e.charCode ? e.charCode : e.keyCode;
|
107
112
|
if (key === 13) {
|
108
|
-
controller.onChange(e.target.value, rowData);
|
109
113
|
controller.saveUpstreamSubscriptions();
|
110
114
|
e.preventDefault();
|
111
115
|
}
|
112
116
|
}}
|
113
117
|
/>
|
114
|
-
|
118
|
+
{controller.quantityValidationInput(rowData) === 'error' &&
|
119
|
+
<HelpBlock>{controller.quantityValidation(rowData)[1]}</HelpBlock>}
|
115
120
|
</FormGroup>
|
116
121
|
</td>
|
117
122
|
),
|