From 41195d94e5e8735b7eeaa4717aedcb49fc0f4a2a Mon Sep 17 00:00:00 2001
From: Quinn James
Date: Wed, 23 Oct 2024 21:02:24 +0000
Subject: [PATCH] Fixes #37946 - Add 'allow_other_types' option in errata
filters
---
.../content_view_filter_rules_controller.rb | 4 +-
...tent_view_erratum_filter_rule_validator.rb | 4 +-
.../katello/content_view_erratum_filter.rb | 20 +++++++-
.../content_view_filter_rules/show.json.rabl | 1 +
...es_to_content_view_erratum_filter_rules.rb | 6 +++
.../content_view_erratum_filter_test.rb | 51 ++++++++++++++++---
.../Filters/CVErrataDateFilterContent.js | 43 +++++++++++++---
7 files changed, 111 insertions(+), 18 deletions(-)
create mode 100644 db/migrate/20241120213713_add_allow_other_types_to_content_view_erratum_filter_rules.rb
diff --git a/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb b/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb
index 0b61607da58..5054a94e332 100644
--- a/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb
+++ b/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb
@@ -40,6 +40,7 @@ def resource_class
param :types, Array, :desc => N_("erratum: types (enhancement, bugfix, security)")
param :date_type, String, :desc => N_("erratum: search using the 'Issued On' or 'Updated On' column of the errata. Values are 'issued'/'updated'")
param :module_stream_ids, Array, :desc => N_("module stream ids")
+ param :allow_other_types, :bool, :desc => N_("erratum: allow types not matching a valid errata type")
def create
rule_clazz = ContentViewFilter.rule_class_for(@filter)
@@ -89,6 +90,7 @@ def show
param :start_date, String, :desc => N_("erratum: start date (YYYY-MM-DD)")
param :end_date, String, :desc => N_("erratum: end date (YYYY-MM-DD)")
param :types, Array, :desc => N_("erratum: types (enhancement, bugfix, security)")
+ param :allow_other_types, :bool, :desc => N_("erratum: allow types not matching a valid errata type")
def update
update_params = rule_params
update_params[:name] = update_params[:name].first if update_params[:name]
@@ -136,7 +138,7 @@ def rule_params
@rule_params ||= params.fetch(:content_view_filter_rule, {}).
permit(:uuid, :version, :min_version, :max_version, :architecture,
- :errata_id, :start_date, :end_date, :date_type,
+ :errata_id, :start_date, :end_date, :date_type, :allow_other_types,
:types => [], :module_stream_ids => [], :errata_ids => [], name: [])
end
diff --git a/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb b/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb
index 3eee2a8f252..7b512488c6d 100644
--- a/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb
+++ b/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb
@@ -2,9 +2,9 @@ module Katello
module Validators
class ContentViewErratumFilterRuleValidator < ActiveModel::Validator
def validate(record)
- if record.errata_id.blank? && record.start_date.blank? && record.end_date.blank? && record.types.blank?
+ if record.errata_id.blank? && record.start_date.blank? && record.end_date.blank? && record.types.blank? && record.allow_other_types == false
invalid_parameters = _("Invalid erratum filter rule specified, Must specify at least one of the following:" \
- " 'errata_id', 'start_date', 'end_date' or 'types'")
+ " 'errata_id', 'start_date', 'end_date', 'types', or 'allow_other_types'")
record.errors.add(:base, invalid_parameters)
return
end
diff --git a/app/models/katello/content_view_erratum_filter.rb b/app/models/katello/content_view_erratum_filter.rb
index ee8d9f98103..a6e39a65c19 100644
--- a/app/models/katello/content_view_erratum_filter.rb
+++ b/app/models/katello/content_view_erratum_filter.rb
@@ -92,9 +92,21 @@ def erratum_arel
end
def types_clause
+ # Create an array to store output clauses for quick type filtering later
+ conditions = []
+
+ # Add clauses for types in the filter
types = erratum_rules.first.types
- return if types.blank?
- errata_types_in(types)
+ conditions << errata_types_in(types) unless types.blank?
+
+ # Add clauses for 'other' types
+ conditions << errata_types_not_in(Erratum::TYPES) if erratum_rules.first.allow_other_types?
+
+ # Reduce the array of clauses to a single clause and return
+ return if conditions.empty?
+ conditions.reduce(nil) do |combined_clause, condition|
+ combined_clause ? combined_clause.or(condition) : condition
+ end
end
def filter_by_id?
@@ -105,6 +117,10 @@ def errata_types_in(types)
erratum_arel[:errata_type].in(types)
end
+ def errata_types_not_in(types)
+ erratum_arel[:errata_type].not_in(types)
+ end
+
def errata_in(ids)
erratum_arel[:errata_id].in(ids)
end
diff --git a/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl b/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl
index 38c1b04a2b1..d7024c206e7 100644
--- a/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl
+++ b/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl
@@ -13,6 +13,7 @@ attributes :start_date, :if => lambda { |rule| rule.respond_to?(:start_date) &&
attributes :end_date, :if => lambda { |rule| rule.respond_to?(:end_date) && !rule.end_date.blank? }
attributes :architecture, :if => lambda { |rule| rule.respond_to?(:architecture) && !rule.architecture.blank? }
attributes :types, :if => lambda { |rule| rule.respond_to?(:types) && !rule.types.blank? }
+attributes :allow_other_types, :if => lambda { |rule| rule.respond_to?(:allow_other_types) }
attributes :date_type, :if => lambda { |rule| rule.respond_to?(:date_type) }
attributes :module_stream_id, :if => lambda { |rule| rule.respond_to?(:module_stream_id) && !rule.module_stream_id.blank? }
if @resource&.try(:module_stream)
diff --git a/db/migrate/20241120213713_add_allow_other_types_to_content_view_erratum_filter_rules.rb b/db/migrate/20241120213713_add_allow_other_types_to_content_view_erratum_filter_rules.rb
new file mode 100644
index 00000000000..adac4544e52
--- /dev/null
+++ b/db/migrate/20241120213713_add_allow_other_types_to_content_view_erratum_filter_rules.rb
@@ -0,0 +1,6 @@
+class AddAllowOtherTypesToContentViewErratumFilterRules < ActiveRecord::Migration[6.1]
+ def change
+ add_column :katello_content_view_erratum_filter_rules, :allow_other_types, :boolean,
+ :default => false, :null => false
+ end
+end
diff --git a/test/models/content_view_erratum_filter_test.rb b/test/models/content_view_erratum_filter_test.rb
index 02491f8baa9..a4439339d70 100644
--- a/test/models/content_view_erratum_filter_test.rb
+++ b/test/models/content_view_erratum_filter_test.rb
@@ -6,6 +6,9 @@ def setup
@repo = katello_repositories(:fedora_17_x86_64)
end
+ TYPICAL_TYPES_RESPONSE =
+ " AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')".freeze
+
def test_erratum_by_id_returns_arel_for_specified_errata_id
erratum = katello_errata(:security)
@repo.errata = [erratum]
@@ -24,7 +27,7 @@ def test_errata_by_start_date_returns_arel_for_errata_by_updated_date_and_errata
filter = id_rule.filter
filter.reload
- assert_equal "\"katello_errata\".\"updated\" >= '#{start_date}' AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')",
+ assert_equal "\"katello_errata\".\"updated\" >= '#{start_date}'" + TYPICAL_TYPES_RESPONSE,
filter.generate_clauses(@repo).to_sql
end
@@ -35,7 +38,7 @@ def test_errata_by_start_date_returns_arel_for_errata_by_issued_date_and_errata_
filter = id_rule.filter
filter.reload
- assert_equal "\"katello_errata\".\"issued\" >= '#{start_date}' AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')",
+ assert_equal "\"katello_errata\".\"issued\" >= '#{start_date}'" + TYPICAL_TYPES_RESPONSE,
filter.generate_clauses(@repo).to_sql
end
@@ -45,7 +48,7 @@ def test_errata_by_end_date_returns_arel_for_errata_by_updated_date_and_errata_t
filter = id_rule.filter
filter.reload
- assert_equal "\"katello_errata\".\"updated\" <= '#{end_date}' AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')",
+ assert_equal "\"katello_errata\".\"updated\" <= '#{end_date}'" + TYPICAL_TYPES_RESPONSE,
filter.generate_clauses(@repo).to_sql
end
@@ -56,7 +59,7 @@ def test_errata_by_end_date_returns_arel_for_errata_by_issued_date_and_errata_ty
filter = id_rule.filter
filter.reload
- assert_equal "\"katello_errata\".\"issued\" <= '#{end_date}' AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')",
+ assert_equal "\"katello_errata\".\"issued\" <= '#{end_date}'" + TYPICAL_TYPES_RESPONSE,
filter.generate_clauses(@repo).to_sql
end
@@ -69,6 +72,16 @@ def test_errata_by_type_returns_arel_by_errata_type
filter.generate_clauses(@repo).to_sql
end
+ def test_errata_by_type_returns_arel_by_errata_type_other
+ id_rule = FactoryBot.create(:katello_content_view_erratum_filter_rule, :allow_other_types => true)
+ id_rule.update!(types: [])
+ filter = id_rule.filter
+ filter.reload
+
+ assert_equal "\"katello_errata\".\"errata_type\" NOT IN ('security', 'bugfix', 'recommended', 'enhancement', 'optional')",
+ filter.generate_clauses(@repo).to_sql
+ end
+
def test_content_unit_pulp_ids_with_empty_errata_list_returns_empty_result
rpm1 = @repo.rpms.first
rpm2 = @repo.rpms.last
@@ -209,13 +222,16 @@ def test_content_unit_pulp_ids_by_issued_end_date_returns_pulp_hrefs
end
def test_content_unit_pulp_ids_by_errata_type
- rpm1 = @repo.rpms.first
- rpm2 = @repo.rpms.last
+ rpm1 = @repo.rpms[0]
+ rpm2 = @repo.rpms[1]
+ rpm3 = @repo.rpms[2]
erratum1 = Katello::Erratum.new(:pulp_id => "one", :errata_id => "ERRATA1", :errata_type => 'bugfix')
erratum1.packages << Katello::ErratumPackage.new(:filename => rpm1.filename, :name => "e1", :nvrea => "e1")
erratum2 = Katello::Erratum.new(:pulp_id => "two", :errata_id => "ERRATA2", :errata_type => 'security')
erratum2.packages << Katello::ErratumPackage.new(:filename => rpm2.filename, :name => "e2", :nvrea => "e2")
+ erratum3 = Katello::Erratum.new(:pulp_id => "three", :errata_id => "ERRATA3", :errata_type => 'not_recognized')
+ erratum3.packages << Katello::ErratumPackage.new(:filename => rpm3.filename, :name => "e3", :nvrea => "e3")
@repo.errata = [erratum2]
@repo.save!
@@ -226,5 +242,28 @@ def test_content_unit_pulp_ids_by_errata_type
assert_equal [rpm2.pulp_id], filter.content_unit_pulp_ids(@repo)
end
+
+ def test_content_unit_pulp_ids_by_errata_type_other
+ rpm1 = @repo.rpms[0]
+ rpm2 = @repo.rpms[1]
+ rpm3 = @repo.rpms[2]
+
+ erratum1 = Katello::Erratum.new(:pulp_id => "one", :errata_id => "ERRATA1", :errata_type => 'bugfix')
+ erratum1.packages << Katello::ErratumPackage.new(:filename => rpm1.filename, :name => "e1", :nvrea => "e1")
+ erratum2 = Katello::Erratum.new(:pulp_id => "two", :errata_id => "ERRATA2", :errata_type => 'security')
+ erratum2.packages << Katello::ErratumPackage.new(:filename => rpm2.filename, :name => "e2", :nvrea => "e2")
+ erratum3 = Katello::Erratum.new(:pulp_id => "three", :errata_id => "ERRATA3", :errata_type => 'not_recognized')
+ erratum3.packages << Katello::ErratumPackage.new(:filename => rpm3.filename, :name => "e3", :nvrea => "e3")
+
+ @repo.errata = [erratum3]
+ @repo.save!
+
+ id_rule = FactoryBot.create(:katello_content_view_erratum_filter_rule, :allow_other_types => true)
+ id_rule.update!(types: [])
+ filter = id_rule.filter
+ filter.reload
+
+ assert_equal [rpm3.pulp_id], filter.content_unit_pulp_ids(@repo)
+ end
end
end
diff --git a/webpack/scenes/ContentViews/Details/Filters/CVErrataDateFilterContent.js b/webpack/scenes/ContentViews/Details/Filters/CVErrataDateFilterContent.js
index 8eb5c2204d9..eec33cb6263 100644
--- a/webpack/scenes/ContentViews/Details/Filters/CVErrataDateFilterContent.js
+++ b/webpack/scenes/ContentViews/Details/Filters/CVErrataDateFilterContent.js
@@ -46,7 +46,12 @@ const CVErrataDateFilterContent = ({
selectCVFilterDetails(state, cvId, filterId), shallowEqual);
const { repositories = [], rules } = filterDetails;
const [{
- id, types, start_date: ruleStartDate, end_date: ruleEndDate, date_type: ruleDateType,
+ id,
+ types,
+ allow_other_types: ruleAllowOtherTypes,
+ start_date: ruleStartDate,
+ end_date: ruleEndDate,
+ date_type: ruleDateType,
} = {}] = rules;
const { permissions } = details;
const [startDate, setStartDate] = useState(convertAPIDateToUIFormat(ruleStartDate));
@@ -54,12 +59,20 @@ const CVErrataDateFilterContent = ({
const [dateType, setDateType] = useState(ruleDateType);
const [dateTypeSelectOpen, setDateTypeSelectOpen] = useState(false);
const [typeSelectOpen, setTypeSelectOpen] = useState(false);
- const [selectedTypes, setSelectedTypes] = useState(types);
const dispatch = useDispatch();
const [activeTabKey, setActiveTabKey] = useState(0);
const [startEntry, setStartEntry] = useState(false);
const [endEntry, setEndEntry] = useState(false);
+ const getInitialSelectedTypes = () => {
+ if (ruleAllowOtherTypes) {
+ return [...types, 'other'];
+ }
+ return types;
+ };
+
+ const [selectedTypes, setSelectedTypes] = useState(getInitialSelectedTypes());
+
const onSave = () => {
dispatch(editCVFilterRule(
filterId,
@@ -68,8 +81,9 @@ const CVErrataDateFilterContent = ({
content_view_filter_id: filterId,
start_date: startDate && startDate !== '' ? dateParse(startDate) : null,
end_date: endDate && endDate !== '' ? dateParse(endDate) : null,
- types: selectedTypes,
+ types: selectedTypes.filter(e => e !== 'other'),
date_type: dateType,
+ allow_other_types: selectedTypes.includes('other'),
},
() => {
dispatch({ type: CONTENT_VIEW_NEEDS_PUBLISH });
@@ -81,15 +95,21 @@ const CVErrataDateFilterContent = ({
const resetFilters = () => {
setStartDate(convertAPIDateToUIFormat(ruleStartDate));
setEndDate(convertAPIDateToUIFormat(ruleEndDate));
- setSelectedTypes(types);
setDateType(ruleDateType);
+ setSelectedTypes(getInitialSelectedTypes());
};
const onTypeSelect = (selection) => {
if (selectedTypes.includes(selection)) {
+ // If the selection is the only selection remaining, do not allow it to be removed
if (selectedTypes.length === 1) return;
+
+ // Filter out the current selection to deselect it
setSelectedTypes(selectedTypes.filter(e => e !== selection));
- } else setSelectedTypes([...selectedTypes, selection]);
+ } else {
+ // Add the selection to the selected types
+ setSelectedTypes([...selectedTypes, selection]);
+ }
};
const singleSelection = selection => (selectedTypes.length === 1
@@ -99,7 +119,7 @@ const CVErrataDateFilterContent = ({
(
isEqual(convertAPIDateToUIFormat(ruleStartDate), startDate) &&
isEqual(convertAPIDateToUIFormat(ruleEndDate), endDate) &&
- isEqual(sortBy(types), sortBy(selectedTypes)) &&
+ isEqual(sortBy(getInitialSelectedTypes()), sortBy(selectedTypes)) &&
isEqual(ruleDateType, dateType)
);
@@ -171,6 +191,15 @@ const CVErrataDateFilterContent = ({
{__('Bugfix')}
+
+
+ {__('Other')}
+
+
@@ -178,7 +207,7 @@ const CVErrataDateFilterContent = ({