katello 5.0.0.rc2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/katello/locale/bn/katello.js +41 -2
  3. data/app/assets/javascripts/katello/locale/bn_IN/katello.js +41 -2
  4. data/app/assets/javascripts/katello/locale/ca/katello.js +41 -2
  5. data/app/assets/javascripts/katello/locale/cs/katello.js +41 -2
  6. data/app/assets/javascripts/katello/locale/cs_CZ/katello.js +42 -3
  7. data/app/assets/javascripts/katello/locale/de/katello.js +42 -3
  8. data/app/assets/javascripts/katello/locale/de_AT/katello.js +41 -2
  9. data/app/assets/javascripts/katello/locale/de_DE/katello.js +41 -2
  10. data/app/assets/javascripts/katello/locale/el/katello.js +41 -2
  11. data/app/assets/javascripts/katello/locale/en/katello.js +41 -2
  12. data/app/assets/javascripts/katello/locale/en_GB/katello.js +41 -2
  13. data/app/assets/javascripts/katello/locale/en_US/katello.js +41 -2
  14. data/app/assets/javascripts/katello/locale/es/katello.js +42 -3
  15. data/app/assets/javascripts/katello/locale/et_EE/katello.js +41 -2
  16. data/app/assets/javascripts/katello/locale/fr/katello.js +42 -3
  17. data/app/assets/javascripts/katello/locale/gl/katello.js +41 -2
  18. data/app/assets/javascripts/katello/locale/gu/katello.js +41 -2
  19. data/app/assets/javascripts/katello/locale/he_IL/katello.js +41 -2
  20. data/app/assets/javascripts/katello/locale/hi/katello.js +41 -2
  21. data/app/assets/javascripts/katello/locale/id/katello.js +41 -2
  22. data/app/assets/javascripts/katello/locale/it/katello.js +42 -3
  23. data/app/assets/javascripts/katello/locale/ja/katello.js +42 -3
  24. data/app/assets/javascripts/katello/locale/ka/katello.js +42 -3
  25. data/app/assets/javascripts/katello/locale/kn/katello.js +41 -2
  26. data/app/assets/javascripts/katello/locale/ko/katello.js +42 -3
  27. data/app/assets/javascripts/katello/locale/ml_IN/katello.js +41 -2
  28. data/app/assets/javascripts/katello/locale/mr/katello.js +41 -2
  29. data/app/assets/javascripts/katello/locale/nl_NL/katello.js +41 -2
  30. data/app/assets/javascripts/katello/locale/or/katello.js +41 -2
  31. data/app/assets/javascripts/katello/locale/pa/katello.js +41 -2
  32. data/app/assets/javascripts/katello/locale/pl/katello.js +41 -2
  33. data/app/assets/javascripts/katello/locale/pl_PL/katello.js +41 -2
  34. data/app/assets/javascripts/katello/locale/pt/katello.js +41 -2
  35. data/app/assets/javascripts/katello/locale/pt_BR/katello.js +42 -3
  36. data/app/assets/javascripts/katello/locale/ro/katello.js +41 -2
  37. data/app/assets/javascripts/katello/locale/ro_RO/katello.js +41 -2
  38. data/app/assets/javascripts/katello/locale/ru/katello.js +42 -3
  39. data/app/assets/javascripts/katello/locale/sl/katello.js +41 -2
  40. data/app/assets/javascripts/katello/locale/sv_SE/katello.js +41 -2
  41. data/app/assets/javascripts/katello/locale/ta/katello.js +41 -2
  42. data/app/assets/javascripts/katello/locale/ta_IN/katello.js +41 -2
  43. data/app/assets/javascripts/katello/locale/te/katello.js +41 -2
  44. data/app/assets/javascripts/katello/locale/tr/katello.js +41 -2
  45. data/app/assets/javascripts/katello/locale/vi/katello.js +41 -2
  46. data/app/assets/javascripts/katello/locale/vi_VN/katello.js +41 -2
  47. data/app/assets/javascripts/katello/locale/zh/katello.js +41 -2
  48. data/app/assets/javascripts/katello/locale/zh_CN/katello.js +42 -3
  49. data/app/assets/javascripts/katello/locale/zh_TW/katello.js +42 -3
  50. data/app/controllers/katello/concerns/api/v2/hosts_controller_extensions.rb +10 -10
  51. data/app/controllers/katello/concerns/registration_commands_controller_extensions.rb +1 -1
  52. data/app/models/katello/concerns/smart_proxy_extensions.rb +1 -1
  53. data/app/views/katello/api/v2/activation_keys/base.json.rabl +18 -18
  54. data/app/views/katello/api/v2/content_facet/base.json.rabl +19 -19
  55. data/app/views/katello/api/v2/content_view_environments/show.json.rabl +10 -10
  56. data/app/views/katello/api/v2/content_view_versions/base.json.rabl +2 -2
  57. data/db/migrate/20220929204746_add_content_view_environment_content_facet.rb +5 -5
  58. data/db/migrate/20240730163043_add_content_view_environment_activation_key.rb +5 -5
  59. data/db/migrate/20260402151630_add_content_view_environment_to_hostgroup_content_facet.rb +4 -4
  60. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-info.html +1 -1
  61. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/new/views/activation-key-new.html +1 -1
  62. data/lib/katello/tasks/upgrades/4.20/clean_cve_with_no_cvv.rake +4 -4
  63. data/lib/katello/version.rb +1 -1
  64. data/lib/rubocop/cop/katello/cve_abbreviation.rb +82 -0
  65. data/locale/bn/LC_MESSAGES/katello.mo +0 -0
  66. data/locale/bn/katello.po +41 -2
  67. data/locale/bn_IN/LC_MESSAGES/katello.mo +0 -0
  68. data/locale/bn_IN/katello.po +41 -2
  69. data/locale/ca/LC_MESSAGES/katello.mo +0 -0
  70. data/locale/ca/katello.po +41 -2
  71. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  72. data/locale/cs/katello.po +41 -2
  73. data/locale/cs_CZ/LC_MESSAGES/katello.mo +0 -0
  74. data/locale/cs_CZ/katello.po +42 -3
  75. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  76. data/locale/de/katello.po +42 -3
  77. data/locale/de_AT/LC_MESSAGES/katello.mo +0 -0
  78. data/locale/de_AT/katello.po +41 -2
  79. data/locale/de_DE/LC_MESSAGES/katello.mo +0 -0
  80. data/locale/de_DE/katello.po +41 -2
  81. data/locale/el/LC_MESSAGES/katello.mo +0 -0
  82. data/locale/el/katello.po +41 -2
  83. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  84. data/locale/en/katello.po +41 -2
  85. data/locale/en_GB/LC_MESSAGES/katello.mo +0 -0
  86. data/locale/en_GB/katello.po +41 -2
  87. data/locale/en_US/LC_MESSAGES/katello.mo +0 -0
  88. data/locale/en_US/katello.po +41 -2
  89. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  90. data/locale/es/katello.po +42 -3
  91. data/locale/et_EE/LC_MESSAGES/katello.mo +0 -0
  92. data/locale/et_EE/katello.po +41 -2
  93. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  94. data/locale/fr/katello.po +42 -3
  95. data/locale/gl/LC_MESSAGES/katello.mo +0 -0
  96. data/locale/gl/katello.po +41 -2
  97. data/locale/gu/LC_MESSAGES/katello.mo +0 -0
  98. data/locale/gu/katello.po +41 -2
  99. data/locale/he_IL/LC_MESSAGES/katello.mo +0 -0
  100. data/locale/he_IL/katello.po +41 -2
  101. data/locale/hi/LC_MESSAGES/katello.mo +0 -0
  102. data/locale/hi/katello.po +41 -2
  103. data/locale/id/LC_MESSAGES/katello.mo +0 -0
  104. data/locale/id/katello.po +41 -2
  105. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  106. data/locale/it/katello.po +42 -3
  107. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  108. data/locale/ja/katello.po +42 -3
  109. data/locale/ka/LC_MESSAGES/katello.mo +0 -0
  110. data/locale/ka/katello.po +42 -3
  111. data/locale/katello.pot +315 -257
  112. data/locale/kn/LC_MESSAGES/katello.mo +0 -0
  113. data/locale/kn/katello.po +41 -2
  114. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  115. data/locale/ko/katello.po +42 -3
  116. data/locale/ml_IN/LC_MESSAGES/katello.mo +0 -0
  117. data/locale/ml_IN/katello.po +41 -2
  118. data/locale/mr/LC_MESSAGES/katello.mo +0 -0
  119. data/locale/mr/katello.po +41 -2
  120. data/locale/nl_NL/LC_MESSAGES/katello.mo +0 -0
  121. data/locale/nl_NL/katello.po +41 -2
  122. data/locale/or/LC_MESSAGES/katello.mo +0 -0
  123. data/locale/or/katello.po +41 -2
  124. data/locale/pa/LC_MESSAGES/katello.mo +0 -0
  125. data/locale/pa/katello.po +41 -2
  126. data/locale/pl/LC_MESSAGES/katello.mo +0 -0
  127. data/locale/pl/katello.po +41 -2
  128. data/locale/pl_PL/LC_MESSAGES/katello.mo +0 -0
  129. data/locale/pl_PL/katello.po +41 -2
  130. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  131. data/locale/pt/katello.po +41 -2
  132. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  133. data/locale/pt_BR/katello.po +42 -3
  134. data/locale/ro/LC_MESSAGES/katello.mo +0 -0
  135. data/locale/ro/katello.po +41 -2
  136. data/locale/ro_RO/LC_MESSAGES/katello.mo +0 -0
  137. data/locale/ro_RO/katello.po +41 -2
  138. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  139. data/locale/ru/katello.po +42 -3
  140. data/locale/sl/LC_MESSAGES/katello.mo +0 -0
  141. data/locale/sl/katello.po +41 -2
  142. data/locale/sv_SE/LC_MESSAGES/katello.mo +0 -0
  143. data/locale/sv_SE/katello.po +41 -2
  144. data/locale/ta/LC_MESSAGES/katello.mo +0 -0
  145. data/locale/ta/katello.po +41 -2
  146. data/locale/ta_IN/LC_MESSAGES/katello.mo +0 -0
  147. data/locale/ta_IN/katello.po +41 -2
  148. data/locale/te/LC_MESSAGES/katello.mo +0 -0
  149. data/locale/te/katello.po +41 -2
  150. data/locale/tr/LC_MESSAGES/katello.mo +0 -0
  151. data/locale/tr/katello.po +41 -2
  152. data/locale/vi/LC_MESSAGES/katello.mo +0 -0
  153. data/locale/vi/katello.po +41 -2
  154. data/locale/vi_VN/LC_MESSAGES/katello.mo +0 -0
  155. data/locale/vi_VN/katello.po +41 -2
  156. data/locale/zh/LC_MESSAGES/katello.mo +0 -0
  157. data/locale/zh/katello.po +41 -2
  158. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  159. data/locale/zh_CN/katello.po +42 -3
  160. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  161. data/locale/zh_TW/katello.po +42 -3
  162. data/package.json +2 -2
  163. data/webpack/ForemanColumnExtensions/index.js +1 -1
  164. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/AssignHostCVModal.js +3 -3
  165. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ContentViewDetailsCard.js +5 -5
  166. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/OrderableAssignments.js +5 -5
  167. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/__tests__/AssignHostCVModal.test.js +17 -17
  168. data/webpack/components/extensions/Hosts/BulkActions/BulkAssignCVEnvsModal/BulkAssignCVEnvsModal.js +4 -8
  169. data/webpack/components/extensions/Hosts/BulkActions/BulkAssignCVEnvsModal/__tests__/BulkAssignCVEnvsModal.test.js +1 -1
  170. data/webpack/components/extensions/Hosts/BulkActions/BulkAssignCVEnvsModal/index.js +2 -1
  171. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/BulkChangeHostCollectionsModal.js +5 -10
  172. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/__tests__/BulkChangeHostCollectionsModal.test.js +6 -24
  173. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/index.js +2 -1
  174. data/webpack/components/extensions/Hosts/BulkActions/BulkErrataWizard/04_ReviewFooter.js +2 -0
  175. data/webpack/components/extensions/Hosts/BulkActions/BulkErrataWizard/BulkErrataWizard.js +2 -1
  176. data/webpack/components/extensions/Hosts/BulkActions/BulkManageTracesModal/BulkManageTracesModal.js +4 -0
  177. data/webpack/components/extensions/Hosts/BulkActions/BulkManageTracesModal/index.js +2 -1
  178. data/webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/04_ReviewFooter.js +2 -0
  179. data/webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/BulkPackagesWizard.js +2 -1
  180. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/03_ReviewFooter.js +2 -0
  181. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/BulkRepositorySetsWizard.js +2 -1
  182. data/webpack/components/extensions/Hosts/BulkActions/BulkSystemPurposeModal/BulkSystemPurposeModal.js +5 -0
  183. data/webpack/components/extensions/Hosts/BulkActions/BulkSystemPurposeModal/index.js +2 -1
  184. data/webpack/components/extensions/Hosts/BulkActions/__tests__/bulkSystemPurposeModal.test.js +6 -1
  185. data/webpack/components/extensions/RegistrationCommands/__tests__/ActivationKeys.test.js +6 -6
  186. data/webpack/components/extensions/RegistrationCommands/fields/ActivationKeys.js +1 -1
  187. data/webpack/containers/Application/config.js +1 -1
  188. data/webpack/eslint-rules/no-cve-abbreviation.js +121 -0
  189. data/webpack/global_index.js +6 -6
  190. data/webpack/redux/reducers/index.js +0 -2
  191. data/webpack/scenes/ActivationKeys/Details/ActivationKeyDetails.js +2 -2
  192. data/webpack/scenes/ActivationKeys/Details/components/AssignAKCVModal.js +3 -3
  193. data/webpack/scenes/ActivationKeys/Details/components/CreateAKCVModal.js +1 -1
  194. data/webpack/scenes/ActivationKeys/Details/components/{CVEDetailsCard.js → CvEnvDetailsCard.js} +9 -9
  195. data/webpack/scenes/ActivationKeys/Details/components/{CVEDetailsCreateCard.js → CvEnvDetailsCreateCard.js} +10 -10
  196. data/webpack/scenes/ActivationKeys/Details/components/__tests__/{CVEDetailsCreateCard.test.js → CvEnvDetailsCreateCard.test.js} +8 -8
  197. data/webpack/scenes/ContainerImages/Booted/BootedContainerImagesPage.js +2 -2
  198. data/webpack/scenes/ContainerImages/ContainerImagesPage.js +59 -52
  199. data/webpack/scenes/ContainerImages/ContainerImagesPage.scss +27 -4
  200. data/webpack/scenes/ContainerImages/Synced/SyncedContainerImagesPage.js +1 -1
  201. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVVersionRemoveReview.js +9 -9
  202. data/webpack/scenes/Hosts/ChangeContentSource/actions.js +2 -2
  203. data/webpack/scenes/Subscriptions/Manifest/CdnTabContent.js +44 -0
  204. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +179 -307
  205. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModalConstants.js +5 -0
  206. data/webpack/scenes/Subscriptions/Manifest/ManifestActions.js +64 -70
  207. data/webpack/scenes/Subscriptions/Manifest/ManifestConstants.js +5 -0
  208. data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryContent.js +61 -0
  209. data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryReducer.js +1 -1
  210. data/webpack/scenes/Subscriptions/Manifest/ManifestTabContent.js +357 -0
  211. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManageManifestModal.test.js +129 -0
  212. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestActions.test.js +69 -110
  213. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestHistoryReducer.test.js +6 -2
  214. data/webpack/scenes/Subscriptions/Manifest/index.js +16 -1
  215. data/webpack/scenes/Subscriptions/SubscriptionActions.js +79 -145
  216. data/webpack/scenes/Subscriptions/SubscriptionConstants.js +4 -35
  217. data/webpack/scenes/Subscriptions/SubscriptionsColumns.js +90 -0
  218. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +297 -217
  219. data/webpack/scenes/Subscriptions/SubscriptionsPage.scss +40 -30
  220. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsActions.js +11 -8
  221. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsConstants.js +0 -1
  222. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +104 -205
  223. data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +12 -8
  224. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTable.js +245 -241
  225. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionsTable.test.js +61 -27
  226. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Table.js +487 -73
  227. data/webpack/scenes/Subscriptions/components/SubscriptionsToolbar/SubscriptionsToolbar.js +22 -4
  228. data/webpack/utils/contentViewEnvironmentLabel.js +3 -3
  229. data/webpack/utils/helpers.js +2 -2
  230. metadata +15 -20
  231. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.scss +0 -39
  232. data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryTableSchema.js +0 -47
  233. data/webpack/scenes/Subscriptions/SubscriptionReducer.js +0 -223
  234. data/webpack/scenes/Subscriptions/SubscriptionsSelectors.js +0 -31
  235. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsActions.test.js +0 -297
  236. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsReducer.test.js +0 -244
  237. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsSelectors.test.js +0 -47
  238. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionNameFormatter.js +0 -23
  239. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionTypeFormatter.js +0 -32
  240. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +0 -169
  241. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/SubscriptionTypeFormatter.test.js +0 -41
  242. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/index.js +0 -2
  243. data/webpack/scenes/Subscriptions/index.js +0 -57
