-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pkg, internal: add pagination to list requests (#2016)
* pkg, internal: add pagination to list requests * address linter * rename All to ListAll * rename file * beautify signature * fix unit tests
- Loading branch information
1 parent
97cdffd
commit 66fafc9
Showing
19 changed files
with
318 additions
and
232 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
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
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
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
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,38 @@ | ||
package paging | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"net/http" | ||
) | ||
|
||
// Response is the paginated response containing the current page results and the total count. | ||
// It is implemented by all supported SDK versions. | ||
type Response[T any] interface { | ||
GetResults() []T | ||
GetTotalCount() int | ||
} | ||
|
||
// ListAll invokes the given pagination list function multiple times until the total count of responses is gathered. | ||
// Once done, all paginated responses are returned. | ||
// If an error occurs, the first error occurrence will be returned. | ||
// | ||
// This is taken over from https://github.com/mongodb/terraform-provider-mongodbatlas/blob/a5581ebb274dbcaffd43d330c5bfbbb329cae51d/internal/common/dsschema/page_request.go#L14-L31. | ||
func ListAll[T any](ctx context.Context, listFunc func(ctx context.Context, pageNum int) (Response[T], *http.Response, error)) ([]T, error) { | ||
var results []T | ||
for currentPage := 1; ; currentPage++ { | ||
resp, _, err := listFunc(ctx, currentPage) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if resp == nil { | ||
return nil, errors.New("no response") | ||
} | ||
currentResults := resp.GetResults() | ||
results = append(results, currentResults...) | ||
if len(currentResults) == 0 || len(results) >= resp.GetTotalCount() { | ||
break | ||
} | ||
} | ||
return results, nil | ||
} |
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,146 @@ | ||
package paging | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
type page struct { | ||
results []string | ||
totalCount int | ||
} | ||
|
||
func (r *page) GetResults() []string { | ||
if r == nil { | ||
return nil | ||
} | ||
return r.results | ||
} | ||
|
||
func (r *page) GetTotalCount() int { | ||
if r == nil { | ||
return 0 | ||
} | ||
return r.totalCount | ||
} | ||
|
||
func responder(pages []*page) func(ctx context.Context, pageNum int) (Response[string], *http.Response, error) { | ||
totalCount := 0 | ||
for _, p := range pages { | ||
if p == nil { | ||
continue | ||
} | ||
totalCount = totalCount + len(p.results) | ||
} | ||
|
||
for _, p := range pages { | ||
if p == nil { | ||
continue | ||
} | ||
p.totalCount = totalCount | ||
} | ||
|
||
return func(ctx context.Context, pageNum int) (Response[string], *http.Response, error) { | ||
if len(pages) == 0 { | ||
return nil, nil, nil | ||
} | ||
return pages[pageNum-1], nil, nil | ||
} | ||
} | ||
|
||
func TestAll(t *testing.T) { | ||
ctx := context.Background() | ||
|
||
for _, tc := range []struct { | ||
name string | ||
pages []*page | ||
wantErr string | ||
wantResult []string | ||
}{ | ||
{ | ||
name: "no response", | ||
wantErr: "no response", | ||
}, | ||
{ | ||
name: "empty response", | ||
pages: []*page{}, | ||
wantErr: "no response", | ||
}, | ||
{ | ||
name: "empty results", | ||
pages: []*page{ | ||
{results: []string{}}, | ||
}, | ||
wantResult: nil, | ||
}, | ||
{ | ||
name: "single result", | ||
pages: []*page{ | ||
{results: []string{"a"}}, | ||
}, | ||
wantResult: []string{"a"}, | ||
}, | ||
{ | ||
name: "multiple results", | ||
pages: []*page{ | ||
{results: []string{"a", "b"}}, | ||
}, | ||
wantResult: []string{"a", "b"}, | ||
}, | ||
{ | ||
name: "one additional nil page", | ||
pages: []*page{ | ||
{results: []string{"a", "b"}}, | ||
nil, | ||
}, | ||
wantResult: []string{"a", "b"}, | ||
}, | ||
{ | ||
name: "one additional empty results page", | ||
pages: []*page{ | ||
{results: []string{"a", "b"}}, | ||
{results: []string{}}, | ||
}, | ||
wantResult: []string{"a", "b"}, | ||
}, | ||
{ | ||
name: "multiple results", | ||
pages: []*page{ | ||
{results: []string{"a", "b"}}, | ||
{results: []string{"c", "d"}}, | ||
}, | ||
wantResult: []string{"a", "b", "c", "d"}, | ||
}, | ||
{ | ||
name: "multiple results with nil page", | ||
pages: []*page{ | ||
{results: []string{"a", "b"}}, | ||
nil, | ||
{results: []string{"c", "d"}}, | ||
}, | ||
wantResult: []string{"a", "b"}, | ||
}, | ||
{ | ||
name: "multiple results with empty results", | ||
pages: []*page{ | ||
{results: []string{"a", "b"}}, | ||
{results: []string{}}, | ||
{results: []string{"c", "d"}}, | ||
}, | ||
wantResult: []string{"a", "b"}, | ||
}, | ||
} { | ||
t.Run(tc.name, func(t *testing.T) { | ||
response, err := ListAll(ctx, responder(tc.pages)) | ||
gotErr := "" | ||
if err != nil { | ||
gotErr = err.Error() | ||
} | ||
require.Equal(t, tc.wantErr, gotErr) | ||
require.Equal(t, tc.wantResult, response) | ||
}) | ||
} | ||
} |
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
Oops, something went wrong.