Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Presence.get and REST.request to new paginated results #301

Merged
merged 8 commits into from
May 13, 2021
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,16 @@ if err := pages.Err(); err != nil {
-->

```go
page, err := channel.Presence.Get(ctx, nil)
for ; err == nil && page != nil; page, err = page.Next(ctx) {
for _, presence := range page.PresenceMessages() {
pages, err := channel.Presence.Get().Pages(ctx)
if err != nil {
panic(err)
}
for pages.Next(ctx) {
for _, presence := range pages.Items() {
fmt.Println(presence)
}
}
if err != nil {
if err := pages.Err(); err != nil {
panic(err)
}
```
Expand Down
62 changes: 28 additions & 34 deletions ably/ablytest/pagination.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ func AllPages(dst, paginatedRequest interface{}) error {
}

type paginationOptions struct {
equal func(x, y interface{}) bool
equal func(x, y interface{}) bool
sortResult func([]interface{})
}

type PaginationOption func(*paginationOptions)
Expand All @@ -34,9 +35,16 @@ func PaginationWithEqual(equal func(x, y interface{}) bool) PaginationOption {
}
}

func PaginationWithSortResult(sort func([]interface{})) PaginationOption {
return func(o *paginationOptions) {
o.sortResult = sort
}
}

func TestPagination(expected, request interface{}, perPage int, options ...PaginationOption) error {
opts := paginationOptions{
equal: reflect.DeepEqual,
equal: reflect.DeepEqual,
sortResult: func(items []interface{}) {},
}
for _, o := range options {
o(&opts)
Expand All @@ -47,40 +55,36 @@ func TestPagination(expected, request interface{}, perPage int, options ...Pagin
for i := 0; i < reflect.ValueOf(expected).Len(); i++ {
items = append(items, rexpected.Index(i).Interface())
}
return testPagination(reflect.ValueOf(request), items, perPage, opts.equal)
return testPagination(reflect.ValueOf(request), items, perPage, opts)
}

func testPagination(request reflect.Value, expectedItems []interface{}, perPage int, equal func(x, y interface{}) bool) error {
func testPagination(request reflect.Value, expectedItems []interface{}, perPage int, opts paginationOptions) error {
getPages, getItems := generalizePagination(request)

var expectedPages [][]interface{}
var page []interface{}
for _, item := range expectedItems {
page = append(page, item)
if len(page) == perPage {
expectedPages = append(expectedPages, page)
page = nil
}
}
if len(page) > 0 {
expectedPages = append(expectedPages, page)
}

for i := 0; i < 2; i++ {
pages, err := getPages()
if err != nil {
return fmt.Errorf("calling Pages: %w", err)
}
var gotPages [][]interface{}
var gotItems []interface{}
pageNum := 1
expectedFullPages := len(expectedItems) / perPage
for pages.next() {
gotPages = append(gotPages, pages.items())
if (pageNum <= expectedFullPages && len(pages.items()) != perPage) ||
(pageNum > expectedFullPages && len(pages.items()) >= perPage) {
return fmt.Errorf("page #%d got %d items, expected at most %d", pageNum, len(pages.items()), perPage)
}
gotItems = append(gotItems, pages.items()...)
pageNum++
}
if err := pages.err(); err != nil {
return fmt.Errorf("iterating pages: %w", err)
}

if !PagesEqual(expectedPages, gotPages, equal) {
return fmt.Errorf("expected pages: %+v, got: %+v", expectedPages, gotPages)
opts.sortResult(gotItems)

if !ItemsEqual(expectedItems, gotItems, opts.equal) {
return fmt.Errorf("expected items: %+v, got: %+v", expectedItems, gotItems)
}

if err := pages.first(); err != nil {
Expand All @@ -101,7 +105,9 @@ func testPagination(request reflect.Value, expectedItems []interface{}, perPage
return fmt.Errorf("iterating items: %w", err)
}

if !ItemsEqual(expectedItems, gotItems, equal) {
opts.sortResult(gotItems)

if !ItemsEqual(expectedItems, gotItems, opts.equal) {
return fmt.Errorf("expected items: %+v, got: %+v", expectedItems, gotItems)
}

Expand Down Expand Up @@ -184,18 +190,6 @@ func generalizePagination(request reflect.Value) (func() (paginatedResult, error
return pages, items
}

func PagesEqual(expected, got [][]interface{}, equal func(x, y interface{}) bool) bool {
if len(expected) != len(got) {
return false
}
for i := range expected {
if !ItemsEqual(expected[i], got[i], equal) {
return false
}
}
return true
}

func ItemsEqual(expected, got []interface{}, equal func(x, y interface{}) bool) bool {
if len(expected) != len(got) {
return false
Expand Down
18 changes: 11 additions & 7 deletions ably/ablytest/sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,22 @@ func DefaultConfig() *Config {
},
Channels: []Channel{
{
Name: "persisted:presence_fixtures",
Presence: []Presence{
{ClientID: "client_bool", Data: "true"},
{ClientID: "client_int", Data: "true"},
{ClientID: "client_string", Data: "true"},
{ClientID: "client_json", Data: `{"test": "This is a JSONObject clientData payload"}`},
},
Name: "persisted:presence_fixtures",
Presence: PresenceFixtures(),
},
},
}
}

var PresenceFixtures = func() []Presence {
return []Presence{
{ClientID: "client_bool", Data: "true"},
{ClientID: "client_int", Data: "true"},
{ClientID: "client_string", Data: "true"},
{ClientID: "client_json", Data: `{"test": "This is a JSONObject clientData payload"}`},
}
}

type Sandbox struct {
Config *Config
Environment string
Expand Down
4 changes: 0 additions & 4 deletions ably/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import (
"time"
)

func (p *PaginatedResult) BuildPath(base, rel string) string {
return p.buildPath(base, rel)
}

func (opts *clientOptions) RestURL() string {
return opts.restURL()
}
Expand Down
59 changes: 0 additions & 59 deletions ably/http_paginated_response.go

This file was deleted.

80 changes: 51 additions & 29 deletions ably/http_paginated_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ably_test
import (
"context"
"net/http"
"net/url"
"reflect"
"sort"
"testing"
Expand All @@ -26,37 +27,43 @@ func TestHTTPPaginatedResponse(t *testing.T) {
t.Fatal(err)
}
t.Run("request_time", func(ts *testing.T) {
res, err := client.Request(context.Background(), "get", "/time", nil, nil, nil)
res, err := client.Request("get", "/time").Pages(context.Background())
if err != nil {
ts.Fatal(err)
}
if res.StatusCode != http.StatusOK {
ts.Errorf("expected %d got %d", http.StatusOK, res.StatusCode)
if res.StatusCode() != http.StatusOK {
ts.Errorf("expected %d got %d", http.StatusOK, res.StatusCode())
}
if !res.Success {
if !res.Success() {
ts.Error("expected success to be true")
}
n := len(res.Items())
res.Next(context.Background())
var items []interface{}
err = res.Items(&items)
if err != nil {
ts.Error(err)
}
n := len(items)
if n != 1 {
ts.Errorf("expected 1 item got %d", n)
}
})

t.Run("request_404", func(ts *testing.T) {
res, err := client.Request(context.Background(), "get", "/keys/ablyjs.test/requestToken", nil, nil, nil)
res, err := client.Request("get", "/keys/ablyjs.test/requestToken").Pages(context.Background())
if err != nil {
ts.Fatal(err)
}
if res.StatusCode != http.StatusNotFound {
ts.Errorf("expected %d got %d", http.StatusNotFound, res.StatusCode)
if res.StatusCode() != http.StatusNotFound {
ts.Errorf("expected %d got %d", http.StatusNotFound, res.StatusCode())
}
if res.ErrorCode != ably.ErrNotFound {
ts.Errorf("expected %d got %d", ably.ErrNotFound, res.ErrorCode)
if res.ErrorCode() != ably.ErrNotFound {
ts.Errorf("expected %d got %d", ably.ErrNotFound, res.ErrorCode())
}
if res.Success {
if res.Success() {
ts.Error("expected success to be false")
}
if res.ErrorMessage == "" {
if res.ErrorMessage() == "" {
ts.Error("expected error message")
}
})
Expand All @@ -71,39 +78,51 @@ func TestHTTPPaginatedResponse(t *testing.T) {

ts.Run("post", func(ts *testing.T) {
for _, message := range msgs {
res, err := client.Request(context.Background(), "POST", channelPath, nil, message, nil)
res, err := client.Request("POST", channelPath, ably.RequestWithBody(message)).Pages(context.Background())
if err != nil {
ts.Fatal(err)
}
if res.StatusCode != http.StatusCreated {
ts.Errorf("expected %d got %d", http.StatusCreated, res.StatusCode)
if res.StatusCode() != http.StatusCreated {
ts.Errorf("expected %d got %d", http.StatusCreated, res.StatusCode())
}
if !res.Success {
if !res.Success() {
ts.Error("expected success to be true")
}
n := len(res.Items())
res.Next(context.Background())
var items []interface{}
err = res.Items(&items)
if err != nil {
ts.Error(err)
}
n := len(items)
if n != 1 {
ts.Errorf("expected 1 item got %d", n)
}
}
})

ts.Run("get", func(ts *testing.T) {
res, err := client.Request(context.Background(), "get", channelPath, &ably.PaginateParams{
Limit: 1,
Direction: "forwards",
}, nil, nil)
res, err := client.Request("get", channelPath, ably.RequestWithParams(url.Values{
"limit": {"1"},
"direction": {"forwards"},
})).Pages(context.Background())
if err != nil {
ts.Fatal(err)
}
if res.StatusCode != http.StatusOK {
ts.Errorf("expected %d got %d", http.StatusOK, res.StatusCode)
if res.StatusCode() != http.StatusOK {
ts.Errorf("expected %d got %d", http.StatusOK, res.StatusCode())
}
n := len(res.Items())
res.Next(context.Background())
var items []interface{}
err = res.Items(&items)
if err != nil {
ts.Error(err)
}
n := len(items)
if n != 1 {
ts.Fatalf("expected 1 item got %d", n)
}
m := res.Items()[0].(map[string]interface{})
m := items[0].(map[string]interface{})
name := m["name"].(string)
data := m["data"].(string)
if name != msgs[0].Name {
Expand All @@ -113,15 +132,18 @@ func TestHTTPPaginatedResponse(t *testing.T) {
ts.Errorf("expected %v got %s", msgs[0].Data, data)
}

res, err = res.Next(context.Background())
if !res.Next(context.Background()) {
ts.Fatal(res.Err())
}
err = res.Items(&items)
if err != nil {
ts.Fatal(err)
ts.Error(err)
}
n = len(res.Items())
n = len(items)
if n != 1 {
ts.Fatalf("expected 1 item got %d", n)
}
m = res.Items()[0].(map[string]interface{})
m = items[0].(map[string]interface{})
name = m["name"].(string)
data = m["data"].(string)
if name != msgs[1].Name {
Expand Down
Loading