Skip to content

Commit 8fc9803

Browse files
committed
RHINENG-21755: use null with multiple severity filters
1 parent 8299785 commit 8fc9803

3 files changed

Lines changed: 104 additions & 23 deletions

File tree

manager/controllers/advisories_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,23 @@ func TestAdvisoriesFilterSeverityNull(t *testing.T) {
229229
assert.Equal(t, "CUSTOM-12", output.Data[0].ID)
230230
}
231231

232+
func TestAdvisoriesFilterSeverityInWithNull(t *testing.T) {
233+
output := testAdvisories(t, "/?sort=id&filter[severity]=in:2,null")
234+
assert.Equal(t, 11, len(output.Data))
235+
foundSeverity2 := false
236+
foundNull := false
237+
for _, adv := range output.Data {
238+
if adv.ID == "RH-3" {
239+
foundSeverity2 = true
240+
}
241+
if adv.ID == "CUSTOM-12" {
242+
foundNull = true
243+
}
244+
}
245+
assert.True(t, foundSeverity2)
246+
assert.True(t, foundNull)
247+
}
248+
232249
func TestAdvisoriesFilterApplicableSystems(t *testing.T) {
233250
output := testAdvisories(t, "/?filter[applicable_systems]=gt:1")
234251
assert.Equal(t, 1, len(output.Data))

manager/controllers/filter.go

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,10 @@ func checkValueCount(operator string, nValues int) bool {
7777
}
7878

7979
// Convert a single filter to where clauses
80+
// nolint funlen
8081
func (t *FilterData) ToWhere(fieldName string, attributes database.AttrMap) (string, []any, error) {
8182
var err error
82-
transformedOperator, transformedValues := t.transformParams(fieldName)
83+
transformedOperator, transformedValues, hasNull := t.transformParams(fieldName)
8384
var values = make([]any, len(transformedValues))
8485
for i, v := range transformedValues {
8586
fieldInfo, found := attributes[fieldName]
@@ -115,8 +116,16 @@ func (t *FilterData) ToWhere(fieldName string, attributes database.AttrMap) (str
115116
case OpBetween:
116117
return fmt.Sprintf("%s BETWEEN ? AND ? ", attributes[fieldName].DataQuery), values, nil
117118
case OpIn:
119+
if hasNull {
120+
return fmt.Sprintf("(%s IN ? OR %s IS NULL) ",
121+
attributes[fieldName].DataQuery, attributes[fieldName].DataQuery), []any{values}, nil
122+
}
118123
return fmt.Sprintf("%s IN (?) ", attributes[fieldName].DataQuery), []any{values}, nil
119124
case OpNotIn:
125+
if hasNull {
126+
return fmt.Sprintf("(%s NOT IN ? AND %s IS NOT NULL) ",
127+
attributes[fieldName].DataQuery, attributes[fieldName].DataQuery), []any{values}, nil
128+
}
120129
return fmt.Sprintf("%s NOT IN (?) ", attributes[fieldName].DataQuery), []any{values}, nil
121130
case OpNull:
122131
return fmt.Sprintf("%s IS NULL ", attributes[fieldName].DataQuery), []any{}, nil
@@ -127,39 +136,65 @@ func (t *FilterData) ToWhere(fieldName string, attributes database.AttrMap) (str
127136
}
128137
}
129138

130-
func (t *FilterData) transformParams(fieldName string) (transformedOperator string, transformedValues []string) {
131-
if len(t.Values) == 1 && (t.Values[0] == "null" || t.Values[0] == "notnull") {
139+
func (t *FilterData) transformParams(fieldName string) (
140+
transformedOperator string, transformedValues []string, hasNull bool) {
141+
if len(t.Values) == 1 && (t.Values[0] == OpNull || t.Values[0] == OpNotNull) {
132142
// handle special cases filter=null and filter=notnull
133-
return t.Values[0], []string{"0"}
143+
return t.Values[0], []string{"0"}, false
134144
}
135-
if fieldName != "advisory_type_name" {
136-
// no change
137-
return t.Operator, t.Values
145+
// special case, in advisory_type_name filter expand "other" into list of what we mean by other
146+
valuesToProcess := t.Values
147+
operatorToUse := t.Operator
148+
if fieldName == "advisory_type_name" {
149+
transformedValues = make([]string, 0, len(t.Values))
150+
for _, originalValue := range t.Values {
151+
if originalValue == "other" {
152+
transformedValues = append(transformedValues, database.OtherAdvisoryTypes...)
153+
} else {
154+
transformedValues = append(transformedValues, originalValue)
155+
}
156+
}
157+
// If "other" was expanded, use the expanded list for further processing
158+
if len(transformedValues) != len(t.Values) {
159+
valuesToProcess = transformedValues
160+
// Convert operator if needed
161+
switch t.Operator {
162+
case OpEq:
163+
operatorToUse = OpIn
164+
case OpNeq:
165+
operatorToUse = OpNotIn
166+
default:
167+
operatorToUse = t.Operator
168+
}
169+
}
138170
}
139171

140-
// special case, in advisory_type_name filter expand "other" into list of what we mean by other
141-
transformedValues = make([]string, 0, len(t.Values))
142-
for _, originalValue := range t.Values {
143-
if originalValue == "other" {
144-
transformedValues = append(transformedValues, database.OtherAdvisoryTypes...)
172+
if fieldName == "severity" && (t.Operator == OpIn || t.Operator == OpNotIn) {
173+
valuesToProcess, hasNull = separateNullFromValues(valuesToProcess)
174+
}
175+
return operatorToUse, valuesToProcess, hasNull
176+
}
177+
178+
func separateNullFromValues(values []string) (regularValues []string, hasNull bool) {
179+
hasNullValue := false
180+
181+
for _, v := range values {
182+
if v == "null" {
183+
hasNullValue = true
145184
} else {
146-
transformedValues = append(transformedValues, originalValue)
185+
regularValues = append(regularValues, v)
147186
}
148187
}
149188

150-
if len(transformedValues) == len(t.Values) {
151-
return t.Operator, t.Values
189+
if hasNullValue && len(regularValues) == 0 {
190+
return []string{"0"}, false
152191
}
153192

154-
switch t.Operator {
155-
case OpEq:
156-
transformedOperator = OpIn
157-
case OpNeq:
158-
transformedOperator = OpNotIn
159-
default:
160-
transformedOperator = t.Operator
193+
// null mixed with values e.g. in:2,null
194+
if hasNullValue && len(regularValues) > 0 {
195+
return regularValues, true
161196
}
162-
return transformedOperator, transformedValues
197+
return regularValues, false
163198
}
164199

165200
func (t Filters) ToQueryParams() string {

manager/controllers/filter_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,32 @@ func TestFilterOtherAdvisoryTypes(t *testing.T) {
108108
// Check the list is loaded from database correctly
109109
assert.Equal(t, []string{"unknown", "unspecified"}, database.OtherAdvisoryTypes)
110110
}
111+
112+
func TestFilterSeverityInWithNull(t *testing.T) {
113+
filter := ParseFilterValue(ColumnFilter, "in:2,null")
114+
attrMap := database.AttrMap{"severity": {DataQuery: "severity", OrderQuery: "severity", Parser: dummyParser}}
115+
query, args, err := filter.ToWhere("severity", attrMap)
116+
117+
assert.NoError(t, err)
118+
assert.Equal(t, "(severity IN ? OR severity IS NULL) ", query)
119+
assert.Equal(t, 1, len(args))
120+
assert.IsType(t, []any{}, args[0])
121+
severityValues := args[0].([]any)
122+
assert.Equal(t, 1, len(severityValues))
123+
assert.Equal(t, "2", severityValues[0])
124+
}
125+
126+
func TestFilterSeverityNotInWithNull(t *testing.T) {
127+
filter := ParseFilterValue(ColumnFilter, "notin:2,3,null")
128+
attrMap := database.AttrMap{"severity": {DataQuery: "severity", OrderQuery: "severity", Parser: dummyParser}}
129+
query, args, err := filter.ToWhere("severity", attrMap)
130+
131+
assert.NoError(t, err)
132+
assert.Equal(t, "(severity NOT IN ? AND severity IS NOT NULL) ", query)
133+
assert.Equal(t, 1, len(args))
134+
assert.IsType(t, []any{}, args[0])
135+
severityValues := args[0].([]any)
136+
assert.Equal(t, 2, len(severityValues))
137+
assert.Equal(t, "2", severityValues[0])
138+
assert.Equal(t, "3", severityValues[1])
139+
}

0 commit comments

Comments
 (0)