Skip to content

Commit 020a2bd

Browse files
committed
Fixes #37946 - Add 'allow_other_types' option in errata filters
1 parent 8201a7a commit 020a2bd

File tree

8 files changed

+126
-27
lines changed

8 files changed

+126
-27
lines changed

app/controllers/katello/api/v2/content_view_filter_rules_controller.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def resource_class
4040
param :types, Array, :desc => N_("erratum: types (enhancement, bugfix, security)")
4141
param :date_type, String, :desc => N_("erratum: search using the 'Issued On' or 'Updated On' column of the errata. Values are 'issued'/'updated'")
4242
param :module_stream_ids, Array, :desc => N_("module stream ids")
43+
param :allow_other_types, :bool, :desc => N_("erratum: allow types not matching a valid errata type")
4344
def create
4445
rule_clazz = ContentViewFilter.rule_class_for(@filter)
4546

@@ -89,6 +90,7 @@ def show
8990
param :start_date, String, :desc => N_("erratum: start date (YYYY-MM-DD)")
9091
param :end_date, String, :desc => N_("erratum: end date (YYYY-MM-DD)")
9192
param :types, Array, :desc => N_("erratum: types (enhancement, bugfix, security)")
93+
param :allow_other_types, :bool, :desc => N_("erratum: allow types not matching a valid errata type")
9294
def update
9395
update_params = rule_params
9496
update_params[:name] = update_params[:name].first if update_params[:name]
@@ -136,7 +138,7 @@ def rule_params
136138

137139
@rule_params ||= params.fetch(:content_view_filter_rule, {}).
138140
permit(:uuid, :version, :min_version, :max_version, :architecture,
139-
:errata_id, :start_date, :end_date, :date_type,
141+
:errata_id, :start_date, :end_date, :date_type, :allow_other_types,
140142
:types => [], :module_stream_ids => [], :errata_ids => [], name: [])
141143
end
142144

app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ module Katello
22
module Validators
33
class ContentViewErratumFilterRuleValidator < ActiveModel::Validator
44
def validate(record)
5-
if record.errata_id.blank? && record.start_date.blank? && record.end_date.blank? && record.types.blank?
5+
if record.errata_id.blank? && record.start_date.blank? && record.end_date.blank? && record.types.blank? && record.allow_other_types == false
66
invalid_parameters = _("Invalid erratum filter rule specified, Must specify at least one of the following:" \
7-
" 'errata_id', 'start_date', 'end_date' or 'types'")
7+
" 'errata_id', 'start_date', 'end_date', 'types', or 'allow_other_types'")
88
record.errors.add(:base, invalid_parameters)
99
return
1010
end

app/models/katello/content_view_erratum_filter.rb

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,21 @@ def erratum_arel
9292
end
9393

9494
def types_clause
95+
# Create an array to store output clauses for quick type filtering later
96+
conditions = []
97+
98+
# Add clauses for types in the filter
9599
types = erratum_rules.first.types
96-
return if types.blank?
97-
errata_types_in(types)
100+
conditions << errata_types_in(types) unless types.blank?
101+
102+
# Add clauses for 'other' types
103+
conditions << errata_types_not_in(Erratum::TYPES) if erratum_rules.first.allow_other_types?
104+
105+
# Reduce the array of clauses to a single clause and return
106+
return if conditions.empty?
107+
conditions.reduce(nil) do |combined_clause, condition|
108+
combined_clause ? combined_clause.or(condition) : condition
109+
end
98110
end
99111

100112
def filter_by_id?
@@ -105,6 +117,10 @@ def errata_types_in(types)
105117
erratum_arel[:errata_type].in(types)
106118
end
107119

120+
def errata_types_not_in(types)
121+
erratum_arel[:errata_type].not_in(types)
122+
end
123+
108124
def errata_in(ids)
109125
erratum_arel[:errata_id].in(ids)
110126
end

