katello 4.18.1 → 4.19.0.rc2

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 (388) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/katello/locale/bn/katello.js +379 -109
  3. data/app/assets/javascripts/katello/locale/bn_IN/katello.js +379 -109
  4. data/app/assets/javascripts/katello/locale/ca/katello.js +379 -109
  5. data/app/assets/javascripts/katello/locale/cs/katello.js +380 -110
  6. data/app/assets/javascripts/katello/locale/cs_CZ/katello.js +382 -112
  7. data/app/assets/javascripts/katello/locale/de/katello.js +385 -115
  8. data/app/assets/javascripts/katello/locale/de_AT/katello.js +379 -109
  9. data/app/assets/javascripts/katello/locale/de_DE/katello.js +379 -109
  10. data/app/assets/javascripts/katello/locale/el/katello.js +380 -110
  11. data/app/assets/javascripts/katello/locale/en/katello.js +380 -110
  12. data/app/assets/javascripts/katello/locale/en_GB/katello.js +379 -109
  13. data/app/assets/javascripts/katello/locale/en_US/katello.js +379 -109
  14. data/app/assets/javascripts/katello/locale/es/katello.js +383 -113
  15. data/app/assets/javascripts/katello/locale/et_EE/katello.js +379 -109
  16. data/app/assets/javascripts/katello/locale/fr/katello.js +388 -118
  17. data/app/assets/javascripts/katello/locale/gl/katello.js +379 -109
  18. data/app/assets/javascripts/katello/locale/gu/katello.js +379 -109
  19. data/app/assets/javascripts/katello/locale/he_IL/katello.js +379 -109
  20. data/app/assets/javascripts/katello/locale/hi/katello.js +379 -109
  21. data/app/assets/javascripts/katello/locale/id/katello.js +379 -109
  22. data/app/assets/javascripts/katello/locale/it/katello.js +379 -109
  23. data/app/assets/javascripts/katello/locale/ja/katello.js +388 -118
  24. data/app/assets/javascripts/katello/locale/ka/katello.js +385 -115
  25. data/app/assets/javascripts/katello/locale/kn/katello.js +379 -109
  26. data/app/assets/javascripts/katello/locale/ko/katello.js +388 -118
  27. data/app/assets/javascripts/katello/locale/ml_IN/katello.js +379 -109
  28. data/app/assets/javascripts/katello/locale/mr/katello.js +379 -109
  29. data/app/assets/javascripts/katello/locale/nl_NL/katello.js +379 -109
  30. data/app/assets/javascripts/katello/locale/or/katello.js +379 -109
  31. data/app/assets/javascripts/katello/locale/pa/katello.js +379 -109
  32. data/app/assets/javascripts/katello/locale/pl/katello.js +379 -109
  33. data/app/assets/javascripts/katello/locale/pl_PL/katello.js +379 -109
  34. data/app/assets/javascripts/katello/locale/pt/katello.js +379 -109
  35. data/app/assets/javascripts/katello/locale/pt_BR/katello.js +383 -113
  36. data/app/assets/javascripts/katello/locale/ro/katello.js +379 -109
  37. data/app/assets/javascripts/katello/locale/ro_RO/katello.js +379 -109
  38. data/app/assets/javascripts/katello/locale/ru/katello.js +381 -111
  39. data/app/assets/javascripts/katello/locale/sl/katello.js +379 -109
  40. data/app/assets/javascripts/katello/locale/sv_SE/katello.js +379 -109
  41. data/app/assets/javascripts/katello/locale/ta/katello.js +379 -109
  42. data/app/assets/javascripts/katello/locale/ta_IN/katello.js +379 -109
  43. data/app/assets/javascripts/katello/locale/te/katello.js +379 -109
  44. data/app/assets/javascripts/katello/locale/tr/katello.js +379 -109
  45. data/app/assets/javascripts/katello/locale/vi/katello.js +379 -109
  46. data/app/assets/javascripts/katello/locale/vi_VN/katello.js +379 -109
  47. data/app/assets/javascripts/katello/locale/zh/katello.js +379 -109
  48. data/app/assets/javascripts/katello/locale/zh_CN/katello.js +388 -118
  49. data/app/assets/javascripts/katello/locale/zh_TW/katello.js +381 -111
  50. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +41 -12
  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_view_filter_rules_controller.rb +1 -1
  54. data/app/controllers/katello/api/v2/content_views_controller.rb +18 -3
  55. data/app/controllers/katello/api/v2/debs_controller.rb +21 -11
  56. data/app/controllers/katello/api/v2/docker_tags_controller.rb +7 -0
  57. data/app/controllers/katello/api/v2/flatpak_remote_repositories_controller.rb +21 -19
  58. data/app/controllers/katello/api/v2/host_debs_controller.rb +16 -1
  59. data/app/controllers/katello/api/v2/host_subscriptions_controller.rb +3 -60
  60. data/app/controllers/katello/api/v2/hosts_bulk_actions_controller.rb +10 -53
  61. data/app/controllers/katello/api/v2/repositories_controller.rb +0 -1
  62. data/app/controllers/katello/concerns/api/v2/hosts_controller_extensions.rb +10 -0
  63. data/app/controllers/katello/concerns/organizations_controller_extensions.rb +3 -0
  64. data/app/lib/actions/candlepin/activation_key/create.rb +0 -2
  65. data/app/lib/actions/candlepin/activation_key/update.rb +0 -2
  66. data/app/lib/actions/candlepin/product/content_create.rb +3 -5
  67. data/app/lib/actions/candlepin/product/content_update.rb +2 -3
  68. data/app/lib/actions/helpers/rolling_cv_repos.rb +1 -1
  69. data/app/lib/actions/katello/activation_key/create.rb +0 -1
  70. data/app/lib/actions/katello/activation_key/update.rb +0 -2
  71. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +1 -6
  72. data/app/lib/actions/katello/content_credential/update.rb +1 -1
  73. data/app/lib/actions/katello/content_view/add_rolling_repo_clone.rb +18 -24
  74. data/app/lib/actions/katello/content_view/create.rb +9 -4
  75. data/app/lib/actions/katello/content_view/refresh_rolling_repo.rb +6 -1
  76. data/app/lib/actions/katello/content_view/remove_rolling_repo_clone.rb +16 -11
  77. data/app/lib/actions/katello/content_view/update.rb +34 -7
  78. data/app/lib/actions/katello/product/content_create.rb +2 -2
  79. data/app/lib/actions/katello/product/content_destroy.rb +1 -1
  80. data/app/lib/actions/katello/repository/check_matching_content.rb +1 -1
  81. data/app/lib/actions/katello/repository/clone_contents.rb +1 -1
  82. data/app/lib/actions/katello/repository/create.rb +1 -1
  83. data/app/lib/actions/katello/repository/destroy.rb +4 -4
  84. data/app/lib/actions/katello/repository/finish_upload.rb +1 -1
  85. data/app/lib/actions/katello/repository/sync.rb +1 -1
  86. data/app/lib/actions/pulp3/orchestration/repository/copy_all_units.rb +2 -2
  87. data/app/lib/actions/pulp3/orchestration/repository/generate_metadata.rb +1 -1
  88. data/app/lib/actions/pulp3/orchestration/repository/multi_copy_all_units.rb +1 -1
  89. data/app/lib/actions/pulp3/repository/save_publication.rb +3 -1
  90. data/app/lib/actions/pulp3/repository/save_version.rb +45 -24
  91. data/app/lib/actions/pulp3/repository/save_versions.rb +2 -1
  92. data/app/lib/katello/resources/candlepin/activation_key.rb +3 -4
  93. data/app/lib/katello/resources/candlepin/upstream_job.rb +9 -1
  94. data/app/lib/katello/resources/candlepin.rb +4 -0
  95. data/app/models/katello/authorization/repository.rb +17 -4
  96. data/app/models/katello/concerns/subscription_facet_host_extensions.rb +0 -7
  97. data/app/models/katello/content_view_deb_filter.rb +10 -0
  98. data/app/models/katello/content_view_deb_filter_rule.rb +7 -0
  99. data/app/models/katello/deb.rb +10 -10
  100. data/app/models/katello/erratum.rb +1 -1
  101. data/app/models/katello/glue/provider.rb +14 -3
  102. data/app/models/katello/host/content_facet.rb +1 -1
  103. data/app/models/katello/host/subscription_facet.rb +1 -7
  104. data/app/models/katello/product_content.rb +2 -2
  105. data/app/models/katello/repository.rb +4 -23
  106. data/app/models/katello/root_repository.rb +2 -5
  107. data/app/services/katello/candlepin/event_handler.rb +0 -33
  108. data/app/services/katello/candlepin/message_handler.rb +0 -41
  109. data/app/services/katello/content_unit_indexer.rb +59 -13
  110. data/app/services/katello/product_content_finder.rb +5 -4
  111. data/app/services/katello/pulp3/alternate_content_source.rb +2 -2
  112. data/app/services/katello/pulp3/ansible_collection.rb +1 -0
  113. data/app/services/katello/pulp3/api/content_guard.rb +5 -5
  114. data/app/services/katello/pulp3/api/core.rb +10 -0
  115. data/app/services/katello/pulp3/deb.rb +1 -0
  116. data/app/services/katello/pulp3/docker_manifest.rb +1 -0
  117. data/app/services/katello/pulp3/docker_manifest_list.rb +1 -0
  118. data/app/services/katello/pulp3/docker_tag.rb +1 -0
  119. data/app/services/katello/pulp3/file_unit.rb +1 -0
  120. data/app/services/katello/pulp3/generic_content_unit.rb +1 -0
  121. data/app/services/katello/pulp3/module_stream.rb +1 -0
  122. data/app/services/katello/pulp3/package_group.rb +1 -0
  123. data/app/services/katello/pulp3/repository/apt.rb +41 -16
  124. data/app/services/katello/pulp3/repository.rb +59 -10
  125. data/app/services/katello/pulp3/rpm.rb +3 -2
  126. data/app/services/katello/pulp3/srpm.rb +3 -2
  127. data/app/services/katello/pulp3/task_group.rb +1 -1
  128. data/app/services/katello/registration_manager.rb +19 -17
  129. data/app/services/katello/repository_type_manager.rb +7 -5
  130. data/app/services/katello/smart_proxy_helper.rb +1 -6
  131. data/app/views/foreman/job_templates/upload_profile.erb +5 -0
  132. data/app/views/katello/api/v2/activation_keys/base.json.rabl +1 -1
  133. data/app/views/katello/api/v2/content_views/base.json.rabl +1 -0
  134. data/app/views/katello/api/v2/debs/thindex.json.rabl +6 -0
  135. data/app/views/katello/api/v2/docker_tags/_base.json.rabl +32 -0
  136. data/app/views/katello/api/v2/docker_tags/show.json.rabl +0 -5
  137. data/app/views/katello/api/v2/flatpak_remotes/index.json.rabl +6 -0
  138. data/app/views/katello/api/v2/host_debs/installed_debs.json.rabl +6 -0
  139. data/app/views/katello/api/v2/hosts_bulk_actions/applicable_errata.json.rabl +1 -1
  140. data/app/views/katello/api/v2/hosts_bulk_actions/applicable_erratum.json.rabl +9 -0
  141. data/app/views/katello/api/v2/hosts_bulk_actions/installable_errata.json.rabl +1 -1
  142. data/app/views/katello/api/v2/hosts_bulk_actions/{erratum.json.rabl → installable_erratum.json.rabl} +3 -3
  143. data/app/views/katello/api/v2/subscription_facet/base.json.rabl +1 -1
  144. data/config/initializers/monkeys.rb +1 -0
  145. data/config/routes/api/v2.rb +2 -2
  146. data/config/routes/overrides.rb +2 -7
  147. data/config/routes.rb +2 -0
  148. data/db/migrate/20211019192121_create_cdn_configuration.katello.rb +1 -1
  149. data/db/migrate/20250912000000_add_pulp_prn_fields.rb +73 -0
  150. data/db/migrate/20250912000001_populate_pulp_prn_fields.rb +403 -0
  151. data/db/migrate/20251009142516_remove_auto_attach_from_activation_keys.rb +5 -0
  152. data/db/migrate/20251009142517_remove_autoheal_from_subscription_facets.rb +5 -0
  153. data/db/seeds.d/111-upgrade_tasks.rb +2 -0
  154. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/bn.po +10 -0
  155. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/bn_IN.po +10 -0
  156. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ca.po +10 -0
  157. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/cs_CZ.po +10 -0
  158. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de.po +10 -0
  159. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de_AT.po +10 -0
  160. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de_DE.po +10 -0
  161. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/el.po +12 -2
  162. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/en_GB.po +10 -0
  163. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/en_US.po +10 -0
  164. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/es.po +10 -0
  165. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/et_EE.po +10 -0
  166. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/fr.po +10 -0
  167. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/gl.po +10 -0
  168. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/gu.po +10 -0
  169. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/he_IL.po +10 -0
  170. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/hi.po +10 -0
  171. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/id.po +10 -0
  172. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/it.po +10 -0
  173. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ja.po +10 -0
  174. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ka.po +10 -0
  175. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/kn.po +10 -0
  176. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ko.po +10 -0
  177. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ml_IN.po +10 -0
  178. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/mr.po +10 -0
  179. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/nl_NL.po +10 -0
  180. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/or.po +10 -0
  181. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pa.po +10 -0
  182. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pl.po +10 -0
  183. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pl_PL.po +10 -0
  184. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt.po +10 -0
  185. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt_BR.po +10 -0
  186. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ro.po +10 -0
  187. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ro_RO.po +10 -0
  188. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ru.po +10 -0
  189. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/sl.po +10 -0
  190. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/sv_SE.po +10 -0
  191. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ta.po +10 -0
  192. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ta_IN.po +10 -0
  193. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/te.po +10 -0
  194. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/tr.po +10 -0
  195. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/vi.po +10 -0
  196. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/vi_VN.po +10 -0
  197. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh.po +10 -0
  198. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_CN.po +10 -0
  199. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_TW.po +10 -0
  200. data/lib/katello/permission_creator.rb +2 -2
  201. data/lib/katello/permissions/host_permissions.rb +0 -6
  202. data/lib/katello/plugin.rb +17 -8
  203. data/lib/katello/tasks/jenkins.rake +1 -1
  204. data/lib/katello/tasks/upgrades/4.19/enable_structured_apt_for_deb.rake +87 -0
  205. data/lib/katello/tasks/upgrades/4.19/populate_repository_version_prns.rake +32 -0
  206. data/lib/katello/version.rb +1 -1
  207. data/lib/monkeys/fix_rpm_repository_gpgcheck.rb +38 -0
  208. data/locale/action_names.rb +0 -6
  209. data/locale/bn/LC_MESSAGES/katello.mo +0 -0
  210. data/locale/bn/katello.po +379 -109
  211. data/locale/bn_IN/LC_MESSAGES/katello.mo +0 -0
  212. data/locale/bn_IN/katello.po +379 -109
  213. data/locale/ca/LC_MESSAGES/katello.mo +0 -0
  214. data/locale/ca/katello.po +379 -109
  215. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  216. data/locale/cs/katello.po +380 -111
  217. data/locale/cs_CZ/LC_MESSAGES/katello.mo +0 -0
  218. data/locale/cs_CZ/katello.po +382 -112
  219. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  220. data/locale/de/katello.po +386 -116
  221. data/locale/de_AT/LC_MESSAGES/katello.mo +0 -0
  222. data/locale/de_AT/katello.po +379 -109
  223. data/locale/de_DE/LC_MESSAGES/katello.mo +0 -0
  224. data/locale/de_DE/katello.po +379 -109
  225. data/locale/el/LC_MESSAGES/katello.mo +0 -0
  226. data/locale/el/katello.po +381 -111
  227. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  228. data/locale/en/katello.po +380 -111
  229. data/locale/en_GB/LC_MESSAGES/katello.mo +0 -0
  230. data/locale/en_GB/katello.po +379 -109
  231. data/locale/en_US/LC_MESSAGES/katello.mo +0 -0
  232. data/locale/en_US/katello.po +379 -109
  233. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  234. data/locale/es/katello.po +383 -113
  235. data/locale/et_EE/LC_MESSAGES/katello.mo +0 -0
  236. data/locale/et_EE/katello.po +379 -109
  237. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  238. data/locale/fr/katello.po +389 -119
  239. data/locale/gl/LC_MESSAGES/katello.mo +0 -0
  240. data/locale/gl/katello.po +379 -109
  241. data/locale/gu/LC_MESSAGES/katello.mo +0 -0
  242. data/locale/gu/katello.po +379 -109
  243. data/locale/he_IL/LC_MESSAGES/katello.mo +0 -0
  244. data/locale/he_IL/katello.po +379 -109
  245. data/locale/hi/LC_MESSAGES/katello.mo +0 -0
  246. data/locale/hi/katello.po +379 -109
  247. data/locale/id/LC_MESSAGES/katello.mo +0 -0
  248. data/locale/id/katello.po +379 -109
  249. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  250. data/locale/it/katello.po +379 -109
  251. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  252. data/locale/ja/katello.po +390 -119
  253. data/locale/ka/LC_MESSAGES/katello.mo +0 -0
  254. data/locale/ka/katello.po +385 -115
  255. data/locale/katello.pot +1273 -831
  256. data/locale/kn/LC_MESSAGES/katello.mo +0 -0
  257. data/locale/kn/katello.po +379 -109
  258. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  259. data/locale/ko/katello.po +389 -119
  260. data/locale/ml_IN/LC_MESSAGES/katello.mo +0 -0
  261. data/locale/ml_IN/katello.po +379 -109
  262. data/locale/mr/LC_MESSAGES/katello.mo +0 -0
  263. data/locale/mr/katello.po +379 -109
  264. data/locale/nl_NL/LC_MESSAGES/katello.mo +0 -0
  265. data/locale/nl_NL/katello.po +379 -109
  266. data/locale/or/LC_MESSAGES/katello.mo +0 -0
  267. data/locale/or/katello.po +379 -109
  268. data/locale/pa/LC_MESSAGES/katello.mo +0 -0
  269. data/locale/pa/katello.po +379 -109
  270. data/locale/pl/LC_MESSAGES/katello.mo +0 -0
  271. data/locale/pl/katello.po +379 -109
  272. data/locale/pl_PL/LC_MESSAGES/katello.mo +0 -0
  273. data/locale/pl_PL/katello.po +379 -109
  274. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  275. data/locale/pt/katello.po +379 -109
  276. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  277. data/locale/pt_BR/katello.po +383 -113
  278. data/locale/ro/LC_MESSAGES/katello.mo +0 -0
  279. data/locale/ro/katello.po +379 -109
  280. data/locale/ro_RO/LC_MESSAGES/katello.mo +0 -0
  281. data/locale/ro_RO/katello.po +379 -109
  282. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  283. data/locale/ru/katello.po +381 -111
  284. data/locale/sl/LC_MESSAGES/katello.mo +0 -0
  285. data/locale/sl/katello.po +379 -109
  286. data/locale/sv_SE/LC_MESSAGES/katello.mo +0 -0
  287. data/locale/sv_SE/katello.po +379 -109
  288. data/locale/ta/LC_MESSAGES/katello.mo +0 -0
  289. data/locale/ta/katello.po +379 -109
  290. data/locale/ta_IN/LC_MESSAGES/katello.mo +0 -0
  291. data/locale/ta_IN/katello.po +379 -109
  292. data/locale/te/LC_MESSAGES/katello.mo +0 -0
  293. data/locale/te/katello.po +379 -109
  294. data/locale/tr/LC_MESSAGES/katello.mo +0 -0
  295. data/locale/tr/katello.po +379 -109
  296. data/locale/vi/LC_MESSAGES/katello.mo +0 -0
  297. data/locale/vi/katello.po +379 -109
  298. data/locale/vi_VN/LC_MESSAGES/katello.mo +0 -0
  299. data/locale/vi_VN/katello.po +379 -109
  300. data/locale/zh/LC_MESSAGES/katello.mo +0 -0
  301. data/locale/zh/katello.po +379 -109
  302. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  303. data/locale/zh_CN/katello.po +389 -119
  304. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  305. data/locale/zh_TW/katello.po +381 -111
  306. data/webpack/ForemanColumnExtensions/index.js +46 -2
  307. data/webpack/components/Content/Details/__tests__/__snapshots__/ContentDetails.test.js.snap +2 -2
  308. data/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeEditModal.js +0 -2
  309. data/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/__tests__/SystemPurposeEditModal.test.js +0 -2
  310. data/webpack/components/extensions/Hosts/ActionsBar/index.js +1 -0
  311. data/webpack/components/extensions/Hosts/BulkActions/BulkActionsConstants.js +7 -0
  312. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/BulkChangeHostCollectionsModal.js +388 -0
  313. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/__tests__/BulkChangeHostCollectionsModal.test.js +640 -0
  314. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/actions.js +28 -0
  315. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCollectionsModal/index.js +71 -0
  316. data/webpack/components/extensions/Hosts/BulkActions/BulkErrataWizard/02_BulkErrataTable.js +1 -1
  317. data/webpack/components/extensions/Hosts/BulkActions/BulkErrataWizard/BulkErrataWizard.js +1 -1
  318. data/webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/02_BulkPackagesTable.js +11 -4
  319. data/webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/BulkPackagesWizard.js +51 -24
  320. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/01_BulkRepositorySetsTable.js +1 -1
  321. data/webpack/components/extensions/Hosts/BulkActions/HostReview.js +8 -1
  322. data/webpack/containers/Application/config.js +11 -1
  323. data/webpack/global_index.js +3 -0
  324. data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/BootedContainerImagesConstants.js +1 -1
  325. data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/BootedContainerImagesPage.js +7 -43
  326. data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/__tests__/bootedContainerImagesPage.test.js +1 -1
  327. data/webpack/scenes/ContainerImages/ContainerImagesPage.js +86 -0
  328. data/webpack/scenes/ContainerImages/LabelsAnnotationsModal.js +105 -0
  329. data/webpack/scenes/ContainerImages/Synced/Details/ManifestDetails.js +218 -0
  330. data/webpack/scenes/ContainerImages/Synced/Details/ManifestDetailsActions.js +15 -0
  331. data/webpack/scenes/ContainerImages/Synced/Details/ManifestDetailsSelectors.js +16 -0
  332. data/webpack/scenes/ContainerImages/Synced/Details/__tests__/ManifestDetails.test.js +395 -0
  333. data/webpack/scenes/ContainerImages/Synced/Details/__tests__/manifestDetails.fixtures.json +43 -0
  334. data/webpack/scenes/ContainerImages/Synced/Details/__tests__/manifestList.fixtures.json +58 -0
  335. data/webpack/scenes/ContainerImages/Synced/Details/index.js +4 -0
  336. data/webpack/scenes/ContainerImages/Synced/SyncedContainerImagesPage.js +359 -0
  337. data/webpack/scenes/ContainerImages/Synced/SyncedContainerImagesPage.scss +21 -0
  338. data/webpack/scenes/ContainerImages/Synced/__tests__/LabelsAnnotationsModal.test.js +69 -0
  339. data/webpack/scenes/ContainerImages/Synced/__tests__/SyncedContainerImagesPage.test.js +335 -0
  340. data/webpack/scenes/ContainerImages/Synced/__tests__/syncedContainerImages.fixtures.json +105 -0
  341. data/webpack/scenes/ContainerImages/TableEmptyState.js +67 -0
  342. data/webpack/scenes/ContainerImages/containerImagesHelpers.js +48 -0
  343. data/webpack/scenes/ContainerImages/index.js +4 -0
  344. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +29 -3
  345. data/webpack/scenes/ContentViews/Create/__tests__/contentViewCreateResult.fixtures.json +1 -0
  346. data/webpack/scenes/ContentViews/Create/__tests__/createContentView.test.js +45 -1
  347. data/webpack/scenes/ContentViews/Delete/__tests__/affectedHosts.fixtures.json +0 -1
  348. data/webpack/scenes/ContentViews/Details/ContentViewInfo.js +59 -1
  349. data/webpack/scenes/ContentViews/Details/Filters/CVDebFilterContent.js +1 -0
  350. data/webpack/scenes/ContentViews/Details/Filters/Rules/DebPackage/AddEditDebPackageRuleModal.js +164 -24
  351. data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVDebFilterContent.test.js +268 -0
  352. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvDebFilterDetail.fixtures.json +95 -0
  353. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvDebFilterRules.fixtures.json +31 -0
  354. data/webpack/scenes/ContentViews/Details/Filters/__tests__/emptyCVDebFilterRules.fixtures.json +10 -0
  355. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/hosts.fixtures.json +0 -1
  356. data/webpack/scenes/ContentViews/Details/Versions/Delete/__tests__/cvAffectedHosts.fixture.json +0 -1
  357. data/webpack/scenes/ContentViews/Details/__tests__/contentViewRollingDetail.test.js +15 -0
  358. data/webpack/scenes/ContentViews/Details/__tests__/contentViewRollingDetails.fixtures.json +1 -0
  359. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +9 -0
  360. data/webpack/scenes/FlatpakRemotes/CreateEdit/CreateFlatpakRemoteModal.js +5 -3
  361. data/webpack/scenes/FlatpakRemotes/CreateEdit/EditFlatpakRemotesModal.js +1 -1
  362. data/webpack/scenes/FlatpakRemotes/CreateEdit/FlatpakRemoteform.js +35 -3
  363. data/webpack/scenes/FlatpakRemotes/Details/FlatpakRemoteDetails.js +1 -1
  364. data/webpack/scenes/FlatpakRemotes/Details/RemoteRepositories/RemoteRepositoriesTable.css +3 -0
  365. data/webpack/scenes/FlatpakRemotes/Details/RemoteRepositories/RemoteRepositoriesTable.js +63 -132
  366. data/webpack/scenes/FlatpakRemotes/FlatpakRemotesPage.js +67 -143
  367. data/webpack/scenes/SmartProxy/ExpandableCvDetails.js +10 -2
  368. data/webpack/scenes/SmartProxy/SmartProxyContentActions.js +13 -2
  369. data/webpack/scenes/SmartProxy/SmartProxyContentConstants.js +1 -0
  370. data/webpack/scenes/SmartProxy/SmartProxyExpandableTable.js +8 -2
  371. data/webpack/scenes/SmartProxy/__tests__/SmartProxyContentTest.js +67 -1
  372. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +2 -2
  373. data/webpack/scenes/Subscriptions/SubscriptionConstants.js +0 -2
  374. data/webpack/scenes/Subscriptions/SubscriptionReducer.js +15 -5
  375. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +22 -5
  376. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +141 -0
  377. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsActions.test.js.snap +2 -2
  378. metadata +82 -56
  379. data/app/lib/actions/katello/host/attach_subscriptions.rb +0 -59
  380. data/app/lib/actions/katello/host/auto_attach_subscriptions.rb +0 -22
  381. data/app/lib/actions/katello/host/remove_subscriptions.rb +0 -50
  382. data/app/lib/actions/katello/organization/simple_content_access/disable.rb +0 -25
  383. data/app/lib/actions/katello/organization/simple_content_access/enable.rb +0 -25
  384. data/app/lib/actions/katello/organization/simple_content_access/toggle.rb +0 -42
  385. data/lib/katello/tasks/migrate_structure_content_for_deb.rake +0 -105
  386. data/lib/katello/tasks/upgrades/4.2/remove_checksum_values.rake +0 -17
  387. /data/webpack/scenes/{BootedContainerImages → ContainerImages/Booted}/__tests__/bootedContainerImages.fixtures.js +0 -0
  388. /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
+ }