@@ -1,5 +1,3 @@
1
- @import 'foremanReact/common/variables';
2
-
3
1
  h1[data-ouia-component-id="subscriptions-title"] {
4
2
  margin: 0;
5
3
  display: flex;
@@ -13,6 +11,7 @@ h1[data-ouia-component-id="subscriptions-title"] {
13
11
  #subscriptions-toolbar {
14
12
  .subscriptions-filter-group {
15
13
  flex-grow: 0;
14
+ margin-inline-end: var(--pf-v5-global--spacer--md);
16
15
  }
17
16
 
18
17
  [data-ouia-component-id="subscriptions-kebab-dropdown"] {
@@ -54,41 +53,52 @@ h1[data-ouia-component-id="subscriptions-title"] {
54
53
  }
55
54
  }
56
55
 
57
- #subscriptions-table {
58
- .editable {
59
- &.changed {
60
- font-weight: bold;
61
- }
56
+ .table-index-page #subscriptions-toolbar {
57
+ width: 100%;
58
+ padding-left: var(--pf-v5-global--spacer--lg);
59
+ padding-right: var(--pf-v5-global--spacer--lg);
60
+
61
+ .subscriptions-toolbar-content {
62
+ padding-left: 0;
63
+ padding-right: 0;
62
64
  }
63
65
 
64
- .pf-table-inline-edit {
65
- tbody {
66
- .form-group {
67
- margin-bottom: 0;
68
- }
66
+ .subscriptions-filter-group {
67
+ width: auto;
68
+ }
69
+ }
69
70
 
70
- tr.open-grouped-row {
71
- background-color: $table-bg-hover;
71
+ #subscriptions-table {
72
+ .editable.changed {
73
+ font-weight: var(--pf-v5-global--FontWeight--bold);
74
+ }
72
75
 
73
- td {
74
- border-bottom-color: $table-border-hover;
75
- }
76
- }
77
- }
76
+ .subscriptions-table-select {
77
+ width: 1%;
78
+ white-space: nowrap;
79
+ vertical-align: middle;
80
+ }
78
81
 
79
- .table-view-pf-select {
80
- width: 50px;
82
+ .subscriptions-table-select-cell {
83
+ display: flex;
84
+ align-items: center;
85
+ gap: var(--pf-v5-global--spacer--sm);
86
+ min-height: var(--pf-v5-global--FontSize--xl);
87
+ }
81
88
 
82
- .collapse-subscription-group-button {
83
- margin-top: 4px;
84
- cursor: pointer;
85
- float: left;
86
- font-weight: bold;
87
- }
89
+ .collapse-subscription-group-button {
90
+ display: inline-flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ padding: 0;
94
+ line-height: 1;
95
+ }
88
96
 
89
- input[type='checkbox'] {
90
- float: right;
91
- }
97
+ tr.open-grouped-row {
98
+ background-color: var(--pf-v5-global--BackgroundColor--200);
99
+
100
+ td {
101
+ border-bottom-color: var(--pf-v5-global--BorderColor--100);
92
102
  }
93
103
  }
94
104
  }
@@ -6,7 +6,6 @@ import api, { orgId } from '../../../services/api';
6
6
  import { apiError, getResponseErrorMsgs } from '../../../utils/helpers';
7
7
  import {
8
8
  SAVE_UPSTREAM_SUBSCRIPTIONS_KEY,
9
- PING_UPSTREAM_SUBSCRIPTIONS_KEY,
10
9
  PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS,
11
10
  PING_UPSTREAM_SUBSCRIPTIONS_FAILURE,
12
11
  } from './UpstreamSubscriptionsConstants';
@@ -34,12 +33,16 @@ export const saveUpstreamSubscriptions = (params, handleSuccess) =>
34
33
  handleSuccess,
35
34
  });
36
35
 
37
- const pingUpstreamSubscriptions = () => dispatch =>
38
- dispatch(APIActions.get({
39
- key: PING_UPSTREAM_SUBSCRIPTIONS_KEY,
40
- url: api.getApiUrl(`/organizations/${orgId()}/upstream_subscriptions/ping`),
41
- handleSuccess: () => dispatch({ type: PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS }),
42
- handleError: error => dispatch(apiError(PING_UPSTREAM_SUBSCRIPTIONS_FAILURE, error)),
43
- }));
36
+ const pingUpstreamSubscriptions = () => async (dispatch) => {
37
+ try {
38
+ const { data } = await api.get(`/organizations/${orgId()}/upstream_subscriptions/ping`);
39
+ return dispatch({
40
+ type: PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS,
41
+ payload: data,
42
+ });
43
+ } catch (error) {
44
+ return dispatch(apiError(PING_UPSTREAM_SUBSCRIPTIONS_FAILURE, error));
45
+ }
46
+ };
44
47
 
45
48
  export default pingUpstreamSubscriptions;
@@ -1,6 +1,5 @@
1
1
  export const UPSTREAM_SUBSCRIPTIONS_KEY = 'UPSTREAM_SUBSCRIPTIONS';
2
2
  export const SAVE_UPSTREAM_SUBSCRIPTIONS_KEY = 'SAVE_UPSTREAM_SUBSCRIPTIONS';
3
- export const PING_UPSTREAM_SUBSCRIPTIONS_KEY = 'PING_UPSTREAM_SUBSCRIPTIONS';
4
3
 
5
4
  export const PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS = 'PING_UPSTREAM_SUBSCRIPTIONS_SUCCESS';
6
5
  export const PING_UPSTREAM_SUBSCRIPTIONS_FAILURE = 'PING_UPSTREAM_SUBSCRIPTIONS_FAILURE';
@@ -1,85 +1,112 @@
1
1
  import React from 'react';
2
- import { render, screen, waitFor } from '@testing-library/react';
2
+ import { render, screen } from '@testing-library/react';
3
3
  import '@testing-library/jest-dom';
4
- import { act } from 'react-dom/test-utils';
4
+ import { useSelector } from 'react-redux';
5
5
  import SubscriptionsPage from '../SubscriptionsPage';
6
- import { successState, permissionDeniedState } from './subscriptions.fixtures';
7
- import { loadAvailableQuantities, updateQuantity } from '../SubscriptionActions';
8
- import pingUpstreamSubscriptions from '../UpstreamSubscriptions/UpstreamSubscriptionsActions';
6
+
7
+ const mockDispatch = jest.fn();
8
+ const mockSelectorState = {
9
+ organization: { id: 1, owner_details: { upstreamConsumer: {} } },
10
+ missingPermissionsFromApi: null,
11
+ };
12
+
13
+ jest.mock('react-redux', () => ({
14
+ ...jest.requireActual('react-redux'),
15
+ useDispatch: () => mockDispatch,
16
+ useSelector: jest.fn(),
17
+ }));
9
18
 
10
19
  jest.mock('foremanReact/components/PermissionDenied', () => ({
11
- __esModule: true, default: ({ missingPermissions }) => <div>PermissionDenied: {missingPermissions.join(', ')}</div>,
20
+ __esModule: true,
21
+ default: ({ missingPermissions }) => (
22
+ <div>{`PermissionDenied: ${missingPermissions.join(', ')}`}</div>
23
+ ),
24
+ }));
25
+ jest.mock('foremanReact/components/ForemanModal', () => ({
26
+ __esModule: true,
27
+ default: () => <div>ForemanModal Mock</div>,
12
28
  }));
13
- jest.mock('foremanReact/components/ForemanModal', () => (<div>ForemanModal Mock</div>));
14
29
  jest.mock('../Manifest/', () => ({
15
30
  __esModule: true, default: () => <div>ManageManifestModal Mock</div>,
16
31
  }));
17
- jest.mock('../components/SubscriptionsTable', () => ({
18
- SubscriptionsTable: () => <div>SubscriptionsTable Mock</div>,
19
- }));
32
+ jest.mock('../components/SubscriptionsTable/SubscriptionsTable', () => {
33
+ /* eslint-disable global-require, react/prop-types */
34
+ const MockReact = require('react');
35
+ const MockSubscriptionsTable = ({ customHeader, customToolbar, onApiResponse }) => {
36
+ MockReact.useEffect(() => {
37
+ if (!onApiResponse) return undefined;
38
+ if (mockSelectorState.missingPermissionsFromApi) {
39
+ onApiResponse({
40
+ results: [],
41
+ loading: false,
42
+ missingPermissions: mockSelectorState.missingPermissionsFromApi,
43
+ activePermissions: {},
44
+ });
45
+ return undefined;
46
+ }
47
+ onApiResponse({
48
+ results: [],
49
+ loading: false,
50
+ itemCount: 0,
51
+ page: 1,
52
+ perPage: 20,
53
+ searchIsActive: false,
54
+ activePermissions: {
55
+ can_import_manifest: true,
56
+ can_delete_manifest: true,
57
+ can_manage_subscription_allocations: true,
58
+ can_edit_organizations: true,
59
+ },
60
+ });
61
+ return undefined;
62
+ }, [onApiResponse]);
63
+
64
+ return (
65
+ <div>
66
+ {customHeader}
67
+ {customToolbar}
68
+ <div>SubscriptionsTable Mock</div>
69
+ </div>
70
+ );
71
+ };
72
+ /* eslint-enable global-require, react/prop-types */
73
+ return {
74
+ __esModule: true,
75
+ default: MockSubscriptionsTable,
76
+ };
77
+ });
20
78
  jest.mock('../components/SubscriptionsToolbar', () => ({
21
79
  __esModule: true, default: () => <div>SubscriptionsToolbar Mock</div>,
22
80
  }));
23
- jest.mock('foremanReact/components/PF4/TableIndexPage/Table/TableIndexHooks', () => ({
24
- useCurrentUserTablePreferences: jest.fn(() => ({
81
+ jest.mock('foremanReact/components/PF4/TableIndexPage/Table/TableIndexHooks', () => {
82
+ const mockPrefs = {
25
83
  columns: [],
26
84
  hasPreference: false,
27
85
  currentUserId: 1,
28
- })),
29
- }));
30
-
31
-
32
- const pollTasks = jest.fn();
33
- const handleStartTask = jest.fn();
34
- const handleFinishedTask = jest.fn();
35
- const mockLoadSubscriptions = jest.fn();
36
- const mockLoadTableColumns = jest.fn();
86
+ };
87
+ return {
88
+ useCurrentUserTablePreferences: jest.fn(() => mockPrefs),
89
+ };
90
+ });
37
91
 
38
- afterEach(() => {
39
- pollTasks.mockClear();
40
- handleStartTask.mockClear();
41
- handleFinishedTask.mockClear();
42
- mockLoadSubscriptions.mockClear();
43
- mockLoadTableColumns.mockClear();
92
+ const buildReduxState = () => ({
93
+ katello: {
94
+ organization: mockSelectorState.organization,
95
+ },
96
+ intervals: {},
97
+ API: {},
44
98
  });
45
99
 
46
100
  describe('subscriptions page', () => {
47
- const noop = () => {
48
- };
49
- const organization = { owner_details: { upstreamConsumer: {} } };
50
-
51
- const getDefaultProps = (subscriptionState = successState) => ({
52
- setModalOpen: noop,
53
- setModalClosed: noop,
54
- organization,
55
- subscriptions: subscriptionState,
56
- loadTableColumns: mockLoadTableColumns,
57
- loadSubscriptions: mockLoadSubscriptions,
58
- loadAvailableQuantities,
59
- pingUpstreamSubscriptions,
60
- updateQuantity,
61
- handleStartTask,
62
- handleFinishedTask,
63
- pollTaskUntilDone: noop,
64
- pollBulkSearch: noop,
65
- pollTasks,
66
- cancelPollTasks: noop,
67
- deleteSubscriptions: () => {},
68
- resetTasks: noop,
69
- uploadManifest: noop,
70
- deleteManifest: noop,
71
- refreshManifest: noop,
72
- updateSearchQuery: noop,
73
- openManageManifestModal: noop,
74
- closeManageManifestModal: noop,
75
- openDeleteModal: noop,
76
- closeDeleteModal: noop,
77
- disableDeleteButton: noop,
78
- enableDeleteButton: noop,
101
+ beforeEach(() => {
102
+ mockDispatch.mockClear();
103
+ mockSelectorState.organization = { id: 1, owner_details: { upstreamConsumer: {} } };
104
+ mockSelectorState.missingPermissionsFromApi = null;
105
+ useSelector.mockImplementation(selector => selector(buildReduxState()));
79
106
  });
80
107
 
81
- it('should render', async () => {
82
- render(<SubscriptionsPage {...getDefaultProps()} />);
108
+ it('should render', () => {
109
+ render(<SubscriptionsPage />);
83
110
 
84
111
  expect(screen.getByText('Subscriptions')).toBeInTheDocument();
85
112
  expect(screen.getByText('SubscriptionsToolbar Mock')).toBeInTheDocument();
@@ -87,170 +114,42 @@ describe('subscriptions page', () => {
87
114
  expect(screen.getByText('ManageManifestModal Mock')).toBeInTheDocument();
88
115
  });
89
116
 
90
- it('should render <PermissionDenied /> when permissions are missing', async () => {
91
- render(<SubscriptionsPage {...getDefaultProps(permissionDeniedState)} />);
117
+ it('should render <PermissionDenied /> when subscription permissions are missing', () => {
118
+ mockSelectorState.missingPermissionsFromApi = ['view_subscriptions'];
119
+ render(<SubscriptionsPage />);
92
120
 
93
- // Check that PermissionDenied is rendered
94
121
  expect(screen.getByText(/PermissionDenied:/)).toBeInTheDocument();
95
-
96
- // Should not render the normal subscriptions page content
97
- expect(screen.queryByText('SubscriptionsTable Mock')).not.toBeInTheDocument();
122
+ expect(screen.getByText(/view_subscriptions/)).toBeInTheDocument();
98
123
  });
99
124
 
100
- it('should render <PermissionDenied /> when organization load fails with 403', async () => {
101
- const orgWith403Error = {
125
+ it('should render <PermissionDenied /> when organization load fails with 403', () => {
126
+ mockSelectorState.organization = {
102
127
  loading: false,
103
- error: {
104
- response: {
105
- status: 403,
106
- },
107
- },
128
+ error: { response: { status: 403 } },
108
129
  };
109
-
110
- render(<SubscriptionsPage
111
- {...getDefaultProps()}
112
- organization={orgWith403Error}
113
- />);
130
+ render(<SubscriptionsPage />);
114
131
 
115
132
  expect(screen.getByText(/PermissionDenied:/)).toBeInTheDocument();
116
133
  expect(screen.getByText(/You do not have permission to view this organization/)).toBeInTheDocument();
117
134
  });
118
135
 
119
- it('should render <PermissionDenied /> when organization load fails with 404', async () => {
120
- const orgWith404Error = {
136
+ it('should render <PermissionDenied /> when organization load fails with 404', () => {
137
+ mockSelectorState.organization = {
121
138
  loading: false,
122
- error: {
123
- response: {
124
- status: 404,
125
- },
126
- },
139
+ error: { response: { status: 404 } },
127
140
  };
128
-
129
- render(<SubscriptionsPage
130
- {...getDefaultProps()}
131
- organization={orgWith404Error}
132
- />);
141
+ render(<SubscriptionsPage />);
133
142
 
134
143
  expect(screen.getByText(/PermissionDenied:/)).toBeInTheDocument();
135
- expect(screen.getByText(/You do not have permission to view this organization/)).toBeInTheDocument();
136
144
  });
137
145
 
138
- it('should not render <PermissionDenied /> when organization is still loading', async () => {
139
- const orgStillLoading = {
146
+ it('should not render <PermissionDenied /> when organization is still loading', () => {
147
+ mockSelectorState.organization = {
140
148
  loading: true,
141
- error: {
142
- response: {
143
- status: 403,
144
- },
145
- },
149
+ error: { response: { status: 403 } },
146
150
  };
151
+ render(<SubscriptionsPage />);
147
152
 
148
- render(<SubscriptionsPage
149
- {...getDefaultProps()}
150
- organization={orgStillLoading}
151
- />);
152
-
153
- // Should not show PermissionDenied while organization is still loading
154
153
  expect(screen.queryByText(/PermissionDenied:/)).not.toBeInTheDocument();
155
154
  });
156
-
157
- it('should render loading state when subscriptions are loading', () => {
158
- const loadingSubscriptionsState = {
159
- ...successState,
160
- loading: true,
161
- results: [],
162
- };
163
-
164
- render(<SubscriptionsPage
165
- {...getDefaultProps(loadingSubscriptionsState)}
166
- />);
167
-
168
- expect(screen.getByText('SubscriptionsTable Mock')).toBeInTheDocument();
169
- });
170
-
171
- it('should render empty state when manifest is imported but no subscriptions', () => {
172
- const emptySubscriptionsState = {
173
- ...successState,
174
- loading: false,
175
- results: [],
176
- };
177
-
178
- render(<SubscriptionsPage
179
- {...getDefaultProps(emptySubscriptionsState)}
180
- isManifestImported
181
- />);
182
-
183
- // SubscriptionsTable should be rendered (emptyState is passed as prop)
184
- expect(screen.getByText('SubscriptionsTable Mock')).toBeInTheDocument();
185
- });
186
-
187
- it('should render empty state prompting manifest import when no manifest', () => {
188
- const emptySubscriptionsState = {
189
- ...successState,
190
- loading: false,
191
- results: [],
192
- };
193
-
194
- render(<SubscriptionsPage
195
- {...getDefaultProps(emptySubscriptionsState)}
196
- isManifestImported={false}
197
- />);
198
-
199
- // SubscriptionsTable should be rendered
200
- expect(screen.getByText('SubscriptionsTable Mock')).toBeInTheDocument();
201
- });
202
-
203
- it('should poll tasks when org changes', async () => {
204
- const { rerender } = render(<SubscriptionsPage {...getDefaultProps()} />);
205
-
206
- await act(async () => {
207
- rerender(<SubscriptionsPage {...getDefaultProps()} organization={{ id: 1 }} />);
208
- });
209
-
210
- await waitFor(() => {
211
- expect(pollTasks).toHaveBeenCalled();
212
- expect(mockLoadSubscriptions).toHaveBeenCalled();
213
- expect(mockLoadTableColumns).toHaveBeenCalled();
214
- });
215
- });
216
-
217
- it('should not poll tasks if org has not changed', async () => {
218
- const { rerender } = render(<SubscriptionsPage {...getDefaultProps()} />);
219
-
220
- pollTasks.mockClear(); // Clear calls from mount
221
-
222
- await act(async () => {
223
- rerender(<SubscriptionsPage {...getDefaultProps()} />);
224
- });
225
-
226
- expect(pollTasks).not.toHaveBeenCalled();
227
- });
228
-
229
- it('should handle its task', async () => {
230
- const mockTask = {
231
- id: '12345',
232
- humanized: {
233
- action: 'Manifest Refresh',
234
- },
235
- };
236
-
237
- const { rerender } = render(<SubscriptionsPage
238
- {...getDefaultProps()}
239
- isTaskPending
240
- isPollingTask
241
- />);
242
-
243
- await act(async () => {
244
- rerender(<SubscriptionsPage
245
- {...getDefaultProps()}
246
- task={mockTask}
247
- isPollingTask
248
- isTaskPending={false}
249
- />);
250
- });
251
-
252
- await waitFor(() => {
253
- expect(handleFinishedTask).toHaveBeenCalledWith(mockTask);
254
- });
255
- });
256
155
  });
@@ -1,5 +1,9 @@
1
1
  import Immutable from 'seamless-immutable';
2
2
  import { toastErrorAction, failureAction } from '../../../services/api/testHelpers';
3
+ import {
4
+ UPDATE_QUANTITY_KEY,
5
+ SUBSCRIPTIONS_QUANTITIES_KEY,
6
+ } from '../SubscriptionConstants';
3
7
 
4
8
  export const initialState = Immutable({
5
9
  loading: true,
@@ -368,27 +372,27 @@ export const poolsUpdate = [{
368
372
 
369
373
  export const updateQuantitySuccessActions = [
370
374
  {
371
- type: 'UPDATE_QUANTITY_REQUEST',
375
+ type: `${UPDATE_QUANTITY_KEY}_REQUEST`,
372
376
  },
373
377
  {
374
- type: 'UPDATE_QUANTITY_SUCCESS',
378
+ type: `${UPDATE_QUANTITY_KEY}_SUCCESS`,
375
379
  response: requestSuccessResponse,
376
380
  },
377
381
  ];
378
382
 
379
383
  export const updateQuantityFailureActions = [
380
384
  {
381
- type: 'UPDATE_QUANTITY_REQUEST',
385
+ type: `${UPDATE_QUANTITY_KEY}_REQUEST`,
382
386
  },
383
- failureAction('UPDATE_QUANTITY_FAILURE'),
387
+ failureAction(`${UPDATE_QUANTITY_KEY}_FAILURE`),
384
388
  toastErrorAction(),
385
389
  ];
386
390
 
387
391
  export const loadQuantitiesFailureActions = [
388
392
  {
389
- type: 'SUBSCRIPTIONS_QUANTITIES_REQUEST',
393
+ type: `${SUBSCRIPTIONS_QUANTITIES_KEY}_REQUEST`,
390
394
  },
391
- failureAction('SUBSCRIPTIONS_QUANTITIES_FAILURE', 'Request failed with status code 500'),
395
+ failureAction(`${SUBSCRIPTIONS_QUANTITIES_KEY}_FAILURE`, 'Request failed with status code 500'),
392
396
  toastErrorAction('Request failed with status code 500'),
393
397
  ];
394
398
 
@@ -396,10 +400,10 @@ export const loadQuantitiesSuccessActionPayload = { 4: 100, 5: 100, 6: 40 };
396
400
 
397
401
  export const loadQuantitiesSuccessActions = [
398
402
  {
399
- type: 'SUBSCRIPTIONS_QUANTITIES_REQUEST',
403
+ type: `${SUBSCRIPTIONS_QUANTITIES_KEY}_REQUEST`,
400
404
  },
401
405
  {
402
- type: 'SUBSCRIPTIONS_QUANTITIES_SUCCESS',
406
+ type: `${SUBSCRIPTIONS_QUANTITIES_KEY}_SUCCESS`,
403
407
  payload: loadQuantitiesSuccessActionPayload,
404
408
  },
405
409
  ];