Add Pagination

This commit is contained in:
178inaba 2017-05-04 21:56:54 +09:00
parent 759ce4a331
commit d32c4d4ed1
12 changed files with 217 additions and 254 deletions

View File

@ -30,7 +30,7 @@ type Account struct {
// GetAccount return Account.
func (c *Client) GetAccount(ctx context.Context, id int) (*Account, error) {
var account Account
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d", id), nil, &account, nil)
_, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d", id), nil, &account, nil)
if err != nil {
return nil, err
}
@ -40,7 +40,7 @@ func (c *Client) GetAccount(ctx context.Context, id int) (*Account, error) {
// GetAccountCurrentUser return Account of current user.
func (c *Client) GetAccountCurrentUser(ctx context.Context) (*Account, error) {
var account Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/verify_credentials", nil, &account, nil)
_, err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/verify_credentials", nil, &account, nil)
if err != nil {
return nil, err
}
@ -76,7 +76,7 @@ func (c *Client) AccountUpdate(ctx context.Context, profile *Profile) (*Account,
}
var account Account
err := c.doAPI(ctx, http.MethodPatch, "/api/v1/accounts/update_credentials", params, &account, nil)
_, err := c.doAPI(ctx, http.MethodPatch, "/api/v1/accounts/update_credentials", params, &account, nil)
if err != nil {
return nil, err
}
@ -84,53 +84,43 @@ func (c *Client) AccountUpdate(ctx context.Context, profile *Profile) (*Account,
}
// GetAccountStatuses return statuses by specified accuont.
func (c *Client) GetAccountStatuses(ctx context.Context, id int64) ([]*Status, error) {
func (c *Client) GetAccountStatuses(ctx context.Context, id int64, pg *Pagination) ([]*Status, *Pagination, error) {
var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/statuses", id), nil, &statuses, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/statuses", id), nil, &statuses, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return statuses, nil
return statuses, retPG, nil
}
// GetAccountFollowers return followers list.
func (c *Client) GetAccountFollowers(ctx context.Context, id int64) ([]*Account, error) {
params := url.Values{}
var total []*Account
for {
var accounts []*Account
var next bool
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/followers", id), params, &accounts, &next)
if err != nil {
return nil, err
}
total = append(total, accounts...)
if !next {
break
}
time.Sleep(c.interval)
func (c *Client) GetAccountFollowers(ctx context.Context, id int64, pg *Pagination) ([]*Account, *Pagination, error) {
var accounts []*Account
retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/followers", id), nil, &accounts, pg)
if err != nil {
return nil, nil, err
}
return total, nil
return accounts, retPG, nil
}
// GetAccountFollowing return following list.
func (c *Client) GetAccountFollowing(ctx context.Context, id int64) ([]*Account, error) {
func (c *Client) GetAccountFollowing(ctx context.Context, id int64, pg *Pagination) ([]*Account, *Pagination, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return accounts, nil
return accounts, retPG, nil
}
// GetBlocks return block list.
func (c *Client) GetBlocks(ctx context.Context) ([]*Account, error) {
func (c *Client) GetBlocks(ctx context.Context, pg *Pagination) ([]*Account, *Pagination, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return accounts, nil
return accounts, retPG, nil
}
// Relationship hold information for relation-ship to the account.
@ -146,7 +136,7 @@ type Relationship struct {
// AccountFollow follow the account.
func (c *Client) AccountFollow(ctx context.Context, id int64) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/follow", id), nil, &relationship, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/follow", id), nil, &relationship, nil)
if err != nil {
return nil, err
}
@ -156,7 +146,7 @@ func (c *Client) AccountFollow(ctx context.Context, id int64) (*Relationship, er
// AccountUnfollow unfollow the account.
func (c *Client) AccountUnfollow(ctx context.Context, id int64) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unfollow", id), nil, &relationship, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unfollow", id), nil, &relationship, nil)
if err != nil {
return nil, err
}
@ -166,7 +156,7 @@ func (c *Client) AccountUnfollow(ctx context.Context, id int64) (*Relationship,
// AccountBlock block the account.
func (c *Client) AccountBlock(ctx context.Context, id int64) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/block", id), nil, &relationship, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/block", id), nil, &relationship, nil)
if err != nil {
return nil, err
}
@ -176,7 +166,7 @@ func (c *Client) AccountBlock(ctx context.Context, id int64) (*Relationship, err
// AccountUnblock unblock the account.
func (c *Client) AccountUnblock(ctx context.Context, id int64) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unblock", id), nil, &relationship, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unblock", id), nil, &relationship, nil)
if err != nil {
return nil, err
}
@ -186,7 +176,7 @@ func (c *Client) AccountUnblock(ctx context.Context, id int64) (*Relationship, e
// AccountMute mute the account.
func (c *Client) AccountMute(ctx context.Context, id int64) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/mute", id), nil, &relationship, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/mute", id), nil, &relationship, nil)
if err != nil {
return nil, err
}
@ -196,7 +186,7 @@ func (c *Client) AccountMute(ctx context.Context, id int64) (*Relationship, erro
// AccountUnmute unmute the account.
func (c *Client) AccountUnmute(ctx context.Context, id int64) (*Relationship, error) {
var relationship Relationship
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unmute", id), nil, &relationship, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unmute", id), nil, &relationship, nil)
if err != nil {
return nil, err
}
@ -211,7 +201,7 @@ func (c *Client) GetAccountRelationships(ctx context.Context, ids []int64) ([]*R
}
var relationships []*Relationship
err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/relationships", params, &relationships, nil)
_, err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/relationships", params, &relationships, nil)
if err != nil {
return nil, err
}
@ -225,7 +215,7 @@ func (c *Client) AccountsSearch(ctx context.Context, q string, limit int64) ([]*
params.Set("limit", fmt.Sprint(limit))
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/search", params, &accounts, nil)
_, err := c.doAPI(ctx, http.MethodGet, "/api/v1/accounts/search", params, &accounts, nil)
if err != nil {
return nil, err
}
@ -238,7 +228,7 @@ func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, er
params.Set("uri", uri)
var account Account
err := c.doAPI(ctx, http.MethodPost, "/api/v1/follows", params, &account, nil)
_, err := c.doAPI(ctx, http.MethodPost, "/api/v1/follows", params, &account, nil)
if err != nil {
return nil, err
}
@ -246,31 +236,33 @@ func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, er
}
// GetFollowRequests return follow-requests.
func (c *Client) GetFollowRequests(ctx context.Context) ([]*Account, error) {
func (c *Client) GetFollowRequests(ctx context.Context, pg *Pagination) ([]*Account, *Pagination, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return accounts, nil
return accounts, retPG, nil
}
// FollowRequestAuthorize is authorize the follow request of user with id.
func (c *Client) FollowRequestAuthorize(ctx context.Context, id int64) error {
return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/authorize", id), nil, nil, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/authorize", id), nil, nil, nil)
return err
}
// FollowRequestReject is rejects the follow request of user with id.
func (c *Client) FollowRequestReject(ctx context.Context, id int64) error {
return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/reject", id), nil, nil, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/reject", id), nil, nil, nil)
return err
}
// GetMutes returns the list of users muted by the current user.
func (c *Client) GetMutes(ctx context.Context) ([]*Account, error) {
func (c *Client) GetMutes(ctx context.Context, pg *Pagination) ([]*Account, *Pagination, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return accounts, nil
return accounts, retPG, nil
}

View File

@ -25,11 +25,11 @@ func TestGetAccount(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
a, err := client.GetAccount(context.Background(), 1)
_, err := client.GetAccount(context.Background(), 1)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
a, err = client.GetAccount(context.Background(), 1234567)
a, err := client.GetAccount(context.Background(), 1234567)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -113,6 +113,7 @@ func TestGetAccountStatuses(t *testing.T) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
//w.Header().Set("Link", `<http://example.com/api/v1/accounts/1234567/statuses?max_id=123>; rel="next", <http://example.com/api/v1/accounts/1234567/statuses?since_id=789>; rel="prev"`)
fmt.Fprintln(w, `[{"content": "foo"}, {"content": "bar"}]`)
return
}))
@ -124,11 +125,11 @@ func TestGetAccountStatuses(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetAccountStatuses(context.Background(), 123)
_, _, err := client.GetAccountStatuses(context.Background(), 123, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
ss, err := client.GetAccountStatuses(context.Background(), 1234567)
ss, _, err := client.GetAccountStatuses(context.Background(), 1234567, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -157,11 +158,11 @@ func TestGetAccountFollowers(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetAccountFollowers(context.Background(), 123)
_, _, err := client.GetAccountFollowers(context.Background(), 123, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
fl, err := client.GetAccountFollowers(context.Background(), 1234567)
fl, _, err := client.GetAccountFollowers(context.Background(), 1234567, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -193,11 +194,11 @@ func TestGetAccountFollowing(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetAccountFollowing(context.Background(), 123)
_, _, err := client.GetAccountFollowing(context.Background(), 123, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
fl, err := client.GetAccountFollowing(context.Background(), 1234567)
fl, _, err := client.GetAccountFollowing(context.Background(), 1234567, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -231,11 +232,11 @@ func TestGetBlocks(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetBlocks(context.Background())
_, _, err := client.GetBlocks(context.Background(), nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
bl, err := client.GetBlocks(context.Background())
bl, _, err := client.GetBlocks(context.Background(), nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -566,11 +567,11 @@ func TestGetFollowRequests(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetFollowRequests(context.Background())
_, _, err := client.GetFollowRequests(context.Background(), nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
fReqs, err := client.GetFollowRequests(context.Background())
fReqs, _, err := client.GetFollowRequests(context.Background(), nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -652,11 +653,11 @@ func TestGetMutes(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetMutes(context.Background())
_, _, err := client.GetMutes(context.Background(), nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
mutes, err := client.GetMutes(context.Background())
mutes, _, err := client.GetMutes(context.Background(), nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}

View File

@ -32,7 +32,7 @@ func ExampleClient() {
if err != nil {
log.Fatal(err)
}
timeline, err := c.GetTimelineHome(context.Background())
timeline, _, err := c.GetTimelineHome(context.Background(), nil)
if err != nil {
log.Fatal(err)
}

View File

@ -37,6 +37,9 @@ func Base64Encode(file *os.File) (string, error) {
";base64," + base64.StdEncoding.EncodeToString(d), nil
}
// Int64 is a helper function to get the pointer value of a int64.
func Int64(v int64) *int64 { return &v }
// String is a helper function to get the pointer value of a string.
func String(v string) *string { return &v }

View File

@ -16,7 +16,7 @@ type Instance struct {
// GetInstance return Instance.
func (c *Client) GetInstance(ctx context.Context) (*Instance, error) {
var instance Instance
err := c.doAPI(ctx, http.MethodGet, "/api/v1/instance", nil, &instance, nil)
_, err := c.doAPI(ctx, http.MethodGet, "/api/v1/instance", nil, &instance, nil)
if err != nil {
return nil, err
}

View File

@ -4,6 +4,8 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
@ -11,8 +13,10 @@ import (
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/tomnomnom/linkheader"
)
// Config is a setting for access mastodon APIs.
@ -26,64 +30,13 @@ type Config struct {
// Client is a API client for mastodon.
type Client struct {
http.Client
config *Config
interval time.Duration
config *Config
}
type page struct {
next string
}
func linkHeader(h http.Header, rel string) []string {
var links []string
for _, v := range h["Link"] {
var p string
for len(v) > 0 {
i := strings.Index(v, ";")
if i < 0 {
break
}
e := i
i++
for i < len(v) {
if v[i] != ' ' {
break
}
i++
}
p = strings.TrimSpace(v[i:])
if !strings.HasPrefix(p, "rel=") {
break
}
i += 4
pos := strings.Index(p[4:], `,`)
if pos > 0 {
p = p[4 : 4+pos]
i += pos
} else {
p = p[4:]
i = len(v) - 1
}
if k := strings.Trim(p, `"`); k == rel {
links = append(links, strings.Trim(v[:e], "<>"))
}
i++
for i < len(v) {
if v[i] != ' ' {
break
}
i++
}
v = v[i:]
}
}
return links
}
func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}, next *bool) error {
func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}, pg *Pagination) (*Pagination, error) {
u, err := url.Parse(c.config.Server)
if err != nil {
return err
return nil, err
}
u.Path = path.Join(u.Path, uri)
@ -92,18 +45,18 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in
if values, ok := params.(url.Values); ok {
var body io.Reader
if method == http.MethodGet {
u.RawQuery = values.Encode()
u.RawQuery = pg.setValues(values).Encode()
} else {
body = strings.NewReader(values.Encode())
}
req, err = http.NewRequest(method, u.String(), body)
if err != nil {
return err
return nil, err
}
} else if file, ok := params.(string); ok {
f, err := os.Open(file)
if err != nil {
return err
return nil, err
}
defer f.Close()
@ -111,25 +64,25 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in
mw := multipart.NewWriter(&buf)
part, err := mw.CreateFormFile("file", filepath.Base(file))
if err != nil {
return err
return nil, err
}
_, err = io.Copy(part, f)
if err != nil {
return err
return nil, err
}
err = mw.Close()
if err != nil {
return err
return nil, err
}
req, err = http.NewRequest(method, u.String(), &buf)
if err != nil {
return err
return nil, err
}
ct = mw.FormDataContentType()
} else {
req, err = http.NewRequest(method, u.String(), nil)
if err != nil {
return err
return nil, err
}
}
req = req.WithContext(ctx)
@ -140,37 +93,32 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in
resp, err := c.Do(req)
if err != nil {
return err
return nil, err
}
defer resp.Body.Close()
if next != nil && params != nil {
nl := linkHeader(resp.Header, "next")
*next = false
if len(nl) > 0 {
u, err = url.Parse(nl[0])
if err == nil {
for k, v := range u.Query() {
params.(url.Values)[k] = v
}
}
*next = true
lh := resp.Header.Get("Link")
var retPG *Pagination
if lh != "" {
retPG, err = newPagination(lh)
if err != nil {
return nil, err
}
}
if resp.StatusCode != http.StatusOK {
return parseAPIError("bad request", resp)
return nil, parseAPIError("bad request", resp)
} else if res == nil {
return nil
return nil, nil
}
return json.NewDecoder(resp.Body).Decode(&res)
return retPG, json.NewDecoder(resp.Body).Decode(&res)
}
// NewClient return new mastodon API client.
func NewClient(config *Config) *Client {
return &Client{
Client: *http.DefaultClient,
config: config,
interval: 10 * time.Second,
Client: *http.DefaultClient,
config: config,
}
}
@ -257,3 +205,66 @@ type Results struct {
Statuses []*Status `json:"statuses"`
Hashtags []string `json:"hashtags"`
}
// Pagination is a struct for specifying the get range.
type Pagination struct {
MaxID *int64
SinceID *int64
Limit *int64
}
func newPagination(rawlink string) (*Pagination, error) {
if rawlink == "" {
return nil, errors.New("empty link header")
}
p := &Pagination{}
for _, link := range linkheader.Parse(rawlink) {
switch link.Rel {
case "next":
maxID, err := getPaginationID(link.URL, "max_id")
if err != nil {
return nil, err
}
p.MaxID = &maxID
case "prev":
sinceID, err := getPaginationID(link.URL, "since_id")
if err != nil {
return nil, err
}
p.SinceID = &sinceID
}
}
return p, nil
}
func getPaginationID(rawurl, key string) (int64, error) {
u, err := url.Parse(rawurl)
if err != nil {
return 0, err
}
id, err := strconv.ParseInt(u.Query().Get(key), 10, 64)
if err != nil {
return 0, err
}
return id, nil
}
func (p *Pagination) setValues(params url.Values) url.Values {
if p != nil {
if p.MaxID != nil {
params.Set("max_id", fmt.Sprint(p.MaxID))
}
if p.SinceID != nil {
params.Set("since_id", fmt.Sprint(p.SinceID))
}
if p.Limit != nil {
params.Set("limit", fmt.Sprint(p.Limit))
}
}
return params
}

View File

@ -6,7 +6,6 @@ import (
"io"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
)
@ -153,7 +152,7 @@ func TestGetTimelineHome(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
tl, err := client.GetTimelineHome(context.Background())
tl, _, err := client.GetTimelineHome(context.Background(), nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -183,7 +182,7 @@ func TestGetTimelineHomeWithCancel(t *testing.T) {
})
ctx, cancel := context.WithCancel(context.Background())
cancel()
_, err := client.GetTimelineHome(ctx)
_, _, err := client.GetTimelineHome(ctx, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
@ -199,48 +198,3 @@ func TestForTheCoverages(t *testing.T) {
(*ErrorEvent)(nil).event()
_ = (&ErrorEvent{io.EOF}).Error()
}
func TestLinkHeader(t *testing.T) {
tests := []struct {
header []string
rel string
want []string
}{
{
header: []string{`<http://example.com/?max_id=3>; rel="foo"`},
rel: "boo",
want: nil,
},
{
header: []string{`<http://example.com/?max_id=3>; rel="foo"`},
rel: "foo",
want: []string{"http://example.com/?max_id=3"},
},
{
header: []string{`<http://example.com/?max_id=3>; rel="foo1"`},
rel: "foo",
want: nil,
},
{
header: []string{`<http://example.com/?max_id=3>; rel="foo", <http://example.com/?max_id=4>; rel="bar"`},
rel: "foo",
want: []string{"http://example.com/?max_id=3"},
},
{
header: []string{`<http://example.com/?max_id=3>; rel="foo", <http://example.com/?max_id=4>; rel="bar"`},
rel: "bar",
want: []string{"http://example.com/?max_id=4"},
},
}
for _, test := range tests {
h := make(http.Header)
for _, he := range test.header {
h.Add("Link", he)
}
got := linkHeader(h, test.rel)
if !reflect.DeepEqual(got, test.want) {
t.Fatalf("want %v but %v", test.want, got)
}
}
}

View File

@ -17,19 +17,19 @@ type Notification struct {
}
// GetNotifications return notifications.
func (c *Client) GetNotifications(ctx context.Context) ([]*Notification, error) {
func (c *Client) GetNotifications(ctx context.Context, pg *Pagination) ([]*Notification, *Pagination, error) {
var notifications []*Notification
err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, &notifications, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, &notifications, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return notifications, nil
return notifications, retPG, nil
}
// GetNotification return notification.
func (c *Client) GetNotification(ctx context.Context, id int64) (*Notification, error) {
var notification Notification
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/notifications/%d", id), nil, &notification, nil)
_, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/notifications/%d", id), nil, &notification, nil)
if err != nil {
return nil, err
}
@ -38,5 +38,6 @@ func (c *Client) GetNotification(ctx context.Context, id int64) (*Notification,
// ClearNotifications clear notifications.
func (c *Client) ClearNotifications(ctx context.Context) error {
return c.doAPI(ctx, http.MethodPost, "/api/v1/notifications/clear", nil, nil, nil)
_, err := c.doAPI(ctx, http.MethodPost, "/api/v1/notifications/clear", nil, nil, nil)
return err
}

View File

@ -32,7 +32,7 @@ func TestGetNotifications(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
ns, err := client.GetNotifications(context.Background())
ns, _, err := client.GetNotifications(context.Background(), nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}

View File

@ -16,7 +16,7 @@ type Report struct {
// GetReports return report of the current user.
func (c *Client) GetReports(ctx context.Context) ([]*Report, error) {
var reports []*Report
err := c.doAPI(ctx, http.MethodGet, "/api/v1/reports", nil, &reports, nil)
_, err := c.doAPI(ctx, http.MethodGet, "/api/v1/reports", nil, &reports, nil)
if err != nil {
return nil, err
}
@ -32,7 +32,7 @@ func (c *Client) Report(ctx context.Context, accountID int64, ids []int64, comme
}
params.Set("comment", comment)
var report Report
err := c.doAPI(ctx, http.MethodPost, "/api/v1/reports", params, &report, nil)
_, err := c.doAPI(ctx, http.MethodPost, "/api/v1/reports", params, &report, nil)
if err != nil {
return nil, err
}

View File

@ -48,19 +48,19 @@ type Card struct {
}
// GetFavourites return the favorite list of the current user.
func (c *Client) GetFavourites(ctx context.Context) ([]*Status, error) {
func (c *Client) GetFavourites(ctx context.Context, pg *Pagination) ([]*Status, *Pagination, error) {
var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return statuses, nil
return statuses, retPG, nil
}
// GetStatus return status specified by id.
func (c *Client) GetStatus(ctx context.Context, id int64) (*Status, error) {
var status Status
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d", id), nil, &status, nil)
_, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d", id), nil, &status, nil)
if err != nil {
return nil, err
}
@ -70,7 +70,7 @@ func (c *Client) GetStatus(ctx context.Context, id int64) (*Status, error) {
// GetStatusContext return status specified by id.
func (c *Client) GetStatusContext(ctx context.Context, id int64) (*Context, error) {
var context Context
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/context", id), nil, &context, nil)
_, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/context", id), nil, &context, nil)
if err != nil {
return nil, err
}
@ -80,7 +80,7 @@ func (c *Client) GetStatusContext(ctx context.Context, id int64) (*Context, erro
// GetStatusCard return status specified by id.
func (c *Client) GetStatusCard(ctx context.Context, id int64) (*Card, error) {
var card Card
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/card", id), nil, &card, nil)
_, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/card", id), nil, &card, nil)
if err != nil {
return nil, err
}
@ -88,29 +88,29 @@ func (c *Client) GetStatusCard(ctx context.Context, id int64) (*Card, error) {
}
// GetRebloggedBy returns the account list of the user who reblogged the toot of id.
func (c *Client) GetRebloggedBy(ctx context.Context, id int64) ([]*Account, error) {
func (c *Client) GetRebloggedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, *Pagination, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return accounts, nil
return accounts, retPG, nil
}
// GetFavouritedBy returns the account list of the user who liked the toot of id.
func (c *Client) GetFavouritedBy(ctx context.Context, id int64) ([]*Account, error) {
func (c *Client) GetFavouritedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, *Pagination, error) {
var accounts []*Account
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return accounts, nil
return accounts, retPG, nil
}
// Reblog is reblog the toot of id and return status of reblog.
func (c *Client) Reblog(ctx context.Context, id int64) (*Status, error) {
var status Status
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/reblog", id), nil, &status, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/reblog", id), nil, &status, nil)
if err != nil {
return nil, err
}
@ -120,7 +120,7 @@ func (c *Client) Reblog(ctx context.Context, id int64) (*Status, error) {
// Unreblog is unreblog the toot of id and return status of the original toot.
func (c *Client) Unreblog(ctx context.Context, id int64) (*Status, error) {
var status Status
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unreblog", id), nil, &status, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unreblog", id), nil, &status, nil)
if err != nil {
return nil, err
}
@ -130,7 +130,7 @@ func (c *Client) Unreblog(ctx context.Context, id int64) (*Status, error) {
// Favourite is favourite the toot of id and return status of the favourite toot.
func (c *Client) Favourite(ctx context.Context, id int64) (*Status, error) {
var status Status
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/favourite", id), nil, &status, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/favourite", id), nil, &status, nil)
if err != nil {
return nil, err
}
@ -140,7 +140,7 @@ func (c *Client) Favourite(ctx context.Context, id int64) (*Status, error) {
// Unfavourite is unfavourite the toot of id and return status of the unfavourite toot.
func (c *Client) Unfavourite(ctx context.Context, id int64) (*Status, error) {
var status Status
err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unfavourite", id), nil, &status, nil)
_, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%d/unfavourite", id), nil, &status, nil)
if err != nil {
return nil, err
}
@ -148,48 +148,48 @@ func (c *Client) Unfavourite(ctx context.Context, id int64) (*Status, error) {
}
// GetTimelineHome return statuses from home timeline.
func (c *Client) GetTimelineHome(ctx context.Context) ([]*Status, error) {
func (c *Client) GetTimelineHome(ctx context.Context, pg *Pagination) ([]*Status, *Pagination, error) {
var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return statuses, nil
return statuses, retPG, nil
}
// GetTimelinePublic return statuses from public timeline.
func (c *Client) GetTimelinePublic(ctx context.Context, isLocal bool) ([]*Status, error) {
func (c *Client) GetTimelinePublic(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, *Pagination, error) {
params := url.Values{}
if isLocal {
params.Set("local", "t")
}
var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return statuses, nil
return statuses, retPG, nil
}
// GetTimelineHashtag return statuses from tagged timeline.
func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal bool) ([]*Status, error) {
func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal bool, pg *Pagination) ([]*Status, *Pagination, error) {
params := url.Values{}
if isLocal {
params.Set("local", "t")
}
var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", url.PathEscape(tag)), params, &statuses, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", url.PathEscape(tag)), params, &statuses, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return statuses, nil
return statuses, retPG, nil
}
// GetTimelineMedia return statuses from media timeline.
// NOTE: This is an experimental feature of pawoo.net.
func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool) ([]*Status, error) {
func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, *Pagination, error) {
params := url.Values{}
params.Set("media", "t")
if isLocal {
@ -197,11 +197,11 @@ func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool) ([]*Status,
}
var statuses []*Status
err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, nil)
retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg)
if err != nil {
return nil, err
return nil, nil, err
}
return statuses, nil
return statuses, retPG, nil
}
// PostStatus post the toot.
@ -227,7 +227,7 @@ func (c *Client) PostStatus(ctx context.Context, toot *Toot) (*Status, error) {
}
var status Status
err := c.doAPI(ctx, http.MethodPost, "/api/v1/statuses", params, &status, nil)
_, err := c.doAPI(ctx, http.MethodPost, "/api/v1/statuses", params, &status, nil)
if err != nil {
return nil, err
}
@ -236,7 +236,8 @@ func (c *Client) PostStatus(ctx context.Context, toot *Toot) (*Status, error) {
// DeleteStatus delete the toot.
func (c *Client) DeleteStatus(ctx context.Context, id int64) error {
return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%d", id), nil, nil, nil)
_, err := c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%d", id), nil, nil, nil)
return err
}
// Search search content with query.
@ -245,7 +246,7 @@ func (c *Client) Search(ctx context.Context, q string, resolve bool) (*Results,
params.Set("q", q)
params.Set("resolve", fmt.Sprint(resolve))
var results Results
err := c.doAPI(ctx, http.MethodGet, "/api/v1/search", params, &results, nil)
_, err := c.doAPI(ctx, http.MethodGet, "/api/v1/search", params, &results, nil)
if err != nil {
return nil, err
}
@ -255,7 +256,7 @@ func (c *Client) Search(ctx context.Context, q string, resolve bool) (*Results,
// UploadMedia upload a media attachment.
func (c *Client) UploadMedia(ctx context.Context, file string) (*Attachment, error) {
var attachment Attachment
err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", file, &attachment, nil)
_, err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", file, &attachment, nil)
if err != nil {
return nil, err
}

View File

@ -21,7 +21,7 @@ func TestGetFavourites(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
favs, err := client.GetFavourites(context.Background())
favs, _, err := client.GetFavourites(context.Background(), nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -152,11 +152,11 @@ func TestGetRebloggedBy(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetRebloggedBy(context.Background(), 123)
_, _, err := client.GetRebloggedBy(context.Background(), 123, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
rbs, err := client.GetRebloggedBy(context.Background(), 1234567)
rbs, _, err := client.GetRebloggedBy(context.Background(), 1234567, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -188,11 +188,11 @@ func TestGetFavouritedBy(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetFavouritedBy(context.Background(), 123)
_, _, err := client.GetFavouritedBy(context.Background(), 123, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
fbs, err := client.GetFavouritedBy(context.Background(), 1234567)
fbs, _, err := client.GetFavouritedBy(context.Background(), 1234567, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -338,11 +338,11 @@ func TestGetTimelinePublic(t *testing.T) {
defer ts.Close()
client := NewClient(&Config{Server: ts.URL})
_, err := client.GetTimelinePublic(context.Background(), false)
_, _, err := client.GetTimelinePublic(context.Background(), false, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
tl, err := client.GetTimelinePublic(context.Background(), true)
tl, _, err := client.GetTimelinePublic(context.Background(), true, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -374,11 +374,11 @@ func TestGetTimelineHashtag(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetTimelineHashtag(context.Background(), "notfound", false)
_, _, err := client.GetTimelineHashtag(context.Background(), "notfound", false, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
tags, err := client.GetTimelineHashtag(context.Background(), "zzz", true)
tags, _, err := client.GetTimelineHashtag(context.Background(), "zzz", true, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}
@ -410,11 +410,11 @@ func TestGetTimelineMedia(t *testing.T) {
ClientSecret: "bar",
AccessToken: "zoo",
})
_, err := client.GetTimelineMedia(context.Background(), false)
_, _, err := client.GetTimelineMedia(context.Background(), false, nil)
if err == nil {
t.Fatalf("should be fail: %v", err)
}
tags, err := client.GetTimelineMedia(context.Background(), true)
tags, _, err := client.GetTimelineMedia(context.Background(), true, nil)
if err != nil {
t.Fatalf("should not be fail: %v", err)
}