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
@@ -0,0 +1,5 @@
1
+ // This component uses the intl context from Foreman, we need to figure out how to mock that
2
+ // context before using this component directly from Foreman in testing
3
+
4
+ export default () => 'A Mocked Date';
5
+
@@ -1,12 +1,19 @@
1
1
  import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Select, SelectOption, SelectVariant, Level, LevelItem } from '@patternfly/react-core';
3
+ import { Spinner, Select, SelectOption, SelectVariant, Level, LevelItem } from '@patternfly/react-core';
4
+ import { ErrorCircleOIcon } from '@patternfly/react-icons';
5
+
4
6
 
5
7
  const SelectableDropdown = ({
6
- items, title, selected, setSelected,
8
+ items, title, selected, setSelected, loading, error,
7
9
  }) => {
8
10
  const [isOpen, setIsOpen] = useState(false);
9
- const onSelect = (event, selection) => {
11
+ const icon = () => {
12
+ if (error) return <span aria-label={`${title} error`}><ErrorCircleOIcon color="red" /></span>;
13
+ if (loading) return <span aria-label={`${title} spinner`}><Spinner size="sm" /></span>;
14
+ return null;
15
+ };
16
+ const onSelect = (_event, selection) => {
10
17
  setSelected(selection);
11
18
  setIsOpen(false);
12
19
  };
@@ -30,6 +37,8 @@ const SelectableDropdown = ({
30
37
  onSelect={onSelect}
31
38
  selections={selected}
32
39
  isOpen={isOpen}
40
+ isDisabled={loading || error}
41
+ toggleIcon={icon()}
33
42
  >
34
43
  {selectItems}
35
44
  </Select>
@@ -43,6 +52,14 @@ SelectableDropdown.propTypes = {
43
52
  title: PropTypes.string.isRequired,
44
53
  selected: PropTypes.string.isRequired,
45
54
  setSelected: PropTypes.func.isRequired,
55
+ // If the items are loaded dynamically, you can pass in loading or error states
56
+ loading: PropTypes.bool,
57
+ error: PropTypes.bool,
58
+ };
59
+
60
+ SelectableDropdown.defaultProps = {
61
+ loading: false,
62
+ error: false,
46
63
  };
47
64
 
48
65
 
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { render } from 'react-testing-lib-wrapper';
3
+
4
+ import SelectableDropdown from '../SelectableDropdown';
5
+
6
+ const type = 'Breakfast';
7
+
8
+ test('Can dynamically load options', async () => {
9
+ const { rerender, queryByText, queryByLabelText } = render(<SelectableDropdown
10
+ items={[]}
11
+ title={type}
12
+ selected=""
13
+ setSelected={jest.fn}
14
+ placeholderText={type}
15
+ loading
16
+ error={false}
17
+ />);
18
+ expect(queryByLabelText(`${type} spinner`)).toBeInTheDocument();
19
+
20
+ rerender(<SelectableDropdown
21
+ items={['donut', 'croissant', 'bear claw']}
22
+ title={type}
23
+ selected=""
24
+ setSelected={jest.fn}
25
+ placeholderText={type}
26
+ loading={false}
27
+ error={false}
28
+ />);
29
+ expect(queryByLabelText(`${type} spinner`)).not.toBeInTheDocument();
30
+ expect(queryByText('donut')).toBeInTheDocument();
31
+ });
32
+
33
+
34
+ test('Can handle error', async () => {
35
+ const { queryByLabelText } = render(<SelectableDropdown
36
+ items={[]}
37
+ title={type}
38
+ selected=""
39
+ setSelected={jest.fn}
40
+ placeholderText={type}
41
+ loading={false}
42
+ error
43
+ />);
44
+ expect(queryByLabelText(`${type} error`)).toBeInTheDocument();
45
+ });
@@ -0,0 +1,3 @@
1
+ import SelectableDropdown from './SelectableDropdown';
2
+
3
+ export default SelectableDropdown;
@@ -3,7 +3,8 @@ import { Pagination, Flex, FlexItem } from '@patternfly/react-core';
3
3
 
4
4
  import PropTypes from 'prop-types';
5
5
  import { useDispatch } from 'react-redux';
6
- import { usePaginationOptions, useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
6
+ import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
7
+ import { usePaginationOptions } from 'foremanReact/components/Pagination/PaginationHooks';
7
8
 
8
9
  import MainTable from './MainTable';
9
10
  import Search from '../../components/Search';
@@ -0,0 +1,14 @@
1
+ // Can be included as a TableWrapper prop for selectable rows
2
+ const onSelect = (rows, setRows) => (_event, isSelected, rowId) => {
3
+ let newRows;
4
+ if (rowId === -1) {
5
+ newRows = rows.map(row => ({ ...row, selected: isSelected }));
6
+ } else {
7
+ newRows = [...rows];
8
+ newRows[rowId].selected = isSelected;
9
+ }
10
+
11
+ setRows(newRows);
12
+ };
13
+
14
+ export default onSelect;
@@ -7,7 +7,6 @@ import keyPressHandler from '../helpers/helpers';
7
7
  import TypeAheadInput from './TypeAheadInput';
8
8
  import TypeAheadItems from './TypeAheadItems';
9
9
  import commonSearchPropTypes from '../helpers/commonPropTypes';
10
- import './TypeAheadSearch.scss';
11
10
 
12
11
  const TypeAheadSearch = ({
13
12
  userInputValue, clearSearch, getInputProps, getItemProps, isOpen, inputValue, highlightedIndex,
@@ -54,3 +54,9 @@ td, th {
54
54
  }
55
55
  }
56
56
  }
57
+
58
+ // Override browser default outline ring on focus
59
+ *:focus {
60
+ outline: none;
61
+ }
62
+
data/webpack/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import componentRegistry from 'foremanReact/components/componentRegistry';
7
7
  import Application from './containers/Application/index';
8
+ import Content from './scenes/SmartProxy/Content';
8
9
  import './redux';
9
10
  // Not currently mocking anything
10
11
  // import './services/api/setupMocks';
@@ -13,3 +14,8 @@ componentRegistry.register({
13
14
  name: 'katello',
14
15
  type: Application,
15
16
  });
17
+
18
+ componentRegistry.register({
19
+ name: 'Content',
20
+ type: Content,
21
+ });
@@ -25,16 +25,16 @@ const recommendedRepositoriesRHEL = [
25
25
  ];
26
26
 
27
27
  const recommendedRepositoriesSatTools = [
28
- 'satellite-tools-6.8-for-rhel-8-x86_64-rpms',
29
- 'rhel-7-server-satellite-tools-6.8-rpms',
30
- 'rhel-6-server-satellite-tools-6.8-rpms',
31
- 'rhel-5-server-els-satellite-tools-6.8-rpms',
28
+ 'satellite-tools-6.9-for-rhel-8-x86_64-rpms',
29
+ 'rhel-7-server-satellite-tools-6.9-rpms',
30
+ 'rhel-6-server-satellite-tools-6.9-rpms',
31
+ 'rhel-5-server-els-satellite-tools-6.9-rpms',
32
32
  'rhel-7-server-satellite-maintenance-6-rpms',
33
33
  ];
34
34
 
35
35
  const recommendedRepositoriesMisc = [
36
36
  'rhel-server-rhscl-7-rpms',
37
- 'rhel-7-server-satellite-capsule-6.8-rpms',
37
+ 'rhel-7-server-satellite-capsule-6.9-rpms',
38
38
  'rhel-7-server-ansible-2.9-rpms',
39
39
  ];
40
40
 
@@ -1,6 +1,8 @@
1
- import { API_OPERATIONS, get } from 'foremanReact/redux/API';
1
+ import { translate as __ } from 'foremanReact/common/I18n';
2
+ import { API_OPERATIONS, get, post } from 'foremanReact/redux/API';
2
3
  import api, { orgId } from '../../services/api';
3
- import CONTENT_VIEWS_KEY from './ContentViewsConstants';
4
+ import CONTENT_VIEWS_KEY, { CREATE_CONTENT_VIEW_KEY, COPY_CONTENT_VIEW_KEY } from './ContentViewsConstants';
5
+ import { getResponseErrorMsgs } from '../../utils/helpers';
4
6
 
5
7
  export const createContentViewsParams = extraParams => ({
6
8
  organization_id: orgId(),
@@ -15,4 +17,31 @@ const getContentViews = extraParams => get({
15
17
  params: createContentViewsParams(extraParams),
16
18
  });
17
19
 
20
+ const cvSuccessToast = (response) => {
21
+ const { data: { name } } = response;
22
+ return __(`Content view ${name} created`);
23
+ };
24
+
25
+ const cvErrorToast = (error) => {
26
+ const message = getResponseErrorMsgs(error.response);
27
+ return message;
28
+ };
29
+
30
+ export const createContentView = params => post({
31
+ type: API_OPERATIONS.POST,
32
+ key: CREATE_CONTENT_VIEW_KEY,
33
+ url: api.getApiUrl('/content_views'),
34
+ params,
35
+ successToast: response => cvSuccessToast(response),
36
+ errorToast: error => cvErrorToast(error),
37
+ });
38
+
39
+ export const copyContentView = params => post({
40
+ type: API_OPERATIONS.POST,
41
+ key: COPY_CONTENT_VIEW_KEY,
42
+ url: api.getApiUrl(`/content_views/${params.id}/copy`),
43
+ params,
44
+ successToast: response => cvSuccessToast(response),
45
+ errorToast: error => cvErrorToast(error),
46
+ });
18
47
  export default getContentViews;
@@ -1,14 +1,18 @@
1
1
  const CONTENT_VIEWS_KEY = 'CONTENT_VIEWS';
2
+ export const CREATE_CONTENT_VIEW_KEY = 'CONTENT_VIEW_CREATE';
3
+ export const COPY_CONTENT_VIEW_KEY = 'CONTENT_VIEW_COPY';
2
4
  export const UPDATE_CONTENT_VIEW = 'UPDATE_CONTENT_VIEW';
3
5
  export const UPDATE_CONTENT_VIEW_SUCCESS = 'UPDATE_CONTENT_VIEW_SUCCESS';
4
6
  export const UPDATE_CONTENT_VIEW_FAILURE = 'UPDATE_CONTENT_VIEW_FAILURE';
5
-
6
7
  export const cvDetailsKey = cvId => `${CONTENT_VIEWS_KEY}_${cvId}`;
7
8
  export const cvDetailsRepoKey = cvId => `${CONTENT_VIEWS_KEY}_REPOSITORIES_${cvId}`;
9
+ export const cvDetailsFilterKey = cvId => `${CONTENT_VIEWS_KEY}_FILTERS_${cvId}`;
8
10
 
9
11
  // Repo added to content view status display and key
10
12
  export const ADDED = 'Added';
11
13
  export const NOT_ADDED = 'Not added';
12
14
  export const ALL_STATUSES = 'All';
13
15
 
16
+ export const REPOSITORY_TYPES = 'REPOSITORY_TYPES';
17
+
14
18
  export default CONTENT_VIEWS_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 { COPY_CONTENT_VIEW_KEY } from '../ContentViewsConstants';
8
+
9
+ export const selectCopyContentViews = state =>
10
+ selectAPIResponse(state, COPY_CONTENT_VIEW_KEY) || {};
11
+
12
+ export const selectCopyContentViewStatus = state =>
13
+ selectAPIStatus(state, COPY_CONTENT_VIEW_KEY) || STATUS.PENDING;
14
+
15
+ export const selectCopyContentViewError = state =>
16
+ selectAPIError(state, COPY_CONTENT_VIEW_KEY);
@@ -0,0 +1,77 @@
1
+ import { STATUS } from 'foremanReact/constants';
2
+ import React, { useState, useEffect } from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import { useDispatch, useSelector } from 'react-redux';
5
+ import { Redirect } from 'react-router-dom';
6
+ import { Form, FormGroup, TextInput, ActionGroup, Button } from '@patternfly/react-core';
7
+ import {
8
+ selectCopyContentViewError, selectCopyContentViews,
9
+ selectCopyContentViewStatus,
10
+ } from './ContentViewCopySelectors';
11
+ import { copyContentView } from '../ContentViewsActions';
12
+
13
+ const CopyContentViewForm = ({ cvId, setModalOpen }) => {
14
+ const dispatch = useDispatch();
15
+ const [name, setName] = useState('');
16
+ const [redirect, setRedirect] = useState(false);
17
+ const [saving, setSaving] = useState(false);
18
+ const response = useSelector(selectCopyContentViews);
19
+ const status = useSelector(selectCopyContentViewStatus);
20
+ const error = useSelector(selectCopyContentViewError);
21
+
22
+ useEffect(() => {
23
+ const { id } = response;
24
+ if (id && status === STATUS.RESOLVED) {
25
+ setSaving(false);
26
+ setRedirect(true);
27
+ } else if (status === STATUS.ERROR) {
28
+ setSaving(false);
29
+ }
30
+ }, [JSON.stringify(response), status, error]);
31
+
32
+ const onSubmit = () => {
33
+ setSaving(true);
34
+ dispatch(copyContentView({
35
+ id: cvId,
36
+ name,
37
+ }));
38
+ };
39
+
40
+ if (redirect) {
41
+ const { id } = response;
42
+ return (<Redirect to={`/labs/content_views/${id}`} />);
43
+ }
44
+
45
+ return (
46
+ <Form>
47
+ <FormGroup label="Name" isRequired fieldId="name">
48
+ <TextInput
49
+ isRequired
50
+ type="text"
51
+ id="name"
52
+ aria-label="input_name"
53
+ name="name"
54
+ value={name}
55
+ onChange={value => setName(value)}
56
+ />
57
+ </FormGroup>
58
+ <ActionGroup>
59
+ <Button aria-label="copy_content_view" variant="primary" isDisabled={saving} onClick={() => onSubmit()}>Copy content view</Button>
60
+ <Button variant="link" onClick={() => setModalOpen(false)}>Cancel</Button>
61
+ </ActionGroup>
62
+ </Form>
63
+ );
64
+ };
65
+
66
+ CopyContentViewForm.propTypes = {
67
+ cvId: PropTypes.string,
68
+ setModalOpen: PropTypes.func,
69
+ };
70
+
71
+ CopyContentViewForm.defaultProps = {
72
+ cvId: null,
73
+ setModalOpen: null,
74
+ };
75
+
76
+
77
+ export default CopyContentViewForm;
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Modal, ModalVariant } from '@patternfly/react-core';
4
+ import CopyContentViewForm from './CopyContentViewForm';
5
+
6
+ const CopyContentViewModal = ({
7
+ cvId, cvName, show, setIsOpen,
8
+ }) => {
9
+ const description = (
10
+ <p>
11
+ This will create a copy of <b>{cvName}</b>, including details,
12
+ repositories, and filters. Generated data such
13
+ as history, tasks and versions will not be copied.
14
+ </p>
15
+ );
16
+ return (
17
+ <Modal
18
+ title="Copy content view"
19
+ variant={ModalVariant.small}
20
+ isOpen={show}
21
+ description={description}
22
+ onClose={() => {
23
+ setIsOpen(false);
24
+ }}
25
+ appendTo={document.body}
26
+ ><CopyContentViewForm cvId={cvId} setModalOpen={setIsOpen} />
27
+ </Modal>);
28
+ };
29
+
30
+ CopyContentViewModal.propTypes = {
31
+ cvId: PropTypes.string,
32
+ cvName: PropTypes.string,
33
+ show: PropTypes.bool,
34
+ setIsOpen: PropTypes.func,
35
+ };
36
+
37
+ CopyContentViewModal.defaultProps = {
38
+ cvId: null,
39
+ cvName: null,
40
+ show: false,
41
+ setIsOpen: null,
42
+ };
43
+
44
+ export default CopyContentViewModal;
@@ -0,0 +1,42 @@
1
+ {
2
+ "content_host_count": 0,
3
+ "composite": false,
4
+ "component_ids": [],
5
+ "default": false,
6
+ "force_puppet_environment": false,
7
+ "version_count": 0,
8
+ "latest_version": null,
9
+ "auto_publish": false,
10
+ "solve_dependencies": false,
11
+ "import_only": false,
12
+ "repository_ids": [],
13
+ "id": 34,
14
+ "name": "copy cv",
15
+ "label": "copy_cv",
16
+ "description": "",
17
+ "organization_id": 1,
18
+ "organization": {
19
+ "name": "Default Organization",
20
+ "label": "Default_Organization",
21
+ "id": 1
22
+ },
23
+ "created_at": "2020-12-14 12:45:59 -0500",
24
+ "updated_at": "2020-12-14 12:45:59 -0500",
25
+ "environments": [],
26
+ "repositories": [],
27
+ "puppet_modules": [],
28
+ "versions": [],
29
+ "components": [],
30
+ "content_view_components": [],
31
+ "activation_keys": [],
32
+ "next_version": "1.0",
33
+ "last_published": null,
34
+ "permissions": {
35
+ "view_content_views": true,
36
+ "edit_content_views": true,
37
+ "destroy_content_views": true,
38
+ "publish_content_views": true,
39
+ "promote_or_remove_content_views": true
40
+ },
41
+ "duplicate_repositories_to_publish": []
42
+ }
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { renderWithRedux, patientlyWaitFor, fireEvent } from 'react-testing-lib-wrapper';
3
+
4
+ import { nockInstance, assertNockRequest } from '../../../../test-utils/nockWrapper';
5
+ import api from '../../../../services/api';
6
+ import CopyContentViewForm from '../CopyContentViewForm';
7
+
8
+ const cvCopyData = require('./contentViewCopyResult.fixtures.json');
9
+
10
+ const cvId = '1';
11
+ const cvCopyPath = api.getApiUrl(`/content_views/${cvId}/copy`);
12
+ const setModalOpen = jest.fn();
13
+
14
+ const copyParams = {
15
+ id: cvId,
16
+ name: 'cv copy',
17
+ };
18
+
19
+ const copiedCVDetails = { ...cvCopyData };
20
+
21
+ const form = <CopyContentViewForm cvId={cvId} setModalOpen={setModalOpen} />;
22
+
23
+ test('Can copy content view from form', async (done) => {
24
+ const copyscope = nockInstance
25
+ .post(cvCopyPath, copyParams)
26
+ .reply(201, copiedCVDetails);
27
+ const { queryByText, getByLabelText } = renderWithRedux(form);
28
+ expect(queryByText('Name')).toBeInTheDocument();
29
+
30
+ fireEvent.change(getByLabelText('input_name'), { target: { value: 'cv copy' } });
31
+
32
+ getByLabelText('copy_content_view').click();
33
+ // Form closes it self on success
34
+ await patientlyWaitFor(() => {
35
+ expect(queryByText('Name')).not.toBeInTheDocument();
36
+ });
37
+
38
+ assertNockRequest(copyscope, done);
39
+ });
@@ -0,0 +1,4 @@
1
+ import { withRouter } from 'react-router-dom';
2
+ import CopyContentViewModal from './CopyContentViewModal';
3
+
4
+ export default withRouter(CopyContentViewModal);
@@ -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 { CREATE_CONTENT_VIEW_KEY } from '../ContentViewsConstants';
8
+
9
+ export const selectCreateContentViews = state =>
10
+ selectAPIResponse(state, CREATE_CONTENT_VIEW_KEY) || {};
11
+
12
+ export const selectCreateContentViewStatus = state =>
13
+ selectAPIStatus(state, CREATE_CONTENT_VIEW_KEY) || STATUS.PENDING;
14
+
15
+ export const selectCreateContentViewError = state =>
16
+ selectAPIError(state, CREATE_CONTENT_VIEW_KEY);
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { translate as __ } from 'foremanReact/common/I18n';
3
+ import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
4
+ import { Tooltip, TooltipPosition, Flex, FlexItem } from '@patternfly/react-core';
5
+ import { autoPublishHelpText, dependenciesHelpText, importOnlyHelpText } from '../helpers';
6
+ import ContentViewIcon from '../components/ContentViewIcon';
7
+
8
+ const HelpToolTip = tooltip => (
9
+ <span className="foreman-spaced-icon">
10
+ <Tooltip
11
+ position={TooltipPosition.top}
12
+ content={tooltip}
13
+ >
14
+ <OutlinedQuestionCircleIcon />
15
+ </Tooltip>
16
+ </span>
17
+ );
18
+
19
+ export const LabelComposite = () => (
20
+ <Flex>
21
+ <FlexItem spacer={{ default: 'spacerNone' }}><ContentViewIcon composite /></FlexItem>
22
+ <FlexItem>{__('Composite Content View')}</FlexItem>
23
+ </Flex>
24
+ );
25
+
26
+ export const LabelComponent = () => (
27
+ <Flex>
28
+ <FlexItem spacer={{ default: 'spacerNone' }}><ContentViewIcon composite={false} /></FlexItem>
29
+ <FlexItem>{__('Component Content View')}</FlexItem>
30
+ </Flex>
31
+ );
32
+
33
+ export const LabelDependencies = () => (
34
+ <Flex>
35
+ <FlexItem spacer={{ default: 'spacerSm' }}>{__('Solve Dependencies')}</FlexItem>
36
+ <FlexItem>
37
+ {HelpToolTip(dependenciesHelpText)}
38
+ </FlexItem>
39
+ </Flex>
40
+ );
41
+
42
+ export const LabelAutoPublish = () => (
43
+ <Flex>
44
+ <FlexItem spacer={{ default: 'spacerSm' }}>{__('Auto Publish')}</FlexItem>
45
+ <FlexItem>
46
+ {HelpToolTip(autoPublishHelpText)}
47
+ </FlexItem>
48
+ </Flex>
49
+ );
50
+
51
+ export const LabelImportOnly = () => (
52
+ <Flex>
53
+ <FlexItem spacer={{ default: 'spacerSm' }}>{__('Import Only')}</FlexItem>
54
+ <FlexItem>
55
+ {HelpToolTip(importOnlyHelpText)}
56
+ </FlexItem>
57
+ </Flex>
58
+ );