Skip to content

Commit

Permalink
new: (PROPOSAL) Add API request & pagination helpers using generics (#…
Browse files Browse the repository at this point in the history
…456)

* cool

* Respect page

* Add method helpers

* one more

* oops

* tidy up

* Add basic tests

* Add unit tests

* make tidy

* oops

* Re-use mock function

* Use t.Helper

* Aggregate -> Get

* pls

* nolint

* Re-run image fixtures

* oops

* Store pages and results in listoptions

* Maintain ListOptions backwards compatibility

* Rerun example fixtures

---------

Co-authored-by: Zhiwei Liang <[email protected]>
  • Loading branch information
lgarber-akamai and zliang-akamai authored Feb 26, 2024
1 parent 852aace commit f8ed8e9
Show file tree
Hide file tree
Showing 38 changed files with 1,319 additions and 710 deletions.
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ module github.com/linode/linodego
require (
github.com/go-resty/resty/v2 v2.11.0
github.com/google/go-cmp v0.6.0
github.com/jarcoal/httpmock v1.3.1
golang.org/x/net v0.21.0
golang.org/x/oauth2 v0.17.0
golang.org/x/text v0.14.0
gopkg.in/ini.v1 v1.66.6
)

require github.com/stretchr/testify v1.8.4 // indirect
require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/stretchr/testify v1.8.4 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

go 1.20

