katello 4.1.4 → 4.2.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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +2 -2
  3. data/app/controllers/katello/api/v2/content_credentials_controller.rb +3 -3
  4. data/app/controllers/katello/api/v2/content_uploads_controller.rb +3 -1
  5. data/app/controllers/katello/api/v2/content_view_components_controller.rb +33 -1
  6. data/app/controllers/katello/api/v2/content_views_controller.rb +12 -0
  7. data/app/controllers/katello/api/v2/host_errata_controller.rb +1 -1
  8. data/app/controllers/katello/api/v2/products_bulk_actions_controller.rb +1 -2
  9. data/app/controllers/katello/api/v2/products_controller.rb +4 -4
  10. data/app/controllers/katello/api/v2/repositories_bulk_actions_controller.rb +3 -11
  11. data/app/controllers/katello/api/v2/repositories_controller.rb +68 -47
  12. data/app/controllers/katello/api/v2/upstream_subscriptions_controller.rb +0 -28
  13. data/app/controllers/katello/concerns/api/v2/registration_commands_controller_extensions.rb +26 -5
  14. data/app/controllers/katello/concerns/api/v2/registration_controller_extensions.rb +26 -1
  15. data/app/lib/actions/candlepin/environment/destroy.rb +2 -0
  16. data/app/lib/actions/katello/agent_action.rb +2 -2
  17. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +3 -2
  18. data/app/lib/actions/katello/{gpg_key → content_credential}/update.rb +1 -1
  19. data/app/lib/actions/katello/content_view/publish.rb +6 -1
  20. data/app/lib/actions/katello/content_view_version/create_repos.rb +1 -1
  21. data/app/lib/actions/katello/content_view_version/incremental_update.rb +0 -47
  22. data/app/lib/actions/katello/orphan_cleanup/remove_orphans.rb +1 -1
  23. data/app/lib/actions/katello/repository/clone_contents.rb +1 -7
  24. data/app/lib/actions/katello/repository/clone_to_environment.rb +1 -7
  25. data/app/lib/actions/katello/repository/create.rb +4 -8
  26. data/app/lib/actions/katello/repository/create_root.rb +1 -1
  27. data/app/lib/actions/katello/repository/destroy.rb +1 -3
  28. data/app/lib/actions/katello/repository/import_upload.rb +3 -2
  29. data/app/lib/actions/katello/repository/instance_update.rb +1 -1
  30. data/app/lib/actions/katello/repository/metadata_generate.rb +2 -8
  31. data/app/lib/actions/katello/repository/multi_clone_contents.rb +0 -1
  32. data/app/lib/actions/katello/repository/refresh_repository.rb +1 -4
  33. data/app/lib/actions/katello/repository/remove_content.rb +6 -4
  34. data/app/lib/actions/katello/repository/sync.rb +5 -25
  35. data/app/lib/actions/katello/repository/update.rb +1 -2
  36. data/app/lib/actions/katello/repository/update_http_proxy_details.rb +2 -5
  37. data/app/lib/actions/katello/repository/update_redhat_repository.rb +1 -1
  38. data/app/lib/actions/katello/repository/upload_files.rb +8 -3
  39. data/app/lib/actions/katello/repository/upload_package_group.rb +2 -11
  40. data/app/lib/actions/katello/repository/verify_checksum.rb +0 -1
  41. data/app/lib/actions/katello/repository_set/enable_repository.rb +1 -1
  42. data/app/lib/actions/pulp3/orchestration/repository/create.rb +2 -2
  43. data/app/lib/actions/pulp3/repository/create.rb +3 -4
  44. data/app/lib/actions/pulp3/repository/create_remote.rb +1 -6
  45. data/app/lib/actions/pulp3/repository/repair.rb +4 -0
  46. data/app/lib/katello/errors.rb +1 -0
  47. data/app/lib/katello/http_resource.rb +26 -73
  48. data/app/lib/katello/qpid/connection.rb +1 -3
  49. data/app/lib/katello/resources/candlepin/consumer.rb +1 -1
  50. data/app/lib/katello/resources/candlepin/environment.rb +2 -0
  51. data/app/lib/katello/resources/registry.rb +7 -20
  52. data/app/lib/katello/util/http_proxy.rb +0 -3
  53. data/app/lib/katello/validators/gpg_key_content_validator.rb +1 -1
  54. data/app/models/katello/authorization/{gpg_key.rb → content_credential.rb} +1 -1
  55. data/app/models/katello/authorization/product.rb +0 -4
  56. data/app/models/katello/concerns/host_managed_extensions.rb +2 -16
  57. data/app/models/katello/concerns/organization_extensions.rb +1 -1
  58. data/app/models/katello/concerns/pulp_database_unit.rb +13 -5
  59. data/app/models/katello/concerns/smart_proxy_extensions.rb +45 -41
  60. data/app/models/katello/{gpg_key.rb → content_credential.rb} +4 -4
  61. data/app/models/katello/content_view.rb +6 -1
  62. data/app/models/katello/generic_content_unit.rb +16 -0
  63. data/app/models/katello/glue/pulp/repos.rb +9 -25
  64. data/app/models/katello/kt_environment.rb +1 -1
  65. data/app/models/katello/product.rb +4 -4
  66. data/app/models/katello/repository.rb +13 -7
  67. data/app/models/katello/repository_generic_content_unit.rb +7 -0
  68. data/app/models/katello/root_repository.rb +38 -7
  69. data/app/models/setting/content.rb +5 -0
  70. data/app/services/cert/certs.rb +16 -8
  71. data/app/services/katello/applicability/applicable_content_helper.rb +1 -2
  72. data/app/services/katello/candlepin/consumer.rb +6 -0
  73. data/app/services/katello/component_view_presenter.rb +27 -0
  74. data/app/services/katello/pulp/repository.rb +1 -1
  75. data/app/services/katello/pulp/server.rb +2 -2
  76. data/app/services/katello/pulp3/api/core.rb +4 -0
  77. data/app/services/katello/pulp3/api/generic.rb +68 -0
  78. data/app/services/katello/pulp3/generic_content_unit.rb +29 -0
  79. data/app/services/katello/pulp3/pulp_content_unit.rb +5 -1
  80. data/app/services/katello/pulp3/repository/generic.rb +94 -0
  81. data/app/services/katello/pulp3/repository/yum.rb +4 -5
  82. data/app/services/katello/pulp3/repository.rb +27 -12
  83. data/app/services/katello/pulp3/repository_mirror.rb +2 -2
  84. data/app/services/katello/pulp3/smart_proxy_repository.rb +4 -4
  85. data/app/services/katello/registration_manager.rb +18 -7
  86. data/app/services/katello/repository_type.rb +59 -1
  87. data/app/services/katello/repository_type_manager.rb +116 -24
  88. data/app/views/katello/api/v2/content_views/base.json.rabl +4 -4
  89. data/app/views/katello/api/v2/repositories/show.json.rabl +1 -0
  90. data/app/views/smart_proxies/plugins/_pulpcore.html.erb +2 -5
  91. data/app/views/smart_proxies/pulp_status.html.erb +0 -7
  92. data/config/katello.yaml.example +0 -21
  93. data/config/routes/api/v2.rb +2 -1
  94. data/db/functions/deb_version_cmp_v01.sql +200 -0
  95. data/db/migrate/20171110082124_add_ssl_certs_to_products_and_repos.rb +5 -1
  96. data/db/migrate/20200402130013_add_repsoitory_docker_meta_tag_f_key.rb +3 -1
  97. data/db/migrate/20210624221630_katello_generic_content.rb +22 -0
  98. data/db/migrate/20210625095042_add_retain_package_versions_count.rb +9 -0
  99. data/db/migrate/20210628182553_add_generic_remote_options_to_root_repository.rb +5 -0
  100. data/db/migrate/20210714140440_remove_repo_export_permission.rb +5 -0
  101. data/db/migrate/20210721163730_change_gpg_keys_to_content_credentials.rb +8 -0
  102. data/db/migrate/20210728130748_create_function_deb_version_cmp.rb +12 -0
  103. data/db/seeds.d/111-upgrade_tasks.rb +1 -2
  104. data/engines/bastion/app/views/bastion/layouts/assets.html.erb +1 -1
  105. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +7 -5
  106. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-errata-modal.html +4 -1
  107. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/views/content-view-details.html +1 -1
  108. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/apply-errata.controller.js +1 -1
  109. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/views/apply-errata-confirm.html +2 -1
  110. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +25 -33
  111. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/views/products-bulk-advanced-sync-modal.html +1 -1
  112. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/product-repositories.controller.js +1 -6
  113. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.controller.js +10 -2
  114. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.filter.js +9 -0
  115. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details.controller.js +0 -2
  116. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-advanced-sync-options.html +1 -25
  117. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-details.html +1 -1
  118. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +31 -13
  119. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +11 -3
  120. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/views/product-repositories.html +0 -6
  121. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/aggregate-task.factory.js +3 -3
  122. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/task.factory.js +1 -1
  123. data/lib/katello/engine.rb +2 -4
  124. data/lib/katello/permission_creator.rb +6 -12
  125. data/lib/katello/plugin.rb +76 -80
  126. data/lib/katello/repository_types/python.rb +37 -0
  127. data/lib/katello/tasks/reimport.rake +0 -9
  128. data/lib/katello/tasks/repository.rake +3 -4
  129. data/lib/katello/version.rb +1 -1
  130. data/locale/action_names.rb +28 -29
  131. data/locale/bn/katello.po +699 -221
  132. data/locale/cs/katello.po +167 -59
  133. data/locale/de/katello.po +585 -352
  134. data/locale/en/katello.po +167 -59
  135. data/locale/es/katello.po +1388 -1189
  136. data/locale/fr/katello.po +1740 -1494
  137. data/locale/gu/katello.po +896 -416
  138. data/locale/hi/katello.po +892 -415
  139. data/locale/it/katello.po +371 -170
  140. data/locale/ja/katello.po +1657 -1439
  141. data/locale/katello.pot +933 -736
  142. data/locale/kn/katello.po +894 -416
  143. data/locale/ko/katello.po +515 -317
  144. data/locale/mr/katello.po +857 -415
  145. data/locale/or/katello.po +894 -416
  146. data/locale/pa/katello.po +874 -411
  147. data/locale/pt/katello.po +347 -154
  148. data/locale/pt_BR/katello.po +1398 -1215
  149. data/locale/ru/katello.po +671 -463
  150. data/locale/ta/katello.po +697 -221
  151. data/locale/te/katello.po +891 -415
  152. data/locale/zh_CN/katello.po +2029 -1845
  153. data/locale/zh_TW/katello.po +735 -407
  154. data/package.json +3 -1
  155. data/webpack/components/EditableTextInput/EditableTextInput.js +3 -3
  156. data/webpack/components/RoutedTabs/RoutedTabs.js +7 -8
  157. data/webpack/components/Table/TableWrapper.js +19 -11
  158. data/webpack/components/Table/helpers.js +1 -1
  159. data/webpack/components/extensions/HostDetails/Tabs/ContentTab.js +42 -0
  160. data/webpack/components/extensions/HostDetails/Tabs/SubscriptionTab.js +12 -0
  161. data/webpack/components/extensions/RegistrationCommands/__tests__/__snapshots__/ActivationKeys.test.js.snap +4 -0
  162. data/webpack/components/extensions/RegistrationCommands/fields/ActivationKeys.js +1 -1
  163. data/webpack/components/extensions/RegistrationCommands/index.js +1 -2
  164. data/webpack/components/pf3Table/formatters/selectionHeaderCellFormatter.js +2 -1
  165. data/webpack/fills_index.js +4 -1
  166. data/webpack/redux/actions/RedHatRepositories/helpers.js +2 -4
  167. data/webpack/redux/reducers/RedHatRepositories/enabled.js +4 -1
  168. data/webpack/scenes/ContentViews/ContentViewsActions.js +16 -1
  169. data/webpack/scenes/ContentViews/ContentViewsConstants.js +15 -0
  170. data/webpack/scenes/ContentViews/ContentViewsPage.js +12 -22
  171. data/webpack/scenes/ContentViews/Copy/CopyContentViewForm.js +4 -3
  172. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +25 -14
  173. data/webpack/scenes/ContentViews/Create/CreateContentViewModal.js +4 -2
  174. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js +153 -0
  175. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentVersion.js +21 -10
  176. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ContentViewComponents.js +157 -19
  177. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.fixtures.json +100 -108
  178. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.test.js +140 -16
  179. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/publishedContentViewDetails.fixtures.json +367 -0
  180. data/webpack/scenes/ContentViews/Details/ContentViewDetailActions.js +59 -6
  181. data/webpack/scenes/ContentViews/Details/ContentViewDetailSelectors.js +43 -0
  182. data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +44 -13
  183. data/webpack/scenes/ContentViews/Details/Filters/Add/CVFilterAddModal.js +161 -0
  184. data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterAdd.test.js +54 -0
  185. data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterCreateResult.fixtures.json +124 -0
  186. data/webpack/scenes/ContentViews/Details/Filters/CVPackageGroupFilterContent.js +8 -6
  187. data/webpack/scenes/ContentViews/Details/Filters/CVRpmFilterContent.js +7 -6
  188. data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilterDetails.js +4 -3
  189. data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilters.js +71 -12
  190. data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewFilters.test.js +77 -0
  191. data/webpack/scenes/ContentViews/Details/Histories/ContentViewHistories.js +13 -12
  192. data/webpack/scenes/ContentViews/Details/Histories/__tests__/contentViewHistory.test.js +2 -2
  193. data/webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js +17 -14
  194. data/webpack/scenes/ContentViews/Details/Repositories/LastSync.js +3 -3
  195. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewAddRemove.test.js +2 -2
  196. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewDetailRepos.test.js +6 -2
  197. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersions.js +61 -20
  198. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewTaskInProgressResponse.fixtures.json +71 -0
  199. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewTaskResponse.fixtures.json +75 -0
  200. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.test.js +86 -1
  201. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersionsWithTask.fixtures.json +713 -0
  202. data/webpack/scenes/ContentViews/Details/__tests__/contentViewDetail.test.js +3 -0
  203. data/webpack/scenes/ContentViews/Publish/CVPublishFinish.js +184 -0
  204. data/webpack/scenes/ContentViews/Publish/CVPublishForm.js +104 -0
  205. data/webpack/scenes/ContentViews/Publish/CVPublishReview.js +71 -0
  206. data/webpack/scenes/ContentViews/Publish/ContentViewPublishSelectors.js +17 -0
  207. data/webpack/scenes/ContentViews/Publish/PublishContentViewWizard.js +145 -0
  208. data/webpack/scenes/ContentViews/Publish/__tests__/environmentPaths.fixtures.json +352 -0
  209. data/webpack/scenes/ContentViews/Publish/__tests__/publishContentView.test.js +184 -0
  210. data/webpack/scenes/ContentViews/Publish/__tests__/publishResponse.fixture.json +69 -0
  211. data/webpack/scenes/ContentViews/Publish/cvPublishForm.scss +3 -0
  212. data/webpack/scenes/ContentViews/Table/ContentViewsTable.js +75 -48
  213. data/webpack/scenes/ContentViews/Table/tableDataGenerator.js +15 -2
  214. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +6 -10
  215. data/webpack/scenes/ContentViews/components/EnvironmentLabels.js +22 -10
  216. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathActions.js +12 -0
  217. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathConstants.js +2 -0
  218. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathSelectors.js +16 -0
  219. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +72 -0
  220. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.scss +8 -0
  221. data/webpack/scenes/ContentViews/components/TaskPresenter/TaskPresenter.js +85 -0
  222. data/webpack/scenes/SmartProxy/SmartProxyContentTable.js +9 -8
  223. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +4 -25
  224. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +0 -3
  225. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +3 -3
  226. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +4 -2
  227. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +24 -0
  228. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Table.js +4 -1
  229. data/webpack/scenes/Subscriptions/index.js +1 -4
  230. metadata +74 -39
  231. data/app/lib/actions/candlepin/environment/create.rb +0 -21
  232. data/app/lib/actions/foreman/environment/destroy.rb +0 -23
  233. data/app/lib/actions/katello/content_view/environment_create.rb +0 -21
  234. data/app/lib/actions/katello/repository/export.rb +0 -85
  235. data/app/lib/actions/katello/repository/purge_empty_content.rb +0 -16
  236. data/app/lib/actions/katello/repository/upload_errata.rb +0 -38
  237. data/app/lib/katello/util/proxy_uri.rb +0 -64
  238. data/app/models/katello/rhsm_fact_importer.rb +0 -20
  239. data/app/models/katello/rhsm_fact_name.rb +0 -17
  240. data/app/models/katello/rhsm_fact_parser.rb +0 -120
