katello 4.18.0 → 4.19.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (345) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/katello/locale/bn/katello.js +52 -349
  3. data/app/assets/javascripts/katello/locale/bn_IN/katello.js +52 -349
  4. data/app/assets/javascripts/katello/locale/ca/katello.js +52 -349
  5. data/app/assets/javascripts/katello/locale/cs/katello.js +53 -350
  6. data/app/assets/javascripts/katello/locale/cs_CZ/katello.js +54 -351
  7. data/app/assets/javascripts/katello/locale/de/katello.js +56 -353
  8. data/app/assets/javascripts/katello/locale/de_AT/katello.js +52 -349
  9. data/app/assets/javascripts/katello/locale/de_DE/katello.js +52 -349
  10. data/app/assets/javascripts/katello/locale/el/katello.js +54 -351
  11. data/app/assets/javascripts/katello/locale/en/katello.js +53 -350
  12. data/app/assets/javascripts/katello/locale/en_GB/katello.js +53 -350
  13. data/app/assets/javascripts/katello/locale/en_US/katello.js +52 -349
  14. data/app/assets/javascripts/katello/locale/es/katello.js +56 -353
  15. data/app/assets/javascripts/katello/locale/et_EE/katello.js +52 -349
  16. data/app/assets/javascripts/katello/locale/fr/katello.js +138 -435
  17. data/app/assets/javascripts/katello/locale/gl/katello.js +52 -349
  18. data/app/assets/javascripts/katello/locale/gu/katello.js +52 -349
  19. data/app/assets/javascripts/katello/locale/he_IL/katello.js +52 -349
  20. data/app/assets/javascripts/katello/locale/hi/katello.js +52 -349
  21. data/app/assets/javascripts/katello/locale/id/katello.js +52 -349
  22. data/app/assets/javascripts/katello/locale/it/katello.js +53 -350
  23. data/app/assets/javascripts/katello/locale/ja/katello.js +142 -439
  24. data/app/assets/javascripts/katello/locale/ka/katello.js +56 -353
  25. data/app/assets/javascripts/katello/locale/kn/katello.js +52 -349
  26. data/app/assets/javascripts/katello/locale/ko/katello.js +135 -432
  27. data/app/assets/javascripts/katello/locale/ml_IN/katello.js +52 -349
  28. data/app/assets/javascripts/katello/locale/mr/katello.js +52 -349
  29. data/app/assets/javascripts/katello/locale/nl_NL/katello.js +52 -349
  30. data/app/assets/javascripts/katello/locale/or/katello.js +52 -349
  31. data/app/assets/javascripts/katello/locale/pa/katello.js +52 -349
  32. data/app/assets/javascripts/katello/locale/pl/katello.js +52 -349
  33. data/app/assets/javascripts/katello/locale/pl_PL/katello.js +52 -349
  34. data/app/assets/javascripts/katello/locale/pt/katello.js +52 -349
  35. data/app/assets/javascripts/katello/locale/pt_BR/katello.js +56 -353
  36. data/app/assets/javascripts/katello/locale/ro/katello.js +52 -349
  37. data/app/assets/javascripts/katello/locale/ro_RO/katello.js +52 -349
  38. data/app/assets/javascripts/katello/locale/ru/katello.js +54 -351
  39. data/app/assets/javascripts/katello/locale/sl/katello.js +52 -349
  40. data/app/assets/javascripts/katello/locale/sv_SE/katello.js +52 -349
  41. data/app/assets/javascripts/katello/locale/ta/katello.js +52 -349
  42. data/app/assets/javascripts/katello/locale/ta_IN/katello.js +52 -349
  43. data/app/assets/javascripts/katello/locale/te/katello.js +52 -349
  44. data/app/assets/javascripts/katello/locale/tr/katello.js +52 -349
  45. data/app/assets/javascripts/katello/locale/vi/katello.js +52 -349
  46. data/app/assets/javascripts/katello/locale/vi_VN/katello.js +52 -349
  47. data/app/assets/javascripts/katello/locale/zh/katello.js +52 -349
  48. data/app/assets/javascripts/katello/locale/zh_CN/katello.js +135 -432
  49. data/app/assets/javascripts/katello/locale/zh_TW/katello.js +54 -351
  50. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +46 -13
  51. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +1 -1
  52. data/app/controllers/katello/api/v2/activation_keys_controller.rb +3 -65
  53. data/app/controllers/katello/api/v2/content_export_incrementals_controller.rb +56 -34
  54. data/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb +1 -1
  55. data/app/controllers/katello/api/v2/content_views_controller.rb +18 -3
  56. data/app/controllers/katello/api/v2/debs_controller.rb +21 -11
  57. data/app/controllers/katello/api/v2/docker_tags_controller.rb +7 -0
  58. data/app/controllers/katello/api/v2/errata_controller.rb +4 -4
  59. data/app/controllers/katello/api/v2/flatpak_remote_repositories_controller.rb +21 -19
  60. data/app/controllers/katello/api/v2/host_debs_controller.rb +16 -1
  61. data/app/controllers/katello/api/v2/host_subscriptions_controller.rb +3 -60
  62. data/app/controllers/katello/api/v2/hosts_bulk_actions_controller.rb +10 -53
  63. data/app/controllers/katello/api/v2/repositories_controller.rb +0 -1
  64. data/app/controllers/katello/concerns/organizations_controller_extensions.rb +3 -0
  65. data/app/lib/actions/candlepin/activation_key/create.rb +0 -2
  66. data/app/lib/actions/candlepin/activation_key/update.rb +0 -2
  67. data/app/lib/actions/candlepin/product/content_create.rb +3 -5
  68. data/app/lib/actions/candlepin/product/content_update.rb +2 -3
  69. data/app/lib/actions/helpers/rolling_cv_repos.rb +1 -1
  70. data/app/lib/actions/katello/activation_key/create.rb +0 -1
  71. data/app/lib/actions/katello/activation_key/update.rb +0 -2
  72. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +1 -6
  73. data/app/lib/actions/katello/content_credential/update.rb +1 -1
  74. data/app/lib/actions/katello/content_view/add_rolling_repo_clone.rb +18 -24
  75. data/app/lib/actions/katello/content_view/create.rb +9 -4
  76. data/app/lib/actions/katello/content_view/publish.rb +7 -7
  77. data/app/lib/actions/katello/content_view/refresh_rolling_repo.rb +6 -1
  78. data/app/lib/actions/katello/content_view/remove_rolling_repo_clone.rb +16 -11
  79. data/app/lib/actions/katello/content_view/update.rb +34 -7
  80. data/app/lib/actions/katello/product/content_create.rb +2 -2
  81. data/app/lib/actions/katello/product/content_destroy.rb +1 -1
  82. data/app/lib/actions/katello/repository/check_matching_content.rb +1 -1
  83. data/app/lib/actions/katello/repository/clone_contents.rb +1 -1
  84. data/app/lib/actions/katello/repository/create.rb +1 -1
  85. data/app/lib/actions/katello/repository/destroy.rb +4 -4
  86. data/app/lib/actions/katello/repository/finish_upload.rb +1 -1
  87. data/app/lib/actions/katello/repository/sync.rb +1 -1
  88. data/app/lib/actions/pulp3/orchestration/repository/copy_all_units.rb +2 -2
  89. data/app/lib/actions/pulp3/orchestration/repository/generate_metadata.rb +1 -1
  90. data/app/lib/actions/pulp3/orchestration/repository/multi_copy_all_units.rb +1 -1
  91. data/app/lib/actions/pulp3/repository/save_publication.rb +3 -1
  92. data/app/lib/actions/pulp3/repository/save_version.rb +45 -24
  93. data/app/lib/actions/pulp3/repository/save_versions.rb +2 -1
  94. data/app/lib/katello/resources/candlepin/activation_key.rb +3 -4
  95. data/app/lib/katello/resources/candlepin/upstream_job.rb +9 -1
  96. data/app/lib/katello/resources/candlepin.rb +4 -0
  97. data/app/models/katello/authorization/repository.rb +17 -4
  98. data/app/models/katello/concerns/subscription_facet_host_extensions.rb +0 -7
  99. data/app/models/katello/content_view_deb_filter.rb +10 -0
  100. data/app/models/katello/content_view_deb_filter_rule.rb +7 -0
  101. data/app/models/katello/deb.rb +10 -12
  102. data/app/models/katello/erratum.rb +1 -1
  103. data/app/models/katello/glue/provider.rb +14 -3
  104. data/app/models/katello/host/content_facet.rb +1 -1
  105. data/app/models/katello/host/subscription_facet.rb +1 -7
  106. data/app/models/katello/product_content.rb +2 -2
  107. data/app/models/katello/repository.rb +4 -23
  108. data/app/models/katello/root_repository.rb +2 -5
  109. data/app/services/katello/candlepin/event_handler.rb +0 -33
  110. data/app/services/katello/candlepin/message_handler.rb +0 -41
  111. data/app/services/katello/content_unit_indexer.rb +59 -13
  112. data/app/services/katello/product_content_finder.rb +16 -7
  113. data/app/services/katello/pulp3/alternate_content_source.rb +2 -2
  114. data/app/services/katello/pulp3/ansible_collection.rb +1 -0
  115. data/app/services/katello/pulp3/api/content_guard.rb +5 -5
  116. data/app/services/katello/pulp3/api/core.rb +10 -0
  117. data/app/services/katello/pulp3/content_view_version/export.rb +25 -10
  118. data/app/services/katello/pulp3/deb.rb +1 -0
  119. data/app/services/katello/pulp3/docker_manifest.rb +1 -0
  120. data/app/services/katello/pulp3/docker_manifest_list.rb +1 -0
  121. data/app/services/katello/pulp3/docker_tag.rb +1 -0
  122. data/app/services/katello/pulp3/file_unit.rb +1 -0
  123. data/app/services/katello/pulp3/generic_content_unit.rb +1 -0
  124. data/app/services/katello/pulp3/module_stream.rb +1 -0
  125. data/app/services/katello/pulp3/package_group.rb +1 -0
  126. data/app/services/katello/pulp3/repository/apt.rb +30 -13
  127. data/app/services/katello/pulp3/repository.rb +59 -10
  128. data/app/services/katello/pulp3/rpm.rb +3 -2
  129. data/app/services/katello/pulp3/srpm.rb +3 -2
  130. data/app/services/katello/pulp3/task_group.rb +1 -1
  131. data/app/services/katello/registration_manager.rb +19 -17
  132. data/app/services/katello/repository_type_manager.rb +7 -5
  133. data/app/services/katello/smart_proxy_helper.rb +1 -6
  134. data/app/views/foreman/job_templates/upload_profile.erb +5 -0
  135. data/app/views/katello/api/v2/activation_keys/base.json.rabl +1 -1
  136. data/app/views/katello/api/v2/content_views/base.json.rabl +1 -0
  137. data/app/views/katello/api/v2/debs/thindex.json.rabl +6 -0
  138. data/app/views/katello/api/v2/docker_tags/_base.json.rabl +32 -0
  139. data/app/views/katello/api/v2/docker_tags/show.json.rabl +0 -5
  140. data/app/views/katello/api/v2/flatpak_remotes/index.json.rabl +6 -0
  141. data/app/views/katello/api/v2/host_debs/installed_debs.json.rabl +6 -0
  142. data/app/views/katello/api/v2/hosts_bulk_actions/applicable_errata.json.rabl +1 -1
  143. data/app/views/katello/api/v2/hosts_bulk_actions/applicable_erratum.json.rabl +9 -0
  144. data/app/views/katello/api/v2/hosts_bulk_actions/installable_errata.json.rabl +1 -1
  145. data/app/views/katello/api/v2/hosts_bulk_actions/{erratum.json.rabl → installable_erratum.json.rabl} +3 -3
  146. data/app/views/katello/api/v2/subscription_facet/base.json.rabl +1 -1
  147. data/config/initializers/monkeys.rb +1 -0
  148. data/config/routes/api/v2.rb +2 -2
  149. data/config/routes/overrides.rb +2 -7
  150. data/config/routes.rb +2 -0
  151. data/db/migrate/20211019192121_create_cdn_configuration.katello.rb +1 -1
  152. data/db/migrate/20250912000000_add_pulp_prn_fields.rb +73 -0
  153. data/db/migrate/20250912000001_populate_pulp_prn_fields.rb +403 -0
  154. data/db/migrate/20251009142516_remove_auto_attach_from_activation_keys.rb +5 -0
  155. data/db/migrate/20251009142517_remove_autoheal_from_subscription_facets.rb +5 -0
  156. data/db/seeds.d/111-upgrade_tasks.rb +2 -0
  157. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/el.po +2 -2
  158. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/fr.po +6 -1
  159. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ja.po +5 -2
  160. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ko.po +5 -2
  161. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_CN.po +5 -2
  162. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/translations.js +4 -4
  163. data/lib/katello/permission_creator.rb +2 -2
  164. data/lib/katello/permissions/host_permissions.rb +0 -6
  165. data/lib/katello/plugin.rb +16 -8
  166. data/lib/katello/tasks/jenkins.rake +1 -1
  167. data/lib/katello/tasks/upgrades/4.19/enable_structured_apt_for_deb.rake +87 -0
  168. data/lib/katello/tasks/upgrades/4.19/populate_repository_version_prns.rake +32 -0
  169. data/lib/katello/version.rb +1 -1
  170. data/lib/monkeys/fix_rpm_repository_gpgcheck.rb +38 -0
  171. data/locale/bn/LC_MESSAGES/katello.mo +0 -0
  172. data/locale/bn/katello.po +52 -349
  173. data/locale/bn_IN/LC_MESSAGES/katello.mo +0 -0
  174. data/locale/bn_IN/katello.po +52 -349
  175. data/locale/ca/LC_MESSAGES/katello.mo +0 -0
  176. data/locale/ca/katello.po +52 -349
  177. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  178. data/locale/cs/katello.po +54 -350
  179. data/locale/cs_CZ/LC_MESSAGES/katello.mo +0 -0
  180. data/locale/cs_CZ/katello.po +54 -351
  181. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  182. data/locale/de/katello.po +56 -353
  183. data/locale/de_AT/LC_MESSAGES/katello.mo +0 -0
  184. data/locale/de_AT/katello.po +52 -349
  185. data/locale/de_DE/LC_MESSAGES/katello.mo +0 -0
  186. data/locale/de_DE/katello.po +52 -349
  187. data/locale/el/LC_MESSAGES/katello.mo +0 -0
  188. data/locale/el/katello.po +55 -352
  189. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  190. data/locale/en/katello.po +54 -350
  191. data/locale/en_GB/LC_MESSAGES/katello.mo +0 -0
  192. data/locale/en_GB/katello.po +53 -350
  193. data/locale/en_US/LC_MESSAGES/katello.mo +0 -0
  194. data/locale/en_US/katello.po +52 -349
  195. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  196. data/locale/es/katello.po +56 -353
  197. data/locale/et_EE/LC_MESSAGES/katello.mo +0 -0
  198. data/locale/et_EE/katello.po +52 -349
  199. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  200. data/locale/fr/katello.po +139 -435
  201. data/locale/gl/LC_MESSAGES/katello.mo +0 -0
  202. data/locale/gl/katello.po +52 -349
  203. data/locale/gu/LC_MESSAGES/katello.mo +0 -0
  204. data/locale/gu/katello.po +52 -349
  205. data/locale/he_IL/LC_MESSAGES/katello.mo +0 -0
  206. data/locale/he_IL/katello.po +52 -349
  207. data/locale/hi/LC_MESSAGES/katello.mo +0 -0
  208. data/locale/hi/katello.po +52 -349
  209. data/locale/id/LC_MESSAGES/katello.mo +0 -0
  210. data/locale/id/katello.po +52 -349
  211. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  212. data/locale/it/katello.po +53 -350
  213. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  214. data/locale/ja/katello.po +143 -439
  215. data/locale/ka/LC_MESSAGES/katello.mo +0 -0
  216. data/locale/ka/katello.po +56 -353
  217. data/locale/katello.pot +695 -1152
  218. data/locale/kn/LC_MESSAGES/katello.mo +0 -0
  219. data/locale/kn/katello.po +52 -349
  220. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  221. data/locale/ko/katello.po +136 -432
  222. data/locale/ml_IN/LC_MESSAGES/katello.mo +0 -0
  223. data/locale/ml_IN/katello.po +52 -349
  224. data/locale/mr/LC_MESSAGES/katello.mo +0 -0
  225. data/locale/mr/katello.po +52 -349
  226. data/locale/nl_NL/LC_MESSAGES/katello.mo +0 -0
  227. data/locale/nl_NL/katello.po +52 -349
  228. data/locale/or/LC_MESSAGES/katello.mo +0 -0
  229. data/locale/or/katello.po +52 -349
  230. data/locale/pa/LC_MESSAGES/katello.mo +0 -0
  231. data/locale/pa/katello.po +52 -349
  232. data/locale/pl/LC_MESSAGES/katello.mo +0 -0
  233. data/locale/pl/katello.po +52 -349
  234. data/locale/pl_PL/LC_MESSAGES/katello.mo +0 -0
  235. data/locale/pl_PL/katello.po +52 -349
  236. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  237. data/locale/pt/katello.po +52 -349
  238. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  239. data/locale/pt_BR/katello.po +56 -353
  240. data/locale/ro/LC_MESSAGES/katello.mo +0 -0
  241. data/locale/ro/katello.po +52 -349
  242. data/locale/ro_RO/LC_MESSAGES/katello.mo +0 -0
  243. data/locale/ro_RO/katello.po +52 -349
  244. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  245. data/locale/ru/katello.po +54 -351
  246. data/locale/sl/LC_MESSAGES/katello.mo +0 -0
  247. data/locale/sl/katello.po +52 -349
  248. data/locale/sv_SE/LC_MESSAGES/katello.mo +0 -0
  249. data/locale/sv_SE/katello.po +52 -349
  250. data/locale/ta/LC_MESSAGES/katello.mo +0 -0
  251. data/locale/ta/katello.po +52 -349
  252. data/locale/ta_IN/LC_MESSAGES/katello.mo +0 -0
  253. data/locale/ta_IN/katello.po +52 -349
  254. data/locale/te/LC_MESSAGES/katello.mo +0 -0
  255. data/locale/te/katello.po +52 -349
  256. data/locale/tr/LC_MESSAGES/katello.mo +0 -0
  257. data/locale/tr/katello.po +52 -349
  258. data/locale/vi/LC_MESSAGES/katello.mo +0 -0
  259. data/locale/vi/katello.po +52 -349
  260. data/locale/vi_VN/LC_MESSAGES/katello.mo +0 -0
  261. data/locale/vi_VN/katello.po +52 -349
  262. data/locale/zh/LC_MESSAGES/katello.mo +0 -0
  263. data/locale/zh/katello.po +52 -349
  264. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  265. data/locale/zh_CN/katello.po +136 -432
  266. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  267. data/locale/zh_TW/katello.po +54 -351
  268. data/webpack/components/Content/Details/__tests__/__snapshots__/ContentDetails.test.js.snap +2 -2
  269. data/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeEditModal.js +0 -2
  270. data/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/__tests__/SystemPurposeEditModal.test.js +0 -2
  271. data/webpack/components/extensions/Hosts/ActionsBar/index.js +1 -0
  272. data/webpack/components/extensions/Hosts/BulkActions/BulkActionsConstants.js +7 -0
  273. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/BulkChangeHostCollectionsModal.js +388 -0
  274. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/__tests__/BulkChangeHostCollectionsModal.test.js +640 -0
  275. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/actions.js +28 -0
  276. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/index.js +71 -0
  277. data/webpack/components/extensions/Hosts/BulkActions/BulkErrataWizard/BulkErrataWizard.js +1 -1
  278. data/webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/02_BulkPackagesTable.js +10 -3
  279. data/webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/BulkPackagesWizard.js +51 -24
  280. data/webpack/components/extensions/Hosts/BulkActions/HostReview.js +7 -0
  281. data/webpack/containers/Application/config.js +11 -1
  282. data/webpack/global_index.js +3 -0
  283. data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/BootedContainerImagesConstants.js +1 -1
  284. data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/BootedContainerImagesPage.js +7 -43
  285. data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/__tests__/bootedContainerImagesPage.test.js +1 -1
  286. data/webpack/scenes/ContainerImages/ContainerImagesPage.js +86 -0
  287. data/webpack/scenes/ContainerImages/LabelsAnnotationsModal.js +105 -0
  288. data/webpack/scenes/ContainerImages/Synced/Details/ManifestDetails.js +218 -0
  289. data/webpack/scenes/ContainerImages/Synced/Details/ManifestDetailsActions.js +15 -0
  290. data/webpack/scenes/ContainerImages/Synced/Details/ManifestDetailsSelectors.js +16 -0
  291. data/webpack/scenes/ContainerImages/Synced/Details/__tests__/ManifestDetails.test.js +395 -0
  292. data/webpack/scenes/ContainerImages/Synced/Details/__tests__/manifestDetails.fixtures.json +43 -0
  293. data/webpack/scenes/ContainerImages/Synced/Details/__tests__/manifestList.fixtures.json +58 -0
  294. data/webpack/scenes/ContainerImages/Synced/Details/index.js +4 -0
  295. data/webpack/scenes/ContainerImages/Synced/SyncedContainerImagesPage.js +359 -0
  296. data/webpack/scenes/ContainerImages/Synced/SyncedContainerImagesPage.scss +21 -0
  297. data/webpack/scenes/ContainerImages/Synced/__tests__/LabelsAnnotationsModal.test.js +69 -0
  298. data/webpack/scenes/ContainerImages/Synced/__tests__/SyncedContainerImagesPage.test.js +335 -0
  299. data/webpack/scenes/ContainerImages/Synced/__tests__/syncedContainerImages.fixtures.json +105 -0
  300. data/webpack/scenes/ContainerImages/TableEmptyState.js +67 -0
  301. data/webpack/scenes/ContainerImages/containerImagesHelpers.js +48 -0
  302. data/webpack/scenes/ContainerImages/index.js +4 -0
  303. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +29 -3
  304. data/webpack/scenes/ContentViews/Create/__tests__/contentViewCreateResult.fixtures.json +1 -0
  305. data/webpack/scenes/ContentViews/Create/__tests__/createContentView.test.js +45 -1
  306. data/webpack/scenes/ContentViews/Delete/__tests__/affectedHosts.fixtures.json +0 -1
  307. data/webpack/scenes/ContentViews/Details/ContentViewInfo.js +59 -1
  308. data/webpack/scenes/ContentViews/Details/Filters/CVDebFilterContent.js +1 -0
  309. data/webpack/scenes/ContentViews/Details/Filters/Rules/DebPackage/AddEditDebPackageRuleModal.js +164 -24
  310. data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVDebFilterContent.test.js +268 -0
  311. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvDebFilterDetail.fixtures.json +95 -0
  312. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvDebFilterRules.fixtures.json +31 -0
  313. data/webpack/scenes/ContentViews/Details/Filters/__tests__/emptyCVDebFilterRules.fixtures.json +10 -0
  314. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/hosts.fixtures.json +0 -1
  315. data/webpack/scenes/ContentViews/Details/Versions/Delete/__tests__/cvAffectedHosts.fixture.json +0 -1
  316. data/webpack/scenes/ContentViews/Details/__tests__/contentViewRollingDetail.test.js +15 -0
  317. data/webpack/scenes/ContentViews/Details/__tests__/contentViewRollingDetails.fixtures.json +1 -0
  318. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +9 -0
  319. data/webpack/scenes/FlatpakRemotes/CreateEdit/CreateFlatpakRemoteModal.js +5 -3
  320. data/webpack/scenes/FlatpakRemotes/CreateEdit/EditFlatpakRemotesModal.js +1 -1
  321. data/webpack/scenes/FlatpakRemotes/CreateEdit/FlatpakRemoteform.js +35 -3
  322. data/webpack/scenes/FlatpakRemotes/Details/FlatpakRemoteDetails.js +1 -1
  323. data/webpack/scenes/FlatpakRemotes/Details/RemoteRepositories/RemoteRepositoriesTable.css +3 -0
  324. data/webpack/scenes/FlatpakRemotes/Details/RemoteRepositories/RemoteRepositoriesTable.js +63 -132
  325. data/webpack/scenes/FlatpakRemotes/FlatpakRemotesPage.js +67 -143
  326. data/webpack/scenes/SmartProxy/ExpandableCvDetails.js +10 -2
  327. data/webpack/scenes/SmartProxy/SmartProxyContentActions.js +13 -2
  328. data/webpack/scenes/SmartProxy/SmartProxyContentConstants.js +1 -0
  329. data/webpack/scenes/SmartProxy/SmartProxyExpandableTable.js +8 -2
  330. data/webpack/scenes/SmartProxy/__tests__/SmartProxyContentTest.js +67 -1
  331. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +2 -2
  332. data/webpack/scenes/Subscriptions/SubscriptionConstants.js +0 -2
  333. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsActions.test.js.snap +2 -2
  334. metadata +83 -55
  335. data/app/lib/actions/katello/host/attach_subscriptions.rb +0 -59
  336. data/app/lib/actions/katello/host/auto_attach_subscriptions.rb +0 -22
  337. data/app/lib/actions/katello/host/remove_subscriptions.rb +0 -50
  338. data/app/lib/actions/katello/organization/simple_content_access/disable.rb +0 -25
  339. data/app/lib/actions/katello/organization/simple_content_access/enable.rb +0 -25
  340. data/app/lib/actions/katello/organization/simple_content_access/toggle.rb +0 -42
  341. data/lib/katello/tasks/migrate_structure_content_for_deb.rake +0 -105
  342. data/lib/katello/tasks/upgrades/4.2/remove_checksum_values.rake +0 -17
  343. data/locale/action_names.rb +0 -186
  344. /data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/__tests__/bootedContainerImages.fixtures.js +0 -0
  345. /data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/index.js +0 -0
