mirror of
https://github.com/niusmallnan/steve.git
synced 2025-08-19 13:47:04 +00:00
Add support for NOT filters
This change adds support for excluding results using the != operator. Example to exclude all results with name "example": ?filter=metadata.name!=example Include all results from namespace "example" but exclude those with name "example": ?filter=metadata.namespace=example&metadata.name!=example Exclude results with name "foo" OR exclude results with name "bar" (effectively includes results of both types): ?filter=metadata.name!=foo,metadata.name!=bar
This commit is contained in:
parent
2e4ee872d9
commit
61a39906f9
@ -2,6 +2,7 @@
|
|||||||
package listprocessor
|
package listprocessor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -24,6 +25,15 @@ const (
|
|||||||
orOp = ","
|
orOp = ","
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var opReg = regexp.MustCompile(`[!]?=`)
|
||||||
|
|
||||||
|
type op string
|
||||||
|
|
||||||
|
const (
|
||||||
|
eq op = ""
|
||||||
|
notEq op = "!="
|
||||||
|
)
|
||||||
|
|
||||||
// ListOptions represents the query parameters that may be included in a list request.
|
// ListOptions represents the query parameters that may be included in a list request.
|
||||||
type ListOptions struct {
|
type ListOptions struct {
|
||||||
ChunkSize int
|
ChunkSize int
|
||||||
@ -40,6 +50,7 @@ type ListOptions struct {
|
|||||||
type Filter struct {
|
type Filter struct {
|
||||||
field []string
|
field []string
|
||||||
match string
|
match string
|
||||||
|
op op
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the filter as a query string.
|
// String returns the filter as a query string.
|
||||||
@ -127,11 +138,15 @@ func ParseQuery(apiOp *types.APIRequest) *ListOptions {
|
|||||||
orFilters := strings.Split(filters, orOp)
|
orFilters := strings.Split(filters, orOp)
|
||||||
orFilter := OrFilter{}
|
orFilter := OrFilter{}
|
||||||
for _, filter := range orFilters {
|
for _, filter := range orFilters {
|
||||||
filter := strings.Split(filter, "=")
|
var op op
|
||||||
|
if strings.Contains(filter, "!=") {
|
||||||
|
op = "!="
|
||||||
|
}
|
||||||
|
filter := opReg.Split(filter, -1)
|
||||||
if len(filter) != 2 {
|
if len(filter) != 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
orFilter.filters = append(orFilter.filters, Filter{field: strings.Split(filter[0], "."), match: filter[1]})
|
orFilter.filters = append(orFilter.filters, Filter{field: strings.Split(filter[0], "."), match: filter[1], op: op})
|
||||||
}
|
}
|
||||||
filterOpts = append(filterOpts, orFilter)
|
filterOpts = append(filterOpts, orFilter)
|
||||||
}
|
}
|
||||||
@ -251,7 +266,7 @@ func matchesOne(obj map[string]interface{}, filter Filter) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
filter = Filter{field: subField, match: filter.match}
|
filter = Filter{field: subField, match: filter.match, op: filter.op}
|
||||||
if matchesOneInList(typedVal, filter) {
|
if matchesOneInList(typedVal, filter) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -282,7 +297,8 @@ func matchesOneInList(obj []interface{}, filter Filter) bool {
|
|||||||
|
|
||||||
func matchesAny(obj map[string]interface{}, filter OrFilter) bool {
|
func matchesAny(obj map[string]interface{}, filter OrFilter) bool {
|
||||||
for _, f := range filter.filters {
|
for _, f := range filter.filters {
|
||||||
if matches := matchesOne(obj, f); matches {
|
matches := matchesOne(obj, f)
|
||||||
|
if (matches && f.op == eq) || (!matches && f.op == notEq) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1070,6 +1070,658 @@ func TestFilterList(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "not filter",
|
||||||
|
objects: [][]unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "fuji",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "granny-smith",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: []OrFilter{
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"data", "color"},
|
||||||
|
match: "pink",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "granny-smith",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "or'ed not filter",
|
||||||
|
objects: [][]unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "fuji",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "granny-smith",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: []OrFilter{
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"data", "color"},
|
||||||
|
match: "pink",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: []string{"data", "color"},
|
||||||
|
match: "green",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "fuji",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "granny-smith",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mixed or'ed filter",
|
||||||
|
objects: [][]unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "fuji",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "granny-smith",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: []OrFilter{
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"data", "color"},
|
||||||
|
match: "pink",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: []string{"metadata", "name"},
|
||||||
|
match: "fuji",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "fuji",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "granny-smith",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "anded and or'ed mixed equality filter",
|
||||||
|
objects: [][]unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "fuji",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "honeycrisp",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "granny-smith",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: []OrFilter{
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"metadata", "name"},
|
||||||
|
match: "fuji",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"data", "color"},
|
||||||
|
match: "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "apple",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "honeycrisp",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match string array with not",
|
||||||
|
objects: [][]unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "apple",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"colors": []interface{}{
|
||||||
|
"pink",
|
||||||
|
"red",
|
||||||
|
"green",
|
||||||
|
"yellow",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "berry",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"colors": []interface{}{
|
||||||
|
"blue",
|
||||||
|
"red",
|
||||||
|
"black",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "banana",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"colors": []interface{}{
|
||||||
|
"yellow",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: []OrFilter{
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"data", "colors"},
|
||||||
|
match: "yellow",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "berry",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"colors": []interface{}{
|
||||||
|
"blue",
|
||||||
|
"red",
|
||||||
|
"black",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match object array with not",
|
||||||
|
objects: [][]unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "apple",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"varieties": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "fuji",
|
||||||
|
"color": "pink",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "granny-smith",
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "red-delicious",
|
||||||
|
"color": "red",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "berry",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"varieties": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "blueberry",
|
||||||
|
"color": "blue",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "raspberry",
|
||||||
|
"color": "red",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "blackberry",
|
||||||
|
"color": "black",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "banana",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"varieties": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "cavendish",
|
||||||
|
"color": "yellow",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "plantain",
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: []OrFilter{
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"data", "varieties", "color"},
|
||||||
|
match: "red",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "banana",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"varieties": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "cavendish",
|
||||||
|
"color": "yellow",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "plantain",
|
||||||
|
"color": "green",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match nested array with not",
|
||||||
|
objects: [][]unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "apple",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"attributes": []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
"pink",
|
||||||
|
"green",
|
||||||
|
"red",
|
||||||
|
"purple",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
"fuji",
|
||||||
|
"granny-smith",
|
||||||
|
"red-delicious",
|
||||||
|
"black-diamond",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "berry",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"attributes": []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
"blue",
|
||||||
|
"red",
|
||||||
|
"black",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
"blueberry",
|
||||||
|
"raspberry",
|
||||||
|
"blackberry",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "banana",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"attributes": []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
"yellow",
|
||||||
|
"green",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
"cavendish",
|
||||||
|
"plantain",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: []OrFilter{
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"data", "attributes"},
|
||||||
|
match: "black",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "banana",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"attributes": []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
"yellow",
|
||||||
|
"green",
|
||||||
|
},
|
||||||
|
[]interface{}{
|
||||||
|
"cavendish",
|
||||||
|
"plantain",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match nested object array with mixed equality",
|
||||||
|
objects: [][]unstructured.Unstructured{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "apple",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"attributes": []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"pink": "fuji",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"green": "granny-smith",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"pink": "honeycrisp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "berry",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"attributes": []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"blue": "blueberry",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"red": "raspberry",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"black": "blackberry",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"kind": "fruit",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "banana",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"attributes": []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"yellow": "cavendish",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"green": "plantain",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: []OrFilter{
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"data", "attributes", "green"},
|
||||||
|
match: "plantain",
|
||||||
|
op: "!=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: []string{"data", "attributes", "green"},
|
||||||
|
match: "granny-smith",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filters: []Filter{
|
||||||
|
{
|
||||||
|
field: []string{"metadata", "name"},
|
||||||
|
match: "banana",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []unstructured.Unstructured{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
@ -281,6 +281,9 @@ func TestList(t *testing.T) {
|
|||||||
newRequest("filter=data.color=green,data.color=pink", "user1"),
|
newRequest("filter=data.color=green,data.color=pink", "user1"),
|
||||||
newRequest("filter=data.color=green,data.color=pink&filter=metadata.name=fuji", "user1"),
|
newRequest("filter=data.color=green,data.color=pink&filter=metadata.name=fuji", "user1"),
|
||||||
newRequest("filter=data.color=green,data.color=pink&filter=metadata.name=crispin", "user1"),
|
newRequest("filter=data.color=green,data.color=pink&filter=metadata.name=crispin", "user1"),
|
||||||
|
newRequest("filter=data.color!=green", "user1"),
|
||||||
|
newRequest("filter=data.color!=green,metadata.name=granny-smith", "user1"),
|
||||||
|
newRequest("filter=data.color!=green&filter=metadata.name!=crispin", "user1"),
|
||||||
},
|
},
|
||||||
access: []map[string]string{
|
access: []map[string]string{
|
||||||
{
|
{
|
||||||
@ -298,6 +301,15 @@ func TestList(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"user1": "roleA",
|
"user1": "roleA",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"user1": "roleA",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user1": "roleA",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user1": "roleA",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
partitions: map[string][]Partition{
|
partitions: map[string][]Partition{
|
||||||
"user1": {
|
"user1": {
|
||||||
@ -347,6 +359,27 @@ func TestList(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Count: 0,
|
Count: 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Count: 2,
|
||||||
|
Objects: []types.APIObject{
|
||||||
|
newApple("fuji").toObj(),
|
||||||
|
newApple("crispin").toObj(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Count: 3,
|
||||||
|
Objects: []types.APIObject{
|
||||||
|
newApple("fuji").toObj(),
|
||||||
|
newApple("granny-smith").toObj(),
|
||||||
|
newApple("crispin").toObj(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Count: 1,
|
||||||
|
Objects: []types.APIObject{
|
||||||
|
newApple("fuji").toObj(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user