Skip to content

Commit

Permalink
aws: Fix Pagination handling of empty string NextToken (aws#94)
Browse files Browse the repository at this point in the history
Fixes the SDK's handling of a Pagination NextToken's value being an
empty string compared to a nil value. The SDK was expecting NextToken's
to always be unset (nil) and treating any non-nil value as a valid
value. This was not the case in MediaLive's List APIs. As those APIs
return a empty string value instead of null or not setting the field at
all.

This issue exists in both the v1 and v2 SDKs.

Fix aws#84
  • Loading branch information
jasdel authored Jan 15, 2018
1 parent 694c387 commit ed4e5c9
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 13 deletions.
26 changes: 21 additions & 5 deletions aws/request_pagination.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,29 @@ func (r *Request) nextPageTokens() []interface{} {
tokens := []interface{}{}
tokenAdded := false
for _, outToken := range r.Operation.OutputTokens {
v, _ := awsutil.ValuesAtPath(r.Data, outToken)
if len(v) > 0 {
tokens = append(tokens, v[0])
tokenAdded = true
} else {
vs, _ := awsutil.ValuesAtPath(r.Data, outToken)

if len(vs) == 0 {
tokens = append(tokens, nil)
continue
}
v := vs[0]

switch tv := v.(type) {
case *string:
if len(StringValue(tv)) == 0 {
tokens = append(tokens, nil)
continue
}
case string:
if len(tv) == 0 {
tokens = append(tokens, nil)
continue
}
}

tokenAdded = true
tokens = append(tokens, v)
}
if !tokenAdded {
return nil
Expand Down
92 changes: 84 additions & 8 deletions aws/request_pagination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,15 +511,15 @@ func TestPaginationWithContextNilInput(t *testing.T) {
}
}

type testPageInput struct {
NextToken string
}
type testPageOutput struct {
Value string
NextToken *string
}

func TestPagination_Standalone(t *testing.T) {
type testPageInput struct {
NextToken string
}
type testPageOutput struct {
Value string
NextToken *string
}

expect := []struct {
Value, PrevToken, NextToken string
}{
Expand Down Expand Up @@ -586,6 +586,82 @@ func TestPagination_Standalone(t *testing.T) {
}
}

func TestPagination_Standalone_Pointers(t *testing.T) {
type testPageInput struct {
NextToken *string
}
type testPageOutput struct {
Value *string
NextToken *string
}

expect := []struct {
Value, PrevToken, NextToken *string
}{
{aws.String("FirstValue"), aws.String("InitalToken"), aws.String("FirstToken")},
{aws.String("SecondValue"), aws.String("FirstToken"), aws.String("SecondToken")},
{aws.String("ThirdValue"), aws.String("SecondToken"), nil},
}
input := testPageInput{
NextToken: expect[0].PrevToken,
}

c := awstesting.NewClient(unit.Config())
i := 0
p := aws.Pagination{
NewRequest: func() (*aws.Request, error) {
r := c.NewRequest(
&aws.Operation{
Name: "Operation",
Paginator: &aws.Paginator{
InputTokens: []string{"NextToken"},
OutputTokens: []string{"NextToken"},
},
},
&input, &testPageOutput{},
)
// Setup handlers for testing
r.Handlers.Clear()
r.Handlers.Build.PushBack(func(req *aws.Request) {
in := req.Params.(*testPageInput)
if e, a := aws.StringValue(expect[i].PrevToken), aws.StringValue(in.NextToken); e != a {
t.Errorf("%d, expect NextToken input %q, got %q", i, e, a)
}
})
r.Handlers.Unmarshal.PushBack(func(req *aws.Request) {
out := &testPageOutput{
Value: expect[i].Value,
}
if expect[i].NextToken != nil {
next := *expect[i].NextToken
out.NextToken = aws.String(next)
}
req.Data = out
})
return r, nil
},
}

for p.Next() {
data := p.Page().(*testPageOutput)

if e, a := expect[i].Value, data.Value; e != a {
t.Errorf("%d, expect Value to be %q, got %q", i, e, a)
}
if e, a := aws.StringValue(expect[i].NextToken), aws.StringValue(data.NextToken); e != a {
t.Errorf("%d, expect NextToken to be %q, got %q", i, e, a)
}

i++
}
if e, a := len(expect), i; e != a {
t.Errorf("expected to process %d pages, did %d", e, a)
}
if err := p.Err(); err != nil {
t.Fatalf("%d, expected no error, got %v", i, err)
}
}

// Benchmarks
var benchResps = []dynamodb.ListTablesOutput{
{TableNames: []string{"TABLE", "NXT"}, LastEvaluatedTableName: aws.String("NXT")},
Expand Down

0 comments on commit ed4e5c9

Please sign in to comment.