katello 4.8.0.rc2 → 4.8.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/v2/simple_content_access_controller.rb +3 -1
  3. data/app/lib/actions/katello/content_view/publish.rb +2 -9
  4. data/app/lib/actions/katello/organization/simple_content_access/enable.rb +10 -0
  5. data/app/lib/actions/katello/organization/simple_content_access/prepare_content_overrides.rb +36 -0
  6. data/app/lib/actions/katello/organization/simple_content_access/toggle.rb +12 -2
  7. data/app/lib/katello/util/content_overrides_migrator.rb +98 -0
  8. data/app/models/katello/content_view.rb +10 -6
  9. data/app/models/katello/host/content_facet.rb +10 -3
  10. data/db/migrate/20220110223754_update_disconnected_settings.rb +8 -4
  11. data/db/migrate/20230119003859_ensure_repo_username_password_nil_not_blank.rb +1 -1
  12. data/lib/katello/plugin.rb +0 -12
  13. data/lib/katello/version.rb +1 -1
  14. data/locale/bn/LC_MESSAGES/katello.mo +0 -0
  15. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  16. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  17. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  18. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  19. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  20. data/locale/gu/LC_MESSAGES/katello.mo +0 -0
  21. data/locale/hi/LC_MESSAGES/katello.mo +0 -0
  22. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  23. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  24. data/locale/ka/LC_MESSAGES/katello.mo +0 -0
  25. data/locale/kn/LC_MESSAGES/katello.mo +0 -0
  26. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  27. data/locale/mr/LC_MESSAGES/katello.mo +0 -0
  28. data/locale/or/LC_MESSAGES/katello.mo +0 -0
  29. data/locale/pa/LC_MESSAGES/katello.mo +0 -0
  30. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  31. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  32. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  33. data/locale/ta/LC_MESSAGES/katello.mo +0 -0
  34. data/locale/te/LC_MESSAGES/katello.mo +0 -0
  35. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  36. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  37. data/webpack/components/Content/{ContentPage.js → GenericContentPage.js} +7 -4
  38. data/webpack/components/Content/__tests__/ContentTable.test.js +1 -1
  39. data/webpack/components/Content/__tests__/GenericContentPage.test.js +35 -0
  40. data/webpack/components/Search/SearchText.js +70 -0
  41. data/webpack/components/Table/EmptyStateMessage.js +2 -2
  42. data/webpack/components/Table/TableWrapper.js +4 -0
  43. data/webpack/components/extensions/HostDetails/HostDetailsConstants.js +0 -1
  44. data/webpack/components/extensions/HostDetails/HostDetailsSelectors.js +0 -6
  45. data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/ModuleStreamsTab.js +2 -2
  46. data/webpack/components/extensions/HostDetails/Tabs/__tests__/moduleStreamsTab.test.js +76 -0
  47. data/webpack/components/extensions/SearchBar/SearchBarConstants.js +3 -0
  48. data/webpack/components/extensions/SearchBar/SearchBarHooks.js +50 -0
  49. data/webpack/components/extensions/SearchBar/SearchBarReducer.js +14 -0
  50. data/webpack/components/extensions/SearchBar/SearchBarSelectors.js +5 -0
  51. data/webpack/redux/reducers/index.js +2 -2
  52. data/webpack/scenes/AlternateContentSources/Create/__tests__/acsCreate.test.js +1 -13
  53. data/webpack/scenes/AlternateContentSources/MainTable/ACSTable.js +1 -0
  54. data/webpack/scenes/Content/{ContentPage.js → GenericContentPage.js} +2 -2
  55. data/webpack/scenes/Content/__tests__/contentTable.test.js +2 -2
  56. data/webpack/scenes/Content/index.js +2 -2
  57. data/webpack/scenes/ContentViews/Details/Filters/Rules/ContainerTag/AddEditContainerTagRuleModal.js +14 -17
  58. data/webpack/scenes/ContentViews/Details/Filters/Rules/Package/AddEditPackageRuleModal.js +24 -28
  59. data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVContainerImageFilterContent.test.js +11 -18
  60. data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVRpmFilterContent.test.js +10 -23
  61. data/webpack/scenes/ContentViews/Details/Filters/__tests__/ContentViewPackageGroupFilter.test.js +0 -2
  62. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.test.js +1 -7
  63. data/webpack/scenes/ContentViews/expansions/__tests__/contentViewComponentsModal.test.js +0 -2
  64. data/webpack/scenes/ModuleStreams/ModuleStreamsPage.js +2 -2
  65. data/webpack/scenes/ModuleStreams/__tests__/ModuleStreamPage.test.js +2 -2
  66. data/webpack/scenes/ModuleStreams/__tests__/__snapshots__/ModuleStreamPage.test.js.snap +1 -1
  67. data/webpack/scenes/Settings/SettingsConstants.js +2 -3
  68. data/webpack/scenes/Settings/SettingsReducer.js +2 -16
  69. data/webpack/scenes/Settings/SettingsSelectors.js +2 -2
  70. data/webpack/test-utils/react-testing-lib-wrapper.js +0 -6
  71. metadata +45 -26
  72. data/webpack/components/Content/__tests__/ContentPage.test.js +0 -32
  73. data/webpack/components/Content/__tests__/__snapshots__/ContentPage.test.js.snap +0 -89
  74. data/webpack/components/Search/Search.js +0 -156
  75. data/webpack/components/Search/__tests__/search.test.js +0 -104
  76. data/webpack/components/Search/helpers.js +0 -6
  77. data/webpack/components/Search/index.js +0 -15
  78. data/webpack/components/TypeAhead/TypeAhead.js +0 -157
  79. data/webpack/components/TypeAhead/TypeAhead.scss +0 -7
  80. data/webpack/components/TypeAhead/helpers/commonPropTypes.js +0 -35
  81. data/webpack/components/TypeAhead/helpers/helpers.js +0 -32
  82. data/webpack/components/TypeAhead/index.js +0 -3
  83. data/webpack/components/TypeAhead/pf3Search/TypeAheadInput.js +0 -44
  84. data/webpack/components/TypeAhead/pf3Search/TypeAheadItems.js +0 -56
  85. data/webpack/components/TypeAhead/pf3Search/TypeAheadSearch.js +0 -53
  86. data/webpack/components/TypeAhead/pf4Search/TypeAheadInput.js +0 -66
  87. data/webpack/components/TypeAhead/pf4Search/TypeAheadInput.scss +0 -12
  88. data/webpack/components/TypeAhead/pf4Search/TypeAheadItems.js +0 -59
  89. data/webpack/components/TypeAhead/pf4Search/TypeAheadSearch.js +0 -81
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 623f96f2d316d946b9a19bbed0bd70a58c56541dfcde05261848efb7f868881d
4
- data.tar.gz: f39b3c6dca68f3b742f6a09fb5a821ab5e9d11128835023f09a5a8df30a27272
3
+ metadata.gz: 81c709207089041832e6dd1b447a01d0d40dc228480d4b4e43b536c853242662
4
+ data.tar.gz: 94ec8c4161ff3f63d2a48d4be430b5942b7a40684c4bc39a6c734597ed9f39bf
5
5
  SHA512:
