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,218 @@
1
+ import React, { useEffect } from 'react';
2
+ import { useParams, useHistory, useLocation } from 'react-router-dom';
3
+ import { useSelector, useDispatch, shallowEqual } from 'react-redux';
4
+ import {
5
+ Breadcrumb,
6
+ BreadcrumbItem,
7
+ Title,
8
+ PageSection,
9
+ Grid,
10
+ GridItem,
11
+ TextContent,
12
+ Text,
13
+ TextVariants,
14
+ ClipboardCopy,
15
+ Label,
16
+ Flex,
17
+ FlexItem,
18
+ } from '@patternfly/react-core';
19
+ import { translate as __ } from 'foremanReact/common/I18n';
20
+ import { STATUS } from 'foremanReact/constants';
21
+ import Loading from 'foremanReact/components/Loading';
22
+ import LongDateTime from 'foremanReact/components/common/dates/LongDateTime';
23
+ import EmptyStateMessage from '../../../../components/Table/EmptyStateMessage';
24
+ import {
25
+ getManifest,
26
+ getShortDigest,
27
+ formatManifestType,
28
+ } from '../../containerImagesHelpers';
29
+ import getDockerTagDetails from './ManifestDetailsActions';
30
+ import {
31
+ selectDockerTagDetails,
32
+ selectDockerTagDetailStatus,
33
+ selectDockerTagDetailError,
34
+ } from './ManifestDetailsSelectors';
35
+
36
+ const ManifestDetails = () => {
37
+ const { id } = useParams();
38
+ const tagId = Number(id);
39
+ const history = useHistory();
40
+ const location = useLocation();
41
+ const dispatch = useDispatch();
42
+
43
+ const searchParams = new URLSearchParams(location.search);
44
+ const manifestId = searchParams.get('manifest');
45
+
46
+ useEffect(() => {
47
+ dispatch(getDockerTagDetails(tagId));
48
+ }, [dispatch, tagId]);
49
+
50
+ const manifestData = useSelector(state =>
51
+ selectDockerTagDetails(state, tagId), shallowEqual) || {};
52
+ const status = useSelector(state =>
53
+ selectDockerTagDetailStatus(state, tagId));
54
+ const error = useSelector(state =>
55
+ selectDockerTagDetailError(state, tagId));
56
+
57
+ const getDisplayManifest = () => {
58
+ const parentManifest = getManifest(manifestData);
59
+
60
+ if (!manifestId || !parentManifest) {
61
+ return parentManifest;
62
+ }
63
+
64
+ if (parentManifest.manifest_type === 'list' && parentManifest.manifests) {
65
+ const childManifestId = parseInt(manifestId, 10);
66
+ return parentManifest.manifests.find(m => m.id === childManifestId);
67
+ }
68
+
69
+ return parentManifest;
70
+ };
71
+
72
+ if (status === STATUS.PENDING) {
73
+ return <Loading />;
74
+ }
75
+
76
+ if (status === STATUS.ERROR) {
77
+ return <EmptyStateMessage error={error} />;
78
+ }
79
+
80
+ const manifest = getDisplayManifest();
81
+ const digest = manifest?.digest || 'N/A';
82
+ const shortDigest = getShortDigest(digest);
83
+ const manifestType = formatManifestType(manifest);
84
+
85
+ // Filter to show only library repositories
86
+ const libraryRepositories = manifestData.repositories?.filter(repo =>
87
+ repo.library_instance) || [];
88
+
89
+ const labels = manifest?.labels || {};
90
+ const labelKeys = Object.keys(labels);
91
+
92
+ return (
93
+ <PageSection variant="light">
94
+ <Grid hasGutter span={12}>
95
+ <GridItem span={12}>
96
+ <Breadcrumb ouiaId="manifest-details-breadcrumb">
97
+ <BreadcrumbItem
98
+ to="/labs/container_images"
99
+ onClick={(e) => {
100
+ e.preventDefault();
101
+ history.push('/labs/container_images');
102
+ }}
103
+ >
104
+ {__('Container images')}
105
+ </BreadcrumbItem>
106
+ <BreadcrumbItem isActive>{shortDigest}</BreadcrumbItem>
107
+ </Breadcrumb>
108
+ </GridItem>
109
+
110
+ <GridItem span={12}>
111
+ <Title headingLevel="h1" size="2xl" ouiaId="manifest-details-title">
112
+ {shortDigest}
113
+ </Title>
114
+ </GridItem>
115
+
116
+ <GridItem span={12}>
117
+ <Grid hasGutter>
118
+ <GridItem span={6}>
119
+ <TextContent>
120
+ <Text component={TextVariants.h6} ouiaId="manifest-name-label">{__('Name')}</Text>
121
+ <Text ouiaId="manifest-name-value">{manifestData.name || 'N/A'}</Text>
122
+ </TextContent>
123
+ </GridItem>
124
+
125
+ <GridItem span={6}>
126
+ <TextContent>
127
+ <Text component={TextVariants.h6} ouiaId="manifest-creation-label">{__('Creation')}</Text>
128
+ {manifest?.created_at ? (
129
+ <LongDateTime date={manifest.created_at} showRelativeTimeTooltip />
130
+ ) : (
131
+ <Text ouiaId="manifest-creation-value">N/A</Text>
132
+ )}
133
+ </TextContent>
134
+ </GridItem>
135
+
136
+ <GridItem span={6}>
137
+ <TextContent>
138
+ <Text component={TextVariants.h6} ouiaId="manifest-repository-label">{__('Repositories')}</Text>
139
+ <Text ouiaId="manifest-repository-value">
140
+ {libraryRepositories.length === 0 ? (
141
+ 'N/A'
142
+ ) : (
143
+ libraryRepositories.map((repo, index) => (
144
+ <React.Fragment key={repo.id}>
145
+ {index > 0 && ', '}
146
+ <a
147
+ href={`/products/${repo.product_id}/repositories/${repo.id}`}
148
+ target="_blank"
149
+ rel="noopener noreferrer"
150
+ >
151
+ {repo.name}
152
+ </a>
153
+ </React.Fragment>
154
+ ))
155
+ )}
156
+ </Text>
157
+ </TextContent>
158
+ </GridItem>
159
+
160
+ <GridItem span={6}>
161
+ <TextContent>
162
+ <Text component={TextVariants.h6} ouiaId="manifest-modified-label">{__('Modified')}</Text>
163
+ {manifest?.updated_at ? (
164
+ <LongDateTime date={manifest.updated_at} showRelativeTimeTooltip />
165
+ ) : (
166
+ <Text ouiaId="manifest-modified-value">N/A</Text>
167
+ )}
168
+ </TextContent>
169
+ </GridItem>
170
+
171
+ <GridItem span={6}>
172
+ <TextContent>
173
+ <Text component={TextVariants.h6} ouiaId="manifest-digest-label">{__('Digest')}</Text>
174
+ {digest !== 'N/A' ? (
175
+ <ClipboardCopy variant="inline-compact" clickTip="Copied">
176
+ {digest}
177
+ </ClipboardCopy>
178
+ ) : (
179
+ <Text ouiaId="manifest-digest-value">N/A</Text>
180
+ )}
181
+ </TextContent>
182
+ </GridItem>
183
+
184
+ <GridItem span={6}>
185
+ <TextContent>
186
+ <Text component={TextVariants.h6} ouiaId="manifest-type-label">{__('Type')}</Text>
187
+ <Text ouiaId="manifest-type-value">{manifestType}</Text>
188
+ </TextContent>
189
+ </GridItem>
190
+
191
+ <GridItem span={6} />
192
+
193
+ <GridItem span={6}>
194
+ <TextContent>
195
+ <Text component={TextVariants.h6} ouiaId="manifest-labels-label">{__('Labels')}</Text>
196
+ {labelKeys.length === 0 ? (
197
+ <div>{__('No labels')}</div>
198
+ ) : (
199
+ <Flex spaceItems={{ default: 'spaceItemsSm' }} flexWrap={{ default: 'wrap' }}>
200
+ {labelKeys.map(key => (
201
+ <FlexItem key={key}>
202
+ <Label color="grey">
203
+ {key} = {labels[key]}
204
+ </Label>
205
+ </FlexItem>
206
+ ))}
207
+ </Flex>
208
+ )}
209
+ </TextContent>
210
+ </GridItem>
211
+ </Grid>
212
+ </GridItem>
213
+ </Grid>
214
+ </PageSection>
215
+ );
216
+ };
217
+
218
+ export default ManifestDetails;
@@ -0,0 +1,15 @@
1
+ import { API_OPERATIONS, get } from 'foremanReact/redux/API';
2
+ import api, { orgId } from '../../../../services/api';
3
+
4
+ export const DOCKER_TAG_DETAILS_KEY = 'DOCKER_TAG_DETAILS';
5
+
6
+ export const dockerTagDetailsKey = id => `${DOCKER_TAG_DETAILS_KEY}_${id}`;
7
+
8
+ const getDockerTagDetails = (id, extraParams = {}) => get({
9
+ type: API_OPERATIONS.GET,
10
+ key: dockerTagDetailsKey(id),
11
+ params: { organization_id: orgId(), ...extraParams },
12
+ url: api.getApiUrl(`/docker_tags/${id}`),
13
+ });
14
+
15
+ export default getDockerTagDetails;
@@ -0,0 +1,16 @@
1
+ import { STATUS } from 'foremanReact/constants';
2
+ import {
3
+ selectAPIResponse,
4
+ selectAPIStatus,
5
+ selectAPIError,
6
+ } from 'foremanReact/redux/API/APISelectors';
7
+ import { dockerTagDetailsKey } from './ManifestDetailsActions';
8
+
9
+ export const selectDockerTagDetails = (state, id) =>
10
+ selectAPIResponse(state, dockerTagDetailsKey(id)) || {};
11
+
12
+ export const selectDockerTagDetailStatus =
13
+ (state, id) => selectAPIStatus(state, dockerTagDetailsKey(id)) || STATUS.PENDING;
14
+
15
+ export const selectDockerTagDetailError =
16
+ (state, id) => selectAPIError(state, dockerTagDetailsKey(id));
@@ -0,0 +1,395 @@
1
+ import React from 'react';
2
+ import { Route } from 'react-router-dom';
3
+ import { renderWithRedux, patientlyWaitFor } from 'react-testing-lib-wrapper';
4
+ import { nockInstance, assertNockRequest } from '../../../../../test-utils/nockWrapper';
5
+ import api from '../../../../../services/api';
6
+ import ManifestDetails from '../ManifestDetails';
7
+ import { DOCKER_TAG_DETAILS_KEY } from '../ManifestDetailsActions';
8
+ import manifestDetailsData from './manifestDetails.fixtures.json';
9
+ import manifestListData from './manifestList.fixtures.json';
10
+
11
+ const withManifestRoute = component => <Route path="/labs/container_images/:id([0-9]+)">{component}</Route>;
12
+
13
+ const renderOptions = (tagId = 2) => ({
14
+ apiNamespace: `${DOCKER_TAG_DETAILS_KEY}_${tagId}`,
15
+ routerParams: {
16
+ initialEntries: [{ pathname: `/labs/container_images/${tagId}` }],
17
+ initialIndex: 1,
18
+ },
19
+ });
20
+
21
+ const manifestDetailsPath = id => api.getApiUrl(`/docker_tags/${id}`);
22
+
23
+ describe('ManifestDetails', () => {
24
+ test('Can call API and display manifest details on load', async () => {
25
+ const scope = nockInstance
26
+ .get(manifestDetailsPath(2))
27
+ .query(true)
28
+ .reply(200, manifestDetailsData);
29
+
30
+ const { getByText, getAllByText } = renderWithRedux(
31
+ withManifestRoute(<ManifestDetails />),
32
+ renderOptions(2),
33
+ );
34
+
35
+ await patientlyWaitFor(() => {
36
+ expect(getByText('v1.0')).toBeInTheDocument();
37
+ });
38
+
39
+ // Check field labels
40
+ expect(getByText('Name')).toBeInTheDocument();
41
+ expect(getByText('Type')).toBeInTheDocument();
42
+ expect(getByText('Repositories')).toBeInTheDocument();
43
+ expect(getByText('Digest')).toBeInTheDocument();
44
+ expect(getByText('Creation')).toBeInTheDocument();
45
+ expect(getByText('Modified')).toBeInTheDocument();
46
+ expect(getByText('Labels')).toBeInTheDocument();
47
+
48
+ // Check values
49
+ expect(getAllByText('v1.0')[0]).toBeInTheDocument();
50
+ expect(getByText('ubi9-container')).toBeInTheDocument();
51
+
52
+ // Check labels are displayed
53
+ expect(getByText(/architecture/)).toBeInTheDocument();
54
+ expect(getByText(/x86_64/)).toBeInTheDocument();
55
+
56
+ assertNockRequest(scope);
57
+ });
58
+
59
+ test('Displays loading state initially', () => {
60
+ nockInstance
61
+ .get(manifestDetailsPath(2))
62
+ .query(true)
63
+ .delay(1000)
64
+ .reply(200, manifestDetailsData);
65
+
66
+ const { getByText } = renderWithRedux(
67
+ withManifestRoute(<ManifestDetails />),
68
+ renderOptions(2),
69
+ );
70
+
71
+ expect(getByText('Loading')).toBeInTheDocument();
72
+ });
73
+
74
+ test('Displays only library repositories', async () => {
75
+ const scope = nockInstance
76
+ .get(manifestDetailsPath(2))
77
+ .query(true)
78
+ .reply(200, manifestDetailsData);
79
+
80
+ const { getByText, queryByText } = renderWithRedux(
81
+ withManifestRoute(<ManifestDetails />),
82
+ renderOptions(2),
83
+ );
84
+
85
+ await patientlyWaitFor(() => {
86
+ expect(getByText('ubi9-container')).toBeInTheDocument();
87
+ });
88
+
89
+ // Should display Repositories label
90
+ expect(getByText('Repositories')).toBeInTheDocument();
91
+
92
+ // Should show library repository
93
+ expect(getByText('ubi9-container')).toBeInTheDocument();
94
+
95
+ // Should NOT show non-library repository
96
+ expect(queryByText('ubi9-container-dev')).not.toBeInTheDocument();
97
+
98
+ assertNockRequest(scope);
99
+ });
100
+
101
+ test('Repository links are clickable and have correct URLs', async () => {
102
+ const scope = nockInstance
103
+ .get(manifestDetailsPath(2))
104
+ .query(true)
105
+ .reply(200, manifestDetailsData);
106
+
107
+ const { getByText } = renderWithRedux(
108
+ withManifestRoute(<ManifestDetails />),
109
+ renderOptions(2),
110
+ );
111
+
112
+ await patientlyWaitFor(() => {
113
+ expect(getByText('ubi9-container')).toBeInTheDocument();
114
+ });
115
+
116
+ const repoLink = getByText('ubi9-container').closest('a');
117
+ expect(repoLink).toBeInTheDocument();
118
+ expect(repoLink).toHaveAttribute('href', '/products/5/repositories/10');
119
+ expect(repoLink).toHaveAttribute('target', '_blank');
120
+ expect(repoLink).toHaveAttribute('rel', 'noopener noreferrer');
121
+
122
+ assertNockRequest(scope);
123
+ });
124
+
125
+ test('Displays "No labels" when labels are empty', async () => {
126
+ const dataWithoutLabels = {
127
+ ...manifestDetailsData,
128
+ manifest: {
129
+ ...manifestDetailsData.manifest,
130
+ labels: {},
131
+ },
132
+ };
133
+
134
+ const scope = nockInstance
135
+ .get(manifestDetailsPath(2))
136
+ .query(true)
137
+ .reply(200, dataWithoutLabels);
138
+
139
+ const { getByText } = renderWithRedux(
140
+ withManifestRoute(<ManifestDetails />),
141
+ renderOptions(2),
142
+ );
143
+
144
+ await patientlyWaitFor(() => {
145
+ expect(getByText('No labels')).toBeInTheDocument();
146
+ });
147
+
148
+ assertNockRequest(scope);
149
+ });
150
+
151
+ test('Handles manifest list with child manifest query param', async () => {
152
+ const scope = nockInstance
153
+ .get(manifestDetailsPath(1))
154
+ .query(true)
155
+ .reply(200, manifestListData);
156
+
157
+ const customRenderOptions = {
158
+ apiNamespace: `${DOCKER_TAG_DETAILS_KEY}_1`,
159
+ routerParams: {
160
+ initialEntries: [{ pathname: '/labs/container_images/1', search: '?manifest=102' }],
161
+ initialIndex: 1,
162
+ },
163
+ };
164
+
165
+ const { getAllByText } = renderWithRedux(
166
+ withManifestRoute(<ManifestDetails />),
167
+ customRenderOptions,
168
+ );
169
+
170
+ await patientlyWaitFor(() => {
171
+ // Should display child manifest digest in short form
172
+ const elements = getAllByText(/sha256:1111aaaa2222/);
173
+ expect(elements.length).toBeGreaterThan(0);
174
+ });
175
+
176
+ assertNockRequest(scope);
177
+ });
178
+
179
+ test('Breadcrumb navigates back to container images list', async () => {
180
+ const scope = nockInstance
181
+ .get(manifestDetailsPath(2))
182
+ .query(true)
183
+ .reply(200, manifestDetailsData);
184
+
185
+ const { getByText } = renderWithRedux(
186
+ withManifestRoute(<ManifestDetails />),
187
+ renderOptions(2),
188
+ );
189
+
190
+ await patientlyWaitFor(() => {
191
+ expect(getByText('Container images')).toBeInTheDocument();
192
+ });
193
+
194
+ const breadcrumbLink = getByText('Container images');
195
+ expect(breadcrumbLink).toBeInTheDocument();
196
+
197
+ assertNockRequest(scope);
198
+ });
199
+
200
+ test('Displays error message on 404 API error', async () => {
201
+ const scope = nockInstance
202
+ .get(manifestDetailsPath(999))
203
+ .query(true)
204
+ .reply(404, {
205
+ error: { message: 'Not Found' },
206
+ });
207
+
208
+ const { getByText } = renderWithRedux(
209
+ withManifestRoute(<ManifestDetails />),
210
+ renderOptions(999),
211
+ );
212
+
213
+ await patientlyWaitFor(() => {
214
+ expect(getByText('Not Found')).toBeInTheDocument();
215
+ });
216
+
217
+ assertNockRequest(scope);
218
+ });
219
+
220
+ test('Displays error message on 500 API error', async () => {
221
+ const scope = nockInstance
222
+ .get(manifestDetailsPath(2))
223
+ .query(true)
224
+ .reply(500, {
225
+ error: { message: 'Internal Server Error' },
226
+ });
227
+
228
+ const { getByText } = renderWithRedux(
229
+ withManifestRoute(<ManifestDetails />),
230
+ renderOptions(2),
231
+ );
232
+
233
+ await patientlyWaitFor(() => {
234
+ expect(getByText(/error/i)).toBeInTheDocument();
235
+ });
236
+
237
+ assertNockRequest(scope);
238
+ });
239
+
240
+ test('Handles manifest list with no child manifests', async () => {
241
+ const dataWithEmptyManifests = {
242
+ ...manifestDetailsData,
243
+ manifest: {
244
+ ...manifestDetailsData.manifest,
245
+ manifest_type: 'list',
246
+ manifests: [],
247
+ },
248
+ };
249
+
250
+ const scope = nockInstance
251
+ .get(manifestDetailsPath(2))
252
+ .query(true)
253
+ .reply(200, dataWithEmptyManifests);
254
+
255
+ const { getByText } = renderWithRedux(
256
+ withManifestRoute(<ManifestDetails />),
257
+ renderOptions(2),
258
+ );
259
+
260
+ await patientlyWaitFor(() => {
261
+ expect(getByText('v1.0')).toBeInTheDocument();
262
+ });
263
+
264
+ // Should still display basic manifest information
265
+ expect(getByText('Name')).toBeInTheDocument();
266
+ expect(getByText('Repositories')).toBeInTheDocument();
267
+ expect(getByText('Digest')).toBeInTheDocument();
268
+
269
+ assertNockRequest(scope);
270
+ });
271
+
272
+ test('Displays N/A when no library repositories are present', async () => {
273
+ const dataWithoutLibraryRepos = {
274
+ ...manifestDetailsData,
275
+ repositories: [
276
+ {
277
+ id: 1,
278
+ name: 'non-library-repo',
279
+ library_instance: false,
280
+ },
281
+ {
282
+ id: 2,
283
+ name: 'another-non-library-repo',
284
+ library_instance: false,
285
+ },
286
+ ],
287
+ };
288
+
289
+ const scope = nockInstance
290
+ .get(manifestDetailsPath(2))
291
+ .query(true)
292
+ .reply(200, dataWithoutLibraryRepos);
293
+
294
+ const { getByText, queryByText } = renderWithRedux(
295
+ withManifestRoute(<ManifestDetails />),
296
+ renderOptions(2),
297
+ );
298
+
299
+ await patientlyWaitFor(() => {
300
+ expect(getByText('v1.0')).toBeInTheDocument();
301
+ });
302
+
303
+ // Repository field should show N/A when no library repositories
304
+ expect(getByText('Repositories')).toBeInTheDocument();
305
+ const repositoryValues = queryByText('N/A');
306
+ expect(repositoryValues).toBeInTheDocument();
307
+
308
+ // Non-library repos should not be displayed
309
+ expect(queryByText('non-library-repo')).not.toBeInTheDocument();
310
+ expect(queryByText('another-non-library-repo')).not.toBeInTheDocument();
311
+
312
+ assertNockRequest(scope);
313
+ });
314
+
315
+ test('Displays correct manifest types', async () => {
316
+ // Test bootable image
317
+ const bootableData = {
318
+ ...manifestDetailsData,
319
+ manifest: {
320
+ ...manifestDetailsData.manifest,
321
+ manifest_type: 'image',
322
+ is_bootable: true,
323
+ is_flatpak: false,
324
+ },
325
+ };
326
+
327
+ const bootableScope = nockInstance
328
+ .get(manifestDetailsPath(3))
329
+ .query(true)
330
+ .reply(200, bootableData);
331
+
332
+ const { getByText: getByTextBootable } = renderWithRedux(
333
+ withManifestRoute(<ManifestDetails />),
334
+ renderOptions(3),
335
+ );
336
+
337
+ await patientlyWaitFor(() => {
338
+ expect(getByTextBootable('Bootable')).toBeInTheDocument();
339
+ });
340
+
341
+ assertNockRequest(bootableScope);
342
+
343
+ // Test flatpak image
344
+ const flatpakData = {
345
+ ...manifestDetailsData,
346
+ manifest: {
347
+ ...manifestDetailsData.manifest,
348
+ manifest_type: 'image',
349
+ is_bootable: false,
350
+ is_flatpak: true,
351
+ },
352
+ };
353
+
354
+ const flatpakScope = nockInstance
355
+ .get(manifestDetailsPath(4))
356
+ .query(true)
357
+ .reply(200, flatpakData);
358
+
359
+ const { getByText: getByTextFlatpak } = renderWithRedux(
360
+ withManifestRoute(<ManifestDetails />),
361
+ renderOptions(4),
362
+ );
363
+
364
+ await patientlyWaitFor(() => {
365
+ expect(getByTextFlatpak('Flatpak')).toBeInTheDocument();
366
+ });
367
+
368
+ assertNockRequest(flatpakScope);
369
+
370
+ // Test manifest list
371
+ const listData = {
372
+ ...manifestDetailsData,
373
+ manifest: {
374
+ ...manifestDetailsData.manifest,
375
+ manifest_type: 'list',
376
+ },
377
+ };
378
+
379
+ const listScope = nockInstance
380
+ .get(manifestDetailsPath(5))
381
+ .query(true)
382
+ .reply(200, listData);
383
+
384
+ const { getByText: getByTextList } = renderWithRedux(
385
+ withManifestRoute(<ManifestDetails />),
386
+ renderOptions(5),
387
+ );
388
+
389
+ await patientlyWaitFor(() => {
390
+ expect(getByTextList('List')).toBeInTheDocument();
391
+ });
392
+
393
+ assertNockRequest(listScope);
394
+ });
395
+ });
@@ -0,0 +1,43 @@
1
+ {
2
+ "id": 2,
3
+ "name": "v1.0",
4
+ "repositories": [
5
+ {
6
+ "id": 10,
7
+ "name": "ubi9-container",
8
+ "full_path": "Default_Organization/Library/ubi9-container",
9
+ "library_instance": true,
10
+ "product_id": 5,
11
+ "product_name": "Red Hat Universal Base Image"
12
+ },
13
+ {
14
+ "id": 11,
15
+ "name": "ubi9-container-dev",
16
+ "full_path": "Default_Organization/Dev/ubi9-container",
17
+ "library_instance": false,
18
+ "product_id": 5,
19
+ "product_name": "Red Hat Universal Base Image"
20
+ }
21
+ ],
22
+ "product": {
23
+ "id": 5,
24
+ "name": "Red Hat Universal Base Image"
25
+ },
26
+ "manifest": {
27
+ "id": 104,
28
+ "digest": "sha256:efgh5678901234efgh5678901234efgh5678901234efgh5678901234efgh5678",
29
+ "schema_version": 2,
30
+ "manifest_type": "image",
31
+ "labels": {
32
+ "architecture": "x86_64",
33
+ "build-date": "2024-08-21T11:28:48",
34
+ "io.buildah.version": "1.37.1",
35
+ "version": "v1.0"
36
+ },
37
+ "annotations": {},
38
+ "is_bootable": true,
39
+ "is_flatpak": false,
40
+ "created_at": "2024-08-21T11:28:48.000Z",
41
+ "updated_at": "2024-08-21T11:28:48.000Z"
42
+ }
43
+ }