@@ -16,6 +16,7 @@ jest.mock('../Repositories/ContentViewRepositories.js', () => () => 'mocked!');
16
16
  jest.mock('../Filters/ContentViewFilters.js', () => () => 'mocked!');
17
17
  jest.mock('../Histories/ContentViewHistories.js', () => () => 'mocked!');
18
18
  jest.mock('../Versions/ContentViewVersions.js', () => () => 'mocked!');
19
+ jest.mock('../../Publish/PublishContentViewWizard.js', () => () => 'mocked!');
19
20
 
20
21
  test('Can call API and show details on page load', async (done) => {
21
22
  const { label, name, description } = cvDetailData;
@@ -33,6 +34,8 @@ test('Can call API and show details on page load', async (done) => {
33
34
  expect(getByLabelText('name text value')).toHaveTextContent(name);
34
35
  expect(getByLabelText('label text value')).toHaveTextContent(label);
35
36
  expect(getByLabelText('description text value')).toHaveTextContent(description);
37
+ expect(getByLabelText('cv_breadcrumb')).toBeInTheDocument();
38
+ expect(getByLabelText('cv_breadcrumb_cv')).toHaveTextContent(name);
36
39
  });
37
40
 
38
41
  assertNockRequest(scope, done);
@@ -0,0 +1,184 @@
1
+ import { STATUS } from 'foremanReact/constants';
2
+ import React, { useState, useEffect, useCallback } from 'react';
3
+ import useDeepCompareEffect from 'use-deep-compare-effect';
4
+ import PropTypes from 'prop-types';
5
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
6
+ import { Bullseye, Button, Grid, GridItem,
7
+ Progress, ProgressSize, ProgressMeasureLocation,
8
+ ProgressVariant, EmptyState, EmptyStateIcon, EmptyStateVariant,
9
+ Title } from '@patternfly/react-core';
10
+ import { ExternalLinkAltIcon, InProgressIcon } from '@patternfly/react-icons';
11
+ import { translate as __ } from 'foremanReact/common/I18n';
12
+ import {
13
+ selectPublishContentViewsError, selectPublishContentViews,
14
+ selectPublishContentViewStatus,
15
+ } from './ContentViewPublishSelectors';
16
+ import { selectPublishTaskPoll, selectPublishTaskPollStatus } from '../Details/ContentViewDetailSelectors';
17
+ import getContentViews, { publishContentView } from '../ContentViewsActions';
18
+ import Loading from '../../../components/Loading';
19
+ import EmptyStateMessage from '../../../components/Table/EmptyStateMessage';
20
+ import { cvVersionPublishKey } from '../ContentViewsConstants';
21
+ import { startPollingTask, stopPollingTask, toastTaskFinished } from '../../Tasks/TaskActions';
22
+ import getContentViewDetails from '../Details/ContentViewDetailActions';
23
+
24
+ const CVPublishFinish = ({
25
+ cvId,
26
+ userCheckedItems, setUserCheckedItems,
27
+ forcePromote, description, setDescription,
28
+ setIsOpen, versionCount, currentStep, setCurrentStep,
29
+ }) => {
30
+ const dispatch = useDispatch();
31
+ const [publishDispatched, setPublishDispatched] = useState(false);
32
+ const [saving, setSaving] = useState(true);
33
+ const [polling, setPolling] = useState(false);
34
+ const [taskErrored, setTaskErrored] = useState(false);
35
+ const response = useSelector(state => selectPublishContentViews(state, cvId, versionCount));
36
+ const status = useSelector(state => selectPublishContentViewStatus(state, cvId, versionCount));
37
+ const error = useSelector(state => selectPublishContentViewsError(state, cvId, versionCount));
38
+ const pollResponse = useSelector(state =>
39
+ selectPublishTaskPoll(state, cvVersionPublishKey(cvId, versionCount)), shallowEqual);
40
+ const pollResponseStatus = useSelector(state =>
41
+ selectPublishTaskPollStatus(state, cvVersionPublishKey(cvId, versionCount)), shallowEqual);
42
+
43
+ const progressCompleted = () => (pollResponse.progress ? pollResponse.progress * 100 : 0);
44
+
45
+ const handleEndTask = useCallback(({ taskComplete }) => {
46
+ if (currentStep !== 1) {
47
+ dispatch(stopPollingTask(cvVersionPublishKey(cvId, versionCount)));
48
+ setCurrentStep(1);
49
+ setIsOpen(false);
50
+ dispatch(getContentViewDetails(cvId));
51
+ dispatch(getContentViews);
52
+ if (taskComplete) {
53
+ dispatch(toastTaskFinished(pollResponse));
54
+ }
55
+ }
56
+ }, [currentStep, cvId, dispatch, pollResponse, setCurrentStep, setIsOpen, versionCount]);
57
+
58
+
59
+ useEffect(() => {
60
+ if (currentStep !== 3 && !publishDispatched) {
61
+ setCurrentStep(3);
62
+ setSaving(true);
63
+ setPublishDispatched(true);
64
+ dispatch(publishContentView({
65
+ id: cvId,
66
+ versionCount,
67
+ description,
68
+ environment_ids: userCheckedItems.map(item => item.id),
69
+ is_force_promote: (forcePromote.length > 0),
70
+ }));
71
+ setDescription('');
72
+ setUserCheckedItems([]);
73
+ }
74
+ }, [dispatch, setSaving, publishDispatched, setPublishDispatched,
75
+ setDescription, setUserCheckedItems, currentStep, setCurrentStep,
76
+ cvId, versionCount, description, forcePromote, userCheckedItems]);
77
+
78
+ useDeepCompareEffect(() => {
79
+ if (!response) return;
80
+ const pollPublishTask = (cvPublishVersionKey, task) => {
81
+ if (!polling) dispatch(startPollingTask(cvPublishVersionKey, task));
82
+ };
83
+
84
+ setSaving(true);
85
+ const { id } = response;
86
+ if (id && status === STATUS.RESOLVED) {
87
+ setSaving(false);
88
+ pollPublishTask(cvVersionPublishKey(cvId, versionCount), response);
89
+ setPolling(true);
90
+ } else if (status === STATUS.ERROR) {
91
+ setSaving(false);
92
+ }
93
+ }, [response, status, error, cvId, versionCount,
94
+ dispatch, polling, setPolling, setSaving]);
95
+
96
+
97
+ useDeepCompareEffect(() => {
98
+ const { state, result } = pollResponse;
99
+ if (state === 'paused' || result === 'error') {
100
+ setTaskErrored(true);
101
+ setTimeout(() => {
102
+ handleEndTask({ taskComplete: true });
103
+ }, 500);
104
+ }
105
+ if (state === 'stopped' && result === 'success') {
106
+ setTimeout(() => {
107
+ handleEndTask({ taskComplete: true });
108
+ }, 500);
109
+ }
110
+ }, [pollResponse, dispatch, setTaskErrored,
111
+ setPolling, setIsOpen, pollResponseStatus, handleEndTask]);
112
+
113
+ if (saving) {
114
+ return <Loading />;
115
+ }
116
+ if (polling && pollResponse) {
117
+ return (
118
+ <>
119
+ <EmptyState style={{ marginTop: '10px' }} variant={EmptyStateVariant.large}>
120
+ <EmptyStateIcon icon={InProgressIcon} />
121
+ <Title headingLevel="h2" size="lg">
122
+ {__('Publishing content view')}
123
+ </Title>
124
+ </EmptyState>
125
+ <Grid hasGutter>
126
+ <GridItem span={12} rowSpan={19}>
127
+ <Progress
128
+ value={progressCompleted()}
129
+ title={__('In progress')}
130
+ measureLocation={ProgressMeasureLocation.outside}
131
+ variant={taskErrored ? ProgressVariant.danger : ProgressVariant.default}
132
+ size={ProgressSize.lg}
133
+ />
134
+ </GridItem>
135
+ <GridItem style={{ marginTop: '10px' }} span={12} rowSpan={1}>
136
+ <Bullseye>
137
+ <Button
138
+ onClick={() => {
139
+ handleEndTask({ taskComplete: false });
140
+ }}
141
+ variant="primary"
142
+ aria-label="publish_content_view"
143
+ >
144
+ {__('Close')}
145
+ </Button>
146
+ <Button
147
+ component="a"
148
+ aria-label="view tasks button"
149
+ href={`/foreman_tasks/tasks/${pollResponse.id}`}
150
+ target="_blank"
151
+ variant="link"
152
+ >
153
+ {__(' View task details ')}
154
+ <ExternalLinkAltIcon />
155
+ </Button>
156
+ </Bullseye>
157
+ </GridItem>
158
+ </Grid>
159
+ </>
160
+ );
161
+ }
162
+ if (status === STATUS.PENDING) return (<Loading />);
163
+ if (status === STATUS.ERROR) return (<EmptyStateMessage error={error} />);
164
+ return <Loading />;
165
+ };
166
+
167
+ CVPublishFinish.propTypes = {
168
+ cvId: PropTypes.oneOfType([
169
+ PropTypes.number,
170
+ PropTypes.string,
171
+ ]).isRequired,
172
+ forcePromote: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
173
+ description: PropTypes.string.isRequired,
174
+ setDescription: PropTypes.func.isRequired,
175
+ userCheckedItems: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
176
+ setUserCheckedItems: PropTypes.func.isRequired,
177
+ setIsOpen: PropTypes.func.isRequired,
178
+ versionCount: PropTypes.number.isRequired,
179
+ currentStep: PropTypes.number.isRequired,
180
+ setCurrentStep: PropTypes.func.isRequired,
181
+ };
182
+
183
+
184
+ export default CVPublishFinish;
@@ -0,0 +1,104 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
4
+ import { Alert, Switch, Flex, FlexItem, TextContent, Text, TextVariants, Form, FormGroup, TextArea } from '@patternfly/react-core';
5
+ import { EnterpriseIcon, RegistryIcon } from '@patternfly/react-icons';
6
+ import EnvironmentPaths from '../components/EnvironmentPaths/EnvironmentPaths';
7
+ import ComponentEnvironments from '../Details/ComponentContentViews/ComponentEnvironments';
8
+ import './cvPublishForm.scss';
9
+
10
+ const CVPublishForm = ({
11
+ description,
12
+ setDescription,
13
+ details,
14
+ userCheckedItems,
15
+ setUserCheckedItems,
16
+ promote,
17
+ setPromote,
18
+ forcePromote,
19
+ }) => {
20
+ const {
21
+ name, composite, next_version: nextVersion,
22
+ } = details;
23
+
24
+ return (
25
+ <>
26
+ <>
27
+ <TextContent>
28
+ <Text style={{ marginBottom: '1em' }} component={TextVariants.h1}>{__('Publish')}</Text>
29
+ </TextContent>
30
+ <Flex flex={{ default: 'inlineFlex' }}>
31
+ <FlexItem>{__('A new version of ')}<b>{composite ? <RegistryIcon /> : <EnterpriseIcon />} {name}</b>
32
+ {__(' will be created and automatically promoted to the ' +
33
+ 'Library environment. You can promote to other environments as well. ')}
34
+ </FlexItem>
35
+ </Flex>
36
+ <TextContent>
37
+ <Text style={{ marginTop: '1em', marginBottom: '1em' }} component={TextVariants.h3}>{__('Publish new version - ')}{nextVersion || '1.0'}</Text>
38
+ </TextContent>
39
+ <Form>
40
+ <FormGroup label="Description" fieldId="description">
41
+ <TextArea
42
+ isRequired
43
+ type="text"
44
+ id="description"
45
+ aria-label="input_description"
46
+ name="description"
47
+ value={description}
48
+ onChange={value => setDescription(value)}
49
+ />
50
+ </FormGroup>
51
+ <FormGroup label="Promote" fieldId="promote" style={{ marginBottom: '2em', outlineStyle: 'none' }}>
52
+ <Switch
53
+ id="promote-switch"
54
+ aria-label="promote-switch"
55
+ isChecked={promote}
56
+ onChange={checked => setPromote(checked)}
57
+ />
58
+ </FormGroup>
59
+ </Form>
60
+ </>
61
+ <>
62
+ {forcePromote.length > 0 && (
63
+ <Alert variant="info" isInline title="Force Promotion">
64
+ <p>{__('Selected environments are out of the environment order. ' +
65
+ 'The recommended practice is to promote to the next environment in the path.')}
66
+ </p>
67
+ <ComponentEnvironments environments={forcePromote} />
68
+ </Alert>
69
+ )
70
+ }
71
+ {promote &&
72
+ <EnvironmentPaths
73
+ userCheckedItems={userCheckedItems}
74
+ setUserCheckedItems={setUserCheckedItems}
75
+ />
76
+ }
77
+ </>
78
+ </>
79
+ );
80
+ };
81
+
82
+ CVPublishForm.propTypes = {
83
+ description: PropTypes.string.isRequired,
84
+ setDescription: PropTypes.func.isRequired,
85
+ userCheckedItems: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
86
+ setUserCheckedItems: PropTypes.func.isRequired,
87
+ promote: PropTypes.bool.isRequired,
88
+ setPromote: PropTypes.func.isRequired,
89
+ forcePromote: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
90
+ details: PropTypes.shape({
91
+ id: PropTypes.oneOfType([
92
+ PropTypes.number,
93
+ PropTypes.string,
94
+ ]),
95
+ name: PropTypes.string.isRequired,
96
+ composite: PropTypes.bool.isRequired,
97
+ next_version: PropTypes.oneOfType([
98
+ PropTypes.number,
99
+ PropTypes.string,
100
+ ]).isRequired,
101
+ }).isRequired,
102
+ };
103
+
104
+ export default CVPublishForm;
@@ -0,0 +1,71 @@
1
+ import React, { useMemo } from 'react';
2
+ import { useSelector } from 'react-redux';
3
+ import PropTypes from 'prop-types';
4
+ import { TableComposable, Thead, Tbody, Tr, Th, Td } from '@patternfly/react-table';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
6
+ import { STATUS } from 'foremanReact/constants';
7
+ import ContentViewIcon from '../components/ContentViewIcon';
8
+ import InactiveText from '../components/InactiveText';
9
+ import ComponentEnvironments from '../Details/ComponentContentViews/ComponentEnvironments';
10
+ import { selectEnvironmentPaths, selectEnvironmentPathsStatus } from '../components/EnvironmentPaths/EnvironmentPathSelectors';
11
+
12
+ const CVPublishReview = ({
13
+ details,
14
+ userCheckedItems,
15
+ }) => {
16
+ const environmentPathResponse = useSelector(selectEnvironmentPaths);
17
+ const environmentPathStatus = useSelector(selectEnvironmentPathsStatus);
18
+ const environmentPathLoading = environmentPathStatus === STATUS.PENDING;
19
+
20
+ const promotedToEnvironments = useMemo(() => {
21
+ if (!environmentPathLoading) {
22
+ const { results } = environmentPathResponse || {};
23
+ const library = results[0].environments[0];
24
+ return [library].concat(userCheckedItems);
25
+ }
26
+ return [];
27
+ }, [environmentPathResponse, environmentPathLoading, userCheckedItems]);
28
+
29
+ const {
30
+ name, composite, next_version: nextVersion,
31
+ } = details;
32
+
33
+ return (
34
+ <TableComposable aria-label="Review Table">
35
+ <Thead>
36
+ <Tr>
37
+ <Th>{__('Content view')}</Th>
38
+ <Th>{__('Version')}</Th>
39
+ <Th>{__('Environments')}</Th>
40
+ </Tr>
41
+ </Thead>
42
+ <Tbody>
43
+ <Tr>
44
+ <Td>
45
+ <><ContentViewIcon composite={composite} description={name} /><InactiveText text={__('Newly published')} /></>
46
+ </Td>
47
+ <Td>
48
+ {__('Version')} {nextVersion}
49
+ </Td>
50
+ <Td>
51
+ <ComponentEnvironments environments={promotedToEnvironments} />
52
+ </Td>
53
+ </Tr>
54
+ </Tbody>
55
+ </TableComposable>
56
+ );
57
+ };
58
+
59
+ CVPublishReview.propTypes = {
60
+ userCheckedItems: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
61
+ details: PropTypes.shape({
62
+ name: PropTypes.string.isRequired,
63
+ composite: PropTypes.bool.isRequired,
64
+ next_version: PropTypes.oneOfType([
65
+ PropTypes.number,
66
+ PropTypes.string,
67
+ ]).isRequired,
68
+ }).isRequired,
69
+ };
70
+
71
+ export default CVPublishReview;
@@ -0,0 +1,17 @@
1
+ import {
2
+ selectAPIStatus,
3
+ selectAPIError,
4
+ selectAPIResponse,
5
+ } from 'foremanReact/redux/API/APISelectors';
6
+ import { STATUS } from 'foremanReact/constants';
7
+ import { cvVersionPublishKey } from '../ContentViewsConstants';
8
+
9
+ export const selectPublishContentViews = (state, cvId, versionCount) =>
10
+ selectAPIResponse(state, cvVersionPublishKey(cvId, versionCount)) || {};
11
+
12
+ export const selectPublishContentViewStatus = (state, cvId, versionCount) =>
13
+ selectAPIStatus(state, cvVersionPublishKey(cvId, versionCount)) || STATUS.PENDING;
14
+
15
+ export const selectPublishContentViewsError = (state, cvId, versionCount) =>
16
+ selectAPIError(state, cvVersionPublishKey(cvId, versionCount));
17
+
@@ -0,0 +1,145 @@
1
+ import React, { useEffect, useState, useMemo, useCallback } from 'react';
2
+ import { useDispatch, useSelector } from 'react-redux';
3
+ import PropTypes from 'prop-types';
4
+ import { STATUS } from 'foremanReact/constants';
5
+ import { Wizard } from '@patternfly/react-core';
6
+ import { translate as __ } from 'foremanReact/common/I18n';
7
+ import CVPublishForm from './CVPublishForm';
8
+ import CVPublishFinish from './CVPublishFinish';
9
+ import getEnvironmentPaths from '../components/EnvironmentPaths/EnvironmentPathActions';
10
+ import CVPublishReview from './CVPublishReview';
11
+ import {
12
+ selectEnvironmentPaths,
13
+ selectEnvironmentPathsStatus,
14
+ } from '../components/EnvironmentPaths/EnvironmentPathSelectors';
15
+ import getContentViews from '../ContentViewsActions';
16
+ import getContentViewDetails from '../Details/ContentViewDetailActions';
17
+ import { stopPollingTask } from '../../Tasks/TaskActions';
18
+ import { cvVersionPublishKey } from '../ContentViewsConstants';
19
+
20
+ const PublishContentViewWizard = ({
21
+ details, show, setIsOpen, currentStep, setCurrentStep,
22
+ }) => {
23
+ const { name, id: cvId, version_count: versionCount } = details;
24
+ const [description, setDescription] = useState('');
25
+ const [userCheckedItems, setUserCheckedItems] = useState([]);
26
+ const [promote, setPromote] = useState(false);
27
+ const [forcePromote, setForcePromote] = useState([]);
28
+ const dispatch = useDispatch();
29
+ const environmentPathResponse = useSelector(selectEnvironmentPaths);
30
+ const environmentPathStatus = useSelector(selectEnvironmentPathsStatus);
31
+ const environmentPathLoading = environmentPathStatus === STATUS.PENDING;
32
+
33
+ const steps = [
34
+ {
35
+ id: 1,
36
+ name: 'Publish',
37
+ component: <CVPublishForm
38
+ description={description}
39
+ setDescription={setDescription}
40
+ details={details}
41
+ show={show}
42
+ userCheckedItems={userCheckedItems}
43
+ setUserCheckedItems={setUserCheckedItems}
44
+ promote={promote}
45
+ setPromote={setPromote}
46
+ forcePromote={forcePromote}
47
+ />,
48
+ },
49
+ {
50
+ id: 2, name: 'Review', component: <CVPublishReview details={details} userCheckedItems={userCheckedItems} show={show} />, nextButtonText: 'Finish',
51
+ },
52
+ {
53
+ id: 3,
54
+ name: 'Finish',
55
+ component: <CVPublishFinish
56
+ description={description}
57
+ setDescription={setDescription}
58
+ userCheckedItems={userCheckedItems}
59
+ setUserCheckedItems={setUserCheckedItems}
60
+ forcePromote={forcePromote}
61
+ cvId={cvId}
62
+ versionCount={versionCount}
63
+ show={show}
64
+ setIsOpen={setIsOpen}
65
+ currentStep={currentStep}
66
+ setCurrentStep={setCurrentStep}
67
+ />,
68
+ isFinishedStep: true,
69
+ },
70
+ ];
71
+
72
+ useEffect(
73
+ () => {
74
+ dispatch(getEnvironmentPaths());
75
+ },
76
+ [dispatch],
77
+ );
78
+
79
+ const envPathFlat = useMemo(() => {
80
+ if (!environmentPathLoading) {
81
+ const { results } = environmentPathResponse || {};
82
+ return results.map(result => result.environments).flatten();
83
+ }
84
+ return [];
85
+ }, [environmentPathResponse, environmentPathLoading]);
86
+
87
+ const prior = useCallback(
88
+ env => envPathFlat.find(item => item.id === env.prior.id),
89
+ [envPathFlat],
90
+ );
91
+ const isChecked = useCallback(
92
+ env => userCheckedItems.includes(env) || env.library,
93
+ [userCheckedItems],
94
+ );
95
+
96
+ const isValid = useCallback((env) => {
97
+ if (!env.prior) return true;
98
+ if (!isChecked(prior(env))) return false;
99
+ return isValid(prior(env));
100
+ }, [prior, isChecked]);
101
+
102
+ useEffect(() => {
103
+ setForcePromote(userCheckedItems.filter(item => !isValid(item)));
104
+ }, [userCheckedItems, setForcePromote, isValid]);
105
+
106
+ return (
107
+ <Wizard
108
+ title={__('Publish')}
109
+ description={currentStep === 3 ? __(`Publishing ${name}`) : __(`Determining settings for ${name}`)}
110
+ steps={steps}
111
+ startAtStep={currentStep}
112
+ onClose={() => {
113
+ setDescription('');
114
+ setUserCheckedItems([]);
115
+ setPromote(false);
116
+ setForcePromote([]);
117
+ if (currentStep === 3) {
118
+ setCurrentStep(1);
119
+ dispatch(getContentViewDetails(cvId));
120
+ dispatch(getContentViews);
121
+ dispatch(stopPollingTask(cvVersionPublishKey(cvId, versionCount)));
122
+ }
123
+ setIsOpen(false);
124
+ }}
125
+ isOpen={show}
126
+ />
127
+ );
128
+ };
129
+
130
+ PublishContentViewWizard.propTypes = {
131
+ show: PropTypes.bool.isRequired,
132
+ setIsOpen: PropTypes.func.isRequired,
133
+ currentStep: PropTypes.number.isRequired,
134
+ setCurrentStep: PropTypes.func.isRequired,
135
+ details: PropTypes.shape({
136
+ id: PropTypes.oneOfType([
137
+ PropTypes.number,
138
+ PropTypes.string,
139
+ ]),
140
+ name: PropTypes.string,
141
+ version_count: PropTypes.number,
142
+ }).isRequired,
143
+ };
144
+
145
+ export default PublishContentViewWizard;