katello 4.3.0.rc1 → 4.3.0.rc2

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 (208) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/v2/api_controller.rb +4 -0
  3. data/app/controllers/katello/api/v2/host_errata_controller.rb +5 -0
  4. data/app/controllers/katello/api/v2/host_packages_controller.rb +2 -0
  5. data/app/controllers/katello/api/v2/host_tracer_controller.rb +4 -0
  6. data/app/controllers/katello/api/v2/repository_sets_controller.rb +2 -2
  7. data/app/controllers/katello/api/v2/root_controller.rb +10 -19
  8. data/app/controllers/katello/concerns/api/v2/bulk_extensions.rb +3 -13
  9. data/app/controllers/katello/remote_execution_controller.rb +1 -1
  10. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +7 -5
  11. data/app/lib/actions/katello/repository/destroy.rb +3 -3
  12. data/app/lib/actions/pulp/repository/sync.rb +0 -2
  13. data/app/lib/actions/pulp3/abstract_async_task.rb +16 -4
  14. data/app/lib/katello/resources/cdn.rb +10 -1
  15. data/app/models/katello/concerns/host_managed_extensions.rb +7 -4
  16. data/app/models/katello/concerns/smart_proxy_extensions.rb +6 -2
  17. data/app/models/katello/content_view_version.rb +1 -6
  18. data/app/models/katello/glue/pulp/repo.rb +1 -2
  19. data/app/models/katello/host_tracer.rb +2 -0
  20. data/app/models/katello/repository.rb +2 -30
  21. data/app/models/katello/root_repository.rb +3 -43
  22. data/app/presenters/katello/host_package_presenter.rb +21 -0
  23. data/app/services/katello/bulk_items_helper.rb +35 -0
  24. data/app/services/katello/smart_proxy_helper.rb +10 -1
  25. data/app/views/foreman/job_templates/install_errata.erb +8 -6
  26. data/app/views/foreman/job_templates/resolve_traces.erb +4 -5
  27. data/app/views/foreman/job_templates/resolve_traces_-_katello_ansible_default.erb +3 -5
  28. data/app/views/foreman/smart_proxies/_content_sync.html.erb +7 -0
  29. data/app/views/katello/api/v2/capsule_content/sync_status.json.rabl +4 -0
  30. data/app/views/katello/api/v2/{organizations/cdn_configuration.rabl → cdn_configurations/show.json.rabl} +4 -0
  31. data/app/views/katello/api/v2/content_facet/show.json.rabl +8 -0
  32. data/app/views/katello/api/v2/content_view_filters/show.json.rabl +0 -1
  33. data/app/views/katello/api/v2/content_views/base.json.rabl +1 -1
  34. data/app/views/katello/api/v2/host_packages/base.json.rabl +2 -0
  35. data/app/views/katello/api/v2/organizations/show.json.rabl +1 -1
  36. data/app/views/katello/api/v2/repositories/show.json.rabl +0 -3
  37. data/config/routes/api/v2.rb +0 -10
  38. data/db/migrate/20211115215210_drop_ostree_branches.rb +13 -0
  39. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/bastion-katello-bootstrap.js +0 -2
  40. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/bastion_katello.js +0 -3
  41. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/content-views.routes.js +0 -10
  42. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/views/content-view-versions.html +0 -3
  43. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/content-view-version-content.controller.js +0 -10
  44. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/content-view-versions.module.js +0 -1
  45. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/views/content-view-version.html +0 -7
  46. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/content.service.js +0 -5
  47. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/environments.module.js +0 -1
  48. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/environments.routes.js +0 -11
  49. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/organizations/fenced-pages.service.js +1 -2
  50. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.controller.js +2 -4
  51. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.filter.js +0 -10
  52. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +1 -1
  53. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/new-repository.controller.js +3 -6
  54. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +4 -1
  55. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/os-versions.service.js +1 -0
  56. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/repositories.routes.js +0 -9
  57. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/views/product-repositories.html +0 -8
  58. data/engines/bastion_katello/lib/bastion_katello/engine.rb +0 -1
  59. data/lib/katello/permission_creator.rb +0 -1
  60. data/lib/katello/plugin.rb +0 -10
  61. data/lib/katello/tasks/reset.rake +2 -2
  62. data/lib/katello/version.rb +1 -1
  63. data/webpack/components/Packages/index.js +63 -0
  64. data/webpack/components/Search/Search.js +7 -1
  65. data/webpack/components/SelectAllCheckbox/index.js +2 -2
  66. data/webpack/components/Table/MainTable.scss +7 -1
  67. data/webpack/components/Table/TableHooks.js +10 -19
  68. data/webpack/components/Table/TableWrapper.js +0 -2
  69. data/webpack/components/WithOrganization/__snapshots__/withOrganization.test.js.snap +3 -3
  70. data/webpack/components/extensions/HostDetails/HostDetailsConstants.js +1 -0
  71. data/webpack/components/extensions/HostDetails/HostDetailsSelectors.js +16 -0
  72. data/webpack/components/extensions/HostDetails/HostErrata/HostErrataConstants.js +2 -0
  73. data/webpack/components/extensions/HostDetails/HostPackages/HostPackagesActions.js +11 -0
  74. data/webpack/components/extensions/HostDetails/HostPackages/HostPackagesConstants.js +2 -0
  75. data/webpack/components/extensions/HostDetails/HostPackages/HostPackagesSelectors.js +16 -0
  76. data/webpack/components/extensions/HostDetails/Tabs/ContentTab/SecondaryTabsRoutes.js +4 -0
  77. data/webpack/components/extensions/HostDetails/Tabs/ContentTab/constants.js +1 -0
  78. data/webpack/components/extensions/HostDetails/Tabs/ErrataTab.js +119 -25
  79. data/webpack/components/extensions/HostDetails/Tabs/HostTracesConstants.js +1 -0
  80. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab.js +127 -0
  81. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab.scss +11 -0
  82. data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionActions.js +30 -4
  83. data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionConstants.js +1 -0
  84. data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsActions.js +73 -0
  85. data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsConstants.js +2 -0
  86. data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsSelectors.js +16 -0
  87. data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsTab.js +347 -0
  88. data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsTab.scss +7 -0
  89. data/webpack/components/extensions/HostDetails/Tabs/TracesTab.js +38 -31
  90. data/webpack/components/extensions/HostDetails/Tabs/__tests__/bookmarks.fixtures.json +12 -0
  91. data/webpack/components/extensions/HostDetails/Tabs/__tests__/contentOverrides.fixtures.json +227 -0
  92. data/webpack/components/extensions/HostDetails/Tabs/__tests__/errataTab.test.js +423 -2
  93. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packages.fixtures.json +28 -0
  94. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packagesTab.test.js +91 -0
  95. data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySets.fixtures.json +120 -0
  96. data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySetsTab.test.js +307 -0
  97. data/webpack/components/extensions/HostDetails/Tabs/__tests__/resolveErrata.fixtures.json +35 -0
  98. data/webpack/components/extensions/HostDetails/Tabs/__tests__/tracesTab.test.js +55 -9
  99. data/webpack/components/extensions/HostDetails/Tabs/customizedRexUrlHelpers.js +28 -14
  100. data/webpack/containers/Application/overrides.scss +31 -9
  101. data/webpack/global_index.js +4 -2
  102. data/webpack/redux/reducers/RedHatRepositories/enabled.fixtures.js +0 -2
  103. data/webpack/scenes/Content/ContentConfig.js +23 -7
  104. data/webpack/scenes/ContentCredentials/ContentCredentialActions.js +18 -0
  105. data/webpack/scenes/ContentCredentials/ContentCredentialConstants.js +2 -0
  106. data/webpack/scenes/ContentCredentials/ContentCredentialSelectors.js +12 -0
  107. data/webpack/scenes/ContentCredentials/__tests__/contentCredentials.fixtures.js +73 -0
  108. data/webpack/scenes/ContentViews/Copy/CopyContentViewForm.js +1 -1
  109. data/webpack/scenes/ContentViews/Create/ContentViewFormComponents.js +3 -3
  110. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +7 -2
  111. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.scss +7 -0
  112. data/webpack/scenes/ContentViews/Create/__tests__/createContentView.test.js +9 -9
  113. data/webpack/scenes/ContentViews/Delete/ContentViewDeleteWizard.js +6 -6
  114. data/webpack/scenes/ContentViews/Delete/Steps/CVDeleteEnvironmentsSelection.js +39 -37
  115. data/webpack/scenes/ContentViews/Delete/Steps/CVDeletionReview.js +35 -33
  116. data/webpack/scenes/ContentViews/Delete/__tests__/contentViewDelete.test.js +7 -5
  117. data/webpack/scenes/ContentViews/Delete/__tests__/cvVersionsData.fixtures.json +2 -6
  118. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentEnvironments.js +13 -14
  119. data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +18 -9
  120. data/webpack/scenes/ContentViews/Details/ContentViewInfo.js +3 -2
  121. data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterCreateResult.fixtures.json +1 -2
  122. data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVRpmFilterContent.test.js +1 -1
  123. data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewErrataByDateDetails.fixtures.json +1 -8
  124. data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewFilterDetail.fixtures.json +1 -2
  125. data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewFilterDetails.test.js +3 -8
  126. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvAllRepos.fixtures.json +0 -2
  127. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvErratumFilterDetails.fixtures.json +1 -2
  128. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvFilterDetailModuleAffectedRepos.fixtures.json +1 -8
  129. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvFilterDetailWithAffectedRepos.fixtures.json +1 -8
  130. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvModuleStreamFilterDetails.fixtures.json +1 -2
  131. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvPackageFilterDetail.fixtures.json +1 -3
  132. data/webpack/scenes/ContentViews/Details/Promote/ContentViewVersionPromote.js +44 -28
  133. data/webpack/scenes/ContentViews/Details/Repositories/ContentCounts.js +2 -1
  134. data/webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js +1 -1
  135. data/webpack/scenes/ContentViews/Details/Repositories/LastSync.js +46 -8
  136. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewDetailRepos.fixtures.json +0 -2
  137. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersionContent.js +19 -3
  138. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersionEnvironments.js +2 -2
  139. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersionErrata.js +5 -3
  140. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersionErrata.scss +5 -2
  141. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersions.js +7 -4
  142. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVEnvironmentSelectionForm.js +59 -53
  143. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVVersionRemoveReview.js +24 -17
  144. data/webpack/scenes/ContentViews/Details/Versions/Delete/__tests__/versionsResponseData.fixtures.json +1 -4
  145. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionRepositoryCell.js +8 -3
  146. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionComponent.fixtures.json +1 -4
  147. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionDetails.fixtures.json +1 -2
  148. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionDetailsCounts.fixtures.json +1 -2
  149. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionRepositories.fixtures.json +1 -18
  150. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.fixtures.json +5 -5
  151. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.test.js +1 -0
  152. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersionsWithTask.fixtures.json +1 -3
  153. data/webpack/scenes/ContentViews/Details/contentViewInfo.scss +0 -4
  154. data/webpack/scenes/ContentViews/Publish/CVPublishForm.js +66 -53
  155. data/webpack/scenes/ContentViews/Publish/CVPublishReview.js +40 -28
  156. data/webpack/scenes/ContentViews/Publish/PublishContentViewWizard.js +3 -3
  157. data/webpack/scenes/ContentViews/Publish/__tests__/publishContentView.test.js +14 -14
  158. data/webpack/scenes/ContentViews/Publish/cvPublishForm.scss +6 -0
  159. data/webpack/scenes/ContentViews/Table/ContentViewsTable.js +53 -12
  160. data/webpack/scenes/ContentViews/Table/tableDataGenerator.js +12 -6
  161. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +6 -6
  162. data/webpack/scenes/ContentViews/components/ContentViewIcon.js +6 -5
  163. data/webpack/scenes/ContentViews/components/ContentViewsCounter.js +2 -2
  164. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +26 -27
  165. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.scss +18 -6
  166. data/webpack/scenes/ContentViews/components/WizardHeader.js +44 -0
  167. data/webpack/scenes/ContentViews/components/contentViewIcon.scss +13 -2
  168. data/webpack/scenes/Organizations/OrganizationActions.js +22 -24
  169. data/webpack/scenes/Organizations/OrganizationConstants.js +1 -3
  170. data/webpack/scenes/Organizations/OrganizationReducer.js +0 -7
  171. data/webpack/scenes/Organizations/OrganizationSelectors.js +16 -0
  172. data/webpack/scenes/Organizations/__tests__/OrganizationActions.test.js +1 -21
  173. data/webpack/scenes/Organizations/__tests__/OrganizationReducer.test.js +0 -20
  174. data/webpack/scenes/Organizations/__tests__/organizations.fixtures.js +34 -23
  175. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +151 -14
  176. data/webpack/scenes/Subscriptions/Manifest/__tests__/ManageManifestModal.test.js +150 -31
  177. data/webpack/scenes/Subscriptions/Manifest/index.js +14 -3
  178. data/webpack/utils/dateTimeHelpers.js +7 -0
  179. data/webpack/utils/helpers.js +1 -1
  180. metadata +45 -43
  181. data/app/controllers/katello/api/v2/ostree_branches_controller.rb +0 -16
  182. data/app/lib/actions/pulp/repository/presenters/ostree_presenter.rb +0 -91
  183. data/app/models/katello/ostree_branch.rb +0 -12
  184. data/app/models/katello/repository_ostree_branch.rb +0 -7
  185. data/app/services/katello/pulp/ostree_branch.rb +0 -14
  186. data/app/services/katello/pulp/repository/ostree.rb +0 -48
  187. data/app/views/katello/api/v2/ostree_branches/compare.json.rabl +0 -10
  188. data/app/views/katello/api/v2/ostree_branches/index.json.rabl +0 -7
  189. data/app/views/katello/api/v2/ostree_branches/show.json.rabl +0 -5
  190. data/app/views/katello/api/v2/root/resource_list.json.rabl +0 -3
  191. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/versions/views/content-view-version-ostree-branches.html +0 -26
  192. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-ostree.html +0 -27
  193. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/details/ostree-branch-repositories.controller.js +0 -77
  194. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/details/ostree-branch.controller.js +0 -31
  195. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/details/views/ostree-branch-info.html +0 -15
  196. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/details/views/ostree-branch-repositories.html +0 -72
  197. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/details/views/ostree-branch.html +0 -30
  198. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/ostree-branch.factory.js +0 -27
  199. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/ostree-branches.controller.js +0 -67
  200. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/ostree-branches.module.js +0 -15
  201. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/ostree-branches.routes.js +0 -50
  202. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/ostree-branches/views/ostree-branches.html +0 -40
  203. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-manage-ostree-branches.html +0 -40
  204. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/ostree-upstream-sync-policy.service.js +0 -26
  205. data/webpack/components/extensions/HostDetails/Tabs/SubscriptionTab.js +0 -12
  206. data/webpack/scenes/Content/Details/ContentCounts.js +0 -42
  207. data/webpack/scenes/Subscriptions/Manifest/__tests__/SimpleContentAccess.test.js +0 -108
  208. data/webpack/scenes/Subscriptions/Manifest/__tests__/__snapshots__/ManageManifestModal.test.js.snap +0 -158
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState, useEffect, useRef } from 'react';
2
2
  import { useDispatch } from 'react-redux';
