katello 3.7.0.rc1 → 3.7.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.

Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/v2/content_views_controller.rb +5 -2
  3. data/app/controllers/katello/api/v2/environments_controller.rb +8 -3
  4. data/app/controllers/katello/api/v2/host_tracer_controller.rb +1 -1
  5. data/app/controllers/katello/api/v2/upstream_subscriptions_controller.rb +4 -4
  6. data/app/helpers/katello/hosts_and_hostgroups_helper.rb +5 -22
  7. data/app/lib/actions/katello/host/update_content_overrides.rb +1 -0
  8. data/app/lib/actions/katello/product/content_create.rb +1 -0
  9. data/app/lib/actions/katello/product/repositories_certs_reset.rb +25 -0
  10. data/app/lib/actions/katello/product/update.rb +6 -0
  11. data/app/lib/katello/resources/candlepin/activation_key.rb +8 -4
  12. data/app/lib/katello/resources/candlepin/product.rb +2 -1
  13. data/app/lib/katello/util/cdn_var_substitutor.rb +5 -3
  14. data/app/lib/katello/util/package.rb +21 -13
  15. data/app/lib/katello/util/package_filter.rb +33 -31
  16. data/app/lib/katello/validators/prior_validator.rb +6 -10
  17. data/app/models/katello/concerns/host_managed_extensions.rb +2 -0
  18. data/app/models/katello/concerns/organization_extensions.rb +1 -0
  19. data/app/models/katello/concerns/subscription_facet_host_extensions.rb +10 -6
  20. data/app/models/katello/content.rb +23 -2
  21. data/app/models/katello/content_view_docker_filter.rb +1 -1
  22. data/app/models/katello/content_view_puppet_module.rb +3 -3
  23. data/app/models/katello/content_view_version.rb +4 -0
  24. data/app/models/katello/environment_prior.rb +7 -0
  25. data/app/models/katello/glue/candlepin/candlepin_object.rb +2 -2
  26. data/app/models/katello/glue/candlepin/pool.rb +10 -13
  27. data/app/models/katello/glue/candlepin/product.rb +19 -9
  28. data/app/models/katello/glue/candlepin/repository.rb +16 -0
  29. data/app/models/katello/glue/candlepin/subscription.rb +1 -1
  30. data/app/models/katello/glue/provider.rb +15 -81
  31. data/app/models/katello/host/subscription_facet.rb +1 -1
  32. data/app/models/katello/kt_environment.rb +39 -8
  33. data/app/models/katello/pool.rb +2 -1
  34. data/app/models/katello/rpm.rb +144 -2
  35. data/app/models/katello/upstream_pool.rb +7 -10
  36. data/app/services/katello/candlepin/pool_service.rb +18 -3
  37. data/app/services/katello/ui_notifications/pulp/proxy_disk_space.rb +13 -16
  38. data/app/views/dashboard/_content_views_widget.html.erb +3 -3
  39. data/app/views/dashboard/_errata_widget.html.erb +2 -2
  40. data/app/views/dashboard/_host_collection_widget.html.erb +3 -3
  41. data/app/views/dashboard/_subscription_status_widget.html.erb +2 -2
  42. data/app/views/dashboard/_subscription_widget.html.erb +1 -1
  43. data/app/views/dashboard/_sync_widget.html.erb +3 -3
  44. data/app/views/katello/api/v2/subscriptions/base.json.rabl +1 -1
  45. data/app/views/katello/api/v2/upstream_subscriptions/base.json.rabl +2 -6
  46. data/app/views/katello/layouts/react.html.erb +3 -3
  47. data/config/katello.yaml +89 -0
  48. data/config/routes.rb +3 -0
  49. data/db/migrate/20160302091113_change_environment_prior.rb +9 -0
  50. data/db/migrate/20180410140909_add_organization_id_to_pool.rb +2 -1
  51. data/db/migrate/20180612163403_add_foreign_key_to_hypervisor_id.rb +10 -0
  52. data/db/migrate/20180612164926_add_content_org_id.rb +39 -0
  53. data/db/migrate/20180612165011_remove_content_fields_from_host.rb +7 -0
  54. data/db/migrate/20180626160422_add_upstream_pool_id_to_katello_pool.rb +9 -0
  55. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-packages-modal.html +1 -1
  56. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register.html +1 -1
  57. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/environment-content.controller.js +2 -3
  58. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment.html +4 -2
  59. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/environments.controller.js +19 -14
  60. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/new-environment.controller.js +18 -5
  61. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/paths.service.js +51 -0
  62. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/views/new-environment.html +16 -3
  63. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/products-bulk-advanced-sync-modal.controller.js +1 -1
  64. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/products.controller.js +2 -2
  65. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/subscriptions.routes.js +3 -3
  66. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/tasks.module.js +1 -6
  67. data/engines/bastion_katello/app/assets/stylesheets/bastion_katello/bastion_katello.scss +4 -0
  68. data/engines/bastion_katello/lib/bastion_katello/engine.rb +1 -1
  69. data/lib/katello/plugin.rb +2 -11
  70. data/lib/katello/scheduled_jobs.rb +2 -14
  71. data/lib/katello/tasks/clean_backend_objects.rake +2 -0
  72. data/lib/katello/tasks/repository.rake +11 -2
  73. data/lib/katello/tasks/upgrades/3.7/import_pools.rake +12 -0
  74. data/lib/katello/version.rb +1 -1
  75. data/package.json +4 -3
  76. data/webpack/components/PaginationRow/index.js +6 -2
  77. data/webpack/containers/Application/config.js +7 -2
  78. data/webpack/index.js +3 -5
  79. data/webpack/move_to_foreman/common/helpers.js +5 -24
  80. data/webpack/move_to_foreman/components/common/emptyState/index.js +12 -7
  81. data/webpack/move_to_foreman/components/common/table/components/CollapseSubscriptionGroupButton.js +31 -0
  82. data/webpack/move_to_foreman/components/common/table/components/CollapseSubscriptionGroupButton.test.js +16 -0
  83. data/webpack/move_to_foreman/components/common/table/components/Table.js +76 -0
  84. data/webpack/move_to_foreman/components/common/table/components/Table.test.js +31 -0
  85. data/webpack/move_to_foreman/components/common/table/components/TableBody.js +27 -0
  86. data/webpack/move_to_foreman/components/common/table/components/TableBody.test.js +18 -0
  87. data/webpack/move_to_foreman/components/common/table/components/TableBodyMessage.js +18 -0
  88. data/webpack/move_to_foreman/components/common/table/components/TableBodyMessage.test.js +12 -0
  89. data/webpack/move_to_foreman/components/common/table/components/TableFixtures.js +14 -0
  90. data/webpack/move_to_foreman/components/common/table/components/TableSelectionCell.js +39 -0
  91. data/webpack/move_to_foreman/components/common/table/components/TableSelectionCell.test.js +16 -0
  92. data/webpack/move_to_foreman/components/common/table/components/TableSelectionHeaderCell.js +34 -0
  93. data/webpack/move_to_foreman/components/common/table/components/TableSelectionHeaderCell.test.js +14 -0
  94. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/CollapseSubscriptionGroupButton.test.js.snap +19 -0
  95. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/Table.test.js.snap +167 -0
  96. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableBody.test.js.snap +28 -0
  97. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableBodyMessage.test.js.snap +13 -0
  98. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionCell.test.js.snap +16 -0
  99. data/webpack/move_to_foreman/components/common/table/components/__snapshots__/TableSelectionHeaderCell.test.js.snap +15 -0
  100. data/webpack/move_to_foreman/components/common/table/components/index.js +6 -0
  101. data/webpack/move_to_foreman/components/common/table/formatters/cellFormatter.js +4 -0
  102. data/webpack/move_to_foreman/components/common/table/formatters/collapseableAndSelectionCellFormatter.js +18 -0
  103. data/webpack/move_to_foreman/components/common/table/formatters/ellipsisCellFormatter.js +5 -0
  104. data/webpack/move_to_foreman/components/common/table/formatters/headerFormatter.js +4 -0
  105. data/webpack/move_to_foreman/components/common/table/formatters/index.js +6 -0
  106. data/webpack/move_to_foreman/components/common/table/formatters/selectionCellFormatter.js +17 -0
  107. data/webpack/move_to_foreman/components/common/table/formatters/selectionHeaderCellFormatter.js +10 -0
  108. data/webpack/move_to_foreman/components/common/table/index.js +2 -88
  109. data/webpack/move_to_pf/LoadingState/LoadingState.js +35 -0
  110. data/webpack/move_to_pf/LoadingState/LoadingState.scss +12 -0
  111. data/webpack/move_to_pf/LoadingState/LoadingState.test.js +28 -0
  112. data/webpack/move_to_pf/LoadingState/__snapshots__/LoadingState.test.js.snap +20 -0
  113. data/webpack/move_to_pf/LoadingState/index.js +3 -0
  114. data/webpack/move_to_pf/test-utils/testHelpers.js +71 -0
  115. data/webpack/redux/actions/RedHatRepositories/enabled.js +1 -1
  116. data/webpack/redux/actions/RedHatRepositories/helpers.js +34 -9
  117. data/webpack/redux/actions/RedHatRepositories/sets.js +28 -6
  118. data/webpack/redux/consts.js +1 -0
  119. data/webpack/redux/reducers/RedHatRepositories/sets.fixtures.js +12 -2
  120. data/webpack/redux/reducers/RedHatRepositories/sets.js +34 -27
  121. data/webpack/redux/reducers/RedHatRepositories/sets.test.js +10 -2
  122. data/webpack/redux/reducers/index.js +2 -0
  123. data/webpack/scenes/Organizations/OrganizationActions.js +3 -3
  124. data/webpack/scenes/RedHatRepositories/components/RecommendedRepositorySetsToggler.js +44 -0
  125. data/webpack/scenes/RedHatRepositories/components/RecommendedRepositorySetsToggler.scss +16 -0
  126. data/webpack/scenes/RedHatRepositories/components/RepositorySet.js +8 -2
  127. data/webpack/scenes/RedHatRepositories/components/RepositorySetRepositories.js +5 -3
  128. data/webpack/scenes/RedHatRepositories/components/RepositorySetRepository.js +4 -2
  129. data/webpack/scenes/RedHatRepositories/components/Search.js +1 -1
  130. data/webpack/scenes/RedHatRepositories/components/SearchBar.js +1 -1
  131. data/webpack/scenes/RedHatRepositories/components/__tests__/RecommendedRepositorySetsToggler.test.js +17 -0
  132. data/webpack/scenes/RedHatRepositories/components/__tests__/__snapshots__/RecommendedRepositorySetsToggler.test.js.snap +37 -0
  133. data/webpack/scenes/RedHatRepositories/helpers.js +1 -1
  134. data/webpack/scenes/RedHatRepositories/index.js +17 -7
  135. data/webpack/scenes/RedHatRepositories/index.scss +16 -4
  136. data/webpack/scenes/Subscriptions/Details/SubscriptionAttributes.js +17 -0
  137. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailActions.js +28 -0
  138. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailAssociations.js +47 -0
  139. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailConstants.js +3 -0
  140. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailInfo.js +65 -0
  141. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailProducts.js +20 -0
  142. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailReducer.js +37 -0
  143. data/webpack/scenes/Subscriptions/Details/SubscriptionDetails.js +58 -0
  144. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailActions.test.js +47 -0
  145. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailAssociations.test.js +16 -0
  146. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailInfo.test.js +15 -0
  147. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailProducts.test.js +16 -0
  148. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetailReducer.test.js +39 -0
  149. data/webpack/scenes/Subscriptions/Details/__tests__/SubscriptionDetails.test.js +28 -0
  150. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailAssociations.test.js.snap +53 -0
  151. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailInfo.test.js.snap +185 -0
  152. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailProducts.test.js.snap +77 -0
  153. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +432 -0
  154. data/webpack/scenes/Subscriptions/Details/__tests__/subscriptionDetails.fixtures.js +167 -0
  155. data/webpack/scenes/Subscriptions/Details/index.js +19 -0
  156. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +58 -12
  157. data/webpack/scenes/Subscriptions/Manifest/Manifest.scss +6 -1
  158. data/webpack/scenes/Subscriptions/Manifest/ManifestActions.js +4 -4
  159. data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryTableSchema.js +7 -7
  160. data/webpack/scenes/Subscriptions/Manifest/__tests__/__snapshots__/ManageManifestModal.test.js.snap +6 -9
  161. data/webpack/scenes/Subscriptions/Manifest/index.js +2 -2
  162. data/webpack/scenes/Subscriptions/SubscriptionActions.js +5 -6
  163. data/webpack/scenes/Subscriptions/SubscriptionReducer.js +2 -3
  164. data/webpack/scenes/Subscriptions/SubscriptionValidations.js +1 -1
  165. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +46 -30
  166. data/webpack/scenes/Subscriptions/SubscriptionsPage.scss +38 -0
  167. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsActions.js +3 -3
  168. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +7 -6
  169. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsTableSchema.js +17 -14
  170. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/__snapshots__/UpstreamSubscriptionsPage.test.js.snap +12 -15
  171. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/upstreamSubscriptions.fixtures.js +4 -4
  172. data/webpack/scenes/Subscriptions/__tests__/SubscriptionValidations.test.js +5 -0
  173. data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +2 -2
  174. data/webpack/scenes/Subscriptions/{EntitlementsInlineEditFormatter.js → components/SubscriptionsTable/EntitlementsInlineEditFormatter.js} +7 -7
  175. data/webpack/scenes/Subscriptions/{SubscriptionsTable.js → components/SubscriptionsTable/SubscriptionsTable.js} +75 -47
  176. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableHelpers.js +60 -0
  177. data/webpack/scenes/Subscriptions/{SubscriptionsTableSchema.js → components/SubscriptionsTable/SubscriptionsTableSchema.js} +37 -26
  178. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionsTable.test.js +56 -0
  179. data/webpack/scenes/Subscriptions/{__tests__ → components/SubscriptionsTable/__tests__}/__snapshots__/SubscriptionsTable.test.js.snap +16 -5
  180. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/index.js +2 -0
  181. data/webpack/scenes/Subscriptions/index.js +2 -2
  182. data/webpack/scenes/Tasks/TaskActions.js +18 -11
  183. data/webpack/scenes/Tasks/__tests__/TaskActions.test.js +92 -9
  184. data/webpack/scenes/Tasks/__tests__/task.fixtures.js +19 -9
  185. data/webpack/services/api/index.js +2 -2
  186. data/webpack/test_setup.js +1 -0
  187. metadata +79 -10
  188. data/webpack/scenes/Subscriptions/Subscriptions.scss +0 -14
  189. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsTable.test.js +0 -47
