katello 3.16.0.rc3.1 → 3.16.0.rc4

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/v2/api_controller.rb +8 -4
  3. data/app/controllers/katello/api/v2/subscriptions_controller.rb +1 -1
  4. data/app/controllers/katello/api/v2/upstream_subscriptions_controller.rb +13 -1
  5. data/app/controllers/katello/concerns/hosts_controller_extensions.rb +11 -0
  6. data/app/lib/katello/errors.rb +25 -0
  7. data/app/lib/katello/resources/candlepin.rb +1 -1
  8. data/app/lib/katello/resources/candlepin/upstream_consumer.rb +6 -0
  9. data/app/models/katello/concerns/host_managed_extensions.rb +7 -0
  10. data/app/models/katello/concerns/organization_extensions.rb +14 -0
  11. data/app/models/katello/pulp3/content_guard.rb +1 -1
  12. data/app/services/katello/candlepin/message_handler.rb +2 -3
  13. data/app/services/katello/pulp3/repository.rb +8 -1
  14. data/app/services/katello/pulp3/repository/yum.rb +8 -0
  15. data/app/services/katello/ui_notifications/subscriptions/manifest_expired_warning.rb +20 -8
  16. data/app/services/katello/upstream_connection_checker.rb +48 -0
  17. data/app/views/katello/api/v2/srpms/backend.json.rabl +11 -0
  18. data/app/views/katello/api/v2/srpms/base.json.rabl +5 -0
  19. data/app/views/katello/api/v2/srpms/compare.json.rabl +10 -0
  20. data/app/views/katello/api/v2/srpms/index.json.rabl +1 -1
  21. data/app/views/katello/api/v2/srpms/show.json.rabl +3 -3
  22. data/config/routes/api/v2.rb +2 -0
  23. data/db/seeds.d/109-katello-notification-blueprints.rb +1 -1
  24. data/lib/katello/permission_creator.rb +1 -1
  25. data/lib/katello/version.rb +1 -1
  26. data/package.json +3 -3
  27. data/webpack/components/Content/ContentTable.js +2 -0
  28. data/webpack/components/Content/Details/ContentDetails.js +3 -0
  29. data/webpack/scenes/AnsibleCollections/Details/AnsibleCollectionDetails.js +3 -0
  30. data/webpack/scenes/ModuleStreams/Details/ModuleStreamDetails.js +3 -0
  31. data/webpack/scenes/RedHatRepositories/RedHatRepositoriesPage.js +2 -0
  32. data/webpack/scenes/RedHatRepositories/components/EnabledRepository/EnabledRepository.js +2 -0
  33. data/webpack/scenes/RedHatRepositories/components/RepositorySetRepository/RepositorySetRepository.js +2 -0
  34. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailAssociations.js +2 -0
  35. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailProductContent.js +2 -0
  36. data/webpack/scenes/Subscriptions/Details/SubscriptionDetailProducts.js +2 -0
  37. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +2 -0
  38. data/webpack/scenes/Subscriptions/SubscriptionActions.js +8 -8
  39. data/webpack/scenes/Subscriptions/SubscriptionConstants.js +3 -1
  40. data/webpack/scenes/Subscriptions/SubscriptionReducer.js +15 -1
  41. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +54 -7
  42. data/webpack/scenes/Subscriptions/SubscriptionsSelectors.js +3 -0
  43. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsActions.js +15 -1
  44. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/{UpstreamSubscriptionsContstants.js → UpstreamSubscriptionsConstants.js} +3 -0
  45. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +2 -0
  46. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsReducer.js +1 -1
  47. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/UpstreamSubscriptionsReducer.test.js +1 -1
  48. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsActions.test.js +0 -13
  49. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +6 -1
  50. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +6 -4
  51. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsReducer.test.js.snap +26 -25
  52. data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +0 -58
  53. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTable.js +10 -4
  54. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionsTable.test.js +1 -0
  55. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +1 -68
  56. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Dialogs/UpdateDialog.js +1 -1
  57. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Dialogs/index.js +4 -4
  58. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Table.js +12 -10
  59. data/webpack/scenes/Subscriptions/index.js +6 -3
  60. metadata +27 -23