3
3
  import { ControlLabel } from 'react-bootstrap';
4
4
  import { loadSetting } from 'foremanReact/components/Settings/SettingsActions';
@@ -26,11 +26,14 @@ const Search = ({
26
26
  }) => {
27
27
  const [items, setItems] = useState([]);
28
28
  const dispatch = useDispatch();
29
+ const mountedRef = useRef(true);
29
30
 
30
31
  const onInputUpdate = async (searchTerm = '') => {
31
32
  const newItems = items.filter(({ text }) => stringIncludes(text, searchTerm));
32
33
 
33
34
  if (newItems.length !== items.length) {
35
+ // Checking whether the current component is mounted before state change events
36
+ if (!mountedRef.current) return;
34
37
  setItems(newItems);
35
38
  }
36
39
 
@@ -43,6 +46,8 @@ const Search = ({
43
46
  } else {
44
47
  data = await api.get(endpoint, undefined, params);
45
48
  }
49
+ // Checking whether the current component is mounted before state change events
50
+ if (!mountedRef.current) return;
46
51
  setItems(data?.data?.filter(({ error }) => !error).map(({ label }) => ({
47
52
  text: label.trim(),
48
53
  })));
@@ -56,6 +61,7 @@ const Search = ({
56
61
  useEffect(() => {
57
62
  dispatch(loadSetting(AUTOSEARCH_DELAY));
58
63
  dispatch(loadSetting(AUTOSEARCH_WHILE_TYPING));
64
+ return () => { mountedRef.current = false; };
59
65
  }, [dispatch]);
60
66
 
61
67
  const onNewSearch = (search) => {
@@ -71,13 +71,13 @@ const SelectAllCheckbox = ({
71
71
  <DropdownItem key="select-none" component="button" isDisabled={selectedCount === 0} onClick={handleSelectNone} >
72
72
  {`${__('Select none')} (0)`}
73
73
  </DropdownItem>,
74
- <DropdownItem key="select-page" component="button" isDisabled={areAllRowsOnPageSelected} onClick={handleSelectPage}>
74
+ <DropdownItem key="select-page" component="button" isDisabled={pageRowCount === 0 || areAllRowsOnPageSelected} onClick={handleSelectPage}>
75
75
  {`${__('Select page')} (${pageRowCount})`}
76
76
  </DropdownItem>,
77
77
  ];
78
78
  if (canSelectAll) {
79
79
  selectAllDropdownItems.push((
80
- <DropdownItem key="select-all" id="all" component="button" isDisabled={areAllRowsSelected} onClick={handleSelectAll}>
80
+ <DropdownItem key="select-all" id="all" component="button" isDisabled={totalCount === 0 || areAllRowsSelected} onClick={handleSelectAll}>
81
81
  {`${__('Select all')} (${totalCount})`}
82
82
  </DropdownItem>));
83
83
  }
@@ -1,11 +1,17 @@
1
- .pf-c-dropdown.pf-m-align-right{
1
+ .pf-c-dropdown.pf-m-align-right {
2
2
  width: 100%;
3
3
  justify-content: flex-end;
4
4
  display: flex;
5
5
  }
6
+
6
7
  .pf-c-dropdown__menu {
7
8
  min-width: 0;
8
9
  }
10
+
9
11
  .pf-c-wizard__footer {
10
12
  z-index: 1;
13
+ }
14
+
15
+ .pf-c-table tbody tr td {
16
+ vertical-align: inherit;
11
17
  }
@@ -179,27 +179,18 @@ export const useBulkSelect = ({
179
179
  };
180
180
 
181
181
  const fetchBulkParams = () => {
182
- const selected = {
183
- included: {
184
- ids: [],
185
- search: null,
186
- },
187
- excluded: {
188
- ids: [],
189
- },
190
- all: false,
182
+ const searchQueryWithExclusionSet = () => {
183
+ const query = [searchQuery,
184
+ !isEmpty(exclusionSet) && `${idColumn} !^ (${[...exclusionSet].join(',')})`];
185
+ return query.filter(item => item).join(' and ');
191
186
  };
192
187
 
193
- if (selectAllMode) {
194
- selected.included.search = searchQuery;
195
- selected.excluded.ids = [...exclusionSet];
196
- selected.all = true;
197
- } else if (!isEmpty(inclusionSet)) {
198
- selected.included.ids = [...inclusionSet];
199
- } else {
200
- return {};
201
- }
202
- return selected;
188
+ const searchQueryWithInclusionSet = () => {
189
+ if (isEmpty(inclusionSet)) throw new Error('Cannot build a search query with no items selected');
190
+ return `${idColumn} ^ (${[...inclusionSet].join(',')})`;
191
+ };
192
+
193
+ return selectAllMode ? searchQueryWithExclusionSet() : searchQueryWithInclusionSet();
203
194
  };
204
195
 
205
196
  const prevSearchRef = usePrevious({ searchQuery });
@@ -63,8 +63,6 @@ const TableWrapper = ({
63
63
  const hasChanged = (oldValue, newValue) => !isEqual(oldValue, newValue);
64
64
 
65
65
  const spawnFetch = useCallback((paginationData) => {
66
- // The search component will update the search query when a search is performed, listen for that
67
- // and perform the search so we can be sure the searchQuery is updated when search is performed.
68
66
  const fetchWithParams = (allParams = {}) => {
69
67
  const newRequest = {
70
68
  ...(paginationData ?? paginationParams()),
@@ -17,7 +17,6 @@ exports[`subscriptions page should render select org page 1`] = `
17
17
  default={[Function]}
18
18
  loadOrganization={[Function]}
19
19
  organization={Object {}}
20
- saveOrganization={[Function]}
21
20
  store={
22
21
  Object {
23
22
  "clearActions": [Function],
@@ -28,6 +27,7 @@ exports[`subscriptions page should render select org page 1`] = `
28
27
  "subscribe": [Function],
29
28
  }
30
29
  }
30
+ updateCdnConfiguration={[Function]}
31
31
  >
32
32
  <Header
33
33
  title="Select Organization"
@@ -73,7 +73,6 @@ exports[`subscriptions page should render the wrapped component 1`] = `
73
73
  default={[Function]}
74
74
  loadOrganization={[Function]}
75
75
  organization={Object {}}
76
- saveOrganization={[Function]}
77
76
  store={
78
77
  Object {
79
78
  "clearActions": [Function],
@@ -84,12 +83,12 @@ exports[`subscriptions page should render the wrapped component 1`] = `
84
83
  "subscribe": [Function],
85
84
  }
86
85
  }
86
+ updateCdnConfiguration={[Function]}
87
87
  >
88
88
  <WrappedComponent
89
89
  default={[Function]}
90
90
  loadOrganization={[Function]}
91
91
  organization={Object {}}
92
- saveOrganization={[Function]}
93
92
  store={
94
93
  Object {
95
94
  "clearActions": [Function],
@@ -100,6 +99,7 @@ exports[`subscriptions page should render the wrapped component 1`] = `
100
99
  "subscribe": [Function],
101
100
  }
102
101
  }
102
+ updateCdnConfiguration={[Function]}
103
103
  >
104
104
  <div>
105
105
  Wrapped!
@@ -0,0 +1 @@
1
+ export default 'HOST_DETAILS';
@@ -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 HOST_DETAILS_KEY from './HostDetailsConstants';
8
+
9
+ export const selectHostDetails = state =>
10
+ selectAPIResponse(state, HOST_DETAILS_KEY) || {};
11
+
12
+ export const selectHostDetailsStatus = state =>
13
+ selectAPIStatus(state, HOST_DETAILS_KEY) || STATUS.PENDING;
14
+
15
+ export const selectHostDetailsError = state =>
16
+ selectAPIError(state, HOST_DETAILS_KEY);
@@ -30,3 +30,5 @@ export const SEVERITIES_TO_PARAM = {
30
30
  };
31
31
 
32
32
  export default HOST_ERRATA_KEY;
33
+
34
+ export const ERRATA_SEARCH_QUERY = 'Errata search query';
@@ -0,0 +1,11 @@
1
+ import { API_OPERATIONS, get } from 'foremanReact/redux/API';
2
+ import { foremanApi } from '../../../../services/api';
3
+ import { HOST_PACKAGES_KEY } from './HostPackagesConstants';
4
+
5
+ export const getInstalledPackagesWithLatest = (hostId, params) => get({
6
+ type: API_OPERATIONS.GET,
7
+ key: HOST_PACKAGES_KEY,
8
+ url: foremanApi.getApiUrl(`/hosts/${hostId}/packages?include_latest_upgradable=true`),
9
+ params,
10
+ });
11
+ export default getInstalledPackagesWithLatest;
@@ -0,0 +1,2 @@
1
+ export const HOST_PACKAGES_KEY = 'HOST_PACKAGES';
2
+ export default HOST_PACKAGES_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 { HOST_PACKAGES_KEY } from './HostPackagesConstants';
8
+
9
+ export const selectHostPackages = state =>
10
+ selectAPIResponse(state, HOST_PACKAGES_KEY) || {};
11
+
12
+ export const selectHostPackagesStatus = state =>
13
+ selectAPIStatus(state, HOST_PACKAGES_KEY) || STATUS.PENDING;
14
+
15
+ export const selectHostPackagesError = state =>
16
+ selectAPIError(state, HOST_PACKAGES_KEY);
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Route, Switch, Redirect } from 'react-router-dom';
3
+ import { PackagesTab } from '../PackagesTab';
3
4
  import { ErrataTab } from '../ErrataTab';
4
5
  import { route } from './helpers';
5
6
 
@@ -8,6 +9,9 @@ const SecondaryTabRoutes = () => (
8
9
  <Route exact path="/Content">
9
10
  <Redirect to={route('errata')} />
10
11
  </Route>
12
+ <Route path={route('packages')}>
13
+ <PackagesTab />
14
+ </Route>
11
15
  <Route path={route('errata')}>
12
16
  <ErrataTab />
13
17
  </Route>
@@ -1,6 +1,7 @@
1
1
  import { translate as __ } from 'foremanReact/common/I18n';
2
2
 
3
3
  const SECONDARY_TABS = [
4
+ { key: 'packages', title: __('Packages') },
4
5
  { key: 'errata', title: __('Errata') },
5
6
  ];
6
7
 
@@ -18,7 +18,6 @@ import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
18
18
  import IsoDate from 'foremanReact/components/common/dates/IsoDate';
19
19
  import { urlBuilder } from 'foremanReact/common/urlHelpers';
20
20
  import { propsToCamelCase } from 'foremanReact/common/helpers';
21
- import { isEmpty } from 'lodash';
22
21
  import SelectableDropdown from '../../../SelectableDropdown';
23
22
  import { useSet, useBulkSelect } from '../../../../components/Table/TableHooks';
24
23
  import TableWrapper from '../../../../components/Table/TableWrapper';
@@ -28,12 +27,15 @@ import ErratumExpansionDetail from './ErratumExpansionDetail';
28
27
  import ErratumExpansionContents from './ErratumExpansionContents';
29
28
  import { selectHostErrataStatus } from '../HostErrata/HostErrataSelectors';
30
29
  import { HOST_ERRATA_KEY, ERRATA_TYPES, ERRATA_SEVERITIES, TYPES_TO_PARAM, SEVERITIES_TO_PARAM } from '../HostErrata/HostErrataConstants';
30
+ import { installErrata } from './RemoteExecutionActions';
31
+ import { errataInstallUrl } from './customizedRexUrlHelpers';
31
32
  import './ErrataTab.scss';
32
33
 
33
34
  export const ErrataTab = () => {
34
35
  const hostDetails = useSelector(state => selectAPIResponse(state, 'HOST_DETAILS'));
35
36
  const {
36
37
  id: hostId,
38
+ name: hostname,
37
39
  content_facet_attributes: contentFacetAttributes,
38
40
  } = hostDetails;
39
41
  const contentFacet = propsToCamelCase(contentFacetAttributes ?? {});
@@ -91,7 +93,7 @@ export const ErrataTab = () => {
91
93
  const response = useSelector(state => selectAPIResponse(state, HOST_ERRATA_KEY));
92
94
  const { results, ...metadata } = response;
93
95
  const status = useSelector(state => selectHostErrataStatus(state));
94
-
96
+ const errataSearchQuery = id => `errata_id = ${id}`;
95
97
  const {
96
98
  selectOne, isSelected, searchQuery, selectedCount, isSelectable,
97
99
  updateSearchQuery, selectNone, fetchBulkParams, ...selectAll
@@ -104,38 +106,99 @@ export const ErrataTab = () => {
104
106
 
105
107
  if (!hostId) return <Skeleton />;
106
108
 
107
- const rowActions = [
108
- {
109
- title: __('Apply via Katello agent'), disabled: true,
110
- },
111
- {
112
- title: __('Apply via remote execution'), disabled: true,
113
- },
114
- {
115
- title: __('Apply via customized remote execution'), disabled: true,
116
- },
117
- ];
109
+ const applyErratumViaRemoteExecution = id => dispatch(installErrata({
110
+ hostname,
111
+ search: errataSearchQuery(id),
112
+ }));
113
+
114
+ const applyViaRemoteExecution = () => {
115
+ dispatch(installErrata({
116
+ hostname, search: fetchBulkParams(),
117
+ }));
118
+
119
+ const params = { page: metadata.page, per_page: metadata.per_page, search: metadata.search };
120
+ dispatch(getInstallableErrata(
121
+ hostId,
122
+ { ...params, include_applicable: toggleGroupState === ALL },
123
+ ));
124
+ };
125
+
126
+ const bulkCustomizedRexUrl = () => errataInstallUrl({
127
+ hostname, search: (selectedCount > 0) ? fetchBulkParams() : '',
128
+ });
118
129
 
119
130
  const recalculateErrata = () => {
120
131
  setIsBulkActionOpen(false);
121
132
  dispatch(regenerateApplicability(hostId));
122
133
  };
123
134
 
135
+ const applyByKatelloAgent = () => {
136
+ const selected = fetchBulkParams();
137
+ setIsBulkActionOpen(false);
138
+ selectNone();
139
+ dispatch(applyViaKatelloAgent(hostId, { search: selected }));
140
+ };
141
+
142
+ const applyErratumViaKatelloAgent = id => dispatch(applyViaKatelloAgent(
143
+ hostId,
144
+ { errata_ids: [id] },
145
+ ));
146
+
147
+ const katelloAgentAvailable = (contentFacet.katelloAgentInstalled &&
148
+ contentFacet.katelloAgentEnabled);
149
+ const apply = () => {
150
+ if (contentFacet.remoteExecutionByDefault || !katelloAgentAvailable) {
151
+ applyViaRemoteExecution();
152
+ } else {
153
+ applyByKatelloAgent();
154
+ }
155
+ };
156
+
124
157
  const dropdownItems = [
125
- <DropdownItem aria-label="bulk_add" key="bulk_add" component="button" onClick={recalculateErrata}>
158
+ <DropdownItem
159
+ aria-label="bulk_add"
160
+ key="bulk_add"
161
+ component="button"
162
+ onClick={recalculateErrata}
163
+ >
126
164
  {__('Recalculate')}
127
165
  </DropdownItem>,
128
166
  ];
129
167
 
130
- const applyByKatelloAgent = () => {
131
- const selected = fetchBulkParams();
132
- if (!isEmpty(selected)) {
133
- const parameters = { bulk_errata_ids: JSON.stringify(selected) };
134
- setIsBulkActionOpen(false);
135
- selectNone();
136
- dispatch(applyViaKatelloAgent(hostId, parameters));
137
- }
138
- };
168
+ if (katelloAgentAvailable) {
169
+ dropdownItems.push((
170
+ <DropdownItem
171
+ aria-label="apply_via_katello_agent"
172
+ key="apply_via_katello_agent"
173
+ component="button"
174
+ onClick={applyByKatelloAgent}
175
+ isDisabled={selectedCount === 0}
176
+ >
177
+ {__('Apply via Katello agent')}
178
+ </DropdownItem>));
179
+ }
180
+
181
+ dropdownItems.push((
182
+ <DropdownItem
183
+ aria-label="apply_via_remote_execution"
184
+ key="apply_via_remote_execution"
185
+ component="button"
186
+ onClick={applyViaRemoteExecution}
187
+ isDisabled={selectedCount === 0}
188
+ >
189
+ {__('Apply via remote execution')}
190
+ </DropdownItem>));
191
+
192
+ dropdownItems.push((
193
+ <DropdownItem
194
+ aria-label="apply_via_customized_remote_execution"
195
+ key="apply_via_customized_remote_execution"
196
+ component="a"
197
+ href={bulkCustomizedRexUrl()}
198
+ isDisabled={selectedCount === 0}
199
+ >
200
+ {__('Apply via customized remote execution')}
201
+ </DropdownItem>));
139
202
 
140
203
  const handleErrataTypeSelected = newType => setErrataTypeSelected((prevType) => {
141
204
  if (prevType === newType) {
@@ -157,7 +220,7 @@ export const ErrataTab = () => {
157
220
  <SplitItem>
158
221
  <ActionList isIconList>
159
222
  <ActionListItem>
160
- <Button isDisabled={selectedCount === 0} onClick={applyByKatelloAgent}> {__('Apply')} </Button>
223
+ <Button isDisabled={selectedCount === 0} onClick={apply}> {__('Apply')} </Button>
161
224
  </ActionListItem>
162
225
  <ActionListItem>
163
226
  <Dropdown
@@ -259,7 +322,7 @@ export const ErrataTab = () => {
259
322
  <Th key="select-all" />
260
323
  {columnHeaders.map(col =>
261
324
  <Th key={col}>{col}</Th>)}
262
- <Th />
325
+ <Th key="action-menu" />
263
326
  </Tr>
264
327
  </Thead>
265
328
  <>
@@ -273,6 +336,36 @@ export const ErrataTab = () => {
273
336
  installable: isInstallable,
274
337
  } = erratum;
275
338
  const isExpanded = erratumIsExpanded(id);
339
+ let rowActions;
340
+ if (isInstallable) {
341
+ rowActions = [
342
+ {
343
+ title: __('Apply via remote execution'),
344
+ onClick: () => applyErratumViaRemoteExecution(errataId),
345
+ },
346
+ {
347
+ title: __('Apply via customized remote execution'),
348
+ component: 'a',
349
+ href: errataInstallUrl({ hostname, search: errataSearchQuery(errataId) }),
350
+ },
351
+ ];
352
+
353
+ if (contentFacet.katelloAgentInstalled && contentFacet.katelloAgentEnabled) {
354
+ rowActions.unshift({
355
+ title: __('Apply via Katello agent'),
356
+ onClick: () => applyErratumViaKatelloAgent(errataId),
357
+ });
358
+ }
359
+ } else {
360
+ rowActions = [
361
+ {
362
+ title: __('Apply Erratum'),
363
+ component: 'a',
364
+ href: urlBuilder(`errata/${id}/content-hosts`, ''),
365
+ },
366
+ ];
367
+ }
368
+
276
369
  return (
277
370
  <Tbody isExpanded={isExpanded} key={`${id}_${createdAt}`}>
278
371
  <Tr>
@@ -319,6 +412,7 @@ export const ErrataTab = () => {
319
412
  actions={{
320
413
  items: rowActions,
321
414
  }}
415
+
322
416
  />
323
417
  </Tr>
324
418
  <Tr key="child_row" isExpanded={isExpanded}>
@@ -1,2 +1,3 @@
1
1
  export const HOST_TRACES_KEY = 'HOST_TRACES';
2
2
  export const KATELLO_TRACER_PACKAGE = 'katello-host-tools-tracer';
3
+ export const TRACES_SEARCH_QUERY = 'Traces search query';
@@ -0,0 +1,127 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { useSelector } from 'react-redux';
3
+ import { Button, Hint, HintBody } from '@patternfly/react-core';
4
+ import { TableVariant, Thead, Tbody, Tr, Th, Td } from '@patternfly/react-table';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
6
+ import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
7
+
8
+ import { urlBuilder } from 'foremanReact/common/urlHelpers';
9
+ import TableWrapper from '../../../../components/Table/TableWrapper';
10
+ import { PackagesStatus, PackagesLatestVersion } from '../../../../components/Packages';
11
+ import { getInstalledPackagesWithLatest } from '../HostPackages/HostPackagesActions';
12
+ import { selectHostPackagesStatus } from '../HostPackages/HostPackagesSelectors';
13
+ import { HOST_PACKAGES_KEY } from '../HostPackages/HostPackagesConstants';
14
+ import './PackagesTab.scss';
15
+
16
+ export const PackagesTab = () => {
17
+ const hostDetails = useSelector(state => selectAPIResponse(state, 'HOST_DETAILS'));
18
+ const { id: hostId } = hostDetails;
19
+ const actionButtons = <Button isDisabled> {__('Upgrade')} </Button>;
20
+
21
+ const [searchQuery, updateSearchQuery] = useState('');
22
+
23
+ const emptyContentTitle = __('This host does not have any packages.');
24
+ const emptyContentBody = __('Packages will appear here when available.');
25
+ const emptySearchTitle = __('No matching packages found');
26
+ const emptySearchBody = __('Try changing your search settings.');
27
+ const columnHeaders = [
28
+ __('Package'),
29
+ __('Status'),
30
+ __('Installed Version'),
31
+ __('Upgradable To'),
32
+ ];
33
+
34
+ const fetchItems = useCallback(
35
+ params => (hostId ? getInstalledPackagesWithLatest(hostId, params) : null),
36
+ [hostId],
37
+ );
38
+
39
+ const response = useSelector(state => selectAPIResponse(state, HOST_PACKAGES_KEY));
40
+ const { results, ...metadata } = response;
41
+ const status = useSelector(state => selectHostPackagesStatus(state));
42
+ const rowActions = [
43
+ {
44
+ title: __('Upgrade via remote execution'), disabled: true,
45
+ },
46
+ {
47
+ title: __('Upgrade via customized remote execution'), disabled: true,
48
+ },
49
+ ];
50
+
51
+ return (
52
+ <div>
53
+ <div id="packages-hint">
54
+ <Hint>
55
+ <HintBody>
56
+ {__('Packages management functionality on this page is incomplete')}.
57
+ <br />
58
+ <Button component="a" variant="link" isInline href={urlBuilder(`content_hosts/${hostId}/packages/installed`, '')}>
59
+ {__('Visit the previous Packages page')}.
60
+ </Button>
61
+ </HintBody>
62
+ </Hint>
63
+ </div>
64
+ <div id="packages-tab">
65
+ <TableWrapper
66
+ {...{
67
+ metadata,
68
+ emptyContentTitle,
69
+ emptyContentBody,
70
+ emptySearchTitle,
71
+ emptySearchBody,
72
+ status,
73
+ actionButtons,
74
+ searchQuery,
75
+ updateSearchQuery,
76
+ }
77
+ }
78
+ additionalListeners={[hostId]}
79
+ fetchItems={fetchItems}
80
+ autocompleteEndpoint={`/hosts/${hostId}/packages/auto_complete_search`}
81
+ foremanApiAutoComplete
82
+ variant={TableVariant.compact}
83
+ >
84
+ <Thead>
85
+ <Tr>
86
+ {columnHeaders.map(col =>
87
+ <Th key={col}>{col}</Th>)}
88
+ <Th />
89
+ </Tr>
90
+ </Thead>
91
+ <Tbody>
92
+ {results?.map((packages) => {
93
+ const {
94
+ id,
95
+ name: packageName,
96
+ nvra: installedVersion,
97
+ rpm_id: rpmId,
98
+ } = packages;
99
+ return (
100
+ <Tr key={`${id}`}>
101
+ <Td>
102
+ {rpmId
103
+ ? <a href={urlBuilder(`packages/${rpmId}`, '')}>{packageName}</a>
104
+ : packageName
105
+ }
106
+ </Td>
107
+ <Td><PackagesStatus {...packages} /></Td>
108
+ <Td>{installedVersion.replace(`${packageName}-`, '')}</Td>
109
+ <Td><PackagesLatestVersion {...packages} /></Td>
110
+ <Td
111
+ key={`rowActions-${id}`}
112
+ actions={{
113
+ items: rowActions,
114
+ }}
115
+ />
116
+ </Tr>
117
+ );
118
+ })
119
+ }
120
+ </Tbody>
121
+ </TableWrapper>
122
+ </div>
123
+ </div>
124
+ );
125
+ };
126
+
127
+ export default PackagesTab;
@@ -0,0 +1,11 @@
1
+ #packages-tab {
2
+ margin: 0 20px;
3
+ margin-bottom: 2em;
4
+ margin-top: 2em;
5
+ }
6
+
7
+ #packages-alert {
8
+ margin: 0 20px;
9
+ margin-bottom: 2em;
10
+ margin-top: 2em;
11
+ }