mirror of
https://github.com/goharbor/harbor
synced 2025-04-14 20:11:51 +00:00
fix: sorting quota (#19538)
fix: sort Project Quotas Signed-off-by: Shengwen Yu <yshengwen@vmware.com>
This commit is contained in:
parent
219e4ba48d
commit
e941f3272b
|
@ -6175,7 +6175,7 @@ parameters:
|
|||
required: false
|
||||
sort:
|
||||
name: sort
|
||||
description: Sort the resource list in ascending or descending order. e.g. sort by field1 in ascending orderr and field2 in descending order with "sort=field1,-field2"
|
||||
description: Sort the resource list in ascending or descending order. e.g. sort by field1 in ascending order and field2 in descending order with "sort=field1,-field2"
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
|
|
|
@ -38,7 +38,7 @@ func Build(q, sort string, pageNumber, pageSize int64) (*Query, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sorts := parseSorting(sort)
|
||||
sorts := ParseSorting(sort)
|
||||
return &Query{
|
||||
Keywords: keywords,
|
||||
Sorts: sorts,
|
||||
|
@ -77,7 +77,10 @@ func parseKeywords(q string) (map[string]interface{}, error) {
|
|||
return keywords, nil
|
||||
}
|
||||
|
||||
func parseSorting(sort string) []*Sort {
|
||||
func ParseSorting(sort string) []*Sort {
|
||||
if sort == "" {
|
||||
return []*Sort{}
|
||||
}
|
||||
var sorts []*Sort
|
||||
for _, sorting := range strings.Split(sort, ",") {
|
||||
key := sorting
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package q
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -266,3 +267,60 @@ func TestParseKeywords(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
assert.Equal(t, "tags=nil", keywords["q"].(string))
|
||||
}
|
||||
|
||||
func TestParseSorting(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
sortQuery string
|
||||
expectRet []*Sort
|
||||
}{
|
||||
{
|
||||
description: "sortQuery is empty",
|
||||
sortQuery: "",
|
||||
expectRet: []*Sort{},
|
||||
},
|
||||
{
|
||||
description: "ascending without the hyphen",
|
||||
sortQuery: "key1",
|
||||
expectRet: []*Sort{
|
||||
{
|
||||
Key: "key1",
|
||||
DESC: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "descending with the hyphen",
|
||||
sortQuery: "-key1",
|
||||
expectRet: []*Sort{
|
||||
{
|
||||
Key: "key1",
|
||||
DESC: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "ascending without the hyphen and descending with hyphen",
|
||||
sortQuery: "key1,-key2",
|
||||
expectRet: []*Sort{
|
||||
{
|
||||
Key: "key1",
|
||||
DESC: false,
|
||||
},
|
||||
{
|
||||
Key: "key2",
|
||||
DESC: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
got := ParseSorting(tc.sortQuery)
|
||||
if !reflect.DeepEqual(got, tc.expectRet) {
|
||||
t.Errorf("ParseSorting() = %#v, want %#v", got, tc.expectRet)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,7 +220,7 @@ func (suite *DaoTestSuite) TestList() {
|
|||
|
||||
{
|
||||
// List quotas by sorting
|
||||
quotas, err := suite.dao.List(ctx, &q.Query{Keywords: q.KeyWords{"reference": reference}, Sorting: "-hard.storage"})
|
||||
quotas, err := suite.dao.List(ctx, &q.Query{Keywords: q.KeyWords{"reference": reference}, Sorts: q.ParseSorting("-hard.storage")})
|
||||
suite.Nil(err)
|
||||
suite.Equal(reference, quotas[0].Reference)
|
||||
suite.Equal("1", quotas[0].ReferenceID)
|
||||
|
|
|
@ -86,32 +86,45 @@ func castQuantity(field string) string {
|
|||
|
||||
func listOrderBy(query *q.Query) string {
|
||||
orderBy := "b.creation_time DESC"
|
||||
if query == nil {
|
||||
return orderBy
|
||||
}
|
||||
|
||||
if query != nil && query.Sorting != "" {
|
||||
if val, ok := quotaOrderMap[query.Sorting]; ok {
|
||||
orderBy = val
|
||||
} else {
|
||||
sort := query.Sorting
|
||||
var orderBySlice []string
|
||||
for _, sortByItem := range query.Sorts {
|
||||
sortKey := ""
|
||||
if sortByItem.DESC {
|
||||
sortKey = "-"
|
||||
}
|
||||
sortKey = sortKey + sortByItem.Key
|
||||
|
||||
order := "ASC"
|
||||
if sort[0] == '-' {
|
||||
order = "DESC"
|
||||
sort = sort[1:]
|
||||
}
|
||||
// check if sortKey is in quotaOrderMap
|
||||
if val, ok := quotaOrderMap[sortKey]; ok {
|
||||
orderBySlice = append(orderBySlice, val)
|
||||
continue
|
||||
}
|
||||
|
||||
prefixes := []string{"hard.", "used."}
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(sort, prefix) {
|
||||
resource := strings.TrimPrefix(sort, prefix)
|
||||
if types.IsValidResource(types.ResourceName(resource)) {
|
||||
field := fmt.Sprintf("%s->>%s", strings.TrimSuffix(prefix, "."), orm.QuoteLiteral(resource))
|
||||
orderBy = fmt.Sprintf("(%s) %s", castQuantity(field), order)
|
||||
break
|
||||
}
|
||||
// now: check SortByItem against "hard.resource_name", "-hard.resource_name", "used.resource_name", "-used.resource_name"
|
||||
order := "ASC"
|
||||
if sortByItem.DESC {
|
||||
order = "DESC"
|
||||
}
|
||||
prefixes := []string{"hard.", "used."}
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(sortByItem.Key, prefix) {
|
||||
resource := strings.TrimPrefix(sortByItem.Key, prefix)
|
||||
if types.IsValidResource(types.ResourceName(resource)) {
|
||||
field := fmt.Sprintf("%s->>%s", strings.TrimSuffix(prefix, "."), orm.QuoteLiteral(resource))
|
||||
orderBy = fmt.Sprintf("(%s) %s", castQuantity(field), order)
|
||||
orderBySlice = append(orderBySlice, orderBy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return orderBy
|
||||
if len(orderBySlice) == 0 {
|
||||
return orderBy
|
||||
}
|
||||
|
||||
return strings.Join(orderBySlice, ", ")
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
func Test_listOrderBy(t *testing.T) {
|
||||
query := func(sort string) *q.Query {
|
||||
return &q.Query{
|
||||
Sorting: sort,
|
||||
Sorts: q.ParseSorting(sort),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,11 +36,13 @@ func Test_listOrderBy(t *testing.T) {
|
|||
want string
|
||||
}{
|
||||
{"no query", args{nil}, "b.creation_time DESC"},
|
||||
{"empty query", args{query("")}, "b.creation_time DESC"},
|
||||
{"order by unsupported field", args{query("unknown")}, "b.creation_time DESC"},
|
||||
{"order by storage of hard", args{query("hard.storage")}, "(CAST( (CASE WHEN (hard->>'storage') IS NULL THEN '0' WHEN (hard->>'storage') = '-1' THEN '9223372036854775807' ELSE (hard->>'storage') END) AS BIGINT )) ASC"},
|
||||
{"order by unsupported hard resource", args{query("hard.unknown")}, "b.creation_time DESC"},
|
||||
{"order by storage of used", args{query("used.storage")}, "(CAST( (CASE WHEN (used->>'storage') IS NULL THEN '0' WHEN (used->>'storage') = '-1' THEN '9223372036854775807' ELSE (used->>'storage') END) AS BIGINT )) ASC"},
|
||||
{"order by unsupported used resource", args{query("used.unknown")}, "b.creation_time DESC"},
|
||||
{"order by multiple fields", args{query("-hard.storage,used.storage")}, "(CAST( (CASE WHEN (hard->>'storage') IS NULL THEN '0' WHEN (hard->>'storage') = '-1' THEN '9223372036854775807' ELSE (hard->>'storage') END) AS BIGINT )) DESC, (CAST( (CASE WHEN (used->>'storage') IS NULL THEN '0' WHEN (used->>'storage') = '-1' THEN '9223372036854775807' ELSE (used->>'storage') END) AS BIGINT )) ASC"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user