@@ -34,6 +34,8 @@ const ContentTable = ({ content, tableSchema, onPaginationChange }) => {
34
34
  ContentTable.propTypes = {
35
35
  content: PropTypes.shape({
36
36
  loading: PropTypes.bool,
37
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
38
+ // eslint-disable-next-line react/forbid-prop-types
37
39
  results: PropTypes.array,
38
40
  pagination: PropTypes.shape({}),
39
41
  itemCount: PropTypes.number,
@@ -59,9 +59,12 @@ ContentDetails.propTypes = {
59
59
  contentDetails: PropTypes.shape({
60
60
  loading: PropTypes.bool,
61
61
  name: PropTypes.string,
62
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
63
+ /* eslint-disable react/forbid-prop-types */
62
64
  profiles: PropTypes.array,
63
65
  repositories: PropTypes.array,
64
66
  artifacts: PropTypes.array,
67
+ /* eslint-enable react/forbid-prop-types */
65
68
  stream: PropTypes.string,
66
69
  }).isRequired,
67
70
  schema: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
@@ -83,8 +83,11 @@ AnsibleCollectionDetails.propTypes = {
83
83
  name: PropTypes.string,
84
84
  namespace: PropTypes.string,
85
85
  version: PropTypes.string,
86
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
87
+ /* eslint-disable react/forbid-prop-types */
86
88
  repositories: PropTypes.array,
87
89
  tags: PropTypes.array,
90
+ /* eslint-enable react/forbid-prop-types */
88
91
  }).isRequired,
89
92
  };
90
93
 
@@ -81,9 +81,12 @@ ModuleStreamDetails.propTypes = {
81
81
  moduleStreamDetails: PropTypes.shape({
82
82
  loading: PropTypes.bool,
83
83
  name: PropTypes.string,
84
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
85
+ /* eslint-disable react/forbid-prop-types */
84
86
  profiles: PropTypes.array,
85
87
  repositories: PropTypes.array,
86
88
  artifacts: PropTypes.array,
89
+ /* eslint-enable react/forbid-prop-types */
87
90
  stream: PropTypes.string,
88
91
  }).isRequired,
89
92
  };
@@ -111,6 +111,8 @@ RedHatRepositoriesPage.propTypes = {
111
111
  recommended: PropTypes.bool,
112
112
  loading: PropTypes.bool,
113
113
  search: PropTypes.shape({}),
114
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
115
+ // eslint-disable-next-line react/forbid-prop-types
114
116
  missingPermissions: PropTypes.array,
115
117
  }).isRequired,
116
118
  };
@@ -94,6 +94,8 @@ EnabledRepository.propTypes = {
94
94
  search: PropTypes.shape({
95
95
  query: PropTypes.string,
96
96
  searchList: PropTypes.string,
97
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
98
+ // eslint-disable-next-line react/forbid-prop-types
97
99
  filters: PropTypes.array,
98
100
  }),
99
101
  pagination: PropTypes.shape({
@@ -141,6 +141,8 @@ RepositorySetRepository.propTypes = {
141
141
  enabledSearch: PropTypes.shape({
142
142
  query: PropTypes.string,
143
143
  searchList: PropTypes.string,
144
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
145
+ // eslint-disable-next-line react/forbid-prop-types
144
146
  filters: PropTypes.array,
145
147
  }),
146
148
  enabledPagination: PropTypes.shape({
@@ -45,6 +45,8 @@ SubscriptionDetailAssociations.propTypes = {
45
45
  subscriptionDetails: PropTypes.shape({
46
46
  id: PropTypes.number,
47
47
  host_count: PropTypes.number,
48
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
49
+ // eslint-disable-next-line react/forbid-prop-types
48
50
  activation_keys: PropTypes.array,
49
51
  }).isRequired,
50
52
  };
@@ -50,6 +50,8 @@ const SubscriptionDetailProductContent = ({ productContent }) => {
50
50
 
51
51
  SubscriptionDetailProductContent.propTypes = {
52
52
  productContent: PropTypes.shape({
53
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
54
+ // eslint-disable-next-line react/forbid-prop-types
53
55
  results: PropTypes.array,
54
56
  }).isRequired,
55
57
  };
@@ -18,6 +18,8 @@ const SubscriptionDetailProducts = ({ subscriptionDetails }) => (
18
18
 
19
19
  SubscriptionDetailProducts.propTypes = {
20
20
  subscriptionDetails: PropTypes.shape({
21
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
22
+ // eslint-disable-next-line react/forbid-prop-types
21
23
  provided_products: PropTypes.array,
22
24
  }).isRequired,
23
25
  };
@@ -352,6 +352,8 @@ ManageManifestModal.propTypes = {
352
352
  simpleContentAccess: PropTypes.bool,
353
353
  manifestHistory: PropTypes.shape({
354
354
  loading: PropTypes.bool,
355
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
356
+ // eslint-disable-next-line react/forbid-prop-types
355
357
  results: PropTypes.array,
356
358
  }).isRequired,
357
359
  setModalClosed: PropTypes.func.isRequired,
@@ -28,8 +28,9 @@ import {
28
28
  SUBSCRIPTIONS_ENABLE_DELETE_BUTTON,
29
29
  BLOCKING_FOREMAN_TASK_TYPES,
30
30
  SUBSCRIPTIONS_RESET_TASKS,
31
+ MANIFEST_DELETE_TASK_LABEL,
31
32
  } from './SubscriptionConstants';
32
- import { filterRHSubscriptions, selectSubscriptionsQuantitiesFromResponse } from './SubscriptionHelpers.js';
33
+ import { selectSubscriptionsQuantitiesFromResponse } from './SubscriptionHelpers.js';
33
34
  import { apiError } from '../../move_to_foreman/common/helpers.js';
34
35
  import {
35
36
  startPollingTask,
@@ -39,6 +40,7 @@ import {
39
40
  toastTaskFinished,
40
41
  } from '../Tasks/TaskActions';
41
42
  import { selectIsPollingTasks } from '../Tasks/TaskSelectors';
43
+ import { pingUpstreamSubscriptions } from './UpstreamSubscriptions/UpstreamSubscriptionsActions';
42
44
 
43
45
  export const createSubscriptionParams = (extendedParams = {}) => ({
44
46
  ...{
@@ -51,10 +53,8 @@ export const createSubscriptionParams = (extendedParams = {}) => ({
51
53
  export const loadAvailableQuantities = (extendedParams = {}) => async (dispatch) => {
52
54
  dispatch({ type: SUBSCRIPTIONS_QUANTITIES_REQUEST });
53
55
 
54
- const params = createSubscriptionParams(extendedParams);
55
-
56
56
  try {
57
- const { data } = await api.get(`/organizations/${orgId()}/upstream_subscriptions`, {}, params);
57
+ const { data } = await api.get(`/organizations/${orgId()}/upstream_subscriptions`, {}, propsToSnakeCase(extendedParams));
58
58
  return dispatch({
59
59
  type: SUBSCRIPTIONS_QUANTITIES_SUCCESS,
60
60
  payload: selectSubscriptionsQuantitiesFromResponse(data),
@@ -76,10 +76,6 @@ export const loadSubscriptions = (extendedParams = {}) => async (dispatch) => {
76
76
  response: data,
77
77
  search: extendedParams.search,
78
78
  });
79
- const poolIds = filterRHSubscriptions(data.results).map(subs => subs.id);
80
- if (poolIds.length > 0) {
81
- dispatch(loadAvailableQuantities({ poolIds }));
82
- }
83
79
  return result;
84
80
  } catch (error) {
85
81
  return dispatch(apiError(SUBSCRIPTIONS_FAILURE, error));
@@ -110,6 +106,10 @@ export const handleFinishedTask = task => (dispatch) => {
110
106
  dispatch(resetTasks());
111
107
  dispatch(pollTasks());
112
108
  dispatch(loadSubscriptions());
109
+
110
+ if (task.label !== MANIFEST_DELETE_TASK_LABEL) {
111
+ dispatch(pingUpstreamSubscriptions());
112
+ }
113
113
  };
114
114
 
115
115
  export const handleStartTask = task => (dispatch) => {
@@ -37,10 +37,12 @@ export const SUBSCRIPTIONS_CLOSE_DELETE_MODAL = 'SUBSCRIPTIONS_CLOSE_DELETE_MODA
37
37
  export const SUBSCRIPTIONS_DISABLE_DELETE_BUTTON = 'SUBSCRIPTIONS_DISABLE_DELETE_BUTTON';
38
38
  export const SUBSCRIPTIONS_ENABLE_DELETE_BUTTON = 'SUBSCRIPTIONS_ENABLE_DELETE_BUTTON';
39
39
 
40
+ export const MANIFEST_DELETE_TASK_LABEL = 'Actions::Katello::Organization::ManifestDelete';
41
+
40
42
  export const BLOCKING_FOREMAN_TASK_TYPES = [
41
43
  'Actions::Katello::Organization::ManifestImport',
42
44
  'Actions::Katello::Organization::ManifestRefresh',
43
- 'Actions::Katello::Organization::ManifestDelete',
45
+ MANIFEST_DELETE_TASK_LABEL,
44
46
  'Actions::Katello::UpstreamSubscriptions::BindEntitlements',
45
47
  'Actions::Katello::UpstreamSubscriptions::UpdateEntitlement',
46
48
  'Actions::Katello::UpstreamSubscriptions::RemoveEntitlements',
@@ -32,9 +32,13 @@ import {
32
32
  REFRESH_MANIFEST_SUCCESS,
33
33
  } from './Manifest/ManifestConstants';
34
34
 
35
+ import {
36
+ PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS,
37
+ PING_UPSTREAM_SUBSCRIPTIONS_FAILURE,
38
+ } from './UpstreamSubscriptions/UpstreamSubscriptionsConstants';
39
+
35
40
  const initialState = Immutable({
36
41
  ...initialApiState,
37
- disconnected: false,
38
42
  searchQuery: '',
39
43
  deleteModalOpened: false,
40
44
  deleteButtonDisabled: true,
@@ -43,10 +47,15 @@ const initialState = Immutable({
43
47
  task: null,
44
48
  tableColumns: [],
45
49
  selectedTableColumns: [],
50
+ hasUpstreamConnection: false,
46
51
  });
47
52
 
48
53
  export default (state = initialState, action) => {
49
54
  switch (action.type) {
55
+ case PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS:
56
+ return state.set('hasUpstreamConnection', true);
57
+ case PING_UPSTREAM_SUBSCRIPTIONS_FAILURE:
58
+ return state.set('hasUpstreamConnection', false);
50
59
  case SUBSCRIPTIONS_REQUEST:
51
60
  return state.set('loading', true);
52
61
  case SUBSCRIPTIONS_COLUMNS_REQUEST:
@@ -130,6 +139,11 @@ export default (state = initialState, action) => {
130
139
  }
131
140
 
132
141
  case DELETE_MANIFEST_SUCCESS:
142
+ return state.merge({
143
+ task: action.response,
144
+ hasUpstreamConnection: false,
145
+ });
146
+
133
147
  case UPLOAD_MANIFEST_SUCCESS:
134
148
  case REFRESH_MANIFEST_SUCCESS:
135
149
  case UPDATE_QUANTITY_SUCCESS:
@@ -11,7 +11,7 @@ import ManageManifestModal from './Manifest/';
11
11
  import { MANAGE_MANIFEST_MODAL_ID } from './Manifest/ManifestConstants';
12
12
  import { SubscriptionsTable } from './components/SubscriptionsTable';
13
13
  import SubscriptionsToolbar from './components/SubscriptionsToolbar';
14
- import { manifestExists } from './SubscriptionHelpers';
14
+ import { filterRHSubscriptions, manifestExists } from './SubscriptionHelpers';
15
15
  import api, { orgId } from '../../services/api';
16
16
 
17
17
  import { createSubscriptionParams } from './SubscriptionActions.js';
@@ -25,6 +25,7 @@ class SubscriptionsPage extends Component {
25
25
  super(props);
26
26
  this.state = {
27
27
  selectedRows: [],
28
+ availableQuantitiesLoaded: false,
28
29
  };
29
30
  }
30
31
 
@@ -39,8 +40,20 @@ class SubscriptionsPage extends Component {
39
40
 
40
41
  componentDidUpdate(prevProps) {
41
42
  const {
42
- organization, task, handleStartTask, handleFinishedTask, isTaskPending, isPollingTask,
43
+ handleStartTask,
44
+ handleFinishedTask,
45
+ isTaskPending,
46
+ isPollingTask,
47
+ hasUpstreamConnection,
48
+ loadAvailableQuantities,
49
+ organization,
50
+ pingUpstreamSubscriptions,
51
+ subscriptions,
52
+ task,
43
53
  } = this.props;
54
+
55
+ const { disconnected } = subscriptions;
56
+
44
57
  if (task) {
45
58
  if (isPollingTask) {
46
59
  if (prevProps.isTaskPending && !isTaskPending) {
@@ -55,6 +68,24 @@ class SubscriptionsPage extends Component {
55
68
  if (!prevProps.organization || prevProps.organization.id !== organization.id) {
56
69
  this.loadData();
57
70
  }
71
+
72
+ if (disconnected === false && disconnected !== prevProps.subscriptions.disconnected) {
73
+ if (manifestExists(organization)) {
74
+ pingUpstreamSubscriptions();
75
+ this.state.availableQuantitiesLoaded = false;
76
+ }
77
+ }
78
+ }
79
+
80
+ if (hasUpstreamConnection) {
81
+ const subscriptionsChanged = subscriptions.results !== prevProps.subscriptions.results;
82
+ if (subscriptionsChanged || !this.state.availableQuantitiesLoaded) {
83
+ const poolIds = filterRHSubscriptions(subscriptions.results).map(subs => subs.id);
84
+ if (poolIds.length > 0) {
85
+ loadAvailableQuantities({ poolIds });
86
+ this.state.availableQuantitiesLoaded = true;
87
+ }
88
+ }
58
89
  }
59
90
  }
60
91
 
@@ -63,7 +94,12 @@ class SubscriptionsPage extends Component {
63
94
  }
64
95
 
65
96
  getDisabledReason(deleteButton) {
66
- const { task, subscriptions, organization } = this.props;
97
+ const {
98
+ hasUpstreamConnection,
99
+ task,
100
+ subscriptions,
101
+ organization,
102
+ } = this.props;
67
103
  const { disconnected } = subscriptions;
68
104
  let disabledReason = null;
69
105
 
@@ -75,6 +111,8 @@ class SubscriptionsPage extends Component {
75
111
  disabledReason = __('This is disabled because no subscriptions are selected.');
76
112
  } else if (!manifestExists(organization)) {
77
113
  disabledReason = __('This is disabled because no manifest has been uploaded.');
114
+ } else if (!hasUpstreamConnection) {
115
+ disabledReason = __('This is disabled because no connection could be made to the upstream Subscription Allocation.');
78
116
  }
79
117
 
80
118
  return disabledReason;
@@ -106,8 +144,8 @@ class SubscriptionsPage extends Component {
106
144
  const {
107
145
  deleteModalOpened, openDeleteModal, closeDeleteModal,
108
146
  deleteButtonDisabled, disableDeleteButton, enableDeleteButton,
109
- searchQuery, updateSearchQuery, simpleContentAccess,
110
- task, activePermissions, subscriptions, organization, subscriptionTableSettings,
147
+ searchQuery, updateSearchQuery, simpleContentAccess, hasUpstreamConnection,
148
+ task, activePermissions, subscriptions, subscriptionTableSettings,
111
149
  } = this.props;
112
150
  // Basic permissions - should we even show this page?
113
151
  if (subscriptions.missingPermissions && subscriptions.missingPermissions.length > 0) {
@@ -122,7 +160,7 @@ class SubscriptionsPage extends Component {
122
160
  canEditOrganizations,
123
161
  } = permissions;
124
162
  const { disconnected } = subscriptions;
125
- const disableManifestActions = !!task || disconnected;
163
+ const disableManifestActions = !!task || disconnected || !hasUpstreamConnection;
126
164
 
127
165
  const openManageManifestModal = () => this.props.setModalOpen({ id: MANAGE_MANIFEST_MODAL_ID });
128
166
 
@@ -199,7 +237,7 @@ class SubscriptionsPage extends Component {
199
237
  disableManifestReason={this.getDisabledReason()}
200
238
  disableDeleteButton={deleteButtonDisabled}
201
239
  disableDeleteReason={this.getDisabledReason(true)}
202
- disableAddButton={!manifestExists(organization)}
240
+ disableAddButton={disableManifestActions}
203
241
  getAutoCompleteParams={getAutoCompleteParams}
204
242
  updateSearchQuery={updateSearchQuery}
205
243
  onDeleteButtonClick={openDeleteModal}
@@ -245,6 +283,7 @@ class SubscriptionsPage extends Component {
245
283
  task={task}
246
284
  selectedRows={this.state.selectedRows}
247
285
  onSelectedRowsChange={this.handleSelectedRowsChange}
286
+ selectionEnabled={!disableManifestActions}
248
287
  />
249
288
  <ModalProgressBar
250
289
  show={!!task}
@@ -261,7 +300,9 @@ class SubscriptionsPage extends Component {
261
300
  }
262
301
 
263
302
  SubscriptionsPage.propTypes = {
303
+ pingUpstreamSubscriptions: PropTypes.func.isRequired,
264
304
  loadSubscriptions: PropTypes.func.isRequired,
305
+ loadAvailableQuantities: PropTypes.func.isRequired,
265
306
  uploadManifest: PropTypes.func.isRequired,
266
307
  deleteManifest: PropTypes.func.isRequired,
267
308
  resetTasks: PropTypes.func.isRequired,
@@ -270,9 +311,13 @@ SubscriptionsPage.propTypes = {
270
311
  simpleContentAccess: PropTypes.bool,
271
312
  subscriptions: PropTypes.shape({
272
313
  disconnected: PropTypes.bool,
314
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
315
+ /* eslint-disable react/forbid-prop-types */
273
316
  tableColumns: PropTypes.array,
274
317
  selectedTableColumns: PropTypes.array,
275
318
  missingPermissions: PropTypes.array,
319
+ results: PropTypes.array,
320
+ /* eslint-enable react/forbid-prop-types */
276
321
  }).isRequired,
277
322
  activePermissions: PropTypes.shape({
278
323
  can_delete_manifest: PropTypes.bool,
@@ -302,6 +347,7 @@ SubscriptionsPage.propTypes = {
302
347
  cancelPollTasks: PropTypes.func.isRequired,
303
348
  handleStartTask: PropTypes.func.isRequired,
304
349
  handleFinishedTask: PropTypes.func.isRequired,
350
+ hasUpstreamConnection: PropTypes.bool,
305
351
  loadSetting: PropTypes.func.isRequired,
306
352
  loadTables: PropTypes.func.isRequired,
307
353
  createColumns: PropTypes.func.isRequired,
@@ -330,6 +376,7 @@ SubscriptionsPage.defaultProps = {
330
376
  deleteButtonDisabled: true,
331
377
  subscriptionTableSettings: {},
332
378
  simpleContentAccess: false,
379
+ hasUpstreamConnection: false,
333
380
  activePermissions: {
334
381
  can_import_manifest: false,
335
382
  can_manage_subscription_allocations: false,
@@ -26,3 +26,6 @@ export const selectIsTaskPending = (state) => {
26
26
 
27
27
  export const selectTableSettings = (state, tableName) =>
28
28
  state.katello.settings.tables[tableName] || undefined;
29
+
30
+ export const selectHasUpstreamConnection = state =>
31
+ selectSubscriptionsState(state).hasUpstreamConnection;
@@ -10,7 +10,21 @@ import {
10
10
  SAVE_UPSTREAM_SUBSCRIPTIONS_REQUEST,
11
11
  SAVE_UPSTREAM_SUBSCRIPTIONS_SUCCESS,
12
12
  SAVE_UPSTREAM_SUBSCRIPTIONS_FAILURE,
13
- } from './UpstreamSubscriptionsContstants';
13
+ PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS,
14
+ PING_UPSTREAM_SUBSCRIPTIONS_FAILURE,
15
+ } from './UpstreamSubscriptionsConstants';
16
+
17
+ export const pingUpstreamSubscriptions = () => async (dispatch) => {
18
+ try {
19
+ const { data } = await api.get(`/organizations/${orgId()}/upstream_subscriptions/ping`);
20
+ return dispatch({
21
+ type: PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS,
22
+ payload: data,
23
+ });
24
+ } catch (error) {
25
+ return dispatch(apiError(PING_UPSTREAM_SUBSCRIPTIONS_FAILURE, error));
26
+ }
27
+ };
14
28
 
15
29
  export const loadUpstreamSubscriptions = (extendedParams = {}) => async (dispatch) => {
16
30
  dispatch({ type: UPSTREAM_SUBSCRIPTIONS_REQUEST });
@@ -5,3 +5,6 @@ export const UPSTREAM_SUBSCRIPTIONS_FAILURE = 'UPSTREAM_SUBSCRIPTIONS_FAILURE';
5
5
  export const SAVE_UPSTREAM_SUBSCRIPTIONS_REQUEST = 'SAVE_UPSTREAM_SUBSCRIPTIONS_REQUEST';
6
6
  export const SAVE_UPSTREAM_SUBSCRIPTIONS_SUCCESS = 'SAVE_UPSTREAM_SUBSCRIPTIONS_SUCCESS';
7
7
  export const SAVE_UPSTREAM_SUBSCRIPTIONS_FAILURE = 'SAVE_UPSTREAM_SUBSCRIPTIONS_FAILURE';
8
+
9
+ export const PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS = 'PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS';
10
+ export const PING_UPSTREAM_SUBSCRIPTIONS_FAILURE = 'PING_UPSTREAM_SUBSCRIPTIONS_FAILURE';
@@ -262,6 +262,8 @@ UpstreamSubscriptionsPage.propTypes = {
262
262
  upstreamSubscriptions: PropTypes.shape({
263
263
  loading: PropTypes.bool,
264
264
  itemCount: PropTypes.number,
265
+ // Disabling rule as existing code failed due to an eslint-plugin-react update
266
+ // eslint-disable-next-line react/forbid-prop-types
265
267
  results: PropTypes.array,
266
268
  pagination: PropTypes.shape({}),
267
269
  task: PropTypes.shape({
@@ -7,7 +7,7 @@ import {
7
7
  SAVE_UPSTREAM_SUBSCRIPTIONS_REQUEST,
8
8
  SAVE_UPSTREAM_SUBSCRIPTIONS_SUCCESS,
9
9
  SAVE_UPSTREAM_SUBSCRIPTIONS_FAILURE,
10
- } from './UpstreamSubscriptionsContstants';
10
+ } from './UpstreamSubscriptionsConstants';
11
11
 
12
12
  const initialState = initialApiState;
13
13
 
@@ -1,4 +1,4 @@
1
- import * as types from '../UpstreamSubscriptionsContstants';
1
+ import * as types from '../UpstreamSubscriptionsConstants';
2
2
 
3
3
  import {
4
4
  initialState,