6
- metadata.gz: 3d4e46aa5cdededbea344d58cc4dd5c43c1fcdd480e5a62594a1bf0892ff80c048e07a3a7460646b2778502849cc58d7ab6f8f22aaeb1ce3fd0ea8aa59f844c7
7
- data.tar.gz: 97fa1540ab108458032dc4a4963b9644af0a6c7207079bf2c3ef2cc895cacab6521eec73e4ed4aadc2eaf8428fc3b2d5b7bec489a03f02a2d972310344b7c07f
6
+ metadata.gz: 1923b0ae05a6bb6c92b7b807793ce795e4ed90a26e598124a0441b49cb30270ec4829eb0ae0a95944de20bb4db5366b9219ac247667e194b8de88c6a7a933e82
7
+ data.tar.gz: 062cd9f6c371824bbe22b4b686735eba4ac0ee5ebd316fe4e06fa74b942e58da6f891ef6c3d5ed92158e9d32878bfc533574419b63836d4f4078a3b85eca54bb
@@ -26,8 +26,10 @@ module Katello
26
26
  api :PUT, "/organizations/:organization_id/simple_content_access/enable",
27
27
  N_("Enable simple content access for a manifest")
28
28
  param :organization_id, :number, :desc => N_("Organization ID"), :required => true
29
+ param :auto_create_overrides, :bool, :desc => N_("Automatically create disabled content overrides for custom products which do not have an attached subscription"), :required => false, :default => true
29
30
  def enable