@@ -0,0 +1,335 @@
1
+ import React from 'react';
2
+ import { renderWithRedux, patientlyWaitFor, act } from 'react-testing-lib-wrapper';
3
+ import { nockInstance, assertNockRequest, mockAutocomplete } from '../../../../test-utils/nockWrapper';
4
+ import SyncedContainerImagesPage from '../SyncedContainerImagesPage';
5
+ import syncedImagesData from './syncedContainerImages.fixtures.json';
6
+
7
+ const syncedImagesUrl = '/katello/api/v2/docker_tags';
8
+ const autocompleteUrl = '/docker_tags/auto_complete_search';
9
+ const autocompleteQuery = {
10
+ organization_id: '1',
11
+ search: '',
12
+ };
13
+
14
+ const buildDockerTag = id => ({
15
+ id,
16
+ name: `tag-${id}`,
17
+ manifest: {
18
+ id: 100 + id,
19
+ digest: `sha256:digest${id}digest${id}digest${id}digest${id}digest${id}digest${id}digest${id}`,
20
+ manifest_type: 'image',
21
+ is_bootable: false,
22
+ is_flatpak: false,
23
+ },
24
+ });
25
+
26
+ const createSyncedImages = (amount) => {
27
+ const response = {
28
+ total: amount,
29
+ subtotal: amount,
30
+ page: 1,
31
+ per_page: 20,
32
+ error: null,
33
+ search: null,
34
+ sort: {
35
+ by: 'name',
36
+ order: 'asc',
37
+ },
38
+ results: [],
39
+ };
40
+
41
+ [...Array(amount).keys()].forEach((_, i) => response.results.push(buildDockerTag(i + 1)));
42
+
43
+ return response;
44
+ };
45
+
46
+ let latestTag;
47
+ let v10Tag;
48
+ let flatpakTag;
49
+ let childManifest1;
50
+ let childManifest2;
51
+
52
+ beforeEach(() => {
53
+ const { results } = syncedImagesData;
54
+ [latestTag, v10Tag, flatpakTag] = results;
55
+ if (latestTag.manifest?.manifests) {
56
+ [childManifest1, childManifest2] = latestTag.manifest.manifests;
57
+ }
58
+ });
59
+
60
+ test('SyncedContainerImagesPage renders correctly', async (done) => {
61
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
62
+ const scope = nockInstance
63
+ .get(syncedImagesUrl)
64
+ .query(true)
65
+ .reply(200, syncedImagesData);
66
+
67
+ const { queryByText } = renderWithRedux(<SyncedContainerImagesPage />);
68
+
69
+ expect(queryByText(latestTag.name)).toBeNull();
70
+
71
+ await patientlyWaitFor(() => {
72
+ expect(queryByText(latestTag.name)).toBeVisible();
73
+ expect(queryByText(v10Tag.name)).toBeVisible();
74
+ expect(queryByText(flatpakTag.name)).toBeVisible();
75
+ });
76
+
77
+ assertNockRequest(autocompleteScope);
78
+ assertNockRequest(scope, done);
79
+ act(done);
80
+ });
81
+
82
+ test('Can expand manifest list and show child manifests', async (done) => {
83
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
84
+ const scope = nockInstance
85
+ .get(syncedImagesUrl)
86
+ .query(true)
87
+ .reply(200, syncedImagesData);
88
+
89
+ const {
90
+ queryByText, queryAllByRole,
91
+ } = renderWithRedux(<SyncedContainerImagesPage />);
92
+
93
+ expect(queryByText(latestTag.name)).toBeNull();
94
+
95
+ await patientlyWaitFor(() => {
96
+ expect(queryByText(latestTag.name)).toBeVisible();
97
+
98
+ // Find and click the expand button for the manifest list
99
+ const expandButtons = queryAllByRole('button');
100
+ const expandButton = expandButtons.find(btn =>
101
+ btn.getAttribute('aria-labelledby')?.includes(`synced-containers-expander-${latestTag.id}`));
102
+
103
+ if (expandButton) {
104
+ expandButton.click();
105
+ }
106
+ });
107
+
108
+ await patientlyWaitFor(() => {
109
+ // Check that child manifest digests appear
110
+ expect(queryByText(childManifest1.digest)).toBeVisible();
111
+ expect(queryByText(childManifest2.digest)).toBeVisible();
112
+ });
113
+
114
+ assertNockRequest(autocompleteScope);
115
+ assertNockRequest(scope, done);
116
+ act(done);
117
+ });
118
+
119
+ test('Displays correct manifest types', async (done) => {
120
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
121
+ const scope = nockInstance
122
+ .get(syncedImagesUrl)
123
+ .query(true)
124
+ .reply(200, syncedImagesData);
125
+
126
+ const { queryByText, queryAllByText } = renderWithRedux(<SyncedContainerImagesPage />);
127
+
128
+ await patientlyWaitFor(() => {
129
+ // Check that manifest types are displayed correctly
130
+ expect(queryByText('List')).toBeVisible(); // Capitalized 'list'
131
+ expect(queryAllByText('Bootable').length).toBeGreaterThan(0); // For bootable images
132
+ expect(queryByText('Flatpak')).toBeVisible(); // For flatpak images
133
+ });
134
+
135
+ assertNockRequest(autocompleteScope);
136
+ assertNockRequest(scope, done);
137
+ act(done);
138
+ });
139
+
140
+ test('Displays manifest digests correctly', async (done) => {
141
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
142
+ const scope = nockInstance
143
+ .get(syncedImagesUrl)
144
+ .query(true)
145
+ .reply(200, syncedImagesData);
146
+
147
+ const { queryByText } = renderWithRedux(<SyncedContainerImagesPage />);
148
+
149
+ await patientlyWaitFor(() => {
150
+ expect(queryByText(latestTag.manifest.digest)).toBeVisible();
151
+ expect(queryByText(v10Tag.manifest.digest)).toBeVisible();
152
+ });
153
+
154
+ assertNockRequest(autocompleteScope);
155
+ assertNockRequest(scope, done);
156
+ act(done);
157
+ });
158
+
159
+ test('Child manifests remain hidden when not expanded', async (done) => {
160
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
161
+ const scope = nockInstance
162
+ .get(syncedImagesUrl)
163
+ .query(true)
164
+ .reply(200, syncedImagesData);
165
+
166
+ const { queryByText } = renderWithRedux(<SyncedContainerImagesPage />);
167
+
168
+ await patientlyWaitFor(() => {
169
+ expect(queryByText(latestTag.name)).toBeVisible();
170
+ // Child manifests should not be visible initially
171
+ expect(queryByText(childManifest1.digest)?.closest('td')).not.toBeVisible();
172
+ expect(queryByText(childManifest2.digest)?.closest('td')).not.toBeVisible();
173
+ });
174
+
175
+ assertNockRequest(autocompleteScope);
176
+ assertNockRequest(scope, done);
177
+ act(done);
178
+ });
179
+
180
+ test('Can handle no container images being present', async (done) => {
181
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
182
+ const noResults = {
183
+ total: 0,
184
+ subtotal: 0,
185
+ page: 1,
186
+ per_page: 20,
187
+ results: [],
188
+ };
189
+ const scope = nockInstance
190
+ .get(syncedImagesUrl)
191
+ .query(true)
192
+ .reply(200, noResults);
193
+
194
+ const { queryByText } = renderWithRedux(<SyncedContainerImagesPage />);
195
+
196
+ expect(queryByText(latestTag.name)).toBeNull();
197
+ await patientlyWaitFor(() => expect(queryByText('No Results')).toBeInTheDocument());
198
+
199
+ assertNockRequest(autocompleteScope);
200
+ assertNockRequest(scope, done);
201
+ });
202
+
203
+ test('Displays loading state while fetching data', async (done) => {
204
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
205
+ const scope = nockInstance
206
+ .get(syncedImagesUrl)
207
+ .query(true)
208
+ .reply(200, syncedImagesData);
209
+
210
+ const { queryByText } = renderWithRedux(<SyncedContainerImagesPage />);
211
+
212
+ // Check for loading state
213
+ expect(queryByText('Loading...')).toBeInTheDocument();
214
+
215
+ await patientlyWaitFor(() => {
216
+ expect(queryByText(latestTag.name)).toBeVisible();
217
+ expect(queryByText('Loading...')).not.toBeInTheDocument();
218
+ });
219
+
220
+ assertNockRequest(autocompleteScope);
221
+ assertNockRequest(scope, done);
222
+ act(done);
223
+ });
224
+
225
+ test('Can handle pagination', async (done) => {
226
+ const largeSyncedData = createSyncedImages(100);
227
+ const { results } = largeSyncedData;
228
+ const firstPage = { ...largeSyncedData, results: results.slice(0, 20) };
229
+ const secondPage = { ...largeSyncedData, page: 2, results: results.slice(20, 40) };
230
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
231
+
232
+ // Match first page API request
233
+ const firstPageScope = nockInstance
234
+ .get(syncedImagesUrl)
235
+ .query(true)
236
+ .reply(200, firstPage);
237
+
238
+ // Match second page API request
239
+ const secondPageScope = nockInstance
240
+ .get(syncedImagesUrl)
241
+ .query(actualQueryObject => (parseInt(actualQueryObject.page, 10) === 2))
242
+ .reply(200, secondPage);
243
+
244
+ const { queryByText, getAllByLabelText } = renderWithRedux(<SyncedContainerImagesPage />);
245
+
246
+ // Wait for first paginated page to load
247
+ await patientlyWaitFor(() => {
248
+ expect(queryByText(results[0].name)).toBeInTheDocument();
249
+ expect(queryByText(results[19].name)).toBeInTheDocument();
250
+ expect(queryByText(results[21].name)).not.toBeInTheDocument();
251
+ });
252
+
253
+ // Navigate to second page
254
+ const [top, bottom] = getAllByLabelText('Go to next page');
255
+ expect(top).toBeInTheDocument();
256
+ expect(bottom).toBeInTheDocument();
257
+ bottom.click();
258
+
259
+ // Wait for second paginated page to load
260
+ await patientlyWaitFor(() => {
261
+ expect(queryByText(results[20].name)).toBeInTheDocument();
262
+ expect(queryByText(results[39].name)).toBeInTheDocument();
263
+ expect(queryByText(results[41].name)).not.toBeInTheDocument();
264
+ });
265
+
266
+ assertNockRequest(autocompleteScope);
267
+ assertNockRequest(firstPageScope);
268
+ assertNockRequest(secondPageScope, done);
269
+ act(done);
270
+ });
271
+
272
+ test('Handles tags with manifest_schema1', async (done) => {
273
+ const dataWithSchema1 = {
274
+ ...syncedImagesData,
275
+ results: [
276
+ {
277
+ id: 10,
278
+ name: 'schema1-tag',
279
+ manifest_schema1: {
280
+ id: 110,
281
+ digest: 'sha256:schema1digestschema1digestschema1digestschema1digestschema1digest',
282
+ manifest_type: 'image',
283
+ is_bootable: false,
284
+ is_flatpak: false,
285
+ },
286
+ },
287
+ ],
288
+ };
289
+
290
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
291
+ const scope = nockInstance
292
+ .get(syncedImagesUrl)
293
+ .query(true)
294
+ .reply(200, dataWithSchema1);
295
+
296
+ const { queryByText } = renderWithRedux(<SyncedContainerImagesPage />);
297
+
298
+ await patientlyWaitFor(() => {
299
+ expect(queryByText('schema1-tag')).toBeVisible();
300
+ expect(queryByText('sha256:schema1digestschema1digestschema1digestschema1digestschema1digest')).toBeVisible();
301
+ });
302
+
303
+ assertNockRequest(autocompleteScope);
304
+ assertNockRequest(scope, done);
305
+ act(done);
306
+ });
307
+
308
+ test('Shows N/A for missing manifest data', async (done) => {
309
+ const dataWithMissingManifest = {
310
+ ...syncedImagesData,
311
+ results: [
312
+ {
313
+ id: 11,
314
+ name: 'no-manifest-tag',
315
+ },
316
+ ],
317
+ };
318
+
319
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl, autocompleteQuery);
320
+ const scope = nockInstance
321
+ .get(syncedImagesUrl)
322
+ .query(true)
323
+ .reply(200, dataWithMissingManifest);
324
+
325
+ const { queryByText, queryAllByText } = renderWithRedux(<SyncedContainerImagesPage />);
326
+
327
+ await patientlyWaitFor(() => {
328
+ expect(queryByText('no-manifest-tag')).toBeVisible();
329
+ expect(queryAllByText('N/A').length).toBeGreaterThan(0);
330
+ });
331
+
332
+ assertNockRequest(autocompleteScope);
333
+ assertNockRequest(scope, done);
334
+ act(done);
335
+ });
@@ -0,0 +1,105 @@
1
+ {
2
+ "total": 3,
3
+ "subtotal": 3,
4
+ "page": 1,
5
+ "per_page": 20,
6
+ "error": null,
7
+ "search": null,
8
+ "sort": {
9
+ "by": "name",
10
+ "order": "asc"
11
+ },
12
+ "results": [
13
+ {
14
+ "id": 1,
15
+ "name": "latest",
16
+ "product": {
17
+ "id": 1,
18
+ "name": "Red Hat Enterprise Linux"
19
+ },
20
+ "repositories": [
21
+ {
22
+ "id": 10,
23
+ "name": "RHEL Base Images",
24
+ "full_path": "Library/RHEL Base Images",
25
+ "library_instance": true,
26
+ "product_id": 1,
27
+ "product_name": "Red Hat Enterprise Linux"
28
+ }
29
+ ],
30
+ "manifest": {
31
+ "id": 101,
32
+ "digest": "sha256:abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234",
33
+ "manifest_type": "list",
34
+ "is_bootable": false,
35
+ "is_flatpak": false,
36
+ "manifests": [
37
+ {
38
+ "id": 102,
39
+ "digest": "sha256:1111aaaa2222bbbb3333cccc4444dddd5555eeee6666ffff7777gggg8888hhhh",
40
+ "manifest_type": "image",
41
+ "is_bootable": false,
42
+ "is_flatpak": false
43
+ },
44
+ {
45
+ "id": 103,
46
+ "digest": "sha256:2222bbbb3333cccc4444dddd5555eeee6666ffff7777gggg8888hhhh9999iiii",
47
+ "manifest_type": "image",
48
+ "is_bootable": true,
49
+ "is_flatpak": false
50
+ }
51
+ ]
52
+ }
53
+ },
54
+ {
55
+ "id": 2,
56
+ "name": "v1.0",
57
+ "product": {
58
+ "id": 2,
59
+ "name": "Container Tools"
60
+ },
61
+ "repositories": [
62
+ {
63
+ "id": 20,
64
+ "name": "Container Images",
65
+ "full_path": "Library/Container Images",
66
+ "library_instance": true,
67
+ "product_id": 2,
68
+ "product_name": "Container Tools"
69
+ }
70
+ ],
71
+ "manifest": {
72
+ "id": 104,
73
+ "digest": "sha256:efgh5678901234efgh5678901234efgh5678901234efgh5678901234efgh5678",
74
+ "manifest_type": "image",
75
+ "is_bootable": true,
76
+ "is_flatpak": false
77
+ }
78
+ },
79
+ {
80
+ "id": 3,
81
+ "name": "flatpak-app",
82
+ "product": {
83
+ "id": 3,
84
+ "name": "Flatpak Applications"
85
+ },
86
+ "repositories": [
87
+ {
88
+ "id": 30,
89
+ "name": "Flatpak Repository",
90
+ "full_path": "Library/Flatpak Repository",
91
+ "library_instance": true,
92
+ "product_id": 3,
93
+ "product_name": "Flatpak Applications"
94
+ }
95
+ ],
96
+ "manifest_schema2": {
97
+ "id": 105,
98
+ "digest": "sha256:ijkl9012345678ijkl9012345678ijkl9012345678ijkl9012345678ijkl9012",
99
+ "manifest_type": "image",
100
+ "is_bootable": false,
101
+ "is_flatpak": true
102
+ }
103
+ }
104
+ ]
105
+ }
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Tbody, Tr, Td } from '@patternfly/react-table';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+ import EmptyPage from 'foremanReact/routes/common/EmptyPage';
6
+ import { STATUS } from './containerImagesHelpers';
7
+
8
+ const TableEmptyState = ({
9
+ status, results, errorMessage, colSpan,
10
+ }) => {
11
+ if (results.length > 0 && !errorMessage) {
12
+ return null;
13
+ }
14
+
15
+ return (
16
+ <Tbody>
17
+ {status === STATUS.PENDING && results.length === 0 && (
18
+ <Tr ouiaId="table-loading">
19
+ <Td colSpan={colSpan}>
20
+ <EmptyPage
21
+ message={{
22
+ type: 'loading',
23
+ text: __('Loading...'),
24
+ }}
25
+ />
26
+ </Td>
27
+ </Tr>
28
+ )}
29
+ {status !== STATUS.PENDING &&
30
+ results.length === 0 &&
31
+ !errorMessage && (
32
+ <Tr ouiaId="table-empty">
33
+ <Td colSpan={colSpan}>
34
+ <EmptyPage
35
+ message={{
36
+ type: 'empty',
37
+ }}
38
+ />
39
+ </Td>
40
+ </Tr>
41
+ )}
42
+ {errorMessage && (
43
+ <Tr ouiaId="table-error">
44
+ <Td colSpan={colSpan}>
45
+ <EmptyPage message={{ type: 'error', text: errorMessage }} />
46
+ </Td>
47
+ </Tr>
48
+ )}
49
+ </Tbody>
50
+ );
51
+ };
52
+
53
+ TableEmptyState.propTypes = {
54
+ status: PropTypes.string,
55
+ results: PropTypes.arrayOf(PropTypes.shape({})),
56
+ errorMessage: PropTypes.string,
57
+ colSpan: PropTypes.number,
58
+ };
59
+
60
+ TableEmptyState.defaultProps = {
61
+ status: STATUS.PENDING,
62
+ results: [],
63
+ errorMessage: null,
64
+ colSpan: 100,
65
+ };
66
+
67
+ export default TableEmptyState;
@@ -0,0 +1,48 @@
1
+ import { translate as __ } from 'foremanReact/common/I18n';
2
+ import { capitalize } from '../../utils/helpers';
3
+
4
+ export const STATUS = {
5
+ PENDING: 'PENDING',
6
+ RESOLVED: 'RESOLVED',
7
+ ERROR: 'ERROR',
8
+ };
9
+
10
+ export const getManifest = (data) => {
11
+ if (!data) return null;
12
+ return data.manifest || data.manifest_schema2 || data.manifest_schema1;
13
+ };
14
+
15
+ export const formatManifestType = (manifest) => {
16
+ if (!manifest || !manifest.manifest_type) return 'N/A';
17
+
18
+ if (manifest.manifest_type === 'image') {
19
+ if (manifest.is_bootable) {
20
+ return __('Bootable');
21
+ }
22
+ if (manifest.is_flatpak) {
23
+ return __('Flatpak');
24
+ }
25
+ }
26
+
27
+ return capitalize(manifest.manifest_type);
28
+ };
29
+
30
+ export const getShortDigest = (digest) => {
31
+ if (!digest) return 'N/A';
32
+ const parts = digest.split(':');
33
+ if (parts.length === 2) {
34
+ return `${parts[0]}:${parts[1].substring(0, 12)}`;
35
+ }
36
+ return digest.substring(0, 19);
37
+ };
38
+
39
+ export const hasLabelsOrAnnotations = (manifest) => {
40
+ if (!manifest) return false;
41
+ return (Object.keys(manifest.labels || {}).length > 0) ||
42
+ (Object.keys(manifest.annotations || {}).length > 0);
43
+ };
44
+
45
+ export const anyChildHasLabelsOrAnnotations = (manifest) => {
46
+ if (!manifest?.manifests) return false;
47
+ return manifest.manifests.some(child => hasLabelsOrAnnotations(child));
48
+ };
@@ -0,0 +1,4 @@
1
+ import { withRouter } from 'react-router-dom';
2
+ import ContainerImagesPage from './ContainerImagesPage';
3
+
4
+ export default withRouter(ContainerImagesPage);
@@ -27,6 +27,8 @@ import {
27
27
  } from './ContentViewCreateSelectors';
