katello 3.18.5 → 4.0.0.rc1

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 (239) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/katello/katello.scss +72 -0
  3. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +6 -4
  4. data/app/controllers/katello/api/rhsm/candlepin_dynflow_proxy_controller.rb +0 -19
  5. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +1 -3
  6. data/app/controllers/katello/api/v2/content_credentials_controller.rb +24 -24
  7. data/app/controllers/katello/api/v2/content_export_incrementals_controller.rb +1 -1
  8. data/app/controllers/katello/api/v2/content_exports_controller.rb +4 -0
  9. data/app/controllers/katello/api/v2/content_views_controller.rb +2 -2
  10. data/app/controllers/katello/api/v2/host_subscriptions_controller.rb +2 -3
  11. data/app/controllers/katello/api/v2/repositories_controller.rb +3 -19
  12. data/app/controllers/katello/api/v2/simple_content_access_controller.rb +34 -0
  13. data/app/controllers/katello/api/v2/subscriptions_controller.rb +1 -1
  14. data/app/controllers/katello/api/v2/upstream_subscriptions_controller.rb +8 -4
  15. data/app/controllers/katello/concerns/api/v2/authorization.rb +1 -14
  16. data/app/controllers/katello/concerns/authorization/api/v2/content_views_controller.rb +1 -1
  17. data/app/helpers/katello/sync_management_helper.rb +0 -2
  18. data/app/lib/actions/candlepin/environment/create.rb +1 -1
  19. data/app/lib/actions/candlepin/environment/set_content.rb +1 -1
  20. data/app/lib/actions/katello/activation_key/create.rb +9 -11
  21. data/app/lib/actions/katello/capsule_content/sync.rb +8 -8
  22. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +9 -0
  23. data/app/lib/actions/katello/check_matching_content.rb +17 -0
  24. data/app/lib/actions/katello/content_view/environment_create.rb +6 -8
  25. data/app/lib/actions/katello/content_view/publish.rb +1 -1
  26. data/app/lib/actions/katello/content_view_version/incremental_update.rb +11 -7
  27. data/app/lib/actions/katello/host/hypervisors_update.rb +4 -4
  28. data/app/lib/actions/katello/organization/create.rb +3 -5
  29. data/app/lib/actions/katello/organization/destroy.rb +1 -1
  30. data/app/lib/actions/katello/organization/manifest_delete.rb +3 -5
  31. data/app/lib/actions/katello/organization/manifest_import.rb +1 -1
  32. data/app/lib/actions/katello/organization/manifest_refresh.rb +1 -1
  33. data/app/lib/actions/katello/orphan_cleanup/remove_orphans.rb +1 -1
  34. data/app/lib/actions/katello/repository/check_matching_content.rb +3 -1
  35. data/app/lib/actions/katello/repository/clone_contents.rb +8 -11
  36. data/app/lib/actions/katello/repository/create.rb +0 -8
  37. data/app/lib/actions/katello/repository/filtered_index_content.rb +3 -0
  38. data/app/lib/actions/katello/repository/index_content.rb +1 -0
  39. data/app/lib/actions/katello/repository/multi_clone_contents.rb +9 -12
  40. data/app/lib/actions/katello/repository/sync.rb +1 -5
  41. data/app/lib/actions/katello/repository/update.rb +0 -8
  42. data/app/lib/actions/middleware/execute_if_contents_changed.rb +4 -1
  43. data/app/lib/actions/pulp/orchestration/repository/refresh_repos.rb +0 -6
  44. data/app/lib/actions/pulp3/capsule_content/refresh_distribution.rb +3 -3
  45. data/app/lib/actions/pulp3/content_guard/refresh_all_distributions.rb +1 -2
  46. data/app/lib/actions/pulp3/content_migration_presenter.rb +2 -5
  47. data/app/lib/actions/pulp3/orchestration/repository/refresh_repos.rb +1 -6
  48. data/app/lib/katello/concerns/base_template_scope_extensions.rb +8 -0
  49. data/app/lib/katello/errors.rb +1 -1
  50. data/app/lib/katello/event_daemon/monitor.rb +53 -0
  51. data/app/lib/katello/event_daemon/runner.rb +99 -0
  52. data/app/lib/katello/logging.rb +32 -0
  53. data/app/lib/katello/messaging/connection.rb +1 -7
  54. data/app/lib/katello/util/pulpcore_content_filters.rb +1 -1
  55. data/app/lib/katello/validators/content_view_puppet_module_validator.rb +1 -1
  56. data/app/models/katello/activation_key.rb +2 -2
  57. data/app/models/katello/candlepin/repository_mapper.rb +1 -1
  58. data/app/models/katello/concerns/hostgroup_extensions.rb +2 -4
  59. data/app/models/katello/concerns/organization_extensions.rb +2 -2
  60. data/app/models/katello/concerns/pulp_database_unit.rb +0 -12
  61. data/app/models/katello/concerns/redhat_extensions.rb +8 -9
  62. data/app/models/katello/concerns/smart_proxy_extensions.rb +24 -0
  63. data/app/models/katello/content_view.rb +5 -1
  64. data/app/models/katello/content_view_environment.rb +2 -2
  65. data/app/models/katello/content_view_puppet_environment.rb +2 -2
  66. data/app/models/katello/content_view_version.rb +2 -1
  67. data/app/models/katello/content_view_version_export_history.rb +20 -0
  68. data/app/models/katello/erratum.rb +3 -1
  69. data/app/models/katello/file_unit.rb +0 -4
  70. data/app/models/katello/glue/candlepin/pool.rb +2 -0
  71. data/app/models/katello/glue/pulp/repo.rb +0 -6
  72. data/app/models/katello/glue/pulp/repos.rb +1 -22
  73. data/app/models/katello/host/content_facet.rb +31 -9
  74. data/app/models/katello/ping.rb +19 -39
  75. data/app/models/katello/pool.rb +5 -0
  76. data/app/models/katello/product.rb +3 -3
  77. data/app/models/katello/repository.rb +3 -3
  78. data/app/presenters/katello/host_subscription_presenter.rb +3 -4
  79. data/app/presenters/katello/host_subscriptions_presenter.rb +24 -0
  80. data/app/services/katello/applicability/applicable_content_helper.rb +6 -8
  81. data/app/services/katello/candlepin_event_listener.rb +11 -19
  82. data/app/services/katello/event_monitor/poller_thread.rb +2 -11
  83. data/app/services/katello/pulp/repository.rb +2 -4
  84. data/app/services/katello/pulp/smart_proxy_repository.rb +0 -15
  85. data/app/services/katello/pulp3/api/core.rb +0 -14
  86. data/app/services/katello/pulp3/erratum.rb +1 -2
  87. data/app/services/katello/pulp3/migration.rb +9 -83
  88. data/app/services/katello/pulp3/migration_plan.rb +5 -54
  89. data/app/services/katello/pulp3/migration_switchover.rb +5 -36
  90. data/app/services/katello/pulp3/repository/apt.rb +2 -1
  91. data/app/services/katello/pulp3/repository/yum.rb +2 -11
  92. data/app/services/katello/pulp3/repository.rb +13 -34
  93. data/app/services/katello/pulp3/rpm.rb +1 -5
  94. data/app/services/katello/pulp3/task.rb +5 -8
  95. data/app/services/katello/pulp3/task_group.rb +5 -13
  96. data/app/services/katello/repository_type.rb +1 -1
  97. data/app/views/foreman/smart_proxies/_content_tab.html.erb +4 -47
  98. data/app/views/foreman/smart_proxies/show.html.erb +1 -1
  99. data/app/views/katello/api/v2/capsule_content/sync_status.json.rabl +22 -25
  100. data/app/views/katello/api/v2/content_view_version_export_histories/show.json.rabl +1 -0
  101. data/app/views/katello/api/v2/organizations/show.json.rabl +7 -9
  102. data/app/views/katello/sync_management/_products.html.erb +1 -1
  103. data/app/views/overrides/organizations/_edit_override.html.erb +1 -4
  104. data/app/views/overrides/smart_proxies/_environment_tab.html.erb +1 -1
  105. data/app/views/overrides/smart_proxies/_environment_tab_pane.html.erb +1 -1
  106. data/config/katello.yaml.example +0 -3
  107. data/config/routes/api/v2.rb +8 -10
  108. data/db/migrate/20191204214919_add_content_view_version_counts.rb +0 -1
  109. data/db/migrate/20200514092553_move_katello_fields_from_hostgroups.katello.rb +1 -1
  110. data/db/migrate/20210119162528_delete_puppet_and_ostree_repos.rb +56 -0
  111. data/db/migrate/20210128231228_add_type_and_from_cvv_to_cvv_export_history.rb +14 -0
  112. data/db/migrate/20210201163238_migrate_background_download_policy_to_migrate.rb +7 -0
  113. data/db/seeds.d/104-proxy.rb +1 -1
  114. data/db/seeds.d/111-upgrade_tasks.rb +2 -1
  115. data/engines/bastion/app/assets/javascripts/bastion/auth/authorization.service.js +1 -1
  116. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +1 -1
  117. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content-host-register-os-client.directive.js +17 -0
  118. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-info.html +2 -2
  119. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/content-hosts.html +2 -2
  120. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register-client.html +11 -4
  121. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register-deb-client.html +38 -0
  122. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register-oracle-client.html +5 -0
  123. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register-sles-client.html +28 -0
  124. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register.html +14 -11
  125. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/filters/views/package-filter-details.html +2 -2
  126. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.controller.js +2 -2
  127. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +1 -1
  128. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/download-policy.service.js +0 -1
  129. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +1 -1
  130. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/products.controller.js +2 -2
  131. data/lib/katello/engine.rb +4 -5
  132. data/lib/katello/middleware/event_daemon.rb +1 -1
  133. data/lib/katello/permission_creator.rb +3 -2
  134. data/lib/katello/plugin.rb +2 -2
  135. data/lib/katello/tasks/jenkins.rake +1 -1
  136. data/lib/katello/tasks/pulp3_content_switchover.rake +20 -31
  137. data/lib/katello/tasks/pulp3_migration.rake +25 -45
  138. data/lib/katello/tasks/pulp3_migration_abort.rake +0 -8
  139. data/lib/katello/tasks/pulp3_migration_stats.rake +3 -46
  140. data/lib/katello/tasks/upgrades/4.0/remove_ostree_puppet_content.rake +16 -0
  141. data/lib/katello/version.rb +1 -1
  142. data/lib/proxy_api/container_gateway.rb +21 -0
  143. data/locale/bn/katello.edit.po +0 -0
  144. data/locale/cs/katello.edit.po +0 -0
  145. data/locale/de/katello.edit.po +0 -0
  146. data/locale/en/katello.edit.po +0 -0
  147. data/locale/es/katello.edit.po +0 -0
  148. data/locale/fr/katello.edit.po +0 -0
  149. data/locale/gu/katello.edit.po +0 -0
  150. data/locale/hi/katello.edit.po +0 -0
  151. data/locale/it/katello.edit.po +0 -0
  152. data/locale/ja/katello.edit.po +0 -0
  153. data/locale/kn/katello.edit.po +0 -0
  154. data/locale/ko/katello.edit.po +0 -0
  155. data/locale/mr/katello.edit.po +0 -0
  156. data/locale/or/katello.edit.po +0 -0
  157. data/locale/pa/katello.edit.po +0 -0
  158. data/locale/pt/katello.edit.po +0 -0
  159. data/locale/pt_BR/katello.edit.po +0 -0
  160. data/locale/ru/katello.edit.po +0 -0
  161. data/locale/ta/katello.edit.po +0 -0
  162. data/locale/te/katello.edit.po +0 -0
  163. data/locale/zh_CN/katello.edit.po +0 -0
  164. data/locale/zh_TW/katello.edit.po +0 -0
  165. data/package.json +1 -1
  166. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js +0 -1
  167. data/webpack/__mocks__/foremanReact/components/Pagination/PaginationHooks.js +2 -0
  168. data/webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js +5 -0
  169. data/webpack/{scenes/ContentViews/Details/Repositories → components/SelectableDropdown}/SelectableDropdown.js +20 -3
  170. data/webpack/components/SelectableDropdown/__tests__/SelectableDropdown.test.js +45 -0
  171. data/webpack/components/SelectableDropdown/index.js +3 -0
  172. data/webpack/components/Table/TableWrapper.js +2 -1
  173. data/webpack/components/Table/helpers.js +14 -0
  174. data/webpack/components/TypeAhead/pf4Search/TypeAheadSearch.js +0 -1
  175. data/webpack/containers/Application/overrides.scss +6 -0
  176. data/webpack/index.js +6 -0
  177. data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -5
  178. data/webpack/scenes/ContentViews/ContentViewsActions.js +31 -2
  179. data/webpack/scenes/ContentViews/ContentViewsConstants.js +5 -1
  180. data/webpack/scenes/ContentViews/Copy/ContentViewCopySelectors.js +16 -0
  181. data/webpack/scenes/ContentViews/Copy/CopyContentViewForm.js +77 -0
  182. data/webpack/scenes/ContentViews/Copy/CopyContentViewModal.js +44 -0
  183. data/webpack/scenes/ContentViews/Copy/__tests__/contentViewCopyResult.fixtures.json +42 -0
  184. data/webpack/scenes/ContentViews/Copy/__tests__/copyContentView.test.js +39 -0
  185. data/webpack/scenes/ContentViews/Copy/index.js +4 -0
  186. data/webpack/scenes/ContentViews/Create/ContentViewCreateSelectors.js +16 -0
  187. data/webpack/scenes/ContentViews/Create/ContentViewFormComponents.js +58 -0
  188. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +175 -0
  189. data/webpack/scenes/ContentViews/Create/CreateContentViewModal.js +27 -0
  190. data/webpack/scenes/ContentViews/Create/__tests__/contentViewCreateResult.fixtures.json +42 -0
  191. data/webpack/scenes/ContentViews/Create/__tests__/createContentView.test.js +92 -0
  192. data/webpack/scenes/ContentViews/Create/index.js +4 -0
  193. data/webpack/scenes/ContentViews/Details/ContentViewDetailActions.js +16 -0
  194. data/webpack/scenes/ContentViews/Details/ContentViewDetailSelectors.js +20 -1
  195. data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +17 -7
  196. data/webpack/scenes/ContentViews/Details/ContentViewInfo.js +19 -13
  197. data/webpack/scenes/ContentViews/Details/Filters/ContentType.js +40 -0
  198. data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilters.js +124 -0
  199. data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewFilters.fixtures.json +134 -0
  200. data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewFilters.test.js +92 -0
  201. data/webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js +44 -25
  202. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewDetailRepos.test.js +17 -7
  203. data/webpack/scenes/ContentViews/Details/__tests__/contentViewDetail.test.js +24 -0
  204. data/webpack/scenes/ContentViews/Table/ContentViewsTable.js +53 -3
  205. data/webpack/scenes/ContentViews/Table/tableDataGenerator.js +4 -3
  206. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +41 -0
  207. data/webpack/scenes/ContentViews/helpers.js +13 -0
  208. data/webpack/scenes/SmartProxy/Content.js +17 -0
  209. data/webpack/scenes/SmartProxy/SmartProxyContentActions.js +11 -0
  210. data/webpack/scenes/SmartProxy/SmartProxyContentConstants.js +3 -0
  211. data/webpack/scenes/SmartProxy/SmartProxyContentSelectors.js +16 -0
  212. data/webpack/scenes/SmartProxy/SmartProxyContentTable.js +152 -0
  213. data/webpack/scenes/SmartProxy/__tests__/SmartProxyContentResult.fixtures.json +140 -0
  214. data/webpack/scenes/SmartProxy/__tests__/SmartProxyContentTest.js +38 -0
  215. data/webpack/scenes/SmartProxy/index.js +4 -0
  216. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +2 -7
  217. data/webpack/scenes/Subscriptions/Manifest/ManifestActions.js +3 -3
  218. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManifestActions.test.js +2 -2
  219. data/webpack/scenes/Subscriptions/Manifest/__tests__/SimpleContentAccess.test.js +2 -2
  220. data/webpack/scenes/Subscriptions/Manifest/index.js +0 -1
  221. data/webpack/test-utils/react-testing-lib-wrapper.js +5 -2
  222. data/webpack/utils/helpers.js +3 -0
  223. metadata +103 -63
  224. data/app/controllers/katello/api/v2/gpg_keys_controller.rb +0 -114
  225. data/app/lib/actions/pulp3/content_migration_reset.rb +0 -22
  226. data/app/lib/katello/util/hostgroup_facets_helper.rb +0 -126
  227. data/app/overrides/disable_turbolinks_on_proxies_index.rb +0 -5
  228. data/app/services/katello/event_daemon.rb +0 -135
  229. data/app/services/katello/pulp/content_counts_calculator.rb +0 -60
  230. data/db/migrate/20210201165835_add_migration_missing_content.rb +0 -12
  231. data/db/migrate/20210420140050_add_pulp3_hrefs_to_content_types_deb.rb +0 -5
  232. data/lib/katello/tasks/check_config.rake +0 -11
  233. data/lib/katello/tasks/fix_hostgroup_facets.rake +0 -8
  234. data/lib/katello/tasks/pulp3_migration_approve_corrupted.rake +0 -21
  235. data/lib/katello/tasks/pulp3_migration_reset.rake +0 -26
  236. data/lib/katello/tasks/reports.rake +0 -7
  237. data/lib/katello/tasks/upgrades/3.10/update_gpg_key_urls.rake +0 -32
  238. data/webpack/components/TypeAhead/pf4Search/TypeAheadSearch.scss +0 -5
  239. data/webpack/scenes/ContentViews/Table/actionResolver.js +0 -28