app/views/katello/api/v2/content_view_filter_rules/show.json.rabl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ attributes :start_date, :if => lambda { |rule| rule.respond_to?(:start_date) &&
1313
attributes :end_date, :if => lambda { |rule| rule.respond_to?(:end_date) && !rule.end_date.blank? }
1414
attributes :architecture, :if => lambda { |rule| rule.respond_to?(:architecture) && !rule.architecture.blank? }
1515
attributes :types, :if => lambda { |rule| rule.respond_to?(:types) && !rule.types.blank? }
16+
attributes :allow_other_types, :if => lambda { |rule| rule.respond_to?(:allow_other_types) }
1617
attributes :date_type, :if => lambda { |rule| rule.respond_to?(:date_type) }
1718
attributes :module_stream_id, :if => lambda { |rule| rule.respond_to?(:module_stream_id) && !rule.module_stream_id.blank? }
1819
if @resource&.try(:module_stream)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class AddAllowOtherTypesToContentViewErratumFilterRules < ActiveRecord::Migration[6.1]
2+
def change
3+
add_column :katello_content_view_erratum_filter_rules, :allow_other_types, :boolean,
4+
:default => false, :null => false
5+
end
6+
end

test/actions/katello/repository_test.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ class DestroyTest < TestBase
235235
let(:in_use_repository) { katello_repositories(:fedora_17_no_arch) }
236236
let(:published_repository) { katello_repositories(:rhel_6_x86_64) }
237237
let(:published_fedora_repository) { katello_repositories(:fedora_17_x86_64) }
238+
let(:published_rhel7_repository) { katello_repositories(:rhel_7_no_arch) }
238239
let(:simplified_acs) { katello_alternate_content_sources(:yum_alternate_content_source) }
239240
def setup
240241
simplified_acs.products << published_repository.product
@@ -378,22 +379,24 @@ def setup
378379
assert_not_equal(0, simplified_acs.products.length)
379380
end
380381

381-
it 'plans ACS product removal when removing the deleting the last repo with URL' do
382-
::Katello::SmartProxyAlternateContentSource.create!(alternate_content_source_id: simplified_acs.id, smart_proxy_id: proxy.id, repository_id: published_repository.id)
382+
it 'plans ACS product removal when deleting the last repo with URL' do
383+
::Katello::SmartProxyAlternateContentSource.create!(alternate_content_source_id: simplified_acs.id, smart_proxy_id: proxy.id, repository_id: published_rhel7_repository.id)
383384
action = create_action action_class
384-
action.stubs(:action_subject).with(published_repository)
385+
action.stubs(:action_subject).with(published_rhel7_repository)
385386

386387
# manually remove the URLs from all repos in product except repository
387-
repository.product.repositories.each do |repo|
388-
repo.root.url = nil unless repo.id == repository.id
388+
testing_repo = repository.product.repositories[4]
389+
testing_repo.product.repositories.each do |repo|
390+
repo.root.url = nil unless repo.id == testing_repo.id || repo.root.id == testing_repo.root.id
391+
repo.root.save!(validate: false)
389392
end
390-
url_sum = repository.product.repositories.count do |repo|
393+
url_sum = testing_repo.product.repositories.count do |repo|
391394
repo.root.url.present?
392395
end
393396
assert_equal(1, url_sum) # double check there's only one URL left
394397

395398
# Since there is only one URL remaining, the product should be removed
396-
plan_action action, published_repository, remove_from_content_view_versions: true
399+
plan_action action, published_rhel7_repository, remove_from_content_view_versions: true
397400
simplified_acs.reload
398401
assert_equal(0, simplified_acs.products.length)
399402
end

test/models/content_view_erratum_filter_test.rb

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ def setup
66
@repo = katello_repositories(:fedora_17_x86_64)
77
end
88

9+
TYPICAL_TYPES_RESPONSE =
10+
" AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')".freeze
11+
912
def test_erratum_by_id_returns_arel_for_specified_errata_id
1013
erratum = katello_errata(:security)
1114
@repo.errata = [erratum]
@@ -24,7 +27,7 @@ def test_errata_by_start_date_returns_arel_for_errata_by_updated_date_and_errata
2427
filter = id_rule.filter
2528
filter.reload
2629

27-
assert_equal "\"katello_errata\".\"updated\" >= '#{start_date}' AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')",
30+
assert_equal "\"katello_errata\".\"updated\" >= '#{start_date}'" + TYPICAL_TYPES_RESPONSE,
2831
filter.generate_clauses(@repo).to_sql
2932
end
3033

@@ -35,7 +38,7 @@ def test_errata_by_start_date_returns_arel_for_errata_by_issued_date_and_errata_
3538
filter = id_rule.filter
3639
filter.reload
3740

38-
assert_equal "\"katello_errata\".\"issued\" >= '#{start_date}' AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')",
41+
assert_equal "\"katello_errata\".\"issued\" >= '#{start_date}'" + TYPICAL_TYPES_RESPONSE,
3942
filter.generate_clauses(@repo).to_sql
4043
end
4144

@@ -45,7 +48,7 @@ def test_errata_by_end_date_returns_arel_for_errata_by_updated_date_and_errata_t
4548
filter = id_rule.filter
4649
filter.reload
4750

48-
assert_equal "\"katello_errata\".\"updated\" <= '#{end_date}' AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')",
51+
assert_equal "\"katello_errata\".\"updated\" <= '#{end_date}'" + TYPICAL_TYPES_RESPONSE,
4952
filter.generate_clauses(@repo).to_sql
5053
end
5154

@@ -56,7 +59,7 @@ def test_errata_by_end_date_returns_arel_for_errata_by_issued_date_and_errata_ty
5659
filter = id_rule.filter
5760
filter.reload
5861

59-
assert_equal "\"katello_errata\".\"issued\" <= '#{end_date}' AND \"katello_errata\".\"errata_type\" IN ('bugfix', 'enhancement', 'security')",
62+
assert_equal "\"katello_errata\".\"issued\" <= '#{end_date}'" + TYPICAL_TYPES_RESPONSE,
6063
filter.generate_clauses(@repo).to_sql
6164
end
6265

@@ -69,6 +72,16 @@ def test_errata_by_type_returns_arel_by_errata_type
6972
filter.generate_clauses(@repo).to_sql
7073
end
7174

75+
def test_errata_by_type_returns_arel_by_errata_type_other
76+
id_rule = FactoryBot.create(:katello_content_view_erratum_filter_rule, :allow_other_types => true)
77+
id_rule.update!(types: [])
78+
filter = id_rule.filter
79+
filter.reload
80+
81+
assert_equal "\"katello_errata\".\"errata_type\" NOT IN ('security', 'bugfix', 'recommended', 'enhancement', 'optional')",
82+
filter.generate_clauses(@repo).to_sql
83+
end
84+
7285
def test_content_unit_pulp_ids_with_empty_errata_list_returns_empty_result
7386
rpm1 = @repo.rpms.first
7487
rpm2 = @repo.rpms.last
@@ -209,13 +222,16 @@ def test_content_unit_pulp_ids_by_issued_end_date_returns_pulp_hrefs
209222
end
210223

211224
def test_content_unit_pulp_ids_by_errata_type
212-
rpm1 = @repo.rpms.first
213-
rpm2 = @repo.rpms.last
225+
rpm1 = @repo.rpms[0]
226+
rpm2 = @repo.rpms[1]
227+
rpm3 = @repo.rpms[2]
214228

215229
erratum1 = Katello::Erratum.new(:pulp_id => "one", :errata_id => "ERRATA1", :errata_type => 'bugfix')
216230
erratum1.packages << Katello::ErratumPackage.new(:filename => rpm1.filename, :name => "e1", :nvrea => "e1")
217231
erratum2 = Katello::Erratum.new(:pulp_id => "two", :errata_id => "ERRATA2", :errata_type => 'security')
218232
erratum2.packages << Katello::ErratumPackage.new(:filename => rpm2.filename, :name => "e2", :nvrea => "e2")
233+
erratum3 = Katello::Erratum.new(:pulp_id => "three", :errata_id => "ERRATA3", :errata_type => 'not_recognized')
234+
erratum3.packages << Katello::ErratumPackage.new(:filename => rpm3.filename, :name => "e3", :nvrea => "e3")
219235

220236
@repo.errata = [erratum2]
221237
@repo.save!
@@ -226,5 +242,28 @@ def test_content_unit_pulp_ids_by_errata_type
226242

227243
assert_equal [rpm2.pulp_id], filter.content_unit_pulp_ids(@repo)
228244
end
245+
246+
def test_content_unit_pulp_ids_by_errata_type_other
247+
rpm1 = @repo.rpms[0]
248+
rpm2 = @repo.rpms[1]
249+
rpm3 = @repo.rpms[2]
250+
251+
erratum1 = Katello::Erratum.new(:pulp_id => "one", :errata_id => "ERRATA1", :errata_type => 'bugfix')
252+
erratum1.packages << Katello::ErratumPackage.new(:filename => rpm1.filename, :name => "e1", :nvrea => "e1")
253+
erratum2 = Katello::Erratum.new(:pulp_id => "two", :errata_id => "ERRATA2", :errata_type => 'security')
254+
erratum2.packages << Katello::ErratumPackage.new(:filename => rpm2.filename, :name => "e2", :nvrea => "e2")
255+
erratum3 = Katello::Erratum.new(:pulp_id => "three", :errata_id => "ERRATA3", :errata_type => 'not_recognized')
256+
erratum3.packages << Katello::ErratumPackage.new(:filename => rpm3.filename, :name => "e3", :nvrea => "e3")
257+
258+
@repo.errata = [erratum3]
259+
@repo.save!
260+
261+
id_rule = FactoryBot.create(:katello_content_view_erratum_filter_rule, :allow_other_types => true)
262+
id_rule.update!(types: [])
263+
filter = id_rule.filter
264+
filter.reload
265+
266+
assert_equal [rpm3.pulp_id], filter.content_unit_pulp_ids(@repo)
267+
end
229268
end
230269
end

webpack/scenes/ContentViews/Details/Filters/CVErrataDateFilterContent.js

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useEffect } from 'react';
22
import PropTypes from 'prop-types';
3-
import { isEqual, sortBy, capitalize } from 'lodash';
3+
import { isEqual, sortBy, capitalize, initial } from 'lodash';
44
import { shallowEqual, useSelector, useDispatch } from 'react-redux';
55
import { Link, useHistory } from 'react-router-dom';
66
import {
@@ -46,30 +46,37 @@ const CVErrataDateFilterContent = ({
4646
selectCVFilterDetails(state, cvId, filterId), shallowEqual);
4747
const { repositories = [], rules } = filterDetails;
4848
const [{
49-
id, types, start_date: ruleStartDate, end_date: ruleEndDate, date_type: ruleDateType,
49+
id, types, allow_other_types, start_date: ruleStartDate, end_date: ruleEndDate, date_type: ruleDateType
5050
} = {}] = rules;
5151
const { permissions } = details;
5252
const [startDate, setStartDate] = useState(convertAPIDateToUIFormat(ruleStartDate));
5353
const [endDate, setEndDate] = useState(convertAPIDateToUIFormat(ruleEndDate));
5454
const [dateType, setDateType] = useState(ruleDateType);
5555
const [dateTypeSelectOpen, setDateTypeSelectOpen] = useState(false);
5656
const [typeSelectOpen, setTypeSelectOpen] = useState(false);
57-
const [selectedTypes, setSelectedTypes] = useState(types);
57+
const [selectedTypes, setSelectedTypes] = useState(() => {
58+
if (allow_other_types) {
59+
return[...types, 'other'];
60+
}
61+
return types;
62+
});
5863
const dispatch = useDispatch();
5964
const [activeTabKey, setActiveTabKey] = useState(0);
6065
const [startEntry, setStartEntry] = useState(false);
6166
const [endEntry, setEndEntry] = useState(false);
6267

6368
const onSave = () => {
69+
console.log('contains other', selectedTypes.includes('other'));
6470
dispatch(editCVFilterRule(
6571
filterId,
6672
{
6773
id,
6874
content_view_filter_id: filterId,
6975
start_date: startDate && startDate !== '' ? dateParse(startDate) : null,
7076
end_date: endDate && endDate !== '' ? dateParse(endDate) : null,
71-
types: selectedTypes,
77+
types: selectedTypes.filter(e => e !== 'other'),
7278
date_type: dateType,
79+
allow_other_types: selectedTypes.includes('other'),
7380
},
7481
() => {
7582
dispatch({ type: CONTENT_VIEW_NEEDS_PUBLISH });
@@ -81,15 +88,31 @@ const CVErrataDateFilterContent = ({
8188
const resetFilters = () => {
8289
setStartDate(convertAPIDateToUIFormat(ruleStartDate));
8390
setEndDate(convertAPIDateToUIFormat(ruleEndDate));
84-
setSelectedTypes(types);
8591
setDateType(ruleDateType);
92+
setSelectedTypes(getInitialSelectedTypes());
93+
};
94+
95+
const getInitialSelectedTypes = () => {
96+
if (allow_other_types) {
97+
return[...types, 'other'];
98+
} else {
99+
return types;
100+
}
86101
};
87102

88103
const onTypeSelect = (selection) => {
104+
let updatedSelectedTypes;
89105
if (selectedTypes.includes(selection)) {
106+
// If the selection is the only selection remaining, do not allow it to be removed
90107
if (selectedTypes.length === 1) return;
91-
setSelectedTypes(selectedTypes.filter(e => e !== selection));
92-
} else setSelectedTypes([...selectedTypes, selection]);
108+
109+
// Filter out the current selection from the selected types to update
110+
updatedSelectedTypes = selectedTypes.filter(e => e !== selection);
111+
} else {
112+
updatedSelectedTypes = [...selectedTypes, selection];
113+
}
114+
115+
setSelectedTypes(updatedSelectedTypes);
93116
};
94117

95118
const singleSelection = selection => (selectedTypes.length === 1
@@ -99,7 +122,7 @@ const CVErrataDateFilterContent = ({
99122
(
100123
isEqual(convertAPIDateToUIFormat(ruleStartDate), startDate) &&
101124
isEqual(convertAPIDateToUIFormat(ruleEndDate), endDate) &&
102-
isEqual(sortBy(types), sortBy(selectedTypes)) &&
125+
isEqual(sortBy(getInitialSelectedTypes()), sortBy(selectedTypes)) &&
103126
isEqual(ruleDateType, dateType)
104127
);
105128

@@ -171,14 +194,23 @@ const CVErrataDateFilterContent = ({
171194
{__('Bugfix')}
172195
</p>
173196
</SelectOption>
197+
<SelectOption
198+
isDisabled={singleSelection('other') || !hasPermission(permissions, 'edit_content_views')}
199+
key="other"
200+
value="other"
201+
>
202+
<p style={{ marginTop: '4px' }}>
203+
{__('Other')}
204+
</p>
205+
</SelectOption>
174206
</Select>
175207
</FlexItem>
176208
<FlexItem span={1} spacer={{ default: 'spacerNone' }}>
177209
{(selectedTypes.length === 1) &&
178210
<Tooltip
179211
position="top"
180212
content={
181-
__('Atleast one errata type needs to be selected.')
213+
__('At least one errata type option needs to be selected.')
182214
}
183215
>
184216
<OutlinedQuestionCircleIcon />

0 commit comments

Comments
 (0)