1
0
mirror of https://github.com/rancher/steve.git synced 2025-09-08 18:59:58 +00:00

SQLite backed cache: Support sorting mgmt clusters on value in a specific condition (#447)

* Replace primary/secondary sort fields with an array of sort directives.

* Allow more than 2 sort-params in a search query.

* Add a virtual 'status.ready' field to clusters.

* Rename status.ready -> status.connected

* Set virtual field 'spec.internal' <- spec.displayName == 'local'

* Need to declare all virtual fields to index.

* Ready clusters have condition[type==Ready && status=True]

* Update the README to reflect generalized sorting.

* Bump lasso to get revised sort directives.

* Review-driven changes, mostly comments and drop unneeded code.

* Add unit tests to verify sort-order stringification.

* Ignore empty-string sort components.

* Fix a rebase mishap.

* Drop unneeded commented-out code.

* Clusters have a 'spec.internal' field, no need to synthesize one.

* Added a note on square-brackets for label references.

This should be added to the README for filter queries in the PR for 46333.

* Bump to latest sqlcache-free lasso
This commit is contained in:
Eric Promislow
2025-01-27 11:55:09 -08:00
committed by GitHub
parent 809e927a0c
commit c1805696ce
14 changed files with 709 additions and 99 deletions

View File

@@ -12,6 +12,7 @@ import (
"github.com/rancher/apiserver/pkg/types"
"github.com/rancher/steve/pkg/sqlcache/informer"
"github.com/rancher/steve/pkg/sqlcache/partition"
"github.com/rancher/steve/pkg/stores/queryhelper"
"github.com/rancher/wrangler/v3/pkg/schemas/validation"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@@ -88,27 +89,19 @@ func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (informer.ListOpt
sortOpts := informer.Sort{}
sortKeys := q.Get(sortParam)
if sortKeys != "" {
sortParts := strings.SplitN(sortKeys, ",", 2)
primaryField := sortParts[0]
if primaryField != "" {
if primaryField[0] == '-' {
sortOpts.Orders = append(sortOpts.Orders, informer.DESC)
primaryField = primaryField[1:]
} else {
sortOpts.Orders = append(sortOpts.Orders, informer.ASC)
}
sortOpts.Fields = append(sortOpts.Fields, strings.Split(primaryField, "."))
}
if len(sortParts) > 1 {
secondaryField := sortParts[1]
if secondaryField != "" {
if secondaryField[0] == '-' {
sortOpts.Orders = append(sortOpts.Orders, informer.DESC)
secondaryField = secondaryField[1:]
} else {
sortOpts.Orders = append(sortOpts.Orders, informer.ASC)
sortParts := strings.Split(sortKeys, ",")
for _, sortPart := range sortParts {
field := sortPart
if len(field) > 0 {
sortOrder := informer.ASC
if field[0] == '-' {
sortOrder = informer.DESC
field = field[1:]
}
if len(field) > 0 {
sortOpts.Fields = append(sortOpts.Fields, queryhelper.SafeSplit(field))
sortOpts.Orders = append(sortOpts.Orders, sortOrder)
}
sortOpts.Fields = append(sortOpts.Fields, strings.Split(secondaryField, "."))
}
}
}

View File

@@ -342,7 +342,7 @@ func TestParseQuery(t *testing.T) {
},
})
tests = append(tests, testCase{
description: "ParseQuery() with no errors returned should returned no errors. If one sort param is given, primary field" +
description: "ParseQuery() with no errors returned should returned no errors. It should sort on the one given" +
" sort option should be set",
req: &types.APIRequest{
Request: &http.Request{
@@ -353,11 +353,8 @@ func TestParseQuery(t *testing.T) {
ChunkSize: defaultLimit,
Sort: informer.Sort{
Fields: [][]string{
{"metadata", "name"},
},
Orders: []informer.SortOrder{
informer.ASC,
},
{"metadata", "name"}},
Orders: []informer.SortOrder{informer.ASC},
},
Filters: make([]informer.OrFilter, 0),
Pagination: informer.Pagination{
@@ -420,6 +417,32 @@ func TestParseQuery(t *testing.T) {
return nil
},
})
tests = append(tests, testCase{
description: "sorting can parse bracketed field names correctly",
req: &types.APIRequest{
Request: &http.Request{
URL: &url.URL{RawQuery: "sort=-metadata.labels[beef.cattle.io/snort],metadata.labels.steer,metadata.labels[bossie.cattle.io/moo],spec.something"},
},
},
expectedLO: informer.ListOptions{
ChunkSize: defaultLimit,
Sort: informer.Sort{
Fields: [][]string{{"metadata", "labels", "beef.cattle.io/snort"},
{"metadata", "labels", "steer"},
{"metadata", "labels", "bossie.cattle.io/moo"},
{"spec", "something"}},
Orders: []informer.SortOrder{informer.DESC, informer.ASC, informer.ASC, informer.ASC},
},
Filters: make([]informer.OrFilter, 0),
Pagination: informer.Pagination{
Page: 1,
},
},
setupNSCache: func() Cache {
return nil
},
})
tests = append(tests, testCase{
description: "ParseQuery() with no errors returned should returned no errors. If continue params is given, resume" +
" should be set with assigned value.",
@@ -522,6 +545,9 @@ func TestParseQuery(t *testing.T) {
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
test.nsc = test.setupNSCache()
if test.description == "sorting can parse bracketed field names correctly" {
fmt.Printf("stop here")
}
lo, err := ParseQuery(test.req, test.nsc)
if test.errExpected {
assert.NotNil(t, err)