From d32c4d4ed1c57de93f2a75827f6dfd013bef3c6d Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Thu, 4 May 2017 21:56:54 +0900 Subject: [PATCH 01/10] Add Pagination --- accounts.go | 92 +++++++++++------------ accounts_test.go | 29 ++++---- example_test.go | 2 +- helper.go | 3 + instance.go | 2 +- mastodon.go | 173 +++++++++++++++++++++++-------------------- mastodon_test.go | 50 +------------ notification.go | 13 ++-- notification_test.go | 2 +- report.go | 4 +- status.go | 79 ++++++++++---------- status_test.go | 22 +++--- 12 files changed, 217 insertions(+), 254 deletions(-) diff --git a/accounts.go b/accounts.go index a52cf9a..01390b8 100644 --- a/accounts.go +++ b/accounts.go @@ -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 } diff --git a/accounts_test.go b/accounts_test.go index cc05d1b..f4dc8d6 100644 --- a/accounts_test.go +++ b/accounts_test.go @@ -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", `; rel="next", ; 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) } diff --git a/example_test.go b/example_test.go index 307b6d5..1d85b25 100644 --- a/example_test.go +++ b/example_test.go @@ -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) } diff --git a/helper.go b/helper.go index 05af20f..281f5a2 100644 --- a/helper.go +++ b/helper.go @@ -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 } diff --git a/instance.go b/instance.go index 9b45c25..43c455c 100644 --- a/instance.go +++ b/instance.go @@ -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 } diff --git a/mastodon.go b/mastodon.go index 16ccb08..b352e32 100644 --- a/mastodon.go +++ b/mastodon.go @@ -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 +} diff --git a/mastodon_test.go b/mastodon_test.go index e0bf960..0eb376a 100644 --- a/mastodon_test.go +++ b/mastodon_test.go @@ -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{`; rel="foo"`}, - rel: "boo", - want: nil, - }, - { - header: []string{`; rel="foo"`}, - rel: "foo", - want: []string{"http://example.com/?max_id=3"}, - }, - { - header: []string{`; rel="foo1"`}, - rel: "foo", - want: nil, - }, - { - header: []string{`; rel="foo", ; rel="bar"`}, - rel: "foo", - want: []string{"http://example.com/?max_id=3"}, - }, - { - header: []string{`; rel="foo", ; 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) - } - } -} diff --git a/notification.go b/notification.go index a25f3f0..5df3485 100644 --- a/notification.go +++ b/notification.go @@ -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, ¬ifications, nil) + retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, ¬ifications, 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, ¬ification, nil) + _, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/notifications/%d", id), nil, ¬ification, 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 } diff --git a/notification_test.go b/notification_test.go index 87f0e32..2459b1e 100644 --- a/notification_test.go +++ b/notification_test.go @@ -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) } diff --git a/report.go b/report.go index 1e6debf..dd9c461 100644 --- a/report.go +++ b/report.go @@ -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 } diff --git a/status.go b/status.go index dbbed03..74cec09 100644 --- a/status.go +++ b/status.go @@ -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 } diff --git a/status_test.go b/status_test.go index bc4eed6..3e7d7b3 100644 --- a/status_test.go +++ b/status_test.go @@ -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) } From eacd4e066d729afc14a6fd13df5a9d64f9efc29b Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Thu, 4 May 2017 23:35:00 +0900 Subject: [PATCH 02/10] Fix doAPI pagination --- mastodon.go | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/mastodon.go b/mastodon.go index b352e32..f5466a5 100644 --- a/mastodon.go +++ b/mastodon.go @@ -45,7 +45,10 @@ 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 = pg.setValues(values).Encode() + if pg != nil { + values = pg.setValues(values) + } + u.RawQuery = values.Encode() } else { body = strings.NewReader(values.Encode()) } @@ -80,6 +83,9 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in } ct = mw.FormDataContentType() } else { + if method == http.MethodGet && pg != nil { + u.RawQuery = pg.toValues().Encode() + } req, err = http.NewRequest(method, u.String(), nil) if err != nil { return nil, err @@ -253,17 +259,19 @@ func getPaginationID(rawurl, key string) (int64, error) { return id, nil } +func (p *Pagination) toValues() url.Values { + return p.setValues(url.Values{}) +} + 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)) - } + 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 From 5fad354d1a0f0dd9eb70a99140a2e1a4b9c316cd Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Thu, 4 May 2017 23:35:05 +0900 Subject: [PATCH 03/10] Add test --- accounts_test.go | 1 - helper_test.go | 8 +++ mastodon_test.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) diff --git a/accounts_test.go b/accounts_test.go index f4dc8d6..338d342 100644 --- a/accounts_test.go +++ b/accounts_test.go @@ -113,7 +113,6 @@ func TestGetAccountStatuses(t *testing.T) { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } - //w.Header().Set("Link", `; rel="next", ; rel="prev"`) fmt.Fprintln(w, `[{"content": "foo"}, {"content": "bar"}]`) return })) diff --git a/helper_test.go b/helper_test.go index 7da80b9..b2599e6 100644 --- a/helper_test.go +++ b/helper_test.go @@ -62,6 +62,14 @@ func TestBase64Encode(t *testing.T) { } } +func TestInt64(t *testing.T) { + i := int64(1234567) + ip := Int64(i) + if *ip != i { + t.Fatalf("want %d but %d", i, *ip) + } +} + func TestString(t *testing.T) { s := "test" sp := String(s) diff --git a/mastodon_test.go b/mastodon_test.go index 0eb376a..c59011f 100644 --- a/mastodon_test.go +++ b/mastodon_test.go @@ -6,10 +6,74 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "testing" "time" ) +func TestDoAPI(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + if q.Get("max_id") == "123" && q.Get("since_id") == "789" && q.Get("limit") == "10" { + w.Header().Set("Link", `; rel="next", ; rel="prev"`) + fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`) + } + w.Header().Set("Link", `<:>; rel="next"`) + })) + defer ts.Close() + + c := NewClient(&Config{Server: ts.URL}) + _, err := c.doAPI(context.Background(), http.MethodGet, "/", nil, nil, &Pagination{ + MaxID: Int64(999), + }) + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + var accounts []Account + pg, err := c.doAPI(context.Background(), http.MethodGet, "/", url.Values{}, &accounts, &Pagination{ + MaxID: Int64(123), + SinceID: Int64(789), + Limit: Int64(10), + }) + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if *pg.MaxID != 234 { + t.Fatalf("want %d but %d", 234, *pg.MaxID) + } + if *pg.SinceID != 890 { + t.Fatalf("want %d but %d", 890, *pg.SinceID) + } + if accounts[0].Username != "foo" { + t.Fatalf("want %q but %q", "foo", accounts[0].Username) + } + if accounts[1].Username != "bar" { + t.Fatalf("want %q but %q", "bar", accounts[1].Username) + } + + pg, err = c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, &Pagination{ + MaxID: Int64(123), + SinceID: Int64(789), + Limit: Int64(10), + }) + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if *pg.MaxID != 234 { + t.Fatalf("want %d but %d", 234, *pg.MaxID) + } + if *pg.SinceID != 890 { + t.Fatalf("want %d but %d", 890, *pg.SinceID) + } + if accounts[0].Username != "foo" { + t.Fatalf("want %q but %q", "foo", accounts[0].Username) + } + if accounts[1].Username != "bar" { + t.Fatalf("want %q but %q", "bar", accounts[1].Username) + } +} + func TestAuthenticate(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.FormValue("username") != "valid" || r.FormValue("password") != "user" { @@ -198,3 +262,73 @@ func TestForTheCoverages(t *testing.T) { (*ErrorEvent)(nil).event() _ = (&ErrorEvent{io.EOF}).Error() } + +func TestNewPagination(t *testing.T) { + _, err := newPagination("") + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + _, err = newPagination(`<:>; rel="next"`) + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + _, err = newPagination(`<:>; rel="prev"`) + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + pg, err := newPagination(`; rel="next", ; rel="prev"`) + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if *pg.MaxID != 123 { + t.Fatalf("want %d but %d", 123, *pg.MaxID) + } + if *pg.SinceID != 789 { + t.Fatalf("want %d but %d", 789, *pg.SinceID) + } +} + +func TestGetPaginationID(t *testing.T) { + _, err := getPaginationID(":", "max_id") + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + _, err = getPaginationID("http://example.com?max_id=abc", "max_id") + if err == nil { + t.Fatalf("should be fail: %v", err) + } + + id, err := getPaginationID("http://example.com?max_id=123", "max_id") + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if id != 123 { + t.Fatalf("want %d but %d", 123, id) + } +} + +func TestPaginationSetValues(t *testing.T) { + p := &Pagination{ + MaxID: Int64(123), + SinceID: Int64(789), + Limit: Int64(10), + } + before := url.Values{"key": {"value"}} + after := p.setValues(before) + if after.Get("key") != "value" { + t.Fatalf("want %q but %q", "value", after.Get("key")) + } + if after.Get("max_id") != "123" { + t.Fatalf("want %q but %q", "123", after.Get("max_id")) + } + if after.Get("since_id") != "789" { + t.Fatalf("want %q but %q", "789", after.Get("since_id")) + } + if after.Get("limit") != "10" { + t.Fatalf("want %q but %q", "10", after.Get("limit")) + } +} From dd0b467062ba3da72262e0dd537e1cef683139b8 Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Fri, 5 May 2017 00:18:17 +0900 Subject: [PATCH 04/10] Fix pagination for mstdn command --- cmd/mstdn/cmd_followers.go | 18 +++++++++++++++--- cmd/mstdn/cmd_followers_test.go | 1 + cmd/mstdn/cmd_notification.go | 2 +- cmd/mstdn/cmd_timeline.go | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/cmd/mstdn/cmd_followers.go b/cmd/mstdn/cmd_followers.go index 8824332..4c21e8d 100644 --- a/cmd/mstdn/cmd_followers.go +++ b/cmd/mstdn/cmd_followers.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "time" "github.com/mattn/go-mastodon" "github.com/urfave/cli" @@ -16,9 +17,20 @@ func cmdFollowers(c *cli.Context) error { if err != nil { return err } - followers, err := client.GetAccountFollowers(context.Background(), account.ID) - if err != nil { - return err + var maxID *int64 + var followers []*mastodon.Account + for { + fs, pg, err := client.GetAccountFollowers( + context.Background(), account.ID, &mastodon.Pagination{MaxID: maxID}) + if err != nil { + return err + } + followers = append(followers, fs...) + if pg.MaxID == nil { + break + } + maxID = pg.MaxID + time.Sleep(10 * time.Second) } s := newScreen(config) for _, follower := range followers { diff --git a/cmd/mstdn/cmd_followers_test.go b/cmd/mstdn/cmd_followers_test.go index 442a068..f8c5080 100644 --- a/cmd/mstdn/cmd_followers_test.go +++ b/cmd/mstdn/cmd_followers_test.go @@ -17,6 +17,7 @@ func TestCmdFollowers(t *testing.T) { fmt.Fprintln(w, `{"id": 123}`) return case "/api/v1/accounts/123/followers": + w.Header().Set("Link", `; rel="prev"`) fmt.Fprintln(w, `[{"id": 234, "username": "ZZZ", "acct": "zzz"}]`) return } diff --git a/cmd/mstdn/cmd_notification.go b/cmd/mstdn/cmd_notification.go index 6d4192c..d96b0ce 100644 --- a/cmd/mstdn/cmd_notification.go +++ b/cmd/mstdn/cmd_notification.go @@ -11,7 +11,7 @@ import ( func cmdNotification(c *cli.Context) error { client := c.App.Metadata["client"].(*mastodon.Client) - notifications, err := client.GetNotifications(context.Background()) + notifications, _, err := client.GetNotifications(context.Background(), nil) if err != nil { return err } diff --git a/cmd/mstdn/cmd_timeline.go b/cmd/mstdn/cmd_timeline.go index d634ebf..4024826 100644 --- a/cmd/mstdn/cmd_timeline.go +++ b/cmd/mstdn/cmd_timeline.go @@ -10,7 +10,7 @@ import ( func cmdTimeline(c *cli.Context) error { client := c.App.Metadata["client"].(*mastodon.Client) config := c.App.Metadata["config"].(*mastodon.Config) - timeline, err := client.GetTimelineHome(context.Background()) + timeline, _, err := client.GetTimelineHome(context.Background(), nil) if err != nil { return err } From 134128cb565e79c7118de034d6d44a75bd7073e2 Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Sat, 6 May 2017 23:03:19 +0900 Subject: [PATCH 05/10] Fix to not return *Pagination --- accounts.go | 78 +++++++++++++++++++++---------------------- accounts_test.go | 24 +++++++------- example_test.go | 2 +- instance.go | 2 +- mastodon.go | 32 +++++++++--------- mastodon_test.go | 16 +++++---- notification.go | 13 ++++---- notification_test.go | 2 +- report.go | 4 +-- status.go | 79 ++++++++++++++++++++++---------------------- status_test.go | 22 ++++++------ 11 files changed, 136 insertions(+), 138 deletions(-) diff --git a/accounts.go b/accounts.go index 01390b8..59b8ec8 100644 --- a/accounts.go +++ b/accounts.go @@ -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,43 +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, pg *Pagination) ([]*Status, *Pagination, error) { +func (c *Client) GetAccountStatuses(ctx context.Context, id int64, pg *Pagination) ([]*Status, error) { var statuses []*Status - retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/statuses", id), nil, &statuses, pg) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/statuses", id), nil, &statuses, pg) if err != nil { - return nil, nil, err + return nil, err } - return statuses, retPG, nil + return statuses, nil } // GetAccountFollowers return followers list. -func (c *Client) GetAccountFollowers(ctx context.Context, id int64, pg *Pagination) ([]*Account, *Pagination, error) { +func (c *Client) GetAccountFollowers(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) { var accounts []*Account - retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/followers", id), nil, &accounts, pg) + 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 nil, err } - return accounts, retPG, nil + return accounts, nil } // GetAccountFollowing return following list. -func (c *Client) GetAccountFollowing(ctx context.Context, id int64, pg *Pagination) ([]*Account, *Pagination, error) { +func (c *Client) GetAccountFollowing(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) { var accounts []*Account - retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts, pg) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts, pg) if err != nil { - return nil, nil, err + return nil, err } - return accounts, retPG, nil + return accounts, nil } // GetBlocks return block list. -func (c *Client) GetBlocks(ctx context.Context, pg *Pagination) ([]*Account, *Pagination, error) { +func (c *Client) GetBlocks(ctx context.Context, pg *Pagination) ([]*Account, error) { var accounts []*Account - retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts, pg) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/blocks", nil, &accounts, pg) if err != nil { - return nil, nil, err + return nil, err } - return accounts, retPG, nil + return accounts, nil } // Relationship hold information for relation-ship to the account. @@ -136,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 } @@ -146,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 } @@ -156,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 } @@ -166,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 } @@ -176,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 } @@ -186,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 } @@ -201,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 } @@ -215,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 } @@ -228,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 } @@ -236,33 +236,31 @@ func (c *Client) FollowRemoteUser(ctx context.Context, uri string) (*Account, er } // GetFollowRequests return follow-requests. -func (c *Client) GetFollowRequests(ctx context.Context, pg *Pagination) ([]*Account, *Pagination, error) { +func (c *Client) GetFollowRequests(ctx context.Context, pg *Pagination) ([]*Account, error) { var accounts []*Account - retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts, pg) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/follow_requests", nil, &accounts, pg) if err != nil { - return nil, nil, err + return nil, err } - return accounts, retPG, nil + return accounts, nil } // FollowRequestAuthorize is authorize the follow request of user with id. func (c *Client) FollowRequestAuthorize(ctx context.Context, id int64) error { - _, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/authorize", id), nil, nil, nil) - return err + return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/authorize", id), nil, nil, nil) } // FollowRequestReject is rejects the follow request of user with id. func (c *Client) FollowRequestReject(ctx context.Context, id int64) error { - _, err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/reject", id), nil, nil, nil) - return err + return c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/follow_requests/%d/reject", id), nil, nil, nil) } // GetMutes returns the list of users muted by the current user. -func (c *Client) GetMutes(ctx context.Context, pg *Pagination) ([]*Account, *Pagination, error) { +func (c *Client) GetMutes(ctx context.Context, pg *Pagination) ([]*Account, error) { var accounts []*Account - retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts, pg) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/mutes", nil, &accounts, pg) if err != nil { - return nil, nil, err + return nil, err } - return accounts, retPG, nil + return accounts, nil } diff --git a/accounts_test.go b/accounts_test.go index 338d342..7628425 100644 --- a/accounts_test.go +++ b/accounts_test.go @@ -124,11 +124,11 @@ func TestGetAccountStatuses(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, _, err := client.GetAccountStatuses(context.Background(), 123, nil) + _, err := client.GetAccountStatuses(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - ss, _, err := client.GetAccountStatuses(context.Background(), 1234567, nil) + ss, err := client.GetAccountStatuses(context.Background(), 1234567, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -157,11 +157,11 @@ func TestGetAccountFollowers(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, _, err := client.GetAccountFollowers(context.Background(), 123, nil) + _, err := client.GetAccountFollowers(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - fl, _, err := client.GetAccountFollowers(context.Background(), 1234567, nil) + fl, err := client.GetAccountFollowers(context.Background(), 1234567, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -193,11 +193,11 @@ func TestGetAccountFollowing(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, _, err := client.GetAccountFollowing(context.Background(), 123, nil) + _, err := client.GetAccountFollowing(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - fl, _, err := client.GetAccountFollowing(context.Background(), 1234567, nil) + fl, err := client.GetAccountFollowing(context.Background(), 1234567, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -231,11 +231,11 @@ func TestGetBlocks(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, _, err := client.GetBlocks(context.Background(), nil) + _, err := client.GetBlocks(context.Background(), nil) if err == nil { t.Fatalf("should be fail: %v", err) } - bl, _, err := client.GetBlocks(context.Background(), nil) + bl, err := client.GetBlocks(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -566,11 +566,11 @@ func TestGetFollowRequests(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, _, err := client.GetFollowRequests(context.Background(), nil) + _, err := client.GetFollowRequests(context.Background(), nil) if err == nil { t.Fatalf("should be fail: %v", err) } - fReqs, _, err := client.GetFollowRequests(context.Background(), nil) + fReqs, err := client.GetFollowRequests(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -652,11 +652,11 @@ func TestGetMutes(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - _, _, err := client.GetMutes(context.Background(), nil) + _, err := client.GetMutes(context.Background(), nil) if err == nil { t.Fatalf("should be fail: %v", err) } - mutes, _, err := client.GetMutes(context.Background(), nil) + mutes, err := client.GetMutes(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } diff --git a/example_test.go b/example_test.go index 1d85b25..9b7c879 100644 --- a/example_test.go +++ b/example_test.go @@ -32,7 +32,7 @@ func ExampleClient() { if err != nil { log.Fatal(err) } - timeline, _, err := c.GetTimelineHome(context.Background(), nil) + timeline, err := c.GetTimelineHome(context.Background(), nil) if err != nil { log.Fatal(err) } diff --git a/instance.go b/instance.go index 43c455c..9b45c25 100644 --- a/instance.go +++ b/instance.go @@ -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 } diff --git a/mastodon.go b/mastodon.go index f5466a5..17acacc 100644 --- a/mastodon.go +++ b/mastodon.go @@ -33,10 +33,10 @@ type Client struct { config *Config } -func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}, pg *Pagination) (*Pagination, error) { +func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}, pg *Pagination) error { u, err := url.Parse(c.config.Server) if err != nil { - return nil, err + return err } u.Path = path.Join(u.Path, uri) @@ -54,12 +54,12 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in } req, err = http.NewRequest(method, u.String(), body) if err != nil { - return nil, err + return err } } else if file, ok := params.(string); ok { f, err := os.Open(file) if err != nil { - return nil, err + return err } defer f.Close() @@ -67,19 +67,19 @@ 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 nil, err + return err } _, err = io.Copy(part, f) if err != nil { - return nil, err + return err } err = mw.Close() if err != nil { - return nil, err + return err } req, err = http.NewRequest(method, u.String(), &buf) if err != nil { - return nil, err + return err } ct = mw.FormDataContentType() } else { @@ -88,7 +88,7 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in } req, err = http.NewRequest(method, u.String(), nil) if err != nil { - return nil, err + return err } } req = req.WithContext(ctx) @@ -99,25 +99,25 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in resp, err := c.Do(req) if err != nil { - return nil, err + return err } defer resp.Body.Close() lh := resp.Header.Get("Link") - var retPG *Pagination if lh != "" { - retPG, err = newPagination(lh) + retPG, err := newPagination(lh) if err != nil { - return nil, err + return err } + *pg = *retPG } if resp.StatusCode != http.StatusOK { - return nil, parseAPIError("bad request", resp) + return parseAPIError("bad request", resp) } else if res == nil { - return nil, nil + return nil } - return retPG, json.NewDecoder(resp.Body).Decode(&res) + return json.NewDecoder(resp.Body).Decode(&res) } // NewClient return new mastodon API client. diff --git a/mastodon_test.go b/mastodon_test.go index c59011f..ff1dd28 100644 --- a/mastodon_test.go +++ b/mastodon_test.go @@ -23,7 +23,7 @@ func TestDoAPI(t *testing.T) { defer ts.Close() c := NewClient(&Config{Server: ts.URL}) - _, err := c.doAPI(context.Background(), http.MethodGet, "/", nil, nil, &Pagination{ + err := c.doAPI(context.Background(), http.MethodGet, "/", nil, nil, &Pagination{ MaxID: Int64(999), }) if err == nil { @@ -31,11 +31,12 @@ func TestDoAPI(t *testing.T) { } var accounts []Account - pg, err := c.doAPI(context.Background(), http.MethodGet, "/", url.Values{}, &accounts, &Pagination{ + pg := &Pagination{ MaxID: Int64(123), SinceID: Int64(789), Limit: Int64(10), - }) + } + err = c.doAPI(context.Background(), http.MethodGet, "/", url.Values{}, &accounts, pg) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -52,11 +53,12 @@ func TestDoAPI(t *testing.T) { t.Fatalf("want %q but %q", "bar", accounts[1].Username) } - pg, err = c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, &Pagination{ + pg = &Pagination{ MaxID: Int64(123), SinceID: Int64(789), Limit: Int64(10), - }) + } + err = c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, pg) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -216,7 +218,7 @@ func TestGetTimelineHome(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - tl, _, err := client.GetTimelineHome(context.Background(), nil) + tl, err := client.GetTimelineHome(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } @@ -246,7 +248,7 @@ func TestGetTimelineHomeWithCancel(t *testing.T) { }) ctx, cancel := context.WithCancel(context.Background()) cancel() - _, _, err := client.GetTimelineHome(ctx, nil) + _, err := client.GetTimelineHome(ctx, nil) if err == nil { t.Fatalf("should be fail: %v", err) } diff --git a/notification.go b/notification.go index 5df3485..15e6cf7 100644 --- a/notification.go +++ b/notification.go @@ -17,19 +17,19 @@ type Notification struct { } // GetNotifications return notifications. -func (c *Client) GetNotifications(ctx context.Context, pg *Pagination) ([]*Notification, *Pagination, error) { +func (c *Client) GetNotifications(ctx context.Context, pg *Pagination) ([]*Notification, error) { var notifications []*Notification - retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, ¬ifications, pg) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/notifications", nil, ¬ifications, pg) if err != nil { - return nil, nil, err + return nil, err } - return notifications, retPG, nil + return notifications, 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, ¬ification, nil) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/notifications/%d", id), nil, ¬ification, nil) if err != nil { return nil, err } @@ -38,6 +38,5 @@ func (c *Client) GetNotification(ctx context.Context, id int64) (*Notification, // ClearNotifications clear notifications. func (c *Client) ClearNotifications(ctx context.Context) error { - _, err := c.doAPI(ctx, http.MethodPost, "/api/v1/notifications/clear", nil, nil, nil) - return err + return c.doAPI(ctx, http.MethodPost, "/api/v1/notifications/clear", nil, nil, nil) } diff --git a/notification_test.go b/notification_test.go index 2459b1e..740dec9 100644 --- a/notification_test.go +++ b/notification_test.go @@ -32,7 +32,7 @@ func TestGetNotifications(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - ns, _, err := client.GetNotifications(context.Background(), nil) + ns, err := client.GetNotifications(context.Background(), nil) if err != nil { t.Fatalf("should not be fail: %v", err) } diff --git a/report.go b/report.go index dd9c461..1e6debf 100644 --- a/report.go +++ b/report.go @@ -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 } diff --git a/status.go b/status.go index 74cec09..5191481 100644 --- a/status.go +++ b/status.go @@ -48,19 +48,19 @@ type Card struct { } // GetFavourites return the favorite list of the current user. -func (c *Client) GetFavourites(ctx context.Context, pg *Pagination) ([]*Status, *Pagination, error) { +func (c *Client) GetFavourites(ctx context.Context, pg *Pagination) ([]*Status, error) { var statuses []*Status - retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, pg) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, pg) if err != nil { - return nil, nil, err + return nil, err } - return statuses, retPG, nil + return statuses, 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, pg *Pagination) ([]*Account, *Pagination, error) { +func (c *Client) GetRebloggedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) { var accounts []*Account - retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts, pg) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/reblogged_by", id), nil, &accounts, pg) if err != nil { - return nil, nil, err + return nil, err } - return accounts, retPG, nil + return accounts, nil } // GetFavouritedBy returns the account list of the user who liked the toot of id. -func (c *Client) GetFavouritedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, *Pagination, error) { +func (c *Client) GetFavouritedBy(ctx context.Context, id int64, pg *Pagination) ([]*Account, error) { var accounts []*Account - retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts, pg) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%d/favourited_by", id), nil, &accounts, pg) if err != nil { - return nil, nil, err + return nil, err } - return accounts, retPG, nil + return accounts, 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, pg *Pagination) ([]*Status, *Pagination, error) { +func (c *Client) GetTimelineHome(ctx context.Context, pg *Pagination) ([]*Status, error) { var statuses []*Status - retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses, pg) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses, pg) if err != nil { - return nil, nil, err + return nil, err } - return statuses, retPG, nil + return statuses, nil } // GetTimelinePublic return statuses from public timeline. -func (c *Client) GetTimelinePublic(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, *Pagination, error) { +func (c *Client) GetTimelinePublic(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, error) { params := url.Values{} if isLocal { params.Set("local", "t") } var statuses []*Status - retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg) if err != nil { - return nil, nil, err + return nil, err } - return statuses, retPG, nil + return statuses, nil } // GetTimelineHashtag return statuses from tagged timeline. -func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal bool, pg *Pagination) ([]*Status, *Pagination, error) { +func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal bool, pg *Pagination) ([]*Status, error) { params := url.Values{} if isLocal { params.Set("local", "t") } var statuses []*Status - retPG, err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", url.PathEscape(tag)), params, &statuses, pg) + err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", url.PathEscape(tag)), params, &statuses, pg) if err != nil { - return nil, nil, err + return nil, err } - return statuses, retPG, nil + return statuses, 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, pg *Pagination) ([]*Status, *Pagination, error) { +func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, error) { params := url.Values{} params.Set("media", "t") if isLocal { @@ -197,11 +197,11 @@ func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool, pg *Paginat } var statuses []*Status - retPG, err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg) + err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg) if err != nil { - return nil, nil, err + return nil, err } - return statuses, retPG, nil + return statuses, 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,8 +236,7 @@ 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 { - _, err := c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%d", id), nil, nil, nil) - return err + return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%d", id), nil, nil, nil) } // Search search content with query. @@ -246,7 +245,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 } @@ -256,7 +255,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 } diff --git a/status_test.go b/status_test.go index 3e7d7b3..1359bb9 100644 --- a/status_test.go +++ b/status_test.go @@ -21,7 +21,7 @@ func TestGetFavourites(t *testing.T) { ClientSecret: "bar", AccessToken: "zoo", }) - favs, _, err := client.GetFavourites(context.Background(), nil) + 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, nil) + _, err := client.GetRebloggedBy(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - rbs, _, err := client.GetRebloggedBy(context.Background(), 1234567, nil) + 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, nil) + _, err := client.GetFavouritedBy(context.Background(), 123, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - fbs, _, err := client.GetFavouritedBy(context.Background(), 1234567, nil) + 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, nil) + _, err := client.GetTimelinePublic(context.Background(), false, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - tl, _, err := client.GetTimelinePublic(context.Background(), true, nil) + 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, nil) + _, 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, nil) + 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, nil) + _, err := client.GetTimelineMedia(context.Background(), false, nil) if err == nil { t.Fatalf("should be fail: %v", err) } - tags, _, err := client.GetTimelineMedia(context.Background(), true, nil) + tags, err := client.GetTimelineMedia(context.Background(), true, nil) if err != nil { t.Fatalf("should not be fail: %v", err) } From 22f47735b41f5f763bc3b0b58c961a2c9dcad54e Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Sat, 6 May 2017 23:34:42 +0900 Subject: [PATCH 06/10] Add processing when pagination is nil --- mastodon.go | 17 ++++++++--------- mastodon_test.go | 24 ++++++++++++++++++------ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/mastodon.go b/mastodon.go index 17acacc..1ac6029 100644 --- a/mastodon.go +++ b/mastodon.go @@ -103,19 +103,18 @@ func (c *Client) doAPI(ctx context.Context, method string, uri string, params in } defer resp.Body.Close() - lh := resp.Header.Get("Link") - if lh != "" { - retPG, err := newPagination(lh) - if err != nil { - return err - } - *pg = *retPG - } - if resp.StatusCode != http.StatusOK { return parseAPIError("bad request", resp) } else if res == nil { return nil + } else if pg != nil { + if lh := resp.Header.Get("Link"); lh != "" { + pg2, err := newPagination(lh) + if err != nil { + return err + } + *pg = *pg2 + } } return json.NewDecoder(resp.Body).Decode(&res) } diff --git a/mastodon_test.go b/mastodon_test.go index ff1dd28..974a380 100644 --- a/mastodon_test.go +++ b/mastodon_test.go @@ -13,24 +13,24 @@ import ( func TestDoAPI(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - if q.Get("max_id") == "123" && q.Get("since_id") == "789" && q.Get("limit") == "10" { + if r.URL.Query().Get("max_id") == "999" { + w.Header().Set("Link", `<:>; rel="next"`) + } else { w.Header().Set("Link", `; rel="next", ; rel="prev"`) - fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`) } - w.Header().Set("Link", `<:>; rel="next"`) + fmt.Fprintln(w, `[{"username": "foo"}, {"username": "bar"}]`) })) defer ts.Close() c := NewClient(&Config{Server: ts.URL}) - err := c.doAPI(context.Background(), http.MethodGet, "/", nil, nil, &Pagination{ + var accounts []Account + err := c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, &Pagination{ MaxID: Int64(999), }) if err == nil { t.Fatalf("should be fail: %v", err) } - var accounts []Account pg := &Pagination{ MaxID: Int64(123), SinceID: Int64(789), @@ -74,6 +74,18 @@ func TestDoAPI(t *testing.T) { if accounts[1].Username != "bar" { t.Fatalf("want %q but %q", "bar", accounts[1].Username) } + + // *Pagination is nil + err = c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, nil) + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if accounts[0].Username != "foo" { + t.Fatalf("want %q but %q", "foo", accounts[0].Username) + } + if accounts[1].Username != "bar" { + t.Fatalf("want %q but %q", "bar", accounts[1].Username) + } } func TestAuthenticate(t *testing.T) { From 1c0e37928e581ceff697e88a1caf250b6951925a Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Sat, 6 May 2017 23:49:46 +0900 Subject: [PATCH 07/10] Fix mstdn cmd pagination --- cmd/mstdn/cmd_followers.go | 4 ++-- cmd/mstdn/cmd_notification.go | 2 +- cmd/mstdn/cmd_timeline.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/mstdn/cmd_followers.go b/cmd/mstdn/cmd_followers.go index 4c21e8d..d505137 100644 --- a/cmd/mstdn/cmd_followers.go +++ b/cmd/mstdn/cmd_followers.go @@ -20,8 +20,8 @@ func cmdFollowers(c *cli.Context) error { var maxID *int64 var followers []*mastodon.Account for { - fs, pg, err := client.GetAccountFollowers( - context.Background(), account.ID, &mastodon.Pagination{MaxID: maxID}) + pg := mastodon.Pagination{MaxID: maxID} + fs, err := client.GetAccountFollowers(context.Background(), account.ID, &pg) if err != nil { return err } diff --git a/cmd/mstdn/cmd_notification.go b/cmd/mstdn/cmd_notification.go index d96b0ce..b32ba4e 100644 --- a/cmd/mstdn/cmd_notification.go +++ b/cmd/mstdn/cmd_notification.go @@ -11,7 +11,7 @@ import ( func cmdNotification(c *cli.Context) error { client := c.App.Metadata["client"].(*mastodon.Client) - notifications, _, err := client.GetNotifications(context.Background(), nil) + notifications, err := client.GetNotifications(context.Background(), nil) if err != nil { return err } diff --git a/cmd/mstdn/cmd_timeline.go b/cmd/mstdn/cmd_timeline.go index 4024826..d9a0024 100644 --- a/cmd/mstdn/cmd_timeline.go +++ b/cmd/mstdn/cmd_timeline.go @@ -10,7 +10,7 @@ import ( func cmdTimeline(c *cli.Context) error { client := c.App.Metadata["client"].(*mastodon.Client) config := c.App.Metadata["config"].(*mastodon.Config) - timeline, _, err := client.GetTimelineHome(context.Background(), nil) + timeline, err := client.GetTimelineHome(context.Background(), nil) if err != nil { return err } From f283f056710f363cb6ba99d2b5425f178fa93501 Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Sun, 7 May 2017 01:36:25 +0900 Subject: [PATCH 08/10] Fix Pagination variable from *int64 to int64 --- cmd/mstdn/cmd_followers.go | 4 ++-- helper.go | 3 --- helper_test.go | 8 ------- mastodon.go | 22 +++++++++---------- mastodon_test.go | 44 +++++++++++++++++++------------------- 5 files changed, 35 insertions(+), 46 deletions(-) diff --git a/cmd/mstdn/cmd_followers.go b/cmd/mstdn/cmd_followers.go index d505137..1d34a12 100644 --- a/cmd/mstdn/cmd_followers.go +++ b/cmd/mstdn/cmd_followers.go @@ -17,7 +17,7 @@ func cmdFollowers(c *cli.Context) error { if err != nil { return err } - var maxID *int64 + var maxID int64 var followers []*mastodon.Account for { pg := mastodon.Pagination{MaxID: maxID} @@ -26,7 +26,7 @@ func cmdFollowers(c *cli.Context) error { return err } followers = append(followers, fs...) - if pg.MaxID == nil { + if pg.MaxID == 0 { break } maxID = pg.MaxID diff --git a/helper.go b/helper.go index 281f5a2..05af20f 100644 --- a/helper.go +++ b/helper.go @@ -37,9 +37,6 @@ 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 } diff --git a/helper_test.go b/helper_test.go index b2599e6..7da80b9 100644 --- a/helper_test.go +++ b/helper_test.go @@ -62,14 +62,6 @@ func TestBase64Encode(t *testing.T) { } } -func TestInt64(t *testing.T) { - i := int64(1234567) - ip := Int64(i) - if *ip != i { - t.Fatalf("want %d but %d", i, *ip) - } -} - func TestString(t *testing.T) { s := "test" sp := String(s) diff --git a/mastodon.go b/mastodon.go index 1ac6029..1729597 100644 --- a/mastodon.go +++ b/mastodon.go @@ -213,9 +213,9 @@ type Results struct { // Pagination is a struct for specifying the get range. type Pagination struct { - MaxID *int64 - SinceID *int64 - Limit *int64 + MaxID int64 + SinceID int64 + Limit int64 } func newPagination(rawlink string) (*Pagination, error) { @@ -231,13 +231,13 @@ func newPagination(rawlink string) (*Pagination, error) { if err != nil { return nil, err } - p.MaxID = &maxID + p.MaxID = maxID case "prev": sinceID, err := getPaginationID(link.URL, "since_id") if err != nil { return nil, err } - p.SinceID = &sinceID + p.SinceID = sinceID } } @@ -263,14 +263,14 @@ func (p *Pagination) toValues() url.Values { } func (p *Pagination) setValues(params url.Values) url.Values { - if p.MaxID != nil { - params.Set("max_id", fmt.Sprint(*p.MaxID)) + if p.MaxID != 0 { + params.Set("max_id", fmt.Sprint(p.MaxID)) } - if p.SinceID != nil { - params.Set("since_id", fmt.Sprint(*p.SinceID)) + if p.SinceID != 0 { + params.Set("since_id", fmt.Sprint(p.SinceID)) } - if p.Limit != nil { - params.Set("limit", fmt.Sprint(*p.Limit)) + if p.Limit != 0 { + params.Set("limit", fmt.Sprint(p.Limit)) } return params diff --git a/mastodon_test.go b/mastodon_test.go index 974a380..eac42cd 100644 --- a/mastodon_test.go +++ b/mastodon_test.go @@ -25,26 +25,26 @@ func TestDoAPI(t *testing.T) { c := NewClient(&Config{Server: ts.URL}) var accounts []Account err := c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, &Pagination{ - MaxID: Int64(999), + MaxID: 999, }) if err == nil { t.Fatalf("should be fail: %v", err) } pg := &Pagination{ - MaxID: Int64(123), - SinceID: Int64(789), - Limit: Int64(10), + MaxID: 123, + SinceID: 789, + Limit: 10, } err = c.doAPI(context.Background(), http.MethodGet, "/", url.Values{}, &accounts, pg) if err != nil { t.Fatalf("should not be fail: %v", err) } - if *pg.MaxID != 234 { - t.Fatalf("want %d but %d", 234, *pg.MaxID) + if pg.MaxID != 234 { + t.Fatalf("want %d but %d", 234, pg.MaxID) } - if *pg.SinceID != 890 { - t.Fatalf("want %d but %d", 890, *pg.SinceID) + if pg.SinceID != 890 { + t.Fatalf("want %d but %d", 890, pg.SinceID) } if accounts[0].Username != "foo" { t.Fatalf("want %q but %q", "foo", accounts[0].Username) @@ -54,19 +54,19 @@ func TestDoAPI(t *testing.T) { } pg = &Pagination{ - MaxID: Int64(123), - SinceID: Int64(789), - Limit: Int64(10), + MaxID: 123, + SinceID: 789, + Limit: 10, } err = c.doAPI(context.Background(), http.MethodGet, "/", nil, &accounts, pg) if err != nil { t.Fatalf("should not be fail: %v", err) } - if *pg.MaxID != 234 { - t.Fatalf("want %d but %d", 234, *pg.MaxID) + if pg.MaxID != 234 { + t.Fatalf("want %d but %d", 234, pg.MaxID) } - if *pg.SinceID != 890 { - t.Fatalf("want %d but %d", 890, *pg.SinceID) + if pg.SinceID != 890 { + t.Fatalf("want %d but %d", 890, pg.SinceID) } if accounts[0].Username != "foo" { t.Fatalf("want %q but %q", "foo", accounts[0].Username) @@ -297,11 +297,11 @@ func TestNewPagination(t *testing.T) { if err != nil { t.Fatalf("should not be fail: %v", err) } - if *pg.MaxID != 123 { - t.Fatalf("want %d but %d", 123, *pg.MaxID) + if pg.MaxID != 123 { + t.Fatalf("want %d but %d", 123, pg.MaxID) } - if *pg.SinceID != 789 { - t.Fatalf("want %d but %d", 789, *pg.SinceID) + if pg.SinceID != 789 { + t.Fatalf("want %d but %d", 789, pg.SinceID) } } @@ -327,9 +327,9 @@ func TestGetPaginationID(t *testing.T) { func TestPaginationSetValues(t *testing.T) { p := &Pagination{ - MaxID: Int64(123), - SinceID: Int64(789), - Limit: Int64(10), + MaxID: 123, + SinceID: 789, + Limit: 10, } before := url.Values{"key": {"value"}} after := p.setValues(before) From 9e07d8951e695a03a7852f20cfb51b2eefac0537 Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Mon, 8 May 2017 11:06:01 +0900 Subject: [PATCH 09/10] Fix not to declare Pagination every time --- cmd/mstdn/cmd_followers.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/mstdn/cmd_followers.go b/cmd/mstdn/cmd_followers.go index 1d34a12..9c99dbe 100644 --- a/cmd/mstdn/cmd_followers.go +++ b/cmd/mstdn/cmd_followers.go @@ -17,10 +17,9 @@ func cmdFollowers(c *cli.Context) error { if err != nil { return err } - var maxID int64 var followers []*mastodon.Account + var pg mastodon.Pagination for { - pg := mastodon.Pagination{MaxID: maxID} fs, err := client.GetAccountFollowers(context.Background(), account.ID, &pg) if err != nil { return err @@ -29,7 +28,7 @@ func cmdFollowers(c *cli.Context) error { if pg.MaxID == 0 { break } - maxID = pg.MaxID + pg.SinceID = 0 time.Sleep(10 * time.Second) } s := newScreen(config) From 403343617540f6180908679665e1c0e7ed96cbf5 Mon Sep 17 00:00:00 2001 From: 178inaba <178inaba@users.noreply.github.com> Date: Mon, 8 May 2017 13:44:49 +0900 Subject: [PATCH 10/10] Fix to set MaxID and SinceID to exclusive --- cmd/mstdn/cmd_followers.go | 1 - mastodon.go | 7 +++---- mastodon_test.go | 17 +++++++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cmd/mstdn/cmd_followers.go b/cmd/mstdn/cmd_followers.go index 9c99dbe..ecb4a79 100644 --- a/cmd/mstdn/cmd_followers.go +++ b/cmd/mstdn/cmd_followers.go @@ -28,7 +28,6 @@ func cmdFollowers(c *cli.Context) error { if pg.MaxID == 0 { break } - pg.SinceID = 0 time.Sleep(10 * time.Second) } s := newScreen(config) diff --git a/mastodon.go b/mastodon.go index 1729597..240833d 100644 --- a/mastodon.go +++ b/mastodon.go @@ -263,13 +263,12 @@ func (p *Pagination) toValues() url.Values { } func (p *Pagination) setValues(params url.Values) url.Values { - if p.MaxID != 0 { + if p.MaxID > 0 { params.Set("max_id", fmt.Sprint(p.MaxID)) - } - if p.SinceID != 0 { + } else if p.SinceID > 0 { params.Set("since_id", fmt.Sprint(p.SinceID)) } - if p.Limit != 0 { + if p.Limit > 0 { params.Set("limit", fmt.Sprint(p.Limit)) } diff --git a/mastodon_test.go b/mastodon_test.go index eac42cd..eb6c476 100644 --- a/mastodon_test.go +++ b/mastodon_test.go @@ -339,10 +339,23 @@ func TestPaginationSetValues(t *testing.T) { if after.Get("max_id") != "123" { t.Fatalf("want %q but %q", "123", after.Get("max_id")) } - if after.Get("since_id") != "789" { - t.Fatalf("want %q but %q", "789", after.Get("since_id")) + if after.Get("since_id") != "" { + t.Fatalf("result should be empty string: %q", after.Get("since_id")) } if after.Get("limit") != "10" { t.Fatalf("want %q but %q", "10", after.Get("limit")) } + + p = &Pagination{ + MaxID: 0, + SinceID: 789, + } + before = url.Values{} + after = p.setValues(before) + if after.Get("max_id") != "" { + t.Fatalf("result should be empty string: %q", after.Get("max_id")) + } + if after.Get("since_id") != "789" { + t.Fatalf("want %q but %q", "789", after.Get("since_id")) + } }