This repository has been archived by the owner on Feb 5, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from qri-io/orderby-util
feat(orderby): added util for orderBy support in api calls
- Loading branch information
Showing
2 changed files
with
153 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package apiutil | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
) | ||
|
||
var ( | ||
// OrderASC defines the ascending order keyword | ||
OrderASC = "asc" | ||
// OrderDESC defines the descending order keyword | ||
OrderDESC = "desc" | ||
) | ||
|
||
// OrderBy represents ordering information | ||
type OrderBy []Order | ||
|
||
// Order represents ordering information for a single key | ||
type Order struct { | ||
Key string | ||
Direction string | ||
} | ||
|
||
// NewOrder constructs a basic order struct | ||
func NewOrder(key, orderDirection string) Order { | ||
if orderDirection != OrderASC && orderDirection != OrderDESC { | ||
orderDirection = OrderDESC | ||
} | ||
return Order{ | ||
Key: key, | ||
Direction: orderDirection, | ||
} | ||
} | ||
|
||
// String implements the stringer interface for OrderBy | ||
func (o OrderBy) String() string { | ||
var b strings.Builder | ||
for i, oi := range o { | ||
if i > 0 { | ||
b.WriteString(",") | ||
} | ||
b.WriteString(oi.String()) | ||
} | ||
return b.String() | ||
} | ||
|
||
// String implements the stringer interface for Order | ||
func (o Order) String() string { | ||
return fmt.Sprintf("%s,%s", o.Key, o.Direction) | ||
} | ||
|
||
// SetQueryParams adds order by info to a url as query parameters | ||
func (o OrderBy) SetQueryParams(u *url.URL) *url.URL { | ||
q := u.Query() | ||
if len(o) > 0 { | ||
q.Set("orderBy", o.String()) | ||
} | ||
|
||
u.RawQuery = q.Encode() | ||
return u | ||
} | ||
|
||
// OrderByFromRequest extracts orderBy params from an http request | ||
func OrderByFromRequest(r *http.Request) OrderBy { | ||
orderBy := r.FormValue("orderBy") | ||
return NewOrderByFromString(orderBy, nil) | ||
} | ||
|
||
// OrderByFromRequestWithKeys extracts orderBy params from an | ||
// http request and only takes the specified keys | ||
func OrderByFromRequestWithKeys(r *http.Request, validKeys []string) OrderBy { | ||
orderBy := r.FormValue("orderBy") | ||
return NewOrderByFromString(orderBy, validKeys) | ||
} | ||
|
||
// NewOrderByFromString converts a commaa delimited string to an OrderBy struct | ||
func NewOrderByFromString(orderBy string, validKeys []string) OrderBy { | ||
orderComponents := strings.Split(orderBy, ",") | ||
if orderBy == "" || len(orderComponents) == 0 || (len(orderComponents) != 1 && len(orderComponents)%2 == 1) { | ||
return []Order{} | ||
} | ||
res := []Order{} | ||
if len(orderComponents) == 1 { | ||
res = append(res, NewOrder(strings.TrimSpace(orderComponents[0]), OrderDESC)) | ||
return res | ||
} | ||
for i := 0; i <= len(orderComponents)/2; i += 2 { | ||
orderDirection := "" | ||
if strings.ToLower(orderComponents[i+1]) == OrderASC { | ||
orderDirection = OrderASC | ||
} else { | ||
orderDirection = OrderDESC | ||
} | ||
orderKey := strings.TrimSpace(orderComponents[i]) | ||
if validKeys != nil { | ||
for _, vkey := range validKeys { | ||
if vkey == orderKey { | ||
res = append(res, NewOrder(orderKey, orderDirection)) | ||
break | ||
} | ||
} | ||
} else { | ||
res = append(res, NewOrder(orderKey, orderDirection)) | ||
} | ||
} | ||
|
||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package apiutil | ||
|
||
import ( | ||
"net/http/httptest" | ||
"testing" | ||
) | ||
|
||
func TestOrderByFromRequest(t *testing.T) { | ||
cases := []struct { | ||
description string | ||
query string | ||
validKeys []string | ||
expNumber int | ||
expOrderBy string | ||
}{ | ||
{"no orderBy specified", "", nil, 0, ""}, | ||
{"orderBy with no direction (defaults to desc)", "title", nil, 1, "title,desc"}, | ||
{"invalid orderBy", "title,asc,date", nil, 0, ""}, | ||
{"orderBy with bad parameters", "title,asc, date,desc", nil, 2, "title,asc,date,desc"}, | ||
{"orderBy with multiple parameters", "title,asc,date,desc", nil, 2, "title,asc,date,desc"}, | ||
{"orderBy with some invalid parameters", "title,asc,date,desc", []string{"title"}, 1, "title,asc"}, | ||
} | ||
|
||
for _, c := range cases { | ||
r := httptest.NewRequest("GET", "/", nil) | ||
q := r.URL.Query() | ||
// add query params | ||
if c.query != "" { | ||
q.Set("orderBy", c.query) | ||
} | ||
|
||
r.URL.RawQuery = q.Encode() | ||
|
||
got := OrderByFromRequestWithKeys(r, c.validKeys) | ||
if c.expNumber != len(got) { | ||
t.Errorf("case '%s' error: number mismatch, expected '%d', got '%d'", c.description, c.expNumber, len(got)) | ||
} | ||
if c.expOrderBy != got.String() { | ||
t.Errorf("case '%s' error: output mismatch, expected '%s', got '%s'", c.description, c.expOrderBy, got.String()) | ||
} | ||
} | ||
|
||
} |