@@ -0,0 +1,19 @@
1
+ import { bindActionCreators } from 'redux';
2
+ import { connect } from 'react-redux';
3
+
4
+ import reducer from './SubscriptionDetailReducer';
5
+ import * as subscriptionDetailActions from './SubscriptionDetailActions';
6
+ import SubscriptionDetails from './SubscriptionDetails';
7
+
8
+ // map state to props
9
+ const mapStateToProps = state => ({
10
+ subscriptionDetails: state.katello.subscriptionDetails,
11
+ });
12
+
13
+ // map action dispatchers to props
14
+ const mapDispatchToProps = dispatch => bindActionCreators(subscriptionDetailActions, dispatch);
15
+
16
+ export const subscriptionDetails = reducer;
17
+
18
+ // export connected component
19
+ export default connect(mapStateToProps, mapDispatchToProps)(SubscriptionDetails);
@@ -2,11 +2,17 @@ import React, { Component } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Col, Tabs, Tab, Form, FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
4
4
  import { bindMethods, Button, Icon, Modal, Spinner, OverlayTrigger, Tooltip } from 'patternfly-react';
5
+ import { isEqual } from 'lodash';
5
6
  import TooltipButton from 'react-bootstrap-tooltip-button';
7
+ import { LoadingState } from '../../../move_to_pf/LoadingState';
6
8
  import { Table } from '../../../move_to_foreman/components/common/table';