28
28
  import { LabelDependencies, LabelAutoPublish } from './ContentViewFormComponents';
29
29
  import ContentViewIcon from '../components/ContentViewIcon';
30
+ import getEnvironmentPaths from '../components/EnvironmentPaths/EnvironmentPathActions';
31
+ import EnvironmentPaths from '../components/EnvironmentPaths/EnvironmentPaths';
30
32
  import './CreateContentViewForm.scss';
31
33
 
32
34
  export const contentViewDescriptions = {
@@ -47,7 +49,7 @@ const CreateContentViewForm = ({ setModalOpen }) => {
47
49
  const [dependencies, setDependencies] = useState(false);
48
50
  const [redirect, setRedirect] = useState(false);
49
51
  const [saving, setSaving] = useState(false);
50
-
52
+ const [selectedEnvs, setSelectedEnvs] = useState([]);
51
53
  const [labelValidated, setLabelValidated] = useState('default');
52
54
  const handleLabelChange = (newLabel, _event) => {
53
55
  setLabel(newLabel);
@@ -74,9 +76,10 @@ const CreateContentViewForm = ({ setModalOpen }) => {
74
76
  }
75
77
  }, [response, status, error, saving]);
76
78
 
79
+ const checkedEnvIds = selectedEnvs?.map(env => env.id) ?? [];
77
80
  const onSave = () => {
78
81
  setSaving(true);
79
- dispatch(createContentView({
82
+ let params = {
80
83
  name,
81
84
  label,
82
85
  description,
@@ -84,9 +87,22 @@ const CreateContentViewForm = ({ setModalOpen }) => {
84
87
  rolling,
85
88
  solve_dependencies: (dependencies && !(rolling || composite)),
86
89
  auto_publish: (autoPublish && composite),
87
- }));
90
+ };
91
+ if (rolling) {
92
+ params = { ...params, environment_ids: checkedEnvIds };
93
+ }
94
+ dispatch(createContentView(params));
88
95
  };
89
96
 
97
+ useEffect(
98
+ () => {
99
+ if (rolling) {
100
+ dispatch(getEnvironmentPaths());
101
+ }
102
+ },
103
+ [rolling, dispatch],
104
+ );
105
+
90
106
  useEffect(() => {
91
107
  setLabel(name.replace(/[^A-Za-z0-9_-]/g, '_'));
92
108
  }, [name]);
@@ -230,6 +246,16 @@ const CreateContentViewForm = ({ setModalOpen }) => {
230
246
  />
231
247
  </FormGroup>
232
248
  )}
249
+ {rolling && (
250
+ <FormGroup isInline fieldId="lifecycleEnvironments" label={__('Lifecycle Environments')}>
251
+ <EnvironmentPaths
252
+ headerText=""
253
+ publishing={false}
254
+ userCheckedItems={selectedEnvs}
255
+ setUserCheckedItems={setSelectedEnvs}
256
+ />
257
+ </FormGroup>
258
+ )}
233
259
  <ActionGroup>
234
260
  <Button
235
261
  ouiaId="create-content-view-form-submit"
@@ -8,6 +8,7 @@
8
8
  "latest_version": null,
9
9
  "auto_publish": false,
10
10
  "solve_dependencies": false,
11
+ "environment_ids": [],
11
12
  "import_only": false,
12
13
  "repository_ids": [],
13
14
  "id": 34,