30
- task = async_task(::Actions::Katello::Organization::SimpleContentAccess::Enable, params[:organization_id])
31
+ auto_create = params.key?(:auto_create_overrides) ? ::Foreman::Cast.to_bool(params[:auto_create_overrides]) : true
32
+ task = async_task(::Actions::Katello::Organization::SimpleContentAccess::Enable, params[:organization_id], auto_create_overrides: auto_create)
31
33
  respond_for_async :resource => task
32
34
  end
33
35
 
@@ -112,15 +112,8 @@ module Actions
112
112
  version.update_content_counts!
113
113
  # update errata applicability counts for all hosts in the CV & Library
114
114
  unless input[:skip_promotion]
115
- content_view = ::Katello::ContentView.find(input[:content_view_id])
116
- lifecycle_environment = ::Katello::KTEnvironment.find(input[:environment_id])
117
- ::Katello::Host::ContentFacet.in_content_views_and_environments(
118
- content_views: [content_view],
119
- lifecycle_environments: [lifecycle_environment]
120
- ).each do |facet|
121
- facet.update_applicability_counts
122
- facet.update_errata_status
123
- end
115
+ environment = ::Katello::KTEnvironment.find(input[:environment_id])
116
+ ::Katello::ContentView.find(input[:content_view_id]).update_host_statuses(environment)
124
117
  end
125
118
 
126
119
  history = ::Katello::ContentViewHistory.find(input[:history_id])
@@ -3,6 +3,16 @@ module Actions
3
3
  module Organization
4
4
  module SimpleContentAccess
5
5
  class Enable < Toggle
6
+ def plan(organization_id, auto_create_overrides: true)
7
+ input[:auto_create_overrides] = auto_create_overrides
8
+ sequence do
9
+ if auto_create_overrides
10
+ plan_action(PrepareContentOverrides, organization_id)
11
+ end
12
+ super(organization_id) # puts plan_self inside the sequence
13
+ end
14
+ end
15
+
6
16
  def content_access_mode_value
7
17
  SIMPLE_CONTENT_ACCESS_ENABLED_VALUE
8
18
  end
@@ -0,0 +1,36 @@
1
+ module Actions
2
+ module Katello
3
+ module Organization
4
+ module SimpleContentAccess
5
+ class PrepareContentOverrides < Actions::Base
6
+ def plan(organization_id)
7
+ Rails.logger.info "PrepareContentOverrides plan: #{organization_id.inspect}"
8
+ organization = ::Organization.find(organization_id.to_i)
9
+ org_name = organization.name
10
+
11
+ plan_self(organization_id: organization_id, organization_name: org_name)
12
+ end
13
+
14
+ def run
15
+ organization = ::Organization.find(input[:organization_id].to_i)
16
+ migrator = ::Katello::Util::ContentOverridesMigrator.new(organization: organization)
17
+
18
+ output[:migrator_result] = migrator.execute_non_sca_overrides!
19
+ end
20
+
21
+ def rescue_strategy
22
+ Dynflow::Action::Rescue::Skip
23
+ end
24
+
25
+ def humanized_name
26
+ N_("Prepare content overrides for Simple Content Access")
27
+ end
28
+
29
+ def humanized_input
30
+ _("for organization %s") % input[:organization_name]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -12,9 +12,15 @@ module Actions
12
12
  attr_reader :organization
13
13
 
14
14
  def plan(organization_id)
15
- @organization = ::Organization.find(organization_id)
15
+ organization = ::Organization.find(organization_id.to_i)
16
+ input[:organization_name] = organization.name
17
+ input[:organization_label] = organization.label
16
18
  action_subject organization
17
- ::Katello::Resources::Candlepin::Owner.update(@organization.label, contentAccessMode: content_access_mode_value)
19
+ plan_self(organization_id: organization_id)
20
+ end
21
+
22
+ def run
23
+ ::Katello::Resources::Candlepin::Owner.update(input[:organization_label], contentAccessMode: content_access_mode_value)
18
24
  end
19
25
 
20
26
  def failure_notification(plan)
@@ -30,6 +36,10 @@ module Actions
30
36
  )
31
37
  end
32
38
 
39
+ def humanized_input
40
+ _("for organization %s") % input[:organization_name]
41
+ end
42
+
33
43
  private
34
44
 
35
45
  def consumer