7
9
  import { columns } from './ManifestHistoryTableSchema';
8
10
  import ConfirmDialog from '../../../move_to_foreman/components/common/ConfirmDialog';
9
11
  import DeleteManifestModalText from './DeleteManifestModalText';
12
+ import {
13
+ BLOCKING_FOREMAN_TASK_TYPES,
14
+ MANIFEST_TASKS_BULK_SEARCH_ID,
15
+ } from '../SubscriptionConstants';
10
16
 
11
17
  class ManageManifestModal extends Component {
12
18
  constructor(props) {
@@ -24,6 +30,8 @@ class ManageManifestModal extends Component {
24
30
  'uploadManifest',
25
31
  'refreshManifest',
26
32
  'deleteManifest',
33
+ 'manifestExists',
34
+ 'disabledTooltipText',
27
35
  ]);
28
36
  }
29
37
 
@@ -31,9 +39,25 @@ class ManageManifestModal extends Component {
31
39
  this.loadData();
32
40
  }
33
41
 
34
- componentWillReceiveProps(props) {
35
- this.setState({ showModal: props.showModal });
36
- this.setState({ actionInProgress: props.taskInProgress });
42
+ static getDerivedStateFromProps(newProps, prevState) {
43
+ if (
44
+ !isEqual(newProps.showModal, prevState.showModal) ||
45
+ !isEqual(newProps.taskInProgress, prevState.actionInProgress)
46
+ ) {
47
+ return {
48
+ showModal: newProps.showModal,
49
+ actionInProgress: newProps.taskInProgress,
50
+ };
51
+ }
52
+ return null;
53
+ }
54
+
55
+ componentDidUpdate(prevProp, prevState) {
56
+ const { actionInProgress } = this.state;
57
+
58
+ if (prevState.actionInProgress && !actionInProgress) {
59
+ this.props.loadOrganization();
60
+ }
37
61
  }
38
62
 
39
63
  loadData() {
@@ -50,10 +74,10 @@ class ManageManifestModal extends Component {
50
74
  }
51
75
 
52
76
  uploadManifest(fileList) {
77
+ this.setState({ actionInProgress: true });
53
78
  if (fileList.length > 0) {
54
- this.props.uploadManifest(fileList[0]).then(this.props.loadOrganization);
79
+ this.props.uploadManifest(fileList[0]);
55
80
  }
56
- this.setState({ actionInProgress: true });
57
81
  }
58
82
 
59
83
  refreshManifest() {
@@ -62,8 +86,15 @@ class ManageManifestModal extends Component {
62
86
  }
63
87
 
64
88
  deleteManifest() {
65
- this.props.deleteManifest().then(this.props.loadOrganization);
66
89
  this.setState({ actionInProgress: true });
90
+ this.props.deleteManifest()
91
+ .then(() =>
92
+ this.props.bulkSearch({
93
+ search_id: MANIFEST_TASKS_BULK_SEARCH_ID,
94
+ type: 'all',
95
+ active_only: true,
96
+ action_types: BLOCKING_FOREMAN_TASK_TYPES,
97
+ }));
67
98
  this.showDeleteManifestModal(false);
68
99
  }
69
100
 
@@ -73,6 +104,19 @@ class ManageManifestModal extends Component {
73
104
  });
74
105
  }
75
106
 
107
+ disabledTooltipText() {
108
+ if (this.state.actionInProgress) {
109
+ return __('This is disabled because a manifest task is in progress');
110
+ }
111
+ return __('This is disabled because no manifest exists');
112
+ }
113
+
114
+ manifestExists() {
115
+ const { organization } = this.props;
116
+
117
+ return organization.owner_details && organization.owner_details.upstreamConsumer;
118
+ }
119
+
76
120
  render() {
77
121
  const {
78
122
  manifestHistory, organization, disableManifestActions, disabledReason,
@@ -126,7 +170,7 @@ class ManageManifestModal extends Component {
126
170
  <FormControl
127
171
  id="cdnUrl"
128
172
  type="text"
129
- value={organization.redhat_repository_url}
173
+ value={organization.redhat_repository_url || ''}
130
174
  onChange={this.saveOrganization}
131
175
  />
132
176
  </Col>
@@ -169,16 +213,17 @@ class ManageManifestModal extends Component {
169
213
  tooltipText={disabledReason}
170
214
  tooltipPlacement="top"
171
215
  title={__('Refresh')}
172
- disabled={actionInProgress || disableManifestActions}
216
+ disabled={!this.manifestExists() ||
217
+ actionInProgress || disableManifestActions}
173
218
  />
174
219
 
175
220
  <TooltipButton
176
221
  onClick={() => this.showDeleteManifestModal(true)}
177
222
  tooltipId="delete-manifest-button-tooltip"
178
- tooltipText={__('This is disabled because a manifest task is in progress.')}
223
+ tooltipText={this.disabledTooltipText()}
179
224
  tooltipPlacement="top"
180
225
  title={__('Delete')}
181
- disabled={actionInProgress}
226
+ disabled={!this.manifestExists() || actionInProgress}
182
227
  />
183
228
 
184
229
  <ConfirmDialog
@@ -198,13 +243,13 @@ class ManageManifestModal extends Component {
198
243
  </Tab>
199
244
 
200
245
  <Tab eventKey={2} title={__('Manifest History')}>
201
- <Spinner loading={manifestHistory.loading} className="small-spacer">
246
+ <LoadingState loading={manifestHistory.loading} loadingText={__('Loading')}>
202
247
  <Table
203
248
  rows={manifestHistory.results}
204
249
  columns={columns}
205
250
  emptyState={emptyStateData()}
206
251
  />
207
- </Spinner>
252
+ </LoadingState>
208
253
  </Tab>
209
254
  </Tabs>
210
255
  </Modal.Body>
@@ -232,6 +277,7 @@ ManageManifestModal.propTypes = {
232
277
  manifestHistory: PropTypes.shape({}).isRequired,
233
278
  showModal: PropTypes.bool.isRequired,
234
279
  onClose: PropTypes.func,
280
+ bulkSearch: PropTypes.func.isRequired,
235
281
  };
236
282
 
237
283
  ManageManifestModal.defaultProps = {
@@ -1,3 +1,8 @@
1
1
  .manifest-actions > * {
2
2
  margin: 10px 5px 0 0 ;
3
- }
3
+ }
4
+
5
+ .modal-body {
6
+ overflow-y: auto;
7
+ overflow-x: hidden;
8
+ }
@@ -27,7 +27,7 @@ export const uploadManifest = file => (dispatch) => {
27
27
  };
28
28
 
29
29
  return api
30
- .post(`/organizations/${orgId}/subscriptions/upload`, formData, config)
30
+ .post(`/organizations/${orgId()}/subscriptions/upload`, formData, config)
31
31
  .then(({ data }) => {
32
32
  dispatch({
33
33
  type: UPLOAD_MANIFEST_SUCCESS,
@@ -50,7 +50,7 @@ export const refreshManifest = (extendedParams = {}) => (dispatch) => {
50
50
  };
51
51
 
52
52
  return api
53
- .put(`/organizations/${orgId}/subscriptions/refresh_manifest`, {}, params)
53
+ .put(`/organizations/${orgId()}/subscriptions/refresh_manifest`, {}, params)
54
54
  .then(({ data }) => {
55
55
  dispatch({
56
56
  type: REFRESH_MANIFEST_SUCCESS,
@@ -73,7 +73,7 @@ export const deleteManifest = (extendedParams = {}) => (dispatch) => {
73
73
  };
74
74
 
75
75
  return api
76
- .post(`/organizations/${orgId}/subscriptions/delete_manifest`, {}, params)
76
+ .post(`/organizations/${orgId()}/subscriptions/delete_manifest`, {}, params)
77
77
  .then(({ data }) => {
78
78
  dispatch({
79
79
  type: DELETE_MANIFEST_SUCCESS,
@@ -96,7 +96,7 @@ export const loadManifestHistory = (extendedParams = {}) => (dispatch) => {
96
96
  };
97
97
 
98
98
  return api
99
- .get(`/organizations/${orgId}/subscriptions/manifest_history`, {}, params)
99
+ .get(`/organizations/${orgId()}/subscriptions/manifest_history`, {}, params)
100
100
  .then(({ data }) => {
101
101
  dispatch({
102
102
  type: MANIFEST_HISTORY_SUCCESS,
@@ -1,30 +1,30 @@
1
- import { headerFormat, cellFormat } from '../../../move_to_foreman/components/common/table';
1
+ import { headerFormatter, cellFormatter } from '../../../move_to_foreman/components/common/table';
2
2
 
3
3
  export const columns = [
4
4
  {
5
5
  property: 'status',
6
6
  header: {
7
7
  label: __('Status'),
8
- formatters: [headerFormat],
8
+ formatters: [headerFormatter],
9
9
  props: {
10
10
  index: 0,
11
11
  },
12
12
  },
13
13
  cell: {
14
- formatters: [cellFormat],
14
+ formatters: [cellFormatter],
15
15
  },
16
16
  },
17
17
  {
18
18
  property: 'statusMessage',
19
19
  header: {
20
20
  label: __('Message'),
21
- formatters: [headerFormat],
21
+ formatters: [headerFormatter],
22
22
  props: {
23
23
  index: 1,
24
24
  },
25
25
  },
26
26
  cell: {
27
- formatters: [cellFormat],
27
+ formatters: [cellFormatter],
28
28
  },
29
29
  },
30
30
  // TODO: use date formatter from tomas' PR
@@ -32,13 +32,13 @@ export const columns = [
32
32
  property: 'created',
33
33
  header: {
34
34
  label: __('Timestamp'),
35
- formatters: [headerFormat],
35
+ formatters: [headerFormatter],
36
36
  props: {
37
37
  index: 2,
38
38
  },
39
39
  },
40
40
  cell: {
41
- formatters: [cellFormat],
41
+ formatters: [cellFormatter],
42
42
  },
43
43
  },
44
44
  ];
@@ -159,7 +159,7 @@ exports[`manage manifest modal should render 1`] = `
159
159
  type="file"
160
160
  />
161
161
  <TooltipButton
162
- disabled={false}
162
+ disabled={true}
163
163
  onClick={[Function]}
164
164
  title="Refresh"
165
165
  tooltipId="refresh-manifest-button-tooltip"
@@ -167,12 +167,12 @@ exports[`manage manifest modal should render 1`] = `
167
167
  tooltipText=""
168
168
  />
169
169
  <TooltipButton
170
- disabled={false}
170
+ disabled={true}
171
171
  onClick={[Function]}
172
172
  title="Delete"
173
173
  tooltipId="delete-manifest-button-tooltip"
174
174
  tooltipPlacement="top"
175
- tooltipText="This is disabled because a manifest task is in progress."
175
+ tooltipText="This is disabled because no manifest exists"
176
176
  />
177
177
  <ConfirmDialog
178
178
  active={false}
@@ -212,12 +212,9 @@ exports[`manage manifest modal should render 1`] = `
212
212
  eventKey={2}
213
213
  title="Manifest History"
214
214
  >
215
- <Spinner
216
- className="small-spacer"
217
- inline={false}
218
- inverse={false}
215
+ <LoadingState
219
216
  loading={false}
220
- size="md"
217
+ loadingText="Loading"
221
218
  >
222
219
  <Table
223
220
  columns={
@@ -340,7 +337,7 @@ exports[`manage manifest modal should render 1`] = `
340
337
  ]
341
338
  }
342
339
  />
343
- </Spinner>
340
+ </LoadingState>
344
341
  </Tab>
345
342
  </Uncontrolled(Tabs)>
346
343
  </ModalBody>
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
4
4
 
5
5
  import * as manifestActions from './ManifestActions';
6
6
  import * as organizationActions from '../../Organizations/OrganizationActions';
7
-
7
+ import * as tasksActions from '../../Tasks/TaskActions';
8
8
  import history from './ManifestHistoryReducer';
9
9
 
10
10
  import ManifestModal from './ManageManifestModal';
@@ -18,7 +18,7 @@ const mapStateToProps = state => ({
18
18
  });
19
19
 
20
20
  // map action dispatchers to props
21
- const actions = { ...manifestActions, ...organizationActions };
21
+ const actions = { ...manifestActions, ...organizationActions, ...tasksActions };
22
22
  const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
23
23
 
24
24
  // export reducers
@@ -16,11 +16,10 @@ import {
16
16
  DELETE_SUBSCRIPTIONS_FAILURE,
17
17
  } from './SubscriptionConstants';
18
18
  import { filterRHSubscriptions } from './SubscriptionHelpers.js';
19
-
20
- const getResponseError = ({ data }) => data && (data.displayMessage || data.error);
19
+ import { getResponseError } from '../../move_to_foreman/common/helpers.js';
21
20
 
22
21
  export const createSubscriptionParams = (extendedParams = {}) => ({
23
- ...{ organization_id: orgId },
22
+ ...{ organization_id: orgId() },
24
23
  ...propsToSnakeCase(extendedParams),
25
24
  });
26
25
 
@@ -29,7 +28,7 @@ export const loadAvailableQuantities = (extendedParams = {}) => (dispatch) => {
29
28
 
30
29
  const params = createSubscriptionParams(extendedParams);
31
30
  return api
32
- .get(`/organizations/${orgId}/upstream_subscriptions`, {}, params)
31
+ .get(`/organizations/${orgId()}/upstream_subscriptions`, {}, params)
33
32
  .then(({ data }) => {
34
33
  dispatch({
35
34
  type: SUBSCRIPTIONS_QUANTITIES_SUCCESS,
@@ -77,7 +76,7 @@ export const updateQuantity = (quantities = {}) => (dispatch) => {
77
76
  };
78
77
 
79
78
  return api
80
- .put(`/organizations/${orgId}/upstream_subscriptions`, params)
79
+ .put(`/organizations/${orgId()}/upstream_subscriptions`, params)
81
80
  .then(({ data }) => {
82
81
  dispatch({
83
82
  type: UPDATE_QUANTITY_SUCCESS,
@@ -100,7 +99,7 @@ export const deleteSubscriptions = poolIds => (dispatch) => {
100
99
  };
101
100
 
102
101
  return api
103
- .delete(`/organizations/${orgId}/upstream_subscriptions`, {}, params)
102
+ .delete(`/organizations/${(orgId())}/upstream_subscriptions`, {}, params)
104
103
  .then(({ data }) => {
105
104
  dispatch({
106
105
  type: DELETE_SUBSCRIPTIONS_SUCCESS,
@@ -97,15 +97,14 @@ export default (state = initialState, action) => {
97
97
  }
98
98
 
99
99
  case TASK_BULK_SEARCH_SUCCESS: {
100
- let tasks = [];
100
+ let tasks;
101
101
 
102
102
  const search = find(action.response, bulkSearch =>
103
103
  bulkSearch.search_params.search_id === MANIFEST_TASKS_BULK_SEARCH_ID);
104
104
 
105
- if (search) {
105
+ if (search && search.results.length > 0) {
106
106
  tasks = search.results;
107
107
  }
108
-
109
108
  return state.set('tasks', tasks);
110
109
  }
111
110
 
@@ -8,7 +8,7 @@ export const validateQuantity = (quantity, availableQuantity) => {
8
8
  if (Number.isNaN(numberValue)) {
9
9
  state = 'error';
10
10
  message = __('Not a number');
11
- } else if (numberValue < 0) {
11
+ } else if (numberValue <= 0) {
12
12
  state = 'error';
13
13
  message = __('Has to be > 0');
14
14
  } else if (availableQuantity && availableQuantity >= 0 && numberValue > availableQuantity) {
@@ -9,7 +9,7 @@ import { notify } from '../../move_to_foreman/foreman_toast_notifications';
9
9
  import helpers from '../../move_to_foreman/common/helpers';
10
10
  import ModalProgressBar from '../../move_to_foreman/components/common/ModalProgressBar';
11
11
  import ManageManifestModal from './Manifest/';
12
- import SubscriptionsTable from './SubscriptionsTable';
12
+ import { SubscriptionsTable } from './components/SubscriptionsTable';
13
13
  import Search from '../../components/Search/index';
14
14
  import api, { orgId } from '../../services/api';
15
15
  import { createSubscriptionParams } from './SubscriptionActions.js';
@@ -19,6 +19,7 @@ import {
19
19
  BULK_TASK_SEARCH_INTERVAL,
20
20
  } from './SubscriptionConstants';
21
21
 
22
+ import './SubscriptionsPage.scss';
22
23
 
23
24
  class SubscriptionsPage extends Component {
24
25
  constructor(props) {
@@ -27,7 +28,6 @@ class SubscriptionsPage extends Component {
27
28
  manifestModalOpen: false,
28
29
  subscriptionDeleteModalOpen: false,
29
30
  disableDeleteButton: true,
30
- polledTask: null,
31
31
  showTaskModal: false,
32
32
  searchQuery: '',
33
33
  };
@@ -37,6 +37,35 @@ class SubscriptionsPage extends Component {
37
37
  this.loadData();
38
38
  }
39
39
 
40
+ static getDerivedStateFromProps(nextProps, prevState) {
41
+ const nextTaskId = nextProps.tasks[0] && nextProps.tasks[0].id;
42
+
43
+ if (nextProps.tasks.length === 0 && prevState.polledTask != null) {
44
+ return { showTaskModal: false, polledTask: undefined };
45
+ } else if (nextProps.tasks.length > 0 && nextTaskId !== prevState.polledTask) {
46
+ return {
47
+ showTaskModal: true,
48
+ manifestModalOpen: false,
49
+ polledTask: nextProps.tasks[0].id,
50
+ };
51
+ }
52
+ return null;
53
+ }
54
+
55
+ componentDidUpdate(prevProps) {
56
+ const { tasks } = this.props;
57
+ const numberOfTasks = tasks.length;
58
+ const numberOfPrevTasks = prevProps.tasks.length;
59
+ let task;
60
+
61
+ if (numberOfTasks > 0) {
62
+ if (numberOfPrevTasks === 0 || prevProps.tasks[0].id !== tasks[0].id) {
63
+ [task] = this.props.tasks;
64
+ this.handleDoneTask(task);
65
+ }
66
+ }
67
+ }
68
+
40
69
  getDisabledReason(deleteButton) {
41
70
  const { tasks, subscriptions } = this.props;
42
71
  const { disconnected } = subscriptions;
@@ -53,16 +82,6 @@ class SubscriptionsPage extends Component {
53
82
  return disabledReason;
54
83
  }
55
84
 
56
- showTaskModal(show) {
57
- if (this.state.showTaskModal !== show) {
58
- this.setState({ showTaskModal: show });
59
-
60
- if (show && this.state.manifestModalOpen) {
61
- this.setState({ manifestModalOpen: false });
62
- }
63
- }
64
- }
65
-
66
85
  loadData() {
67
86
  this.props.pollBulkSearch({
68
87
  search_id: MANIFEST_TASKS_BULK_SEARCH_ID,
@@ -77,23 +96,25 @@ class SubscriptionsPage extends Component {
77
96
 
78
97
  handleDoneTask(taskToPoll) {
79
98
  const POLL_TASK_INTERVAL = 5000;
99
+ const { pollTaskUntilDone, loadSubscriptions } = this.props;
80
100
 
81
- if (!this.state.polledTask) {
82
- this.setState({ polledTask: taskToPoll });
83
- this.props.pollTaskUntilDone(taskToPoll.id, {}, POLL_TASK_INTERVAL).then((task) => {
101
+ pollTaskUntilDone(taskToPoll.id, {}, POLL_TASK_INTERVAL)
102
+ .then((task) => {
84
103
  function getErrors() {
85
104
  return (
86
105
  <ul>
87
- {
88
- task.humanized.errors.map(error => <li key={error}> {error} </li>)
89
- }
106
+ {task.humanized.errors.map(error => (
107
+ <li key={error}> {error} </li>
108
+ ))}
90
109
  </ul>
91
110
  );
92
111
  }
93
112
 
94
113
  const message = (
95
114
  <span>
96
- <span>{__(`Task ${task.humanized.action} completed with a result of ${task.result}`)} </span>
115
+ <span>
116
+ {`${__(`Task ${task.humanized.action} completed with a result of ${task.result}.`)} `}
117
+ </span>
97
118
  {task.errors ? getErrors() : ''}
98
119
  <a href={helpers.urlBuilder('foreman_tasks/tasks', '', task.id)}>
99
120
  {__('Click here to go to the tasks page for the task.')}
@@ -101,11 +122,13 @@ class SubscriptionsPage extends Component {
101
122
  </span>
102
123
  );
103
124
 
104
- notify({ message: ReactDOMServer.renderToStaticMarkup(message), type: task.result });
125
+ notify({
126
+ message: ReactDOMServer.renderToStaticMarkup(message),
127
+ type: task.result,
128
+ });
105
129
 
106
- this.props.loadSubscriptions();
130
+ loadSubscriptions();
107
131
  });
108
- }
109
132
  }
110
133
 
111
134
  render() {
@@ -118,13 +141,6 @@ class SubscriptionsPage extends Component {
118
141
 
119
142
  if (taskInProgress) {
120
143
  [task] = tasks;
121
- this.handleDoneTask(task);
122
- }
123
-
124
- if (task) {
125
- this.showTaskModal(true);
126
- } else {
127
- this.showTaskModal(false);
128
144
  }
129
145
 
130
146
  const onSearch = (search) => {
@@ -134,7 +150,7 @@ class SubscriptionsPage extends Component {
134
150
  const getAutoCompleteParams = search => ({
135
151
  endpoint: '/subscriptions/auto_complete_search',
136
152
  params: {
137
- organization_id: orgId,
153
+ organization_id: orgId(),
138
154
  search,
139
155
  },
140
156
  });