@@ -41,7 +41,6 @@ const buildRow = (contentView, openColumn) => {
41
41
  },
42
42
  ];
43
43
  if (openColumn) row[openColumn].props.isOpen = true;
44
-
45
44
  return row;
46
45
  };
47
46
 
@@ -100,7 +99,7 @@ const buildRowsAndMapping = (contentViews, newRowMapping) => {
100
99
  const rows = [];
101
100
 
102
101
  contentViews.forEach((contentView) => {
103
- const { id } = contentView;
102
+ const { id, name } = contentView;
104
103
  const rowIndex = rows.length;
105
104
  const needsUpdate = !Object.keys(updatedRowMap).find(i => updatedRowMap[i].id === id) ||
106
105
  !Object.keys(updatedRowMap[rowIndex] || {}).includes('expandedColumn');
@@ -109,7 +108,9 @@ const buildRowsAndMapping = (contentViews, newRowMapping) => {
109
108
  const cells = buildRow(contentView, openColumn);
110
109
  const isOpen = !!openColumn;
111
110
 
112
- rows.push({ isOpen, cells });
111
+ rows.push({
112
+ cvId: id, cvName: name, isOpen, cells,
113
+ });
113
114
  rows.push(...buildDetailDropdowns(id, rowIndex, openColumn));
114
115
  });
115
116
 
@@ -246,4 +246,45 @@ test('No results message is shown for empty search', async (done) => {
246
246
  assertNockRequest(withSearchScope);
247
247
  assertNockRequest(searchResultScope, done);
248
248
  });
249
+
250
+ test('Displays Create Content View and opens modal with Form', async () => {
251
+ mockAutocomplete(nockInstance, autocompleteUrl);
252
+
253
+ const noResults = {
254
+ total: 0,
255
+ subtotal: 0,
256
+ page: 1,
257
+ per_page: 20,
258
+ results: [],
259
+ };
260
+ nockInstance
261
+ .get(cvIndexPath)
262
+ .query(true)
263
+ .reply(200, noResults);
264
+ const {
265
+ getByText, queryByText, getByLabelText,
266
+ } = renderWithRedux(<ContentViewsPage />, renderOptions);
267
+ await patientlyWaitFor(() => expect(queryByText('Create content view')).toBeTruthy());
268
+
269
+ expect(queryByText('Description')).not.toBeInTheDocument();
270
+ expect(queryByText('Name')).not.toBeInTheDocument();
271
+ expect(queryByText('Label')).not.toBeInTheDocument();
272
+ expect(queryByText('Composite content view')).not.toBeInTheDocument();
273
+ expect(queryByText('Component content view')).not.toBeInTheDocument();
274
+ expect(queryByText('Solve Dependencies')).not.toBeInTheDocument();
275
+ expect(queryByText('Auto Publish')).not.toBeInTheDocument();
276
+ expect(queryByText('Import Only')).not.toBeInTheDocument();
277
+
278
+ getByLabelText('create_content_view').click();
279
+
280
+ expect(getByText('Description')).toBeInTheDocument();
281
+ expect(getByText('Name')).toBeInTheDocument();
282
+ expect(getByText('Label')).toBeInTheDocument();
283
+ expect(getByText('Composite content view')).toBeInTheDocument();
284
+ expect(getByText('Component content view')).toBeInTheDocument();
285
+ expect(getByText('Solve Dependencies')).toBeInTheDocument();
286
+ expect(queryByText('Auto Publish')).not.toBeInTheDocument();
287
+ expect(getByText('Import Only')).toBeInTheDocument();
288
+ });
289
+
249
290
  /* eslint-enable no-useless-escape */
@@ -0,0 +1,13 @@
1
+ import { translate as __ } from 'foremanReact/common/I18n';
2
+
3
+ export const dependenciesHelpText = __('This will solve RPM and module stream dependencies on every publish of this content view. ' +
4
+ 'Dependency solving significantly increases publish time (publishes can take over three times as long) ' +
5
+ 'and filters will be ignored when adding packages to solve dependencies. ' +
6
+ 'Also, certain scenarios involving errata may still cause dependency errors.');
7
+
8
+ export const autoPublishHelpText = __('Automatically publish a new version of the composite content view whenever one of its content views is published. ' +
9
+ 'Autopublish will only happen for component views that use the \'Always use latest version\' option.');
10
+
11
+ export const importOnlyHelpText = __('Designate whether this content view is for importing from an upstream server. ' +
12
+ 'Import-only content views cannot be published directly.');
13
+
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import SmartProxyContentTable from './SmartProxyContentTable';
4
+
5
+ const Content = ({ smartProxyId }) => (
6
+ <SmartProxyContentTable smartProxyId={smartProxyId} />
7
+ );
8
+
9
+ Content.propTypes = {
10
+ smartProxyId: PropTypes.number,
11
+ };
12
+
13
+ Content.defaultProps = {
14
+ smartProxyId: null,
15
+ };
16
+
17
+ export default Content;
@@ -0,0 +1,11 @@
1
+ import { API_OPERATIONS, get } from 'foremanReact/redux/API';
2
+ import api, { orgId } from '../../services/api';
3
+ import SMART_PROXY_CONTENT_KEY from './SmartProxyContentConstants';
4
+
5
+ const getSmartProxyContent = ({ smartProxyId }) => get({
6
+ type: API_OPERATIONS.GET,
7
+ key: SMART_PROXY_CONTENT_KEY,
8
+ url: api.getApiUrl(`/capsules/${smartProxyId}/content/sync?${orgId()}`),
9
+ });
10
+
11
+ export default getSmartProxyContent;
@@ -0,0 +1,3 @@
1
+ const SMART_PROXY_CONTENT_KEY = 'SMART_PROXY_CONTENT';
2
+
3
+ export default SMART_PROXY_CONTENT_KEY;
@@ -0,0 +1,16 @@
1
+ import {
2
+ selectAPIStatus,
3
+ selectAPIError,
4
+ selectAPIResponse,
5
+ } from 'foremanReact/redux/API/APISelectors';
6
+ import { STATUS } from 'foremanReact/constants';
7
+ import SMART_PROXY_CONTENT_KEY from './SmartProxyContentConstants';
8
+
9
+ export const selectSmartProxyContent = state =>
10
+ selectAPIResponse(state, SMART_PROXY_CONTENT_KEY) || {};
11
+
12
+ export const selectSmartProxyContentStatus = state =>
13
+ selectAPIStatus(state, SMART_PROXY_CONTENT_KEY) || STATUS.PENDING;
14
+
15
+ export const selectSmartProxyContentError = state =>
16
+ selectAPIError(state, SMART_PROXY_CONTENT_KEY);
@@ -0,0 +1,152 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useSelector, useDispatch } from 'react-redux';
4
+ import { wrappable } from '@patternfly/react-table';
5
+ import { CheckCircleIcon, TimesCircleIcon } from '@patternfly/react-icons';
6
+ import { STATUS } from 'foremanReact/constants';
7
+ import { translate as __ } from 'foremanReact/common/I18n';
8
+ import { urlBuilder } from 'foremanReact/common/urlHelpers';
9
+ import LongDateTime from 'foremanReact/components/common/dates/LongDateTime';
10
+ import MainTable from '../../components/Table/MainTable';
11
+ import getSmartProxyContent from './SmartProxyContentActions';
12
+ import ContentViewIcon from '../../scenes/ContentViews/components/ContentViewIcon';
13
+ import {
14
+ selectSmartProxyContent,
15
+ selectSmartProxyContentStatus,
16
+ selectSmartProxyContentError,
17
+ } from './SmartProxyContentSelectors';
18
+
19
+ const SmartProxyContentTable = ({ smartProxyId }) => {
20
+ const dispatch = useDispatch();
21
+ const [rows, setRows] = useState([]);
22
+ const response = useSelector(state => selectSmartProxyContent(state));
23
+ const status = useSelector(state => selectSmartProxyContentStatus(state));
24
+ const error = useSelector(state => selectSmartProxyContentError(state));
25
+ const columnHeaders = [
26
+ {
27
+ title: __('Environment'),
28
+ transforms: [wrappable],
29
+ },
30
+ {
31
+ title: __('Content view'),
32
+ transforms: [wrappable],
33
+ },
34
+ {
35
+ title: __('Type'),
36
+ transforms: [wrappable],
37
+ },
38
+ {
39
+ title: __('Last published'),
40
+ transforms: [wrappable],
41
+ },
42
+ {
43
+ title: __('Repositories'),
44
+ transforms: [wrappable],
45
+ },
46
+ {
47
+ title: __('Synced to smart proxy'),
48
+ transforms: [wrappable],
49
+ },
50
+ ];
51
+
52
+ const fetchWithParams = () => {
53
+ dispatch(getSmartProxyContent({ smartProxyId }));
54
+ };
55
+
56
+ const buildrows = (results) => {
57
+ const newRows = [];
58
+ let envCount = 0;
59
+ results.forEach((env) => {
60
+ const { name, content_views: contentViews } = env;
61
+ const cellEnv = {
62
+ isOpen: false,
63
+ cells: [name, null, null, null, null, null],
64
+ };
65
+ newRows.push(cellEnv);
66
+ contentViews.forEach((cv) => {
67
+ const {
68
+ id, name: cvName, composite, last_published: lastPublished, up_to_date: upToDate, counts,
69
+ } = cv;
70
+ const { repositories } = counts;
71
+ const cvType = <ContentViewIcon composite={composite} />;
72
+ const upToDateVal = upToDate ? <CheckCircleIcon /> : <TimesCircleIcon />;
73
+ const cellCv =
74
+ {
75
+ parent: envCount,
76
+ cells: [
77
+ {
78
+ title: null,
79
+ props: {
80
+ colSpan: 1,
81
+ },
82
+ },
83
+ {
84
+ title: <a href={urlBuilder('content_views', '', id)}>{cvName}</a>,
85
+ props: {
86
+ colSpan: 1,
87
+ },
88
+ },
89
+ {
90
+ title: cvType,
91
+ props: {
92
+ colSpan: 1,
93
+ },
94
+ },
95
+ {
96
+ title: <LongDateTime date={lastPublished} showRelativeTimeTooltip />,
97
+ props: {
98
+ colSpan: 1,
99
+ },
100
+ },
101
+ {
102
+ title: repositories,
103
+ props: {
104
+ colSpan: 1,
105
+ },
106
+ },
107
+ {
108
+ title: upToDateVal,
109
+ props: {
110
+ colSpan: 1,
111
+ },
112
+ },
113
+ ],
114
+ };
115
+ newRows.push(cellCv);
116
+ });
117
+ envCount = newRows.length;
118
+ });
119
+ return newRows;
120
+ };
121
+
122
+ const onCollapse = (row, setRow) => (event, rowKey, isOpen) => {
123
+ const newRows = [...row];
124
+ newRows[rowKey].isOpen = isOpen;
125
+ setRow(newRows);
126
+ };
127
+
128
+
129
+ useEffect(() => fetchWithParams(), []);
130
+
131
+ useEffect(() => {
132
+ if (status !== STATUS.PENDING && response) {
133
+ const { lifecycle_environments: env } = response;
134
+ setRows(buildrows(env));
135
+ }
136
+ }, [JSON.stringify(response), status, error]);
137
+
138
+
139
+ return (
140
+ <MainTable onCollapse={onCollapse(rows, setRows)} status={status} cells={columnHeaders} rows={rows} error={error} emptyContentTitle="No content synced" emptyContentBody="No content synced to smart proxy" emptySearchTitle="Empty" emptySearchBody="Empty" />
141
+ );
142
+ };
143
+
144
+ SmartProxyContentTable.propTypes = {
145
+ smartProxyId: PropTypes.number,
146
+ };
147
+
148
+ SmartProxyContentTable.defaultProps = {
149
+ smartProxyId: null,
150
+ };
151
+
152
+ export default SmartProxyContentTable;
@@ -0,0 +1,140 @@
1
+ {
2
+ "last_sync_time": "2021-01-12 14:39:01 -0500",
3
+ "active_sync_tasks": [],
4
+ "last_failed_sync_tasks": [],
5
+ "lifecycle_environments": [
6
+ {
7
+ "library": true,
8
+ "id": 1,
9
+ "name": "Library",
10
+ "label": "Library",
11
+ "description": null,
12
+ "organization_id": 1,
13
+ "organization": {
14
+ "name": "Default Organization",
15
+ "label": "Default_Organization",
16
+ "id": 1
17
+ },
18
+ "syncable": false,
19
+ "counts": {
20
+ "content_hosts": 0,
21
+ "content_views": 2,
22
+ "products": 1
23
+ },
24
+ "content_views": [
25
+ {
26
+ "id": 1,
27
+ "label": "Default_Organization_View",
28
+ "name": "Default Organization View",
29
+ "composite": false,
30
+ "last_published": "2021-01-05 09:55:20 -0500",
31
+ "default": true,
32
+ "up_to_date": true,
33
+ "counts": {
34
+ "content_hosts": 0,
35
+ "products": 1,
36
+ "repositories": 2
37
+ }
38
+ },
39
+ {
40
+ "id": 2,
41
+ "label": "cv1",
42
+ "name": "cv1",
43
+ "composite": false,
44
+ "last_published": "2021-01-12 14:36:31 -0500",
45
+ "default": false,
46
+ "up_to_date": true,
47
+ "counts": {
48
+ "content_hosts": 0,
49
+ "products": 1,
50
+ "repositories": 1
51
+ }
52
+ },
53
+ {
54
+ "id": 3,
55
+ "label": "cv2",
56
+ "name": "cv2",
57
+ "composite": false,
58
+ "last_published": "2021-01-12 14:37:05 -0500",
59
+ "default": false,
60
+ "up_to_date": true,
61
+ "counts": {
62
+ "content_hosts": 0,
63
+ "products": 1,
64
+ "repositories": 1
65
+ }
66
+ }
67
+ ]
68
+ },
69
+ {
70
+ "library": false,
71
+ "id": 2,
72
+ "name": "dev",
73
+ "label": "dev",
74
+ "description": null,
75
+ "organization_id": 1,
76
+ "organization": {
77
+ "name": "Default Organization",
78
+ "label": "Default_Organization",
79
+ "id": 1
80
+ },
81
+ "syncable": false,
82
+ "counts": {
83
+ "content_hosts": 0,
84
+ "content_views": 1,
85
+ "products": 1
86
+ },
87
+ "content_views": [
88
+ {
89
+ "id": 2,
90
+ "label": "cv1",
91
+ "name": "cv1",
92
+ "composite": false,
93
+ "last_published": "2021-01-12 14:36:31 -0500",
94
+ "default": false,
95
+ "up_to_date": true,
96
+ "counts": {
97
+ "content_hosts": 0,
98
+ "products": 1,
99
+ "repositories": 1
100
+ }
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "library": false,
106
+ "id": 3,
107
+ "name": "test",
108
+ "label": "test",
109
+ "description": null,
110
+ "organization_id": 1,
111
+ "organization": {
112
+ "name": "Default Organization",
113
+ "label": "Default_Organization",
114
+ "id": 1
115
+ },
116
+ "syncable": true,
117
+ "counts": {
118
+ "content_hosts": 0,
119
+ "content_views": 1,
120
+ "products": 1
121
+ },
122
+ "content_views": [
123
+ {
124
+ "id": 2,
125
+ "label": "cv1",
126
+ "name": "cv1",
127
+ "composite": false,
128
+ "last_published": "2021-01-12 14:36:31 -0500",
129
+ "default": false,
130
+ "up_to_date": false,
131
+ "counts": {
132
+ "content_hosts": 0,
133
+ "products": 1,
134
+ "repositories": 0
135
+ }
136
+ }
137
+ ]
138
+ }
139
+ ]
140
+ }
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { renderWithRedux, patientlyWaitFor } from 'react-testing-lib-wrapper';
3
+
4
+ import { nockInstance, assertNockRequest } from '../../../test-utils/nockWrapper';
5
+ import api from '../../../services/api';
6
+ import SmartProxyContentTable from '../SmartProxyContentTable';
7
+
8
+ const smartProxyContentData = require('./SmartProxyContentResult.fixtures.json');
9
+
10
+ const smartProxyContentPath = api.getApiUrl('/capsules/1/content/sync');
11
+
12
+ const smartProxyContent = { ...smartProxyContentData };
13
+
14
+ const contentTable = <SmartProxyContentTable smartProxyId={1} />;
15
+
16
+ test('Can display Smart proxy content table', async (done) => {
17
+ const detailsScope = nockInstance
18
+ .get(smartProxyContentPath)
19
+ .query(true)
20
+ .reply(200, smartProxyContent);
21
+
22
+ const { getByText, getAllByLabelText } = renderWithRedux(contentTable);
23
+ await patientlyWaitFor(() => expect(getByText('Environment')).toBeInTheDocument());
24
+ expect(getByText('Content view')).toBeInTheDocument();
25
+ expect(getByText('Type')).toBeInTheDocument();
26
+ expect(getByText('Last published')).toBeInTheDocument();
27
+ expect(getByText('Repositories')).toBeInTheDocument();
28
+ expect(getByText('Synced to smart proxy')).toBeInTheDocument();
29
+ expect(getAllByLabelText('Details')[0]).toHaveAttribute('aria-expanded', 'false');
30
+ getAllByLabelText('Details')[0].click();
31
+ expect(getAllByLabelText('Details')[0]).toHaveAttribute('aria-expanded', 'true');
32
+ expect(getByText('Library')).toBeInTheDocument();
33
+ expect(getByText('Default Organization View')).toBeInTheDocument();
34
+ expect(getByText('dev')).toBeInTheDocument();
35
+
36
+
37
+ assertNockRequest(detailsScope, done);
38
+ });
@@ -0,0 +1,4 @@
1
+ import { withRouter } from 'react-router-dom';
2
+ import Content from './Content';
3
+
4
+ export default withRouter(Content);
@@ -51,11 +51,8 @@ class ManageManifestModal extends Component {
51
51
  showDeleteManifestModal = () =>
52
52
  this.props.setModalOpen({ id: DELETE_MANIFEST_MODAL_ID });
53
53
 
54
- hideDeleteManifestModal = () => {
55
- if (this.props.deleteManifestModalExists) {
56
- this.props.setModalClosed({ id: DELETE_MANIFEST_MODAL_ID });
57
- }
58
- };
54
+ hideDeleteManifestModal = () =>
55
+ this.props.setModalClosed({ id: DELETE_MANIFEST_MODAL_ID });
59
56
 
60
57
  updateRepositoryUrl = (event) => {
61
58
  this.setState({ redhat_repository_url: event.target.value });
@@ -329,7 +326,6 @@ ManageManifestModal.propTypes = {
329
326
  canImportManifest: PropTypes.bool,
330
327
  canDeleteManifest: PropTypes.bool,
331
328
  isManifestImported: PropTypes.bool,
332
- deleteManifestModalExists: PropTypes.bool,
333
329
  canEditOrganizations: PropTypes.bool,
334
330
  disableManifestActions: PropTypes.bool,
335
331
  disabledReason: PropTypes.string,
@@ -355,7 +351,6 @@ ManageManifestModal.defaultProps = {
355
351
  canImportManifest: false,
356
352
  canDeleteManifest: false,
357
353
  isManifestImported: false,
358
- deleteManifestModalExists: false,
359
354
  canEditOrganizations: false,
360
355
  simpleContentAccess: false,
361
356
  simpleContentAccessEligible: undefined,
@@ -88,7 +88,7 @@ export const checkSimpleContentAccessEligible = () => async (dispatch) => {
88
88
  dispatch({ type: SIMPLE_CONTENT_ACCESS_ELIGIBLE_REQUEST });
89
89
 
90
90
  try {
91
- const { data } = await api.get(`/organizations/${orgId()}/upstream_subscriptions/simple_content_access/eligible`, {});
91
+ const { data } = await api.get(`/organizations/${orgId()}/simple_content_access/eligible`, {});
92
92
  return dispatch({
93
93
  type: SIMPLE_CONTENT_ACCESS_ELIGIBLE_SUCCESS,
94
94
  response: data,
@@ -106,7 +106,7 @@ export const enableSimpleContentAccess = (extendedParams = {}) => async (dispatc
106
106
  };
107
107
 
108
108
  try {
109
- const { data } = await api.put(`/organizations/${orgId()}/upstream_subscriptions/simple_content_access/enable`, {}, params);
109
+ const { data } = await api.put(`/organizations/${orgId()}/simple_content_access/enable`, {}, params);
110
110
  return dispatch({
111
111
  type: ENABLE_SIMPLE_CONTENT_ACCESS_SUCCESS,
112
112
  response: data,
@@ -124,7 +124,7 @@ export const disableSimpleContentAccess = (extendedParams = {}) => async (dispat
124
124
  };
125
125
 
126
126
  try {
127
- const { data } = await api.put(`/organizations/${orgId()}/upstream_subscriptions/simple_content_access/disable`, {}, params);
127
+ const { data } = await api.put(`/organizations/${orgId()}/simple_content_access/disable`, {}, params);
128
128
  return dispatch({
129
129
  type: DISABLE_SIMPLE_CONTENT_ACCESS_SUCCESS,
130
130
  response: data,
@@ -136,7 +136,7 @@ describe('manifest actions', () => {
136
136
  });
137
137
 
138
138
  describe('creates ENABLE_SIMPLE_CONTENT_ACCESS_REQUEST', () => {
139
- const url = '/katello/api/v2/organizations/1/upstream_subscriptions/simple_content_access/enable';
139
+ const url = '/katello/api/v2/organizations/1/simple_content_access/enable';
140
140
 
141
141
  it('and then fails with 422', async () => {
142
142
  mockErrorRequest({
@@ -158,7 +158,7 @@ describe('creates ENABLE_SIMPLE_CONTENT_ACCESS_REQUEST', () => {
158
158
  });
159
159
 
160
160
  describe('creates DISABLE_SIMPLE_CONTENT_ACCESS_REQUEST', () => {
161
- const url = '/katello/api/v2/organizations/1/upstream_subscriptions/simple_content_access/disable';
161
+ const url = '/katello/api/v2/organizations/1/simple_content_access/disable';
162
162
 
163
163
  it('and then fails with 422', async () => {
164
164
  mockErrorRequest({
@@ -55,8 +55,8 @@ const initialState = {
55
55
  },
56
56
  };
57
57
 
58
- const enableSimpleContetAccessPath = api.getApiUrl('/organizations/1/upstream_subscriptions/simple_content_access/enable');
59
- const disableSimpleContetAccessPath = api.getApiUrl('/organizations/1/upstream_subscriptions/simple_content_access/disable');
58
+ const enableSimpleContetAccessPath = api.getApiUrl('/organizations/1/simple_content_access/enable');
59
+ const disableSimpleContetAccessPath = api.getApiUrl('/organizations/1/simple_content_access/disable');
60
60
  const manifestHistoryPath = api.getApiUrl('/organizations/1/subscriptions/manifest_history');
61
61
 
62
62
  test('Enable Simple Content Access after toggle switch value to true', async (done) => {
@@ -20,7 +20,6 @@ const mapStateToProps = state => ({
20
20
  simpleContentAccess: selectSimpleContentAccessEnabled(state),
21
21
  isManifestImported: selectIsManifestImported(state),
22
22
  modalOpenState: state.foremanModals.ManageManifestModal,
23
- deleteManifestModalExists: !!state.foremanModals.deleteManifestModal,
24
23
  manifestActionStarted: selectManifestActionStarted(state),
25
24
  simpleContentAccessEligible: selectSimpleContentAccessEligible(state),
26
25
  });
@@ -8,7 +8,7 @@ import { APIMiddleware, reducers as apiReducer } from 'foremanReact/redux/API';
8
8
  import { reducers as fillReducers } from 'foremanReact/components/common/Fill';
9
9
  import { reducers as foremanModalReducer } from 'foremanReact/components/ForemanModal';
10
10
  import { STATUS } from 'foremanReact/constants';
11
- import { render, waitFor } from '@testing-library/react';
11
+ import { render, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
12
12
  import { createStore, applyMiddleware, combineReducers } from 'redux';
13
13
  import { Provider } from 'react-redux';
14
14
  import { MemoryRouter } from 'react-router-dom';
@@ -63,7 +63,10 @@ function renderWithRedux(
63
63
 
64
64
  // When the tests run slower, they can hit the default waitFor timeout, which is 1000ms
65
65
  // There doesn't seem to be a way to set it globally for r-t-lib, so using this wrapper function
66
- export const patientlyWaitFor = waitForFunc => waitFor(waitForFunc, { timeout: 5000 });
66
+ const rtlTimeout = 5000;
67
+ export const patientlyWaitFor = waitForFunc => waitFor(waitForFunc, { timeout: rtlTimeout });
68
+ export const patientlyWaitForRemoval = waitForFunc =>
69
+ waitForElementToBeRemoved(waitForFunc, { timeout: rtlTimeout });
67
70
 
68
71
  // re-export everything, so the library can be used from this wrapper.
69
72
  export * from '@testing-library/react';
@@ -96,6 +96,9 @@ export const apiError = (actionType, result, additionalData = {}) => (dispatch)
96
96
  return resultWithSuccessFlag(result);
97
97
  };
98
98
 
99
+ export const capitalize = s => s && s[0].toUpperCase() + s.slice(1);
100
+
101
+ export const truncate = (str, truncateLimit = 50) => (str.length > truncateLimit ? `${str.substring(0, truncateLimit - 3)}...` : str);
99
102
 
100
103
  export default {
101
104
  getResponseErrorMsgs,