diff --git a/pkg/stores/partition/listprocessor/processor.go b/pkg/stores/partition/listprocessor/processor.go index f028e5a9..e284c66b 100644 --- a/pkg/stores/partition/listprocessor/processor.go +++ b/pkg/stores/partition/listprocessor/processor.go @@ -260,17 +260,15 @@ func getLimit(apiOp *types.APIRequest) int { // FilterList accepts a channel of unstructured objects and a slice of filters and returns the filtered list. // Filters are ANDed together. -func FilterList(list <-chan []unstructured.Unstructured, filters []OrFilter) []unstructured.Unstructured { +func FilterList(list []unstructured.Unstructured, filters []OrFilter) []unstructured.Unstructured { result := []unstructured.Unstructured{} - for items := range list { - for _, item := range items { - if len(filters) == 0 { - result = append(result, item) - continue - } - if matchesAll(item.Object, filters) { - result = append(result, item) - } + for _, item := range list { + if len(filters) == 0 { + result = append(result, item) + continue + } + if matchesAll(item.Object, filters) { + result = append(result, item) } } return result diff --git a/pkg/stores/partition/listprocessor/processor_test.go b/pkg/stores/partition/listprocessor/processor_test.go index 2354d3ff..f23577e2 100644 --- a/pkg/stores/partition/listprocessor/processor_test.go +++ b/pkg/stores/partition/listprocessor/processor_test.go @@ -14,34 +14,32 @@ import ( func TestFilterList(t *testing.T) { tests := []struct { name string - objects [][]unstructured.Unstructured + objects []unstructured.Unstructured filters []OrFilter want []unstructured.Unstructured }{ { name: "single filter", - objects: [][]unstructured.Unstructured{ + 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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -72,39 +70,37 @@ func TestFilterList(t *testing.T) { }, { name: "multi filter", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -143,28 +139,26 @@ func TestFilterList(t *testing.T) { }, { name: "no matches", - objects: [][]unstructured.Unstructured{ + 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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -183,28 +177,26 @@ func TestFilterList(t *testing.T) { }, { name: "no filters", - objects: [][]unstructured.Unstructured{ + 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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -237,39 +229,37 @@ func TestFilterList(t *testing.T) { }, { name: "filter field does not match", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -288,39 +278,37 @@ func TestFilterList(t *testing.T) { }, { name: "filter subfield does not match", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -339,17 +327,15 @@ func TestFilterList(t *testing.T) { }, { name: "almost valid filter key", - objects: [][]unstructured.Unstructured{ + objects: []unstructured.Unstructured{ { - { - Object: map[string]interface{}{ - "kind": "apple", - "metadata": map[string]interface{}{ - "name": "granny-smith", - }, - "data": map[string]interface{}{ - "color": "green", - }, + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -368,49 +354,47 @@ func TestFilterList(t *testing.T) { }, { name: "match string array", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "banana", + }, + "data": map[string]interface{}{ + "colors": []interface{}{ + "yellow", }, }, }, @@ -460,72 +444,70 @@ func TestFilterList(t *testing.T) { }, { name: "match object array", - objects: [][]unstructured.Unstructured{ + 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": "apple", }, - }, - { - 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", - }, + "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": "banana", + }, + { + 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", + }, }, - "data": map[string]interface{}{ - "varieties": []interface{}{ - map[string]interface{}{ - "name": "cavendish", - "color": "yellow", - }, - map[string]interface{}{ - "name": "plantain", - "color": "green", - }, + }, + }, + }, + { + 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", }, }, }, @@ -595,70 +577,68 @@ func TestFilterList(t *testing.T) { }, { name: "match nested array", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "banana", + }, + "data": map[string]interface{}{ + "attributes": []interface{}{ + []interface{}{ + "yellow", + "green", + }, + []interface{}{ + "cavendish", + "plantain", }, }, }, @@ -726,69 +706,67 @@ func TestFilterList(t *testing.T) { }, { name: "match nested object array", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + 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", }, }, }, @@ -831,49 +809,47 @@ func TestFilterList(t *testing.T) { }, { name: "match element in array", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "banana", + }, + "data": map[string]interface{}{ + "colors": []interface{}{ + "yellow", }, }, }, @@ -925,25 +901,23 @@ func TestFilterList(t *testing.T) { }, { name: "single or filter, filter on one value", - objects: [][]unstructured.Unstructured{ + objects: []unstructured.Unstructured{ { - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "pink-lady", - }, + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "pink-lady", }, }, - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "pomegranate", - }, - "data": map[string]interface{}{ - "color": "pink", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "pomegranate", + }, + "data": map[string]interface{}{ + "color": "pink", }, }, }, @@ -986,26 +960,24 @@ func TestFilterList(t *testing.T) { }, { name: "single or filter, filter on different value", - objects: [][]unstructured.Unstructured{ + objects: []unstructured.Unstructured{ { - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "pink-lady", - }, - }, - }, - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "pomegranate", - }, + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "pink-lady", }, }, }, - }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "pomegranate", + }, + }, + }, + }, filters: []OrFilter{ { filters: []Filter{ @@ -1041,22 +1013,20 @@ func TestFilterList(t *testing.T) { }, { name: "single or filter, no matches", - objects: [][]unstructured.Unstructured{ + objects: []unstructured.Unstructured{ { - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "pink-lady", - }, + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "pink-lady", }, }, - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "pomegranate", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "pomegranate", }, }, }, @@ -1079,42 +1049,40 @@ func TestFilterList(t *testing.T) { }, { name: "and-ed or filters", - objects: [][]unstructured.Unstructured{ + objects: []unstructured.Unstructured{ { - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "pink-lady", - }, - "data": map[string]interface{}{ - "flavor": "sweet", - }, + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "pink-lady", + }, + "data": map[string]interface{}{ + "flavor": "sweet", }, }, - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "pomegranate", - }, - "data": map[string]interface{}{ - "color": "pink", - "flavor": "sweet", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "pomegranate", + }, + "data": map[string]interface{}{ + "color": "pink", + "flavor": "sweet", }, }, - { - Object: map[string]interface{}{ - "kind": "fruit", - "metadata": map[string]interface{}{ - "name": "grapefruit", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "grapefruit", + }, + "data": map[string]interface{}{ + "color": "pink", "data": map[string]interface{}{ - "color": "pink", - "data": map[string]interface{}{ - "flavor": "bitter", - }, + "flavor": "bitter", }, }, }, @@ -1170,28 +1138,26 @@ func TestFilterList(t *testing.T) { }, { name: "not filter", - objects: [][]unstructured.Unstructured{ + 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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -1223,28 +1189,26 @@ func TestFilterList(t *testing.T) { }, { name: "or'ed not filter", - objects: [][]unstructured.Unstructured{ + 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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -1292,28 +1256,26 @@ func TestFilterList(t *testing.T) { }, { name: "mixed or'ed filter", - objects: [][]unstructured.Unstructured{ + 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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -1360,39 +1322,37 @@ func TestFilterList(t *testing.T) { }, { name: "anded and or'ed mixed equality filter", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "apple", + "metadata": map[string]interface{}{ + "name": "granny-smith", + }, + "data": map[string]interface{}{ + "color": "green", }, }, }, @@ -1432,49 +1392,47 @@ func TestFilterList(t *testing.T) { }, { name: "match string array with not", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "banana", + }, + "data": map[string]interface{}{ + "colors": []interface{}{ + "yellow", }, }, }, @@ -1511,72 +1469,70 @@ func TestFilterList(t *testing.T) { }, { name: "match object array with not", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + 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", }, }, }, @@ -1619,70 +1575,68 @@ func TestFilterList(t *testing.T) { }, { name: "match nested array with not", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + Object: map[string]interface{}{ + "kind": "fruit", + "metadata": map[string]interface{}{ + "name": "banana", + }, + "data": map[string]interface{}{ + "attributes": []interface{}{ + []interface{}{ + "yellow", + "green", + }, + []interface{}{ + "cavendish", + "plantain", }, }, }, @@ -1725,69 +1679,67 @@ func TestFilterList(t *testing.T) { }, { name: "match nested object array with mixed equality", - objects: [][]unstructured.Unstructured{ + 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": "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": "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", - }, + }, + { + 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", }, }, }, @@ -1823,14 +1775,7 @@ func TestFilterList(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - ch := make(chan []unstructured.Unstructured) - go func() { - for _, o := range test.objects { - ch <- o - } - close(ch) - }() - got := FilterList(ch, test.filters) + got := FilterList(test.objects, test.filters) assert.Equal(t, test.want, got) }) } diff --git a/pkg/stores/partition/store.go b/pkg/stores/partition/store.go index 0fb36a69..e3f3d7af 100644 --- a/pkg/stores/partition/store.go +++ b/pkg/stores/partition/store.go @@ -110,7 +110,7 @@ func (s *Store) Delete(apiOp *types.APIRequest, schema *types.APISchema, id stri if err != nil { return types.APIObject{}, err } - return toAPI(schema, obj, warnings), nil + return toAPI(schema, moveToUnderscore(obj), warnings), nil } // ByID looks up a single object by its ID. @@ -124,7 +124,7 @@ func (s *Store) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string if err != nil { return types.APIObject{}, err } - return toAPI(schema, obj, warnings), nil + return toAPI(schema, moveToUnderscore(obj), warnings), nil } func (s *Store) listPartition(ctx context.Context, apiOp *types.APIRequest, schema *types.APISchema, partition Partition, @@ -194,7 +194,8 @@ func (s *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.AP if err != nil { return result, err } - list = listprocessor.FilterList(stream, opts.Filters) + list = preprocessList(stream) + list = listprocessor.FilterList(list, opts.Filters) // Check for any errors returned during the parallel listing requests. // We don't want to cache the list or bother with further processing if the list is empty or corrupt. // FilterList guarantees that the stream has been consumed and the error is populated if there is any. @@ -262,7 +263,7 @@ func (s *Store) Create(apiOp *types.APIRequest, schema *types.APISchema, data ty if err != nil { return types.APIObject{}, err } - return toAPI(schema, obj, warnings), nil + return toAPI(schema, moveToUnderscore(obj), warnings), nil } // Update updates a single object in the store. @@ -276,7 +277,7 @@ func (s *Store) Update(apiOp *types.APIRequest, schema *types.APISchema, data ty if err != nil { return types.APIObject{}, err } - return toAPI(schema, obj, warnings), nil + return toAPI(schema, moveToUnderscore(obj), warnings), nil } // Watch returns a channel of events for a list or resource. @@ -327,10 +328,6 @@ func toAPI(schema *types.APISchema, obj runtime.Object, warnings []types.Warning return types.APIObject{} } - if unstr, ok := obj.(*unstructured.Unstructured); ok { - obj = moveToUnderscore(unstr) - } - apiObject := types.APIObject{ Type: schema.ID, Object: obj, @@ -389,7 +386,11 @@ func toAPIEvent(apiOp *types.APIRequest, schema *types.APISchema, event watch.Ev return apiEvent } - apiEvent.Object = toAPI(schema, event.Object, nil) + obj := event.Object + if unst, ok := event.Object.(*unstructured.Unstructured); ok { + obj = moveToUnderscore(unst) + } + apiEvent.Object = toAPI(schema, obj, nil) m, err := meta.Accessor(event.Object) if err != nil { @@ -399,3 +400,14 @@ func toAPIEvent(apiOp *types.APIRequest, schema *types.APISchema, event watch.Ev apiEvent.Revision = m.GetResourceVersion() return apiEvent } + +func preprocessList(list <-chan []unstructured.Unstructured) []unstructured.Unstructured { + result := []unstructured.Unstructured{} + for items := range list { + for _, item := range items { + processed := moveToUnderscore(&item) + result = append(result, *processed) + } + } + return result +} diff --git a/pkg/stores/partition/store_test.go b/pkg/stores/partition/store_test.go index fdbd2931..4a8de03a 100644 --- a/pkg/stores/partition/store_test.go +++ b/pkg/stores/partition/store_test.go @@ -288,6 +288,7 @@ func TestList(t *testing.T) { 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"), + newRequest("filter=_type=baking", "user1"), // type is a reserved word and gets transmuted to _type }, access: []map[string]string{ { @@ -314,6 +315,9 @@ func TestList(t *testing.T) { { "user1": "roleA", }, + { + "user1": "roleA", + }, }, partitions: map[string][]Partition{ "user1": { @@ -325,10 +329,10 @@ func TestList(t *testing.T) { objects: map[string]*unstructured.UnstructuredList{ "all": { Items: []unstructured.Unstructured{ - newApple("fuji").Unstructured, - newApple("granny-smith").Unstructured, - newApple("bramley").Unstructured, - newApple("crispin").Unstructured, + newApple("fuji").withType("snacking").Unstructured, + newApple("granny-smith").withType("baking").Unstructured, + newApple("bramley").withType("cooking").Unstructured, + newApple("crispin").withType("snacking").Unstructured, }, }, }, @@ -336,28 +340,28 @@ func TestList(t *testing.T) { { Count: 2, Objects: []types.APIObject{ - newApple("granny-smith").toObj(), - newApple("bramley").toObj(), + newApple("granny-smith").withUnderType("baking").toObj(), + newApple("bramley").withUnderType("cooking").toObj(), }, }, { Count: 1, Objects: []types.APIObject{ - newApple("bramley").toObj(), + newApple("bramley").withUnderType("cooking").toObj(), }, }, { Count: 3, Objects: []types.APIObject{ - newApple("fuji").toObj(), - newApple("granny-smith").toObj(), - newApple("bramley").toObj(), + newApple("fuji").withUnderType("snacking").toObj(), + newApple("granny-smith").withUnderType("baking").toObj(), + newApple("bramley").withUnderType("cooking").toObj(), }, }, { Count: 1, Objects: []types.APIObject{ - newApple("fuji").toObj(), + newApple("fuji").withUnderType("snacking").toObj(), }, }, { @@ -366,22 +370,28 @@ func TestList(t *testing.T) { { Count: 2, Objects: []types.APIObject{ - newApple("fuji").toObj(), - newApple("crispin").toObj(), + newApple("fuji").withUnderType("snacking").toObj(), + newApple("crispin").withUnderType("snacking").toObj(), }, }, { Count: 3, Objects: []types.APIObject{ - newApple("fuji").toObj(), - newApple("granny-smith").toObj(), - newApple("crispin").toObj(), + newApple("fuji").withUnderType("snacking").toObj(), + newApple("granny-smith").withUnderType("baking").toObj(), + newApple("crispin").withUnderType("snacking").toObj(), }, }, { Count: 1, Objects: []types.APIObject{ - newApple("fuji").toObj(), + newApple("fuji").withUnderType("snacking").toObj(), + }, + }, + { + Count: 1, + Objects: []types.APIObject{ + newApple("granny-smith").withUnderType("baking").toObj(), }, }, }, @@ -444,6 +454,7 @@ func TestList(t *testing.T) { apiOps: []*types.APIRequest{ newRequest("sort=metadata.name", "user1"), newRequest("sort=-metadata.name", "user1"), + newRequest("sort=_type", "user1"), }, access: []map[string]string{ { @@ -452,6 +463,9 @@ func TestList(t *testing.T) { { "user1": "roleA", }, + { + "user1": "roleA", + }, }, partitions: map[string][]Partition{ "user1": { @@ -463,10 +477,10 @@ func TestList(t *testing.T) { objects: map[string]*unstructured.UnstructuredList{ "all": { Items: []unstructured.Unstructured{ - newApple("fuji").Unstructured, - newApple("granny-smith").Unstructured, - newApple("bramley").Unstructured, - newApple("crispin").Unstructured, + newApple("fuji").withType("snacking").Unstructured, + newApple("granny-smith").withType("baking").Unstructured, + newApple("bramley").withType("cooking").Unstructured, + newApple("crispin").withType("snacking").Unstructured, }, }, }, @@ -474,19 +488,28 @@ func TestList(t *testing.T) { { Count: 4, Objects: []types.APIObject{ - newApple("bramley").toObj(), - newApple("crispin").toObj(), - newApple("fuji").toObj(), - newApple("granny-smith").toObj(), + newApple("bramley").withUnderType("cooking").toObj(), + newApple("crispin").withUnderType("snacking").toObj(), + newApple("fuji").withUnderType("snacking").toObj(), + newApple("granny-smith").withUnderType("baking").toObj(), }, }, { Count: 4, Objects: []types.APIObject{ - newApple("granny-smith").toObj(), - newApple("fuji").toObj(), - newApple("crispin").toObj(), - newApple("bramley").toObj(), + newApple("granny-smith").withUnderType("baking").toObj(), + newApple("fuji").withUnderType("snacking").toObj(), + newApple("crispin").withUnderType("snacking").toObj(), + newApple("bramley").withUnderType("cooking").toObj(), + }, + }, + { + Count: 4, + Objects: []types.APIObject{ + newApple("granny-smith").withUnderType("baking").toObj(), + newApple("bramley").withUnderType("cooking").toObj(), + newApple("fuji").withUnderType("snacking").toObj(), + newApple("crispin").withUnderType("snacking").toObj(), }, }, }, @@ -2355,6 +2378,16 @@ func (a apple) withNamespace(namespace string) apple { return a } +func (a apple) withType(typ string) apple { + a.Object["type"] = typ + return a +} + +func (a apple) withUnderType(typ string) apple { + a.Object["_type"] = typ + return a +} + type mockAccessSetLookup struct { accessID string userRoles []map[string]string