@@ -0,0 +1,98 @@
1
+ module Katello
2
+ module Util
3
+ class ContentOverridesMigrator # used in Actions::Katello::Organization::SimpleContentAccess::PrepareContentOverrides
4
+ include ActionView::Helpers::TextHelper
5
+
6
+ def initialize(organization:)
7
+ @organization = organization
8
+ end
9
+
10
+ def execute_non_sca_overrides!
11
+ host_errors = create_disabled_overrides_for_non_sca_org_hosts(organization: @organization)
12
+ ak_errors = create_disabled_overrides_for_non_sca_org_activation_keys(organization: @organization)
13
+
14
+ total_errors = host_errors + ak_errors
15
+ finish_message = "Finished creating overrides in non-SCA orgs; #{total_errors == 0 ? "no errors" : "#{pluralize(total_errors, "error")}"}"
16
+ messages = { result: finish_message, errors: total_errors }
17
+ messages[:host_errors] = "Hosts - #{pluralize(host_errors, "error")} creating disabled overrides for unsubscribed content; see log messages above" if host_errors > 0
18
+ messages[:ak_errors] = "Activation keys - #{pluralize(ak_errors, "error")} creating disabled overrides for unsubscribed content; see log messages above" if ak_errors > 0
19
+ messages[:success_message] = "You may now switch all organizations to Simple Content Access mode without any change in access to content." if total_errors == 0
20
+ Rails.logger.info finish_message
21
+ Rails.logger.info messages[:host_errors] if messages[:host_errors]
22
+ Rails.logger.info messages[:ak_errors] if messages[:ak_errors]
23
+ Rails.logger.info messages[:success_message] if messages[:success_message]
24
+ messages
25
+ end
26
+
27
+ def create_disabled_overrides_for_non_sca(consumable:)
28
+ content_finder = ::Katello::ProductContentFinder.new(
29
+ match_subscription: false,
30
+ match_environment: false,
31
+ consumable: consumable
32
+ )
33
+ subscribed_content_finder = ::Katello::ProductContentFinder.new(
34
+ match_subscription: true,
35
+ match_environment: false,
36
+ consumable: consumable
37
+ )
38
+ candlepin_resource = consumable.is_a?(::Katello::Host::SubscriptionFacet) ? ::Katello::Resources::Candlepin::Consumer : ::Katello::Resources::Candlepin::ActivationKey
39
+ consumable_id = consumable.is_a?(::Katello::Host::SubscriptionFacet) ? consumable.uuid : consumable.cp_id
40
+ repos_with_existing_overrides = candlepin_resource.content_overrides(consumable_id).map do |override|
41
+ override[:contentLabel]
42
+ end
43
+ unsubscribed_content = content_finder.custom_content_labels - subscribed_content_finder.custom_content_labels - repos_with_existing_overrides
44
+ new_overrides = unsubscribed_content.map do |repo_label|
45
+ ::Katello::ContentOverride.new(
46
+ repo_label,
47
+ { name: "enabled", value: "0" } # Override to disabled
48
+ )
49
+ end
50
+ return if new_overrides.blank?
51
+ if consumable.is_a? ::Katello::Host::SubscriptionFacet
52
+ ::Katello::Resources::Candlepin::Consumer.update_content_overrides(
53
+ consumable.uuid,
54
+ new_overrides.map(&:to_entitlement_hash)
55
+ )
56
+ else
57
+ ::Katello::Resources::Candlepin::ActivationKey.update_content_overrides(
58
+ consumable.cp_id,
59
+ new_overrides.map(&:to_entitlement_hash)
60
+ )
61
+ end
62
+ end
63
+
64
+ def create_disabled_overrides_for_non_sca_org_hosts(organization:)
65
+ errors = 0
66
+ fail _("Organization must be specified") if organization.blank?
67
+ return 0 if organization.simple_content_access? # subscription attachment is meaningless with SCA
68
+ Rails.logger.info("Hosts - Creating disabled overrides for unsubscribed content in organization #{organization.name}")
69
+ # only registered hosts with content!
70
+ hosts_to_update = organization.hosts.joins(:subscription_facet).where.not("#{Katello::Host::SubscriptionFacet.table_name}.host_id" => nil)
71
+ hosts_to_update.each do |host|
72
+ create_disabled_overrides_for_non_sca(consumable: host.subscription_facet)
73
+ rescue => e
74
+ errors += 1
75
+ Rails.logger.error("Failed to update host #{host.name}: #{e.message}")
76
+ Rails.logger.debug e.backtrace.join("\n")
77
+ end
78
+ errors
79
+ end
80
+
81
+ def create_disabled_overrides_for_non_sca_org_activation_keys(organization:)
82
+ errors = 0
83
+ fail _("Organization must be specified") if organization.blank?
84
+ return 0 if organization.simple_content_access? # subscription attachment is meaningless with SCA
85
+ Rails.logger.info("Activation keys - Creating disabled overrides for unsubscribed content in organization #{organization.name}")
86
+ aks_to_update = organization.activation_keys
87
+ aks_to_update.each do |ak|
88
+ create_disabled_overrides_for_non_sca(consumable: ak)
89
+ rescue => e
90
+ errors += 1
91
+ Rails.logger.error("Failed to update activation key #{ak.name}: #{e.message}")
92
+ Rails.logger.debug e.backtrace.join("\n")
93
+ end
94
+ errors
95
+ end
96
+ end
97
+ end
98
+ end
@@ -652,12 +652,16 @@ module Katello
652
652
  end
653
653
 
654
654
  def check_orphaned_content_facets!(environments: [])
655
- ::Katello::Host::ContentFacet.in_content_views_and_environments(
656
- content_views: [self],
657
- lifecycle_environments: environments
658
- ).each do |facet|
659
- unless facet.host
660
- fail _("Orphaned content facets for deleted hosts exist for the content view and environment. Please run rake task : katello:clean_orphaned_facets and try again!")
655
+ Location.no_taxonomy_scope do
656
+ User.as_anonymous_admin do
657
+ ::Katello::Host::ContentFacet.in_content_views_and_environments(
658
+ content_views: [self],
659
+ lifecycle_environments: environments
660
+ ).each do |facet|
661
+ unless facet.host
662
+ fail _("Orphaned content facets for deleted hosts exist for the content view and environment. Please run rake task : katello:clean_orphaned_facets and try again!")
663
+ end
664
+ end
661
665
  end
662
666
  end
663
667
  end
@@ -298,19 +298,26 @@ module Katello
298
298
  end
299
299
 
300
300
  def katello_agent_installed?
301
- self.host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => 'katello-agent').any?
301
+ self.host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => 'katello-agent').any? ||
302
+ self.host.installed_debs.where("#{Katello::InstalledDeb.table_name}.name" => 'katello-agent').any?
302
303
  end
303
304
 
304
305
  def tracer_installed?
305
306
  self.host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => [ "python-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
306
307
  "python3-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
307
- HOST_TOOLS_TRACER_PACKAGE_NAME ]).any?
308
+ HOST_TOOLS_TRACER_PACKAGE_NAME ]).any? ||
309
+ self.host.installed_debs.where("#{Katello::InstalledDeb.table_name}.name" => [ "python-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
310
+ "python3-#{HOST_TOOLS_TRACER_PACKAGE_NAME}",
311
+ HOST_TOOLS_TRACER_PACKAGE_NAME ]).any?
308
312
  end
309
313
 
310
314
  def host_tools_installed?
311
315
  host.installed_packages.where("#{Katello::InstalledPackage.table_name}.name" => [ "python-#{HOST_TOOLS_PACKAGE_NAME}",
312
316
  "python3-#{HOST_TOOLS_PACKAGE_NAME}",
313
- HOST_TOOLS_PACKAGE_NAME ]).any?
317
+ HOST_TOOLS_PACKAGE_NAME ]).any? ||
318
+ host.installed_debs.where("#{Katello::InstalledDeb.table_name}.name" => [ "python-#{HOST_TOOLS_PACKAGE_NAME}",
319
+ "python3-#{HOST_TOOLS_PACKAGE_NAME}",
320
+ HOST_TOOLS_PACKAGE_NAME ]).any?
314
321
  end
315
322
 
316
323
  def update_errata_status
@@ -1,19 +1,23 @@
1
1
  class UpdateDisconnectedSettings < ActiveRecord::Migration[6.0]
2
+ class FakeSetting < Katello::Model
3
+ self.table_name = 'settings'
4
+ end
5
+
2
6
  def up
3
- setting_disconnected = Setting.find_by(name: 'content_disconnected')
7
+ setting_disconnected = FakeSetting.find_by(name: 'content_disconnected')
4
8
  setting = Setting.find_by(name: 'subscription_connection_enabled')
5
9
 
6
10
  setting&.update!(
7
11
  value: !setting_disconnected&.value
8
12
  )
9
- Setting.where(:name => 'content_disconnected').delete_all
13
+ FakeSetting.where(:name => 'content_disconnected').delete_all
10
14
  end
11
15
 
12
16
  def down
13
17
  remove_column :katello_cdn_configurations, :airgapped
14
18
  setting_disconnected = Setting.find_by(name: 'subscription_connection_enabled')
15
- Setting.set('content_disconnected', N_("A server operating in disconnected mode does not communicate with the Red Hat CDN."),
16
- !setting_disconnected.value, N_('Disconnected mode'))
19
+ FakeSetting.set('content_disconnected', N_("A server operating in disconnected mode does not communicate with the Red Hat CDN."),
20
+ !setting_disconnected.value, N_('Disconnected mode'))
17
21
 
18
22
  Setting.where(:name => 'subscription_connection_enabled').delete_all
19
23
  end
@@ -2,7 +2,7 @@ class EnsureRepoUsernamePasswordNilNotBlank < ActiveRecord::Migration[6.1]
2
2
  def change
3
3
  ::Katello::Repository.library.each do |repo|
4
4
  if repo.upstream_username == '' && repo.upstream_password == ''
5
- repo.update(upstream_username: nil, upstream_password: nil)
5
+ repo.root.update(upstream_username: nil, upstream_password: nil)
6
6
  end
7
7
  end
8
8
  end
@@ -651,18 +651,6 @@ Foreman::Plugin.register :katello do
651
651
  full_name: N_('Applicability Batch Size'),
652
652
  description: N_("Number of host applicability calculations to process per task.")
653
653
 
654
- setting 'autosearch_while_typing',
655
- type: :boolean,
656
- default: true,
657
- full_name: N_('Autosearch'),
658
- description: N_('For pages that support it, automatically perform search while typing in search input.')
659
-
660
- setting 'autosearch_delay',
661
- type: :integer,
662
- default: 500,
663
- full_name: N_('Autosearch delay'),
664
- description: N_('If Autosearch is enabled, delay in milliseconds before executing searches while typing.')
665
-
666
654
  setting 'bulk_load_size',
667
655
  type: :integer,
668
656
  default: 2000,
@@ -1,3 +1,3 @@
1
1
  module Katello
2
- VERSION = "4.8.0.rc2".freeze
2
+ VERSION = "4.8.1".freeze
3
3
  end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -4,8 +4,9 @@ import { Grid, Col, Row, Form, FormGroup } from 'react-bootstrap';
4
4
  import SearchBar from 'foremanReact/components/SearchBar';
5
5
  import { getControllerSearchProps } from 'foremanReact/constants';
6
6
  import ContentTable from './ContentTable';
7
+ import { useClearSearch } from '../extensions/SearchBar/SearchBarHooks';
7
8
 
8
- const ContentPage = ({
9
+ const GenericContentPage = ({
9
10
  header, onSearch, bookmarkController,
10
11
  autocompleteEndpoint, autocompleteQueryParams,
11
12
  updateSearchQuery, initialInputValue,
@@ -15,6 +16,7 @@ const ContentPage = ({
15
16
  ...getControllerSearchProps(autocompleteEndpoint, `searchBar-content-page-${header}`, true, autocompleteQueryParams),
16
17
  controller: bookmarkController,
17
18
  };
19
+ const searchBarKey = useClearSearch({ updateSearchQuery });
18
20
  return (
19
21
  <Grid bsClass="container-fluid">
20
22
  <Row>
@@ -27,6 +29,7 @@ const ContentPage = ({
27
29
  <Form className="toolbar-pf-actions">
28
30
  <FormGroup className="toolbar-pf toolbar-pf-filter">
29
31
  <SearchBar
32
+ key={searchBarKey}
30
33
  data={searchDataProp}
31
34
  onSearch={onSearch}
32
35
  onSearchChange={updateSearchQuery}
@@ -49,7 +52,7 @@ const ContentPage = ({
49
52
  );
50
53
  };
51
54
 
52
- ContentPage.propTypes = {
55
+ GenericContentPage.propTypes = {
53
56
  header: PropTypes.string.isRequired,
54
57
  content: PropTypes.shape({}).isRequired,
55
58
  tableSchema: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
@@ -62,10 +65,10 @@ ContentPage.propTypes = {
62
65
  bookmarkController: PropTypes.string,
63
66
  };
64
67
 
65
- ContentPage.defaultProps = {
68
+ GenericContentPage.defaultProps = {
66
69
  autocompleteEndpoint: undefined,
67
70
  autocompleteQueryParams: undefined,
68
71
  bookmarkController: undefined,
69
72
  };
70
73
 
71
- export default ContentPage;
74
+ export default GenericContentPage;
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
2
  import { shallow } from 'enzyme';
3
3
  import toJson from 'enzyme-to-json';
4
- import ContentTable from '../ContentTable';
5
4
  import { LoadingState } from '../../../components/LoadingState';
6
5
  import { Table } from '../../../components/pf3Table';
6
+ import ContentTable from '../ContentTable';
7
7
 
8
8
  describe('Content Table', () => {
9
9
  it('should render and contain appropriate components', async () => {
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import { act } from 'react-test-renderer';
3
+ import { renderWithRedux, patientlyWaitFor } from 'react-testing-lib-wrapper';
4
+ import { CONTENT_KEY } from '../../../scenes/Content/ContentConstants';
5
+ import GenericContentPage from '../GenericContentPage';
6
+
7
+ const renderOptions = () => ({
8
+ apiNamespace: CONTENT_KEY,
9
+ });
10
+
11
+ test('Can render the basic component with no data', async (done) => {
12
+ const contentHeader = 'Content Header';
13
+ const content = { results: [] };
14
+ const onSearch = jest.fn();
15
+ const updateSearchQuery = jest.fn();
16
+ const searchQuery = '';
17
+ const onPaginationChange = jest.fn();
18
+ const TableSchema = [];
19
+ const bookmarkController = 'module_streams';
20
+
21
+ const { getByText } = renderWithRedux(<GenericContentPage
22
+ header={contentHeader}
23
+ content={content}
24
+ tableSchema={TableSchema}
25
+ onSearch={onSearch}
26
+ updateSearchQuery={updateSearchQuery}
27
+ initialInputValue={searchQuery}
28
+ onPaginationChange={onPaginationChange}
29
+ bookmarkController={bookmarkController}
30
+ />, renderOptions());
31
+
32
+ await patientlyWaitFor(() =>
33
+ expect(getByText(contentHeader)).toBeInTheDocument());
34
+ act(done);
35
+ });
@@ -0,0 +1,70 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { SearchAutocomplete } from 'foremanReact/components/SearchBar/SearchAutocomplete';
4
+ import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
5
+ import { STATUS } from 'foremanReact/constants';
6
+ import { noop } from 'foremanReact/common/helpers';
7
+
8
+ const SearchText = ({
9
+ data: {
10
+ autocomplete: { url, apiParams } = { url: '' },
11
+ disabled,
12
+ },
13
+ initialQuery,
14
+ onSearchChange,
15
+ name,
16
+ }) => {
17
+ const [search, setSearch] = useState(initialQuery || '');
18
+ const getAPIparams = input => ({ ...apiParams(input) });
19
+ const { response, status, setAPIOptions } = useAPI('get', url, {
20
+ params: getAPIparams(search),
21
+ });
22
+ const onChange = (newValue) => {
23
+ onSearchChange(newValue);
24
+ setSearch(newValue);
25
+ setAPIOptions({ params: { ...getAPIparams(newValue) } });
26
+ };
27
+ const error =
28
+ status === STATUS.ERROR || response?.[0]?.error
29
+ ? response?.[0]?.error || response.message
30
+ : null;
31
+
32
+ let results = [];
33
+ if (Array.isArray(response) && !error) {
34
+ results = response.map(item => ({ label: item, category: '' }));
35
+ }
36
+
37
+ return (
38
+ <div className="foreman-search-text">
39
+ <SearchAutocomplete
40
+ results={results}
41
+ onSearchChange={onChange}
42
+ value={search}
43
+ disabled={disabled}
44
+ error={error}
45
+ name={name}
46
+ />
47
+ </div>
48
+ );
49
+ };
50
+
51
+ SearchText.propTypes = {
52
+ data: PropTypes.shape({
53
+ autocomplete: PropTypes.shape({
54
+ url: PropTypes.string.isRequired,
55
+ apiParams: PropTypes.func,
56
+ }).isRequired,
57
+ disabled: PropTypes.bool,
58
+ }).isRequired,
59
+ initialQuery: PropTypes.string,
60
+ onSearchChange: PropTypes.func,
61
+ name: PropTypes.string,
62
+ };
63
+
64
+ SearchText.defaultProps = {
65
+ initialQuery: '',
66
+ onSearchChange: noop,
67
+ name: null,
68
+ };
69
+
70
+ export default SearchText;
@@ -14,7 +14,7 @@ import { translate as __ } from 'foremanReact/common/I18n';
14
14
  import { CubeIcon, ExclamationCircleIcon, SearchIcon, CheckCircleIcon, PlusCircleIcon } from '@patternfly/react-icons';
15
15
  import { global_danger_color_200 as dangerColor, global_success_color_100 as successColor } from '@patternfly/react-tokens';
16
16
  import { useDispatch, useSelector } from 'react-redux';
17
- import { selectHostDetailsClearSearch } from '../extensions/HostDetails/HostDetailsSelectors';
17
+ import { selectSearchBarClearSearch } from '../extensions/SearchBar/SearchBarSelectors';
18
18
 
19
19
  const KatelloEmptyStateIcon = ({
20
20
  error, search, customIcon, happyIcon,
@@ -51,7 +51,7 @@ const EmptyStateMessage = ({
51
51
  const defaultSecondaryActionText = searchIsActive ? __('Clear search') : __('Clear filters');
52
52
  const secondaryActionText = secondaryActionTextOverride || defaultSecondaryActionText;
53
53
  const dispatch = useDispatch();
54
- const clearSearch = useSelector(selectHostDetailsClearSearch);
54
+ const clearSearch = useSelector(selectSearchBarClearSearch);
55
55
  const showSecondaryActionAnchor = showSecondaryAction && secondaryActionLink;
56
56
  const handleClick = () => {
57
57
  if (searchIsActive) {
@@ -14,6 +14,7 @@ import MainTable from './MainTable';
14
14
  import { getPageStats } from './helpers';
15
15
  import SelectAllCheckbox from '../SelectAllCheckbox';
16
16
  import { orgId } from '../../services/api';
17
+ import { useClearSearch } from '../extensions/SearchBar/SearchBarHooks';
17
18
 
18
19
  /* Patternfly 4 table wrapper */
19
20
  const TableWrapper = ({
@@ -132,6 +133,8 @@ const TableWrapper = ({
132
133
  spawnFetch();
133
134
  }, [searchQuery, spawnFetch, additionalListeners]);
134
135
 
136
+ const searchBarKey = useClearSearch({ updateSearchQuery });
137
+
135
138
  // If the new page wouldn't exist because of a perPage change,
136
139
  // we should set the current page to the last page.
137
140
  const validatePagination = (data) => {
@@ -194,6 +197,7 @@ const TableWrapper = ({
194
197
  data={searchDataProp}
195
198
  initialQuery={searchQuery}
196
199
  onSearch={search => updateSearchQuery(search)}
200
+ key={searchBarKey}
197
201
  />
198
202
  </FlexItem>
199
203
  }
@@ -1,3 +1,2 @@
1
- export const SET_CLEAR_SEARCH = 'SET_CLEAR_SEARCH';
2
1
  const HOST_DETAILS = 'HOST_DETAILS';
3
2
  export default HOST_DETAILS;
@@ -14,9 +14,3 @@ export const selectHostDetailsStatus = state =>
14
14
 
15
15
  export const selectHostDetailsError = state =>
16
16
  selectAPIError(state, HOST_DETAILS_KEY);
17
-
18
- export const selectHostDetailsState = state =>
19
- state.katello.hostDetails;
20
-
21
- export const selectHostDetailsClearSearch = state =>
22
- selectHostDetailsState(state).clearSearch;
@@ -49,8 +49,8 @@ import {
49
49
  const moduleStreamSupported = ({ os, version }) =>
50
50
  os.match(/RedHat|RHEL|CentOS|Rocky|AlmaLinux|OracleLinux/i) && Number(version) > 7;
51
51
  export const hideModuleStreamsTab = ({ hostDetails }) => {
52
- const osMatch = hostDetails?.operatingsystem_name?.match(/(\w+) (\d+)/);
53
- if (!osMatch) return true;
52
+ const osMatch = hostDetails?.operatingsystem_name?.match(/(\D+) (\d+)/);
53
+ if (!osMatch) return false;
54
54
  const [, os, version] = osMatch;
55
55
  return !(osMatch && moduleStreamSupported({ os, version }));
56
56
  };