Expand Down
19 changes: 19 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
Expand All @@ -12,6 +20,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
Expand All @@ -20,6 +29,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -37,6 +48,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
Expand All @@ -51,6 +63,13 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
Expand Down
114 changes: 34 additions & 80 deletions images.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package linodego
import (
"context"
"encoding/json"
"fmt"
"io"
"net/url"
"time"

"github.com/go-resty/resty/v2"
Expand Down Expand Up @@ -105,110 +103,66 @@ func (i Image) GetUpdateOptions() (iu ImageUpdateOptions) {
return
}

// ImagesPagedResponse represents a linode API response for listing of images
type ImagesPagedResponse struct {
*PageOptions
Data []Image `json:"data"`
}

func (ImagesPagedResponse) endpoint(_ ...any) string {
return "images"
}

func (resp *ImagesPagedResponse) castResult(r *resty.Request, e string) (int, int, error) {
res, err := coupleAPIErrors(r.SetResult(ImagesPagedResponse{}).Get(e))
if err != nil {
return 0, 0, err
}
castedRes := res.Result().(*ImagesPagedResponse)
resp.Data = append(resp.Data, castedRes.Data...)
return castedRes.Pages, castedRes.Results, nil
}

// ListImages lists Images
func (c *Client) ListImages(ctx context.Context, opts *ListOptions) ([]Image, error) {
response := ImagesPagedResponse{}
err := c.listHelper(ctx, &response, opts)
if err != nil {
return nil, err
}
return response.Data, nil
return getPaginatedResults[Image](
ctx,
c,
"images",
opts,
)
}

// GetImage gets the Image with the provided ID
func (c *Client) GetImage(ctx context.Context, imageID string) (*Image, error) {
imageID = url.PathEscape(imageID)

e := fmt.Sprintf("images/%s", imageID)
req := c.R(ctx).SetResult(&Image{})
r, err := coupleAPIErrors(req.Get(e))
if err != nil {
return nil, err
}
return r.Result().(*Image), nil
return doGETRequest[Image](
ctx,
c,
formatAPIPath("images/%s", imageID),
)
}

// CreateImage creates an Image
func (c *Client) CreateImage(ctx context.Context, opts ImageCreateOptions) (*Image, error) {
body, err := json.Marshal(opts)
if err != nil {
return nil, err
}

e := "images"
req := c.R(ctx).SetResult(&Image{}).SetBody(string(body))
r, err := coupleAPIErrors(req.Post(e))
if err != nil {
return nil, err
}
return r.Result().(*Image), nil
return doPOSTRequest[Image](
ctx,
c,
"images",
opts,
)
}

// UpdateImage updates the Image with the specified id
func (c *Client) UpdateImage(ctx context.Context, imageID string, opts ImageUpdateOptions) (*Image, error) {
body, err := json.Marshal(opts)
if err != nil {
return nil, err
}

imageID = url.PathEscape(imageID)

e := fmt.Sprintf("images/%s", imageID)
req := c.R(ctx).SetResult(&Image{}).SetBody(string(body))
r, err := coupleAPIErrors(req.Put(e))
if err != nil {
return nil, err
}
return r.Result().(*Image), nil
return doPUTRequest[Image](
ctx,
c,
formatAPIPath("images/%s", imageID),
opts,
)
}

// DeleteImage deletes the Image with the specified id
func (c *Client) DeleteImage(ctx context.Context, imageID string) error {
imageID = url.PathEscape(imageID)
e := fmt.Sprintf("images/%s", imageID)
_, err := coupleAPIErrors(c.R(ctx).Delete(e))
return err
return doDELETERequest(
ctx,
c,
formatAPIPath("images/%s", imageID),
)
}

// CreateImageUpload creates an Image and an upload URL
func (c *Client) CreateImageUpload(ctx context.Context, opts ImageCreateUploadOptions) (*Image, string, error) {
body, err := json.Marshal(opts)
result, err := doPOSTRequest[ImageCreateUploadResponse](
ctx,
c,
"images/upload",
opts,
)
if err != nil {
return nil, "", err
}

e := "images/upload"
req := c.R(ctx).SetResult(&ImageCreateUploadResponse{}).SetBody(string(body))
r, err := coupleAPIErrors(req.Post(e))
if err != nil {
return nil, "", err
}

result, ok := r.Result().(*ImageCreateUploadResponse)
if !ok {
return nil, "", fmt.Errorf("failed to parse result")
}

return result.Image, result.UploadTo, nil
}

Expand Down
77 changes: 77 additions & 0 deletions internal/testutil/mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package testutil

import (
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"regexp"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/jarcoal/httpmock"
"golang.org/x/oauth2"
)

var validTestAPIKey = "NOTANAPIKEY"

func MockRequestURL(path string) *regexp.Regexp {
return regexp.MustCompile(fmt.Sprintf("/[a-zA-Z0-9]+/%s", strings.TrimPrefix(path, "/")))
}

func MockRequestBodyValidate(t *testing.T, expected interface{}, response interface{}) httpmock.Responder {
t.Helper()

return func(request *http.Request) (*http.Response, error) {
eType := reflect.TypeOf(expected)
result := reflect.New(eType)

i := result.Interface()

data, err := io.ReadAll(request.Body)
if err != nil {
t.Fatal(err)
}

if err := json.Unmarshal(data, &i); err != nil {
t.Fatal(err)
}

// Deref the pointer if necessary
if result.Kind() == reflect.Pointer {
result = result.Elem()
}

resultValue := result.Interface()

if !reflect.DeepEqual(expected, resultValue) {
t.Fatalf("request body does not match request options: %s", cmp.Diff(expected, resultValue))
}

return httpmock.NewJsonResponse(200, response)
}
}

// CreateMockClient is generic because importing the linodego package will result
// in a cyclic dependency. This pattern isn't ideal but works for now.
func CreateMockClient[T any](t *testing.T, createFunc func(*http.Client) T) *T {
t.Helper()

tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: validTestAPIKey})

client := &http.Client{
Transport: &oauth2.Transport{
Source: tokenSource,
},
}
httpmock.ActivateNonDefault(client)

t.Cleanup(func() {
httpmock.DeactivateAndReset()
})

result := createFunc(client)
return &result
}
4 changes: 2 additions & 2 deletions k8s/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/oauth2 v0.17.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
Expand Down
9 changes: 5 additions & 4 deletions k8s/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand Down Expand Up @@ -95,8 +96,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -147,8 +148,8 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
3 changes: 2 additions & 1 deletion pagination_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package linodego

import (
"github.com/google/go-cmp/cmp"
"reflect"
"testing"

"github.com/google/go-cmp/cmp"
)

func TestFlattenQueryStruct(t *testing.T) {
Expand Down
Loading

0 comments on commit f8ed8e9

Please sign in to comment.