Skip to content

Commit

Permalink
Utility functions to support page token handling (#109)
Browse files Browse the repository at this point in the history
* Changes for ErrorContainer to return error message

* Utility finctions to support page token handling (moved from contacts app)

* Fix Travis failure

* Address review comment
  • Loading branch information
Evgeniy-L authored Aug 23, 2018
1 parent 15951c4 commit cd8e00f
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 2 deletions.
4 changes: 2 additions & 2 deletions errors/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ func InitContainer() *Container {
return (&Container{}).New(codes.Unknown, "Unknown")
}

// Error function returns "Unknown".
func (c Container) Error() string { return "Unknown" }
// Error function returns error message currently associated with container.
func (c Container) Error() string { return c.errMessage }

// GRPCStatus function returns an error container as GRPC status.
func (c *Container) GRPCStatus() *status.Status {
Expand Down
6 changes: 6 additions & 0 deletions errors/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ func TestError(t *testing.T) {
if c.Error() != "Unknown" {
t.Errorf("Unexpected error: expected %q, got %q", "Unknown", c.Error())
}

c.New(codes.InvalidArgument, "Custom error %v", "message")
expected := "Custom error message"
if c.Error() != expected {
t.Errorf("Unexpected error: expected %q, got %q", expected, c.Error())
}
}

func TestSet(t *testing.T) {
Expand Down
55 changes: 55 additions & 0 deletions query/page_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package query

import (
"encoding/base64"
"fmt"
"strconv"
"strings"

"google.golang.org/grpc/codes"

"github.com/infobloxopen/atlas-app-toolkit/errors"
)

// DecodePageToken decodes page token from the user's request.
// Return error if provided token is malformed or contains ivalid values,
// otherwise return offset, limit.
func DecodePageToken(ptoken string) (offset, limit int32, err error) {
errC := errors.InitContainer()
data, err := base64.StdEncoding.DecodeString(ptoken)
if err != nil {
return 0, 0, errC.New(codes.InvalidArgument, "Invalid page token %q.", err)
}
vals := strings.SplitN(string(data), ":", 2)
if len(vals) != 2 {
return 0, 0, errC.New(codes.InvalidArgument, "Malformed page token.")
}

o, err := strconv.Atoi(vals[0])
if err != nil {
errC.Set("page_token", codes.InvalidArgument, "invalid offset value %q.", vals[0])
errC.WithField("offset", "Invalid offset value. The valid value is an unsigned integer.")
}

l, err := strconv.Atoi(vals[1])
if err != nil {
errC.Set("page_token", codes.InvalidArgument, "invalid limit value %q.", vals[1])
errC.WithField("limit", "Invalid limit value. The valid value is an unsigned integer.")
}

limit = int32(l)
offset = int32(o)

if err := errC.IfSet(codes.InvalidArgument, "Page token validation failed."); err != nil {
return 0, 0, errC
}

return offset, limit, nil
}

// EncodePageToken encodes offset and limit to a string in application specific
// format (offset:limit) in base64 encoding.
func EncodePageToken(offset, limit int32) string {
data := fmt.Sprintf("%d:%d", offset, limit)
return base64.StdEncoding.EncodeToString([]byte(data))
}
84 changes: 84 additions & 0 deletions query/page_token_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package query

import (
"testing"
)

func TestDecodePageToken(t *testing.T) {
tcases := []struct {
PageToken string
Offset int32
Limit int32
ExpectedError string
}{
{
PageToken: "asd", //invalid
ExpectedError: "Invalid page token \"illegal base64 data at input byte 0\".",
},
{
PageToken: "MTI=", //12
ExpectedError: "Malformed page token.",
},
{
PageToken: "YTpi", //a:b
ExpectedError: "Page token validation failed.",
},
{
PageToken: "MTI6Yg==", //12:b
ExpectedError: "Page token validation failed.",
},
{
PageToken: "YToxMg==", //a:12
ExpectedError: "Page token validation failed.",
},
{
PageToken: "MTI6MzQ=", //12:34
Limit: 34,
Offset: 12,
},
{
PageToken: "MzQ6MTI=", //34:12
Limit: 12,
Offset: 34,
},
}

for n, tc := range tcases {
offset, limit, err := DecodePageToken(tc.PageToken)
if (err != nil && tc.ExpectedError != err.Error()) || (err == nil && tc.ExpectedError != "") {
t.Fatalf("tc %d: invalid error %q, expected %q", n, err, tc.ExpectedError)
}
if limit != tc.Limit {
t.Fatalf("tc %d: invalid limit %d, expected %d", n, limit, tc.Limit)
}
if offset != tc.Offset {
t.Fatalf("tc %d: invalid offset %d, expected %d", n, offset, tc.Offset)
}
}
}

func TestEncodePageToken(t *testing.T) {
tcases := []struct {
Offset int32
Limit int32
PageToken string
}{
{
Limit: 34,
Offset: 12,
PageToken: "MTI6MzQ=",
},
{
Limit: 12,
Offset: 34,
PageToken: "MzQ6MTI=",
},
}

for n, tc := range tcases {
ptoken := EncodePageToken(tc.Offset, tc.Limit)
if ptoken != tc.PageToken {
t.Fatalf("tc %d: invalid page token %q, expected %q", n, ptoken, tc.PageToken)
}
}
}

0 comments on commit cd8e00f

Please sign in to comment.