mirror of
https://github.com/rancher/steve.git
synced 2025-09-13 13:59:40 +00:00
Further fixes to the projectsornamespaces param, with new tests. (#772)
* Further fixes to the projectsornamespaces param, with new tests. * Simplify projectsornamespaces on namespace kind Move the processing of this special case from the SQL code generator to the parser, and map it to simple `metadata.name=` and `metadata.labels[...]` tests. * Fix `projectsornamespace!=` code gen. These should generate (f.namespace != X AND (negative label test). The code was generating an `OR` giving too many false positives. * Fix the projORns not error in the SQL
This commit is contained in:
@@ -691,18 +691,10 @@ func (l *ListOptionIndexer) constructQuery(lo *sqltypes.ListOptions, partitions
|
|||||||
if _, exists := joinTableIndexByLabelName[projectIDFieldLabel]; !exists {
|
if _, exists := joinTableIndexByLabelName[projectIDFieldLabel]; !exists {
|
||||||
joinTableIndexByLabelName[projectIDFieldLabel] = jtIndex
|
joinTableIndexByLabelName[projectIDFieldLabel] = jtIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're looking for ProjectsOrNamespaces while querying for _v1_Namespaces (not very usual, but possible),
|
|
||||||
// we need to change the field prefix from 'nsf' which means namespaces fields to 'f' which is the default join
|
|
||||||
// name for every object, also we do not need to join namespaces again
|
|
||||||
fieldPrefix := "f"
|
|
||||||
if dbName != namespacesDbName {
|
|
||||||
fieldPrefix = "nsf"
|
|
||||||
query += "\n "
|
|
||||||
query += fmt.Sprintf(`JOIN "%s_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"`, namespacesDbName)
|
|
||||||
}
|
|
||||||
query += "\n "
|
query += "\n "
|
||||||
query += fmt.Sprintf(`LEFT OUTER JOIN "%s_labels" lt%d ON %s.key = lt%d.key`, namespacesDbName, jtIndex, fieldPrefix, jtIndex)
|
query += fmt.Sprintf(`LEFT OUTER JOIN "%s_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"`, namespacesDbName)
|
||||||
|
query += "\n "
|
||||||
|
query += fmt.Sprintf(`LEFT OUTER JOIN "%s_labels" lt%d ON nsf.key = lt%d.key`, namespacesDbName, jtIndex, jtIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2- Filtering: WHERE clauses (from lo.Filters)
|
// 2- Filtering: WHERE clauses (from lo.Filters)
|
||||||
@@ -720,11 +712,7 @@ func (l *ListOptionIndexer) constructQuery(lo *sqltypes.ListOptions, partitions
|
|||||||
|
|
||||||
// WHERE clauses (from lo.ProjectsOrNamespaces)
|
// WHERE clauses (from lo.ProjectsOrNamespaces)
|
||||||
if len(lo.ProjectsOrNamespaces.Filters) > 0 {
|
if len(lo.ProjectsOrNamespaces.Filters) > 0 {
|
||||||
fieldPrefix := "nsf"
|
projOrNsClause, projOrNsParams, err := l.buildClauseFromProjectsOrNamespaces(lo.ProjectsOrNamespaces, dbName, joinTableIndexByLabelName)
|
||||||
if dbName == namespacesDbName {
|
|
||||||
fieldPrefix = "f"
|
|
||||||
}
|
|
||||||
projOrNsClause, projOrNsParams, err := l.buildClauseFromProjectsOrNamespaces(lo.ProjectsOrNamespaces, dbName, joinTableIndexByLabelName, fieldPrefix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return queryInfo, err
|
return queryInfo, err
|
||||||
}
|
}
|
||||||
@@ -1034,7 +1022,7 @@ func (l *ListOptionIndexer) buildORClauseFromFilters(orFilters sqltypes.OrFilter
|
|||||||
return fmt.Sprintf("(%s)", strings.Join(clauses, ") OR (")), params, nil
|
return fmt.Sprintf("(%s)", strings.Join(clauses, ") OR (")), params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ListOptionIndexer) buildClauseFromProjectsOrNamespaces(orFilters sqltypes.OrFilter, dbName string, joinTableIndexByLabelName map[string]int, fieldPrefix string) (string, []any, error) {
|
func (l *ListOptionIndexer) buildClauseFromProjectsOrNamespaces(orFilters sqltypes.OrFilter, dbName string, joinTableIndexByLabelName map[string]int) (string, []any, error) {
|
||||||
var params []any
|
var params []any
|
||||||
var newParams []any
|
var newParams []any
|
||||||
var newClause string
|
var newClause string
|
||||||
@@ -1053,7 +1041,7 @@ func (l *ListOptionIndexer) buildClauseFromProjectsOrNamespaces(orFilters sqltyp
|
|||||||
}
|
}
|
||||||
newClause, newParams, err = l.getProjectsOrNamespacesLabelFilter(index, filter, dbName)
|
newClause, newParams, err = l.getProjectsOrNamespacesLabelFilter(index, filter, dbName)
|
||||||
} else {
|
} else {
|
||||||
newClause, newParams, err = l.getProjectsOrNamespacesFieldFilter(filter, fieldPrefix)
|
newClause, newParams, err = l.getProjectsOrNamespacesFieldFilter(filter)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
@@ -1221,9 +1209,9 @@ func (l *ListOptionIndexer) getFieldFilter(filter sqltypes.Filter, prefix string
|
|||||||
return "", nil, fmt.Errorf("unrecognized operator: %s", opString)
|
return "", nil, fmt.Errorf("unrecognized operator: %s", opString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ListOptionIndexer) getProjectsOrNamespacesFieldFilter(filter sqltypes.Filter, prefix string) (string, []any, error) {
|
func (l *ListOptionIndexer) getProjectsOrNamespacesFieldFilter(filter sqltypes.Filter) (string, []any, error) {
|
||||||
opString := ""
|
opString := ""
|
||||||
fieldEntry, err := l.getValidFieldEntry(prefix, filter.Field)
|
fieldEntry, err := l.getValidFieldEntry("nsf", filter.Field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
@@ -1270,8 +1258,9 @@ func (l *ListOptionIndexer) getProjectsOrNamespacesLabelFilter(index int, filter
|
|||||||
clause1 := fmt.Sprintf(`(lt%d.label = ? AND lt%d.value NOT IN %s)`, index, index, target)
|
clause1 := fmt.Sprintf(`(lt%d.label = ? AND lt%d.value NOT IN %s)`, index, index, target)
|
||||||
clause2 := fmt.Sprintf(`(o.key NOT IN (SELECT o1.key FROM "%s" o1
|
clause2 := fmt.Sprintf(`(o.key NOT IN (SELECT o1.key FROM "%s" o1
|
||||||
JOIN "%s_fields" f1 ON o1.key = f1.key
|
JOIN "%s_fields" f1 ON o1.key = f1.key
|
||||||
LEFT OUTER JOIN "%s_labels" lt%di1 ON o1.key = lt%di1.key
|
LEFT OUTER JOIN "_v1_Namespace_fields" nsf1 ON f1."metadata.namespace" = nsf1."metadata.name"
|
||||||
WHERE lt%di1.label = ?))`, dbName, dbName, dbName, index, index, index)
|
LEFT OUTER JOIN "_v1_Namespace_labels" lt%di1 ON nsf1.key = lt%di1.key
|
||||||
|
WHERE lt%di1.label = ?))`, dbName, dbName, index, index, index)
|
||||||
matches = append(matches, labelName)
|
matches = append(matches, labelName)
|
||||||
clause := fmt.Sprintf("%s OR %s", clause1, clause2)
|
clause := fmt.Sprintf("%s OR %s", clause1, clause2)
|
||||||
return clause, matches, nil
|
return clause, matches, nil
|
||||||
|
@@ -32,15 +32,9 @@ import (
|
|||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeListOptionIndexer(ctx context.Context, opts ListOptionIndexerOptions, shouldEncrypt bool) (*ListOptionIndexer, string, error) {
|
var emptyNamespaceList = &unstructured.UnstructuredList{Object: map[string]any{"items": []any{}}, Items: []unstructured.Unstructured{}}
|
||||||
gvk := schema.GroupVersionKind{
|
|
||||||
Group: "",
|
func makeListOptionIndexer(ctx context.Context, opts ListOptionIndexerOptions, shouldEncrypt bool, nsList *unstructured.UnstructuredList) (*ListOptionIndexer, string, error) {
|
||||||
Version: "v1",
|
|
||||||
Kind: "ConfigMap",
|
|
||||||
}
|
|
||||||
example := &unstructured.Unstructured{}
|
|
||||||
example.SetGroupVersionKind(gvk)
|
|
||||||
name := informerNameFromGVK(gvk)
|
|
||||||
m, err := encryption.NewManager()
|
m, err := encryption.NewManager()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
@@ -50,11 +44,47 @@ func makeListOptionIndexer(ctx context.Context, opts ListOptionIndexerOptions, s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
// First create a namespace table so the projectsornamespaces query succeeds
|
||||||
|
gvk := schema.GroupVersionKind{
|
||||||
|
Group: "",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Namespace",
|
||||||
|
}
|
||||||
|
example := &unstructured.Unstructured{}
|
||||||
|
example.SetGroupVersionKind(gvk)
|
||||||
|
name := informerNameFromGVK(gvk)
|
||||||
s, err := store.NewStore(ctx, example, cache.DeletionHandlingMetaNamespaceKeyFunc, db, shouldEncrypt, gvk, name, nil, nil)
|
s, err := store.NewStore(ctx, example, cache.DeletionHandlingMetaNamespaceKeyFunc, db, shouldEncrypt, gvk, name, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
ns_opts := ListOptionIndexerOptions{
|
||||||
|
Fields: [][]string{},
|
||||||
|
IsNamespaced: false,
|
||||||
|
}
|
||||||
|
listOptionIndexer, err := NewListOptionIndexer(ctx, s, ns_opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
for _, item := range nsList.Items {
|
||||||
|
err = listOptionIndexer.Add(&item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gvk = schema.GroupVersionKind{
|
||||||
|
Group: "",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
}
|
||||||
|
example = &unstructured.Unstructured{}
|
||||||
|
example.SetGroupVersionKind(gvk)
|
||||||
|
name = informerNameFromGVK(gvk)
|
||||||
|
|
||||||
|
s, err = store.NewStore(ctx, example, cache.DeletionHandlingMetaNamespaceKeyFunc, db, shouldEncrypt, gvk, name, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
if opts.IsNamespaced {
|
if opts.IsNamespaced {
|
||||||
// Can't use slices.Compare because []string doesn't implement comparable
|
// Can't use slices.Compare because []string doesn't implement comparable
|
||||||
idEntry := []string{"id"}
|
idEntry := []string{"id"}
|
||||||
@@ -65,7 +95,7 @@ func makeListOptionIndexer(ctx context.Context, opts ListOptionIndexerOptions, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listOptionIndexer, err := NewListOptionIndexer(ctx, s, opts)
|
listOptionIndexer, err = NewListOptionIndexer(ctx, s, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@@ -491,8 +521,25 @@ func TestNewListOptionIndexerEasy(t *testing.T) {
|
|||||||
obj04_milk,
|
obj04_milk,
|
||||||
obj05__guard_lodgepole,
|
obj05__guard_lodgepole,
|
||||||
}
|
}
|
||||||
|
ns_a := map[string]any{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": "ns-a",
|
||||||
|
"labels": map[string]any{
|
||||||
|
"guard.cattle.io": "ponderosa",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ns_b := map[string]any{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": "ns-b",
|
||||||
|
"labels": map[string]any{
|
||||||
|
"field.cattle.io/projectId": "ns-b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
itemList := makeList(t, allObjects...)
|
itemList := makeList(t, allObjects...)
|
||||||
|
namespaceList := makeList(t, ns_a, ns_b)
|
||||||
|
|
||||||
var tests []testCase
|
var tests []testCase
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
@@ -1062,6 +1109,56 @@ func TestNewListOptionIndexerEasy(t *testing.T) {
|
|||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
tests = append(tests, testCase{
|
||||||
|
description: "ListByOptions with a positive projectsornamespaces test should work",
|
||||||
|
listOptions: sqltypes.ListOptions{
|
||||||
|
ProjectsOrNamespaces: sqltypes.OrFilter{
|
||||||
|
Filters: []sqltypes.Filter{
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "name"},
|
||||||
|
Matches: []string{"ns-b"},
|
||||||
|
Op: sqltypes.In,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||||
|
Matches: []string{"ns-b"},
|
||||||
|
Op: sqltypes.In,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
partitions: []partition.Partition{{All: true}},
|
||||||
|
ns: "",
|
||||||
|
expectedList: makeList(t, obj05__guard_lodgepole),
|
||||||
|
expectedTotal: 1,
|
||||||
|
expectedContToken: "",
|
||||||
|
expectedErr: nil,
|
||||||
|
})
|
||||||
|
tests = append(tests, testCase{
|
||||||
|
description: "ListByOptions with a negative projectsornamespaces test should work",
|
||||||
|
listOptions: sqltypes.ListOptions{
|
||||||
|
ProjectsOrNamespaces: sqltypes.OrFilter{
|
||||||
|
Filters: []sqltypes.Filter{
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "name"},
|
||||||
|
Matches: []string{"ns-a"},
|
||||||
|
Op: sqltypes.NotIn,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||||
|
Matches: []string{"ns-a"},
|
||||||
|
Op: sqltypes.NotIn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
partitions: []partition.Partition{{All: true}},
|
||||||
|
ns: "",
|
||||||
|
expectedList: makeList(t, obj05__guard_lodgepole),
|
||||||
|
expectedTotal: 1,
|
||||||
|
expectedContToken: "",
|
||||||
|
expectedErr: nil,
|
||||||
|
})
|
||||||
//tests = append(tests, testCase{
|
//tests = append(tests, testCase{
|
||||||
// description: "ListByOptions with a Namespace Partition should select only items where metadata.namespace is equal to Namespace and all other conditions are met",
|
// description: "ListByOptions with a Namespace Partition should select only items where metadata.namespace is equal to Namespace and all other conditions are met",
|
||||||
// partitions: []partition.Partition{
|
// partitions: []partition.Partition{
|
||||||
@@ -1079,6 +1176,8 @@ func TestNewListOptionIndexerEasy(t *testing.T) {
|
|||||||
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
// First curl the namespaces to load up the namespace database tables.
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.description, func(t *testing.T) {
|
t.Run(test.description, func(t *testing.T) {
|
||||||
fields := [][]string{
|
fields := [][]string{
|
||||||
@@ -1093,7 +1192,10 @@ func TestNewListOptionIndexerEasy(t *testing.T) {
|
|||||||
Fields: fields,
|
Fields: fields,
|
||||||
IsNamespaced: true,
|
IsNamespaced: true,
|
||||||
}
|
}
|
||||||
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false)
|
if test.description == "ListByOptions with a positive projectsornamespaces test should work" {
|
||||||
|
fmt.Println("Stop here")
|
||||||
|
}
|
||||||
|
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false, namespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -1101,12 +1203,16 @@ func TestNewListOptionIndexerEasy(t *testing.T) {
|
|||||||
err = loi.Add(&item)
|
err = loi.Add(&item)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
if test.description == "ListByOptions with a positive projectsornamespaces test should work" {
|
||||||
|
fmt.Println("Stop here")
|
||||||
|
}
|
||||||
|
|
||||||
list, total, contToken, err := loi.ListByOptions(ctx, &test.listOptions, test.partitions, test.ns)
|
list, total, contToken, err := loi.ListByOptions(ctx, &test.listOptions, test.partitions, test.ns)
|
||||||
if test.expectedErr != nil {
|
if test.expectedErr != nil {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, test.expectedTotal, total)
|
assert.Equal(t, test.expectedTotal, total)
|
||||||
assert.Equal(t, test.expectedList, list)
|
assert.Equal(t, test.expectedList, list)
|
||||||
@@ -1190,7 +1296,7 @@ func BenchmarkNamespaceNameList(b *testing.B) {
|
|||||||
opts := ListOptionIndexerOptions{
|
opts := ListOptionIndexerOptions{
|
||||||
IsNamespaced: true,
|
IsNamespaced: true,
|
||||||
}
|
}
|
||||||
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false)
|
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false, emptyNamespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(b, err)
|
assert.NoError(b, err)
|
||||||
for _, item := range itemList.Items {
|
for _, item := range itemList.Items {
|
||||||
@@ -1442,7 +1548,7 @@ func TestUserDefinedExtractFunction(t *testing.T) {
|
|||||||
Fields: fields,
|
Fields: fields,
|
||||||
IsNamespaced: true,
|
IsNamespaced: true,
|
||||||
}
|
}
|
||||||
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false)
|
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false, emptyNamespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -1555,7 +1661,7 @@ func TestConstructQuery(t *testing.T) {
|
|||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
JOIN "something_fields" f ON o.key = f.key
|
||||||
JOIN "_v1_Namespace_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"
|
LEFT OUTER JOIN "_v1_Namespace_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"
|
||||||
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON nsf.key = lt1.key
|
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON nsf.key = lt1.key
|
||||||
WHERE
|
WHERE
|
||||||
((nsf."metadata.name" IN (?)) OR (lt1.label = ? AND lt1.value IN (?)))
|
((nsf."metadata.name" IN (?)) OR (lt1.label = ? AND lt1.value IN (?)))
|
||||||
@@ -1590,7 +1696,7 @@ func TestConstructQuery(t *testing.T) {
|
|||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
JOIN "something_fields" f ON o.key = f.key
|
||||||
JOIN "_v1_Namespace_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"
|
LEFT OUTER JOIN "_v1_Namespace_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"
|
||||||
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON nsf.key = lt1.key
|
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON nsf.key = lt1.key
|
||||||
WHERE
|
WHERE
|
||||||
((nsf."metadata.name" IN (?, ?)) OR (lt1.label = ? AND lt1.value IN (?, ?)))
|
((nsf."metadata.name" IN (?, ?)) OR (lt1.label = ? AND lt1.value IN (?, ?)))
|
||||||
@@ -1621,12 +1727,13 @@ func TestConstructQuery(t *testing.T) {
|
|||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
JOIN "something_fields" f ON o.key = f.key
|
||||||
JOIN "_v1_Namespace_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"
|
LEFT OUTER JOIN "_v1_Namespace_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"
|
||||||
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON nsf.key = lt1.key
|
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON nsf.key = lt1.key
|
||||||
WHERE
|
WHERE
|
||||||
((nsf."metadata.name" NOT IN (?)) AND ((lt1.label = ? AND lt1.value NOT IN (?)) OR (o.key NOT IN (SELECT o1.key FROM "something" o1
|
((nsf."metadata.name" NOT IN (?)) AND ((lt1.label = ? AND lt1.value NOT IN (?)) OR (o.key NOT IN (SELECT o1.key FROM "something" o1
|
||||||
JOIN "something_fields" f1 ON o1.key = f1.key
|
JOIN "something_fields" f1 ON o1.key = f1.key
|
||||||
LEFT OUTER JOIN "something_labels" lt1i1 ON o1.key = lt1i1.key
|
LEFT OUTER JOIN "_v1_Namespace_fields" nsf1 ON f1."metadata.namespace" = nsf1."metadata.name"
|
||||||
|
LEFT OUTER JOIN "_v1_Namespace_labels" lt1i1 ON nsf1.key = lt1i1.key
|
||||||
WHERE lt1i1.label = ?))))
|
WHERE lt1i1.label = ?))))
|
||||||
ORDER BY f."metadata.name" ASC `,
|
ORDER BY f."metadata.name" ASC `,
|
||||||
expectedStmtArgs: []any{"some_namespace", "field.cattle.io/projectId", "some_namespace", "field.cattle.io/projectId"},
|
expectedStmtArgs: []any{"some_namespace", "field.cattle.io/projectId", "some_namespace", "field.cattle.io/projectId"},
|
||||||
@@ -1655,12 +1762,13 @@ func TestConstructQuery(t *testing.T) {
|
|||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
JOIN "something_fields" f ON o.key = f.key
|
||||||
JOIN "_v1_Namespace_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"
|
LEFT OUTER JOIN "_v1_Namespace_fields" nsf ON f."metadata.namespace" = nsf."metadata.name"
|
||||||
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON nsf.key = lt1.key
|
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON nsf.key = lt1.key
|
||||||
WHERE
|
WHERE
|
||||||
((nsf."metadata.name" NOT IN (?, ?)) AND ((lt1.label = ? AND lt1.value NOT IN (?, ?)) OR (o.key NOT IN (SELECT o1.key FROM "something" o1
|
((nsf."metadata.name" NOT IN (?, ?)) AND ((lt1.label = ? AND lt1.value NOT IN (?, ?)) OR (o.key NOT IN (SELECT o1.key FROM "something" o1
|
||||||
JOIN "something_fields" f1 ON o1.key = f1.key
|
JOIN "something_fields" f1 ON o1.key = f1.key
|
||||||
LEFT OUTER JOIN "something_labels" lt1i1 ON o1.key = lt1i1.key
|
LEFT OUTER JOIN "_v1_Namespace_fields" nsf1 ON f1."metadata.namespace" = nsf1."metadata.name"
|
||||||
|
LEFT OUTER JOIN "_v1_Namespace_labels" lt1i1 ON nsf1.key = lt1i1.key
|
||||||
WHERE lt1i1.label = ?))))
|
WHERE lt1i1.label = ?))))
|
||||||
ORDER BY f."metadata.name" ASC `,
|
ORDER BY f."metadata.name" ASC `,
|
||||||
expectedStmtArgs: []any{"some_namespace", "p-example", "field.cattle.io/projectId", "some_namespace", "p-example", "field.cattle.io/projectId"},
|
expectedStmtArgs: []any{"some_namespace", "p-example", "field.cattle.io/projectId", "some_namespace", "p-example", "field.cattle.io/projectId"},
|
||||||
@@ -2314,7 +2422,10 @@ SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
|||||||
}
|
}
|
||||||
lii := &ListOptionIndexer{
|
lii := &ListOptionIndexer{
|
||||||
Indexer: i,
|
Indexer: i,
|
||||||
indexedFields: []string{"metadata.queryField1", "status.queryField2", "spec.containers.image", "metadata.name"},
|
indexedFields: []string{"metadata.queryField1", "status.queryField2", "spec.containers.image", "metadata.name", "metadata.namespace"},
|
||||||
|
}
|
||||||
|
if test.description == "TestConstructQuery: handles ProjectOrNamespaces NOT IN" {
|
||||||
|
fmt.Println("stop here")
|
||||||
}
|
}
|
||||||
queryInfo, err := lii.constructQuery(&test.listOptions, test.partitions, test.ns, "something")
|
queryInfo, err := lii.constructQuery(&test.listOptions, test.partitions, test.ns, "something")
|
||||||
if test.expectedErr != nil {
|
if test.expectedErr != nil {
|
||||||
@@ -2608,7 +2719,7 @@ func TestWatchEncryption(t *testing.T) {
|
|||||||
IsNamespaced: true,
|
IsNamespaced: true,
|
||||||
}
|
}
|
||||||
// shouldEncrypt = true to ensure we can write + read from encrypted events
|
// shouldEncrypt = true to ensure we can write + read from encrypted events
|
||||||
loi, dbPath, err := makeListOptionIndexer(ctx, opts, true)
|
loi, dbPath, err := makeListOptionIndexer(ctx, opts, true, emptyNamespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -2694,7 +2805,7 @@ func TestWatchMany(t *testing.T) {
|
|||||||
},
|
},
|
||||||
IsNamespaced: true,
|
IsNamespaced: true,
|
||||||
}
|
}
|
||||||
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false)
|
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false, emptyNamespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -2951,7 +3062,7 @@ func TestWatchFilter(t *testing.T) {
|
|||||||
Fields: [][]string{{"metadata", "somefield"}},
|
Fields: [][]string{{"metadata", "somefield"}},
|
||||||
IsNamespaced: true,
|
IsNamespaced: true,
|
||||||
}
|
}
|
||||||
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false)
|
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false, emptyNamespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -3045,7 +3156,7 @@ func TestWatchResourceVersion(t *testing.T) {
|
|||||||
opts := ListOptionIndexerOptions{
|
opts := ListOptionIndexerOptions{
|
||||||
IsNamespaced: true,
|
IsNamespaced: true,
|
||||||
}
|
}
|
||||||
loi, dbPath, err := makeListOptionIndexer(parentCtx, opts, false)
|
loi, dbPath, err := makeListOptionIndexer(parentCtx, opts, false, emptyNamespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -3199,7 +3310,7 @@ func TestWatchGarbageCollection(t *testing.T) {
|
|||||||
GCInterval: 40 * time.Millisecond,
|
GCInterval: 40 * time.Millisecond,
|
||||||
GCKeepCount: 2,
|
GCKeepCount: 2,
|
||||||
}
|
}
|
||||||
loi, dbPath, err := makeListOptionIndexer(parentCtx, opts, false)
|
loi, dbPath, err := makeListOptionIndexer(parentCtx, opts, false, emptyNamespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -3310,7 +3421,7 @@ func TestNonNumberResourceVersion(t *testing.T) {
|
|||||||
Fields: [][]string{{"metadata", "somefield"}},
|
Fields: [][]string{{"metadata", "somefield"}},
|
||||||
IsNamespaced: true,
|
IsNamespaced: true,
|
||||||
}
|
}
|
||||||
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false)
|
loi, dbPath, err := makeListOptionIndexer(ctx, opts, false, emptyNamespaceList)
|
||||||
defer cleanTempFiles(dbPath)
|
defer cleanTempFiles(dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/rancher/steve/pkg/stores/queryhelper"
|
"github.com/rancher/steve/pkg/stores/queryhelper"
|
||||||
"github.com/rancher/steve/pkg/stores/sqlpartition/queryparser"
|
"github.com/rancher/steve/pkg/stores/sqlpartition/queryparser"
|
||||||
"github.com/rancher/steve/pkg/stores/sqlpartition/selection"
|
"github.com/rancher/steve/pkg/stores/sqlpartition/selection"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ func k8sRequirementToOrFilter(requirement queryparser.Requirement) (sqltypes.Fil
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseQuery parses the query params of a request and returns a ListOptions.
|
// ParseQuery parses the query params of a request and returns a ListOptions.
|
||||||
func ParseQuery(apiOp *types.APIRequest) (sqltypes.ListOptions, error) {
|
func ParseQuery(apiOp *types.APIRequest, gvKind string) (sqltypes.ListOptions, error) {
|
||||||
opts := sqltypes.ListOptions{}
|
opts := sqltypes.ListOptions{}
|
||||||
|
|
||||||
q := apiOp.Request.URL.Query()
|
q := apiOp.Request.URL.Query()
|
||||||
@@ -146,7 +147,23 @@ func ParseQuery(apiOp *types.APIRequest) (sqltypes.ListOptions, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if projectsOrNamespaces != "" {
|
if projectsOrNamespaces != "" {
|
||||||
opts.ProjectsOrNamespaces = parseNamespaceOrProjectFilters(projectsOrNamespaces, op)
|
if gvKind == "Namespace" {
|
||||||
|
projNSFilter := parseNamespaceOrProjectFilters(projectsOrNamespaces, op)
|
||||||
|
if len(projNSFilter.Filters) == 2 {
|
||||||
|
if op == sqltypes.In {
|
||||||
|
opts.Filters = append(opts.Filters, projNSFilter)
|
||||||
|
} else {
|
||||||
|
opts.Filters = append(opts.Filters, sqltypes.OrFilter{Filters: []sqltypes.Filter{projNSFilter.Filters[0]}})
|
||||||
|
opts.Filters = append(opts.Filters, sqltypes.OrFilter{Filters: []sqltypes.Filter{projNSFilter.Filters[1]}})
|
||||||
|
}
|
||||||
|
} else if len(projNSFilter.Filters) == 0 {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
logrus.Infof("Ignoring unexpected filter for query %q: parseNamespaceOrProjectFilters returned %d filters, expecting 2", q, len(projNSFilter.Filters))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opts.ProjectsOrNamespaces = parseNamespaceOrProjectFilters(projectsOrNamespaces, op)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return opts, nil
|
return opts, nil
|
||||||
|
@@ -16,6 +16,7 @@ func TestParseQuery(t *testing.T) {
|
|||||||
type testCase struct {
|
type testCase struct {
|
||||||
description string
|
description string
|
||||||
req *types.APIRequest
|
req *types.APIRequest
|
||||||
|
gvKind string // This is to distinguish Namespace projectornamespaces from others
|
||||||
expectedLO sqltypes.ListOptions
|
expectedLO sqltypes.ListOptions
|
||||||
errExpected bool
|
errExpected bool
|
||||||
errorText string
|
errorText string
|
||||||
@@ -63,6 +64,118 @@ func TestParseQuery(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
tests = append(tests, testCase{
|
||||||
|
description: "ParseQuery() with only projectsornamespaces on a namespace GVK should return a standard filter.",
|
||||||
|
req: &types.APIRequest{
|
||||||
|
Request: &http.Request{
|
||||||
|
URL: &url.URL{RawQuery: "projectsornamespaces=elm&filter=metadata.name~beech"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gvKind: "Namespace",
|
||||||
|
expectedLO: sqltypes.ListOptions{
|
||||||
|
Filters: []sqltypes.OrFilter{
|
||||||
|
{
|
||||||
|
Filters: []sqltypes.Filter{
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "name"},
|
||||||
|
Matches: []string{"beech"},
|
||||||
|
Op: sqltypes.Eq,
|
||||||
|
Partial: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Filters: []sqltypes.Filter{
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "name"},
|
||||||
|
Matches: []string{"elm"},
|
||||||
|
Op: sqltypes.In,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||||
|
Matches: []string{"elm"},
|
||||||
|
Op: sqltypes.In,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Pagination: sqltypes.Pagination{
|
||||||
|
Page: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
tests = append(tests, testCase{
|
||||||
|
description: "ParseQuery() with only a negative projectsornamespaces should return a project/ns filter.",
|
||||||
|
req: &types.APIRequest{
|
||||||
|
Request: &http.Request{
|
||||||
|
URL: &url.URL{RawQuery: "projectsornamespaces!=somethin"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedLO: sqltypes.ListOptions{
|
||||||
|
Filters: []sqltypes.OrFilter{},
|
||||||
|
ProjectsOrNamespaces: sqltypes.OrFilter{
|
||||||
|
Filters: []sqltypes.Filter{
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "name"},
|
||||||
|
Matches: []string{"somethin"},
|
||||||
|
Op: sqltypes.NotIn,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||||
|
Matches: []string{"somethin"},
|
||||||
|
Op: sqltypes.NotIn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Pagination: sqltypes.Pagination{
|
||||||
|
Page: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
tests = append(tests, testCase{
|
||||||
|
description: "ParseQuery() with only negative projectsornamespaces on a namespace GVK should return a standard filter.",
|
||||||
|
req: &types.APIRequest{
|
||||||
|
Request: &http.Request{
|
||||||
|
URL: &url.URL{RawQuery: "projectsornamespaces!=elm&filter=metadata.name~beech"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gvKind: "Namespace",
|
||||||
|
expectedLO: sqltypes.ListOptions{
|
||||||
|
Filters: []sqltypes.OrFilter{
|
||||||
|
{
|
||||||
|
Filters: []sqltypes.Filter{
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "name"},
|
||||||
|
Matches: []string{"beech"},
|
||||||
|
Op: sqltypes.Eq,
|
||||||
|
Partial: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Filters: []sqltypes.Filter{
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "name"},
|
||||||
|
Matches: []string{"elm"},
|
||||||
|
Op: sqltypes.NotIn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Filters: []sqltypes.Filter{
|
||||||
|
{
|
||||||
|
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||||
|
Matches: []string{"elm"},
|
||||||
|
Op: sqltypes.NotIn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Pagination: sqltypes.Pagination{
|
||||||
|
Page: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "ParseQuery() with filter param set should include filter with partial set to true in list options.",
|
description: "ParseQuery() with filter param set should include filter with partial set to true in list options.",
|
||||||
req: &types.APIRequest{
|
req: &types.APIRequest{
|
||||||
@@ -758,7 +871,7 @@ func TestParseQuery(t *testing.T) {
|
|||||||
//if test.description == "ParseQuery() with no errors: if projectsornamespaces is not empty, it should return an empty filter array" {
|
//if test.description == "ParseQuery() with no errors: if projectsornamespaces is not empty, it should return an empty filter array" {
|
||||||
// fmt.Println("stop here")
|
// fmt.Println("stop here")
|
||||||
//}
|
//}
|
||||||
lo, err := ParseQuery(test.req)
|
lo, err := ParseQuery(test.req, test.gvKind)
|
||||||
if test.errExpected {
|
if test.errExpected {
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
if test.errorText != "" {
|
if test.errorText != "" {
|
||||||
|
@@ -804,7 +804,7 @@ func (s *Store) ListByPartitions(apiOp *types.APIRequest, apiSchema *types.APISc
|
|||||||
return nil, 0, "", fmt.Errorf("cachefor %v: %w", gvk, err)
|
return nil, 0, "", fmt.Errorf("cachefor %v: %w", gvk, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts, err := listprocessor.ParseQuery(apiOp)
|
opts, err := listprocessor.ParseQuery(apiOp, gvk.Kind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var apiError *apierror.APIError
|
var apiError *apierror.APIError
|
||||||
if errors.As(err, &apiError) {
|
if errors.As(err, &apiError) {
|
||||||
|
@@ -256,7 +256,7 @@ func TestListByPartitions(t *testing.T) {
|
|||||||
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
||||||
// items is equal to the list returned by ListByParititons doesn't ensure no mutation happened
|
// items is equal to the list returned by ListByParititons doesn't ensure no mutation happened
|
||||||
copy(listToReturn.Items, expectedItems)
|
copy(listToReturn.Items, expectedItems)
|
||||||
opts, err := listprocessor.ParseQuery(req)
|
opts, err := listprocessor.ParseQuery(req, "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
||||||
// This tests that fields are being extracted from schema columns and the type specific fields map
|
// This tests that fields are being extracted from schema columns and the type specific fields map
|
||||||
@@ -329,7 +329,7 @@ func TestListByPartitions(t *testing.T) {
|
|||||||
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
||||||
// items is equal to the list returned by ListByPartitions doesn't ensure no mutation happened
|
// items is equal to the list returned by ListByPartitions doesn't ensure no mutation happened
|
||||||
copy(listToReturn.Items, expectedItems)
|
copy(listToReturn.Items, expectedItems)
|
||||||
_, err := listprocessor.ParseQuery(req)
|
_, err := listprocessor.ParseQuery(req, "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(nil, fmt.Errorf("error"))
|
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(nil, fmt.Errorf("error"))
|
||||||
|
|
||||||
@@ -403,7 +403,7 @@ func TestListByPartitions(t *testing.T) {
|
|||||||
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
||||||
// items is equal to the list returned by ListByParititons doesn't ensure no mutation happened
|
// items is equal to the list returned by ListByParititons doesn't ensure no mutation happened
|
||||||
copy(listToReturn.Items, expectedItems)
|
copy(listToReturn.Items, expectedItems)
|
||||||
opts, err := listprocessor.ParseQuery(req)
|
opts, err := listprocessor.ParseQuery(req, "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ func TestListByPartitions(t *testing.T) {
|
|||||||
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
||||||
// items is equal to the list returned by ListByParititons doesn't ensure no mutation happened
|
// items is equal to the list returned by ListByParititons doesn't ensure no mutation happened
|
||||||
copy(listToReturn.Items, expectedItems)
|
copy(listToReturn.Items, expectedItems)
|
||||||
_, err := listprocessor.ParseQuery(req)
|
_, err := listprocessor.ParseQuery(req, "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
||||||
// This tests that fields are being extracted from schema columns and the type specific fields map
|
// This tests that fields are being extracted from schema columns and the type specific fields map
|
||||||
@@ -556,7 +556,7 @@ func TestListByPartitions(t *testing.T) {
|
|||||||
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
// ListByPartitions copies point so we need some original record of items to ensure as asserting listToReturn's
|
||||||
// items is equal to the list returned by ListByParititons doesn't ensure no mutation happened
|
// items is equal to the list returned by ListByParititons doesn't ensure no mutation happened
|
||||||
copy(listToReturn.Items, expectedItems)
|
copy(listToReturn.Items, expectedItems)
|
||||||
opts, err := listprocessor.ParseQuery(req)
|
opts, err := listprocessor.ParseQuery(req, "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
||||||
// This tests that fields are being extracted from schema columns and the type specific fields map
|
// This tests that fields are being extracted from schema columns and the type specific fields map
|
||||||
|
Reference in New Issue
Block a user