# HG changeset patch # User Mikael Berthe # Date 1492244796 -7200 # Node ID 579912e9d0ef43dd49dd3e68263b3ced2f76261a # Parent 22c8c58ad61b77925f2be6d7cbf0a7eb015b376c Refactor API calls diff -r 22c8c58ad61b -r 579912e9d0ef account.go --- a/account.go Sat Apr 15 00:39:43 2017 +0200 +++ b/account.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,7 +1,6 @@ package gondole import ( - "encoding/json" "fmt" "strconv" @@ -19,110 +18,75 @@ } // getSingleAccount returns an account entity -// The target can be "account", "verify_credentials", "follow", "unfollow", -// "block", "unblock", "mute", "unmute", "follow_requests/authorize" or -// "follow_requests/reject". -// The id is optional and depends on the target. -func (g *Client) getSingleAccount(target string, id int) (*Account, error) { +// The operation 'op' can be "account", "verify_credentials", "follow", +// "unfollow", "block", "unblock", "mute", "unmute", +// "follow_requests/authorize" or // "follow_requests/reject". +// The id is optional and depends on the operation. +func (g *Client) getSingleAccount(op string, id int) (*Account, error) { var endPoint string method := rest.Get strID := strconv.Itoa(id) - switch target { + switch op { case "account": endPoint = "accounts/" + strID case "verify_credentials": endPoint = "accounts/verify_credentials" case "follow", "unfollow", "block", "unblock", "mute", "unmute": - endPoint = "accounts/" + strID + "/" + target + endPoint = "accounts/" + strID + "/" + op method = rest.Post case "follow_requests/authorize", "follow_requests/reject": // The documentation is incorrect, the endpoint actually // is "follow_requests/:id/{authorize|reject}" - endPoint = target[:16] + strID + "/" + target[16:] + endPoint = op[:16] + strID + "/" + op[16:] method = rest.Post default: return nil, ErrInvalidParameter } - req := g.prepareRequest(endPoint) - req.Method = method - - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("getAccount (%s): %s", target, err.Error()) - } - - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var account Account - err = json.Unmarshal([]byte(r.Body), &account) - if err != nil { - return nil, fmt.Errorf("getAccount (%s) API: %s", target, err.Error()) + if err := g.apiCall(endPoint, method, nil, &account); err != nil { + return nil, err } return &account, nil } // getMultipleAccounts returns a list of account entities -// The target can be "followers", "following", "search", "blocks", "mutes", -// "follow_requests". -// The id is optional and depends on the target. -func (g *Client) getMultipleAccounts(target string, opts *getAccountsOptions) ([]Account, error) { +// The operation 'op' can be "followers", "following", "search", "blocks", +// "mutes", "follow_requests". +// The id is optional and depends on the operation. +func (g *Client) getMultipleAccounts(op string, opts *getAccountsOptions) ([]Account, error) { var endPoint string - switch target { + + switch op { case "followers", "following": if opts == nil || opts.ID < 1 { return []Account{}, ErrInvalidID } - endPoint = "accounts/" + strconv.Itoa(opts.ID) + "/" + target + endPoint = "accounts/" + strconv.Itoa(opts.ID) + "/" + op case "follow_requests", "blocks", "mutes": - endPoint = target + endPoint = op case "search": if opts == nil || opts.Q == "" { return []Account{}, ErrInvalidParameter } - endPoint = "accounts/" + target + endPoint = "accounts/" + op default: return nil, ErrInvalidParameter } - req := g.prepareRequest(endPoint) - // Handle target-specific query parameters - if target == "search" { - req.QueryParams["q"] = opts.Q + params := make(apiCallParams) + if op == "search" { + params["q"] = opts.Q if opts.Limit > 0 { - req.QueryParams["limit"] = strconv.Itoa(opts.Limit) + params["limit"] = strconv.Itoa(opts.Limit) } } - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("getAccount (%s): %s", target, err.Error()) - } - - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var accounts []Account - err = json.Unmarshal([]byte(r.Body), &accounts) - if err != nil { - return nil, fmt.Errorf("getAccount (%s) API: %s", target, err.Error()) + if err := g.apiCall(endPoint, rest.Get, params, &accounts); err != nil { + return nil, err } return accounts, nil } @@ -190,34 +154,18 @@ } // FollowRemoteAccount follows a remote account -// The parameter 'id' is a URI (username@domain). -func (g *Client) FollowRemoteAccount(id string) (*Account, error) { - if id == "" { +// The parameter 'uri' is a URI (e.g. "username@domain"). +func (g *Client) FollowRemoteAccount(uri string) (*Account, error) { + if uri == "" { return nil, ErrInvalidID } - req := g.prepareRequest("follows") - req.Method = rest.Post - req.QueryParams["uri"] = id - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("FollowRemoteAccount: %s", err.Error()) - } + params := make(apiCallParams) + params["uri"] = uri - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var account Account - err = json.Unmarshal([]byte(r.Body), &account) - if err != nil { - return nil, fmt.Errorf("FollowRemoteAccount API: %s", err.Error()) + if err := g.apiCall("follows", rest.Post, params, &account); err != nil { + return nil, err } if account.ID == 0 { return nil, ErrEntityNotFound @@ -302,38 +250,22 @@ return nil, ErrInvalidID } - req := g.prepareRequest("accounts/relationships") - if len(accountIDs) > 1 { // XXX return nil, fmt.Errorf("accounts/relationships currently does not work with more than 1 ID") } - req.QueryParams["id"] = strconv.Itoa(accountIDs[0]) + + params := make(apiCallParams) + params["id"] = strconv.Itoa(accountIDs[0]) /* for i, id := range accountIDList { qID := fmt.Sprintf("id[%d]", i+1) - req.QueryParams[qID] = strconv.Itoa(id) + params[qID] = strconv.Itoa(id) } */ - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("GetAccountRelationships: %s", err.Error()) - } - - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var rl []Relationship - err = json.Unmarshal([]byte(r.Body), &rl) - if err != nil { - return nil, fmt.Errorf("accounts/relationships API: %s", err.Error()) + if err := g.apiCall("accounts/relationships", rest.Get, params, &rl); err != nil { + return nil, err } return rl, nil } @@ -347,34 +279,17 @@ } endPoint := "accounts/" + strconv.Itoa(accountID) + "/" + "statuses" - req := g.prepareRequest(endPoint) - + params := make(apiCallParams) if onlyMedia { - req.QueryParams["only_media"] = "true" + params["only_media"] = "true" } if excludeReplies { - req.QueryParams["exclude_replies"] = "true" - } - - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("GetAccountStatuses: %s", err.Error()) + params["exclude_replies"] = "true" } - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var sl []Status - err = json.Unmarshal([]byte(r.Body), &sl) - if err != nil { - return nil, fmt.Errorf("accounts/statuses API: %s", err.Error()) + if err := g.apiCall(endPoint, rest.Get, params, &sl); err != nil { + return nil, err } return sl, nil } diff -r 22c8c58ad61b -r 579912e9d0ef app.go --- a/app.go Sat Apr 15 00:39:43 2017 +0200 +++ b/app.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,8 +1,6 @@ package gondole import ( - "encoding/json" - "log" "net/url" "strings" @@ -37,30 +35,22 @@ InstanceURL: instanceURL, } - req := g.prepareRequest("apps") + params := make(apiCallParams) + params["client_name"] = name + params["scopes"] = strings.Join(scopes, " ") if redirectURI != "" { - req.QueryParams["redirect_uris"] = redirectURI + params["redirect_uris"] = redirectURI } else { - req.QueryParams["redirect_uris"] = NoRedirect - } - req.QueryParams["client_name"] = name - req.QueryParams["scopes"] = strings.Join(scopes, " ") - req.Method = rest.Post - - r, err := rest.API(req) - if err != nil { - log.Fatalf("error can not register app: %v", err) + params["redirect_uris"] = NoRedirect } - var resp registerApp - - err = json.Unmarshal([]byte(r.Body), &resp) - if err != nil { - log.Fatalf("error can not register app: %v", err) + var app registerApp + if err := g.apiCall("apps", rest.Post, params, &app); err != nil { + return nil, err } - g.ID = resp.ClientID - g.Secret = resp.ClientSecret + g.ID = app.ClientID + g.Secret = app.ClientSecret return } diff -r 22c8c58ad61b -r 579912e9d0ef cmd/gondole-cli/main.go --- a/cmd/gondole-cli/main.go Sat Apr 15 00:39:43 2017 +0200 +++ b/cmd/gondole-cli/main.go Sat Apr 15 10:26:36 2017 +0200 @@ -108,6 +108,9 @@ } instance, err = gondole.NewApp("gondole-cli", scopes, gondole.NoRedirect, instanceURL) + if err != nil { + log.Fatalf("error: can not register application:", err.Error()) + } server := &Server{ ID: instance.ID, diff -r 22c8c58ad61b -r 579912e9d0ef favourites.go --- a/favourites.go Sat Apr 15 00:39:43 2017 +0200 +++ b/favourites.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,32 +1,15 @@ package gondole import ( - "encoding/json" - "fmt" - "github.com/sendgrid/rest" ) // GetFavourites returns the list of the user's favourites func (g *Client) GetFavourites() ([]Status, error) { var faves []Status - - req := g.prepareRequest("favourites") - r, err := rest.API(req) + err := g.apiCall("favourites", rest.Get, nil, &faves) if err != nil { - return faves, fmt.Errorf("favourites API query: %s", err.Error()) + return nil, err } - - println(r.Body) - err = json.Unmarshal([]byte(r.Body), &faves) - if err != nil { - var errorRes Error - err2 := json.Unmarshal([]byte(r.Body), &errorRes) - if err2 == nil { - return faves, fmt.Errorf("%s", errorRes.Text) - } - return faves, fmt.Errorf("favourites API: %s", err.Error()) - } - return faves, nil } diff -r 22c8c58ad61b -r 579912e9d0ef gondole.go --- a/gondole.go Sat Apr 15 00:39:43 2017 +0200 +++ b/gondole.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,12 +1,16 @@ package gondole import ( + "encoding/json" "errors" "fmt" "github.com/sendgrid/rest" ) +// apiCallParams is a map with the parameters for an API call +type apiCallParams map[string]string + const ( // GondoleVersion contains the version of the Gondole implementation GondoleVersion = "0.0" @@ -27,16 +31,12 @@ ErrInvalidID = errors.New("incorrect entity ID") ) -// prepareRequest insert all pre-defined stuff -func (g *Client) prepareRequest(what string) (req rest.Request) { - var endPoint string +// prepareRequest inserts all pre-defined stuff +func (g *Client) prepareRequest(target string, method rest.Method, params apiCallParams) (req rest.Request) { + endPoint := g.APIBase + "/" + target - endPoint = g.APIBase + "/" + what - // Add at least one option, the APIkey if present + // Request headers hdrs := make(map[string]string) - opts := make(map[string]string) - - // Insert our sig hdrs["User-Agent"] = fmt.Sprintf("Gondole/%s", GondoleVersion) if g.userToken != nil { hdrs["Authorization"] = fmt.Sprintf("Bearer %s", g.userToken.AccessToken) @@ -45,7 +45,36 @@ req = rest.Request{ BaseURL: endPoint, Headers: hdrs, - QueryParams: opts, + Method: method, + QueryParams: params, } return } + +// apiCall makes a call to the Mastodon API server +func (g *Client) apiCall(endPoint string, method rest.Method, params apiCallParams, data interface{}) error { + // Prepare query + req := g.prepareRequest(endPoint, method, params) + + // Make API call + r, err := rest.API(req) + if err != nil { + return fmt.Errorf("API query (%s) failed: %s", endPoint, err.Error()) + } + + // Check for error reply + var errorResult Error + if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { + // The empty object is not an error + if errorResult.Text != "" { + return fmt.Errorf("%s", errorResult.Text) + } + } + + // Not an error reply; let's unmarshal the data + err = json.Unmarshal([]byte(r.Body), &data) + if err != nil { + return fmt.Errorf("cannot decode API response (%s): %s", method, err.Error()) + } + return nil +} diff -r 22c8c58ad61b -r 579912e9d0ef gondole_test.go --- a/gondole_test.go Sat Apr 15 00:39:43 2017 +0200 +++ b/gondole_test.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,20 +1,20 @@ package gondole import ( - "testing" - "github.com/stretchr/testify/assert" + "testing" + + "github.com/sendgrid/rest" + "github.com/stretchr/testify/assert" ) func TestPrepareRequest(t *testing.T) { - g := &Client{ - Name: "foo", - ID: "666", - Secret: "biiiip", - APIBase: "http://example.com", - } + g := &Client{ + Name: "foo", + ID: "666", + Secret: "biiiip", + APIBase: "http://example.com", + } - req := g.prepareRequest("bar") - assert.NotNil(t, req.Headers, "not nil") - assert.NotNil(t, req.QueryParams, "not nil") + req := g.prepareRequest("bar", rest.Get, nil) + assert.NotNil(t, req.Headers, "not nil") } - diff -r 22c8c58ad61b -r 579912e9d0ef instance.go --- a/instance.go Sat Apr 15 00:39:43 2017 +0200 +++ b/instance.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,34 +1,14 @@ package gondole import ( - "encoding/json" - "fmt" - "github.com/sendgrid/rest" ) // GetCurrentInstance returns current instance information func (g *Client) GetCurrentInstance() (*Instance, error) { - req := g.prepareRequest("instance") - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("instance: %s", err.Error()) - } - - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var i Instance - err = json.Unmarshal([]byte(r.Body), &i) - if err != nil { - return nil, fmt.Errorf("instance API: %s", err.Error()) + if err := g.apiCall("instance", rest.Get, nil, &i); err != nil { + return nil, err } return &i, nil } diff -r 22c8c58ad61b -r 579912e9d0ef notifications.go --- a/notifications.go Sat Apr 15 00:39:43 2017 +0200 +++ b/notifications.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,8 +1,6 @@ package gondole import ( - "encoding/json" - "fmt" "strconv" "github.com/sendgrid/rest" @@ -11,23 +9,9 @@ // GetNotifications returns the list of the user's notifications func (g *Client) GetNotifications() ([]Notification, error) { var notifications []Notification - - req := g.prepareRequest("notifications") - r, err := rest.API(req) - if err != nil { - return notifications, fmt.Errorf("notifications API query: %s", err.Error()) + if err := g.apiCall("notifications", rest.Get, nil, ¬ifications); err != nil { + return nil, err } - - err = json.Unmarshal([]byte(r.Body), ¬ifications) - if err != nil { - var errorRes Error - err2 := json.Unmarshal([]byte(r.Body), &errorRes) - if err2 == nil { - return notifications, fmt.Errorf("%s", errorRes.Text) - } - return notifications, fmt.Errorf("notifications API: %s", err.Error()) - } - return notifications, nil } @@ -35,40 +19,23 @@ // The returned notification can be nil if there is an error or if the // requested notification does not exist. func (g *Client) GetNotification(id int) (*Notification, error) { - var notification Notification - - req := g.prepareRequest("notifications/" + strconv.Itoa(id)) - r, err := rest.API(req) - if err != nil { - return ¬ification, fmt.Errorf("notification API query: %s", err.Error()) + if id < 1 { + return nil, ErrInvalidID } - err = json.Unmarshal([]byte(r.Body), ¬ification) - if err != nil { - var errorRes Error - err2 := json.Unmarshal([]byte(r.Body), &errorRes) - if err2 == nil { - return ¬ification, fmt.Errorf("%s", errorRes.Text) - } - return ¬ification, fmt.Errorf("notification API: %s", err.Error()) + var endPoint = "notifications/" + strconv.Itoa(id) + var notification Notification + if err := g.apiCall(endPoint, rest.Get, nil, ¬ification); err != nil { + return nil, err } - if notification.ID == 0 { return nil, ErrEntityNotFound } - return ¬ification, nil } // ClearNotifications deletes all notifications from the Mastodon server for // the authenticated user func (g *Client) ClearNotifications() error { - req := g.prepareRequest("notifications/clear") - req.Method = rest.Post - _, err := rest.API(req) - if err != nil { - return fmt.Errorf("notifications/clear API query: %s", err.Error()) - } - - return nil // TODO: check returned object (should be empty) + return g.apiCall("notifications/clear", rest.Post, nil, &Notification{}) } diff -r 22c8c58ad61b -r 579912e9d0ef report.go --- a/report.go Sat Apr 15 00:39:43 2017 +0200 +++ b/report.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,8 +1,6 @@ package gondole import ( - "encoding/json" - "fmt" "strconv" "github.com/sendgrid/rest" @@ -10,26 +8,9 @@ // GetReports returns the current user's reports func (g *Client) GetReports() ([]Report, error) { - req := g.prepareRequest("reports") - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("reports: %s", err.Error()) - } - - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var reports []Report - err = json.Unmarshal([]byte(r.Body), &reports) - if err != nil { - return nil, fmt.Errorf("reports API: %s", err.Error()) + if err := g.apiCall("reports", rest.Get, nil, &reports); err != nil { + return nil, err } return reports, nil } @@ -41,32 +22,15 @@ return nil, ErrInvalidParameter } - req := g.prepareRequest("reports") - req.Method = rest.Post - req.QueryParams["account_id"] = strconv.Itoa(accountID) + params := make(apiCallParams) + params["account_id"] = strconv.Itoa(accountID) // XXX Sending only the first one since I'm not sure arrays work - req.QueryParams["status_ids"] = strconv.Itoa(statusIDs[0]) - req.QueryParams["comment"] = comment - - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("reports: %s", err.Error()) - } + params["status_ids"] = strconv.Itoa(statusIDs[0]) + params["comment"] = comment - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var report Report - err = json.Unmarshal([]byte(r.Body), &report) - if err != nil { - return nil, fmt.Errorf("reports API: %s", err.Error()) + if err := g.apiCall("reports", rest.Post, params, &report); err != nil { + return nil, err } return &report, nil } diff -r 22c8c58ad61b -r 579912e9d0ef search.go --- a/search.go Sat Apr 15 00:39:43 2017 +0200 +++ b/search.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,9 +1,6 @@ package gondole import ( - "encoding/json" - "fmt" - "github.com/sendgrid/rest" ) @@ -12,30 +9,16 @@ if query == "" { return nil, ErrInvalidParameter } - req := g.prepareRequest("search") - req.QueryParams["q"] = query + + params := make(apiCallParams) + params["q"] = query if resolve { - req.QueryParams["resolve"] = "true" - } - r, err := rest.API(req) - if err != nil { - return nil, fmt.Errorf("search: %s", err.Error()) + params["resolve"] = "true" } - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return nil, fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data var results Results - err = json.Unmarshal([]byte(r.Body), &results) - if err != nil { - return nil, fmt.Errorf("search API: %s", err.Error()) + if err := g.apiCall("search", rest.Get, params, &results); err != nil { + return nil, err } return &results, nil } diff -r 22c8c58ad61b -r 579912e9d0ef status.go --- a/status.go Sat Apr 15 00:39:43 2017 +0200 +++ b/status.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,7 +1,6 @@ package gondole import ( - "encoding/json" "fmt" "strconv" @@ -23,59 +22,41 @@ } // queryStatusData queries the statuses API -// The subquery can be empty or "status" (the status itself), "context", +// The operation 'op' can be empty or "status" (the status itself), "context", // "card", "reblogged_by", "favourited_by". // The data argument will receive the object(s) returned by the API server. -func (g *Client) queryStatusData(statusID int, subquery string, data interface{}) error { - endPoint := "statuses/" + strconv.Itoa(statusID) - +func (g *Client) queryStatusData(statusID int, op string, data interface{}) error { if statusID < 1 { return ErrInvalidID } - if subquery != "" && subquery != "status" { - switch subquery { + endPoint := "statuses/" + strconv.Itoa(statusID) + + if op != "" && op != "status" { + switch op { case "context", "card", "reblogged_by", "favourited_by": default: return ErrInvalidParameter } - endPoint += "/" + subquery - } - req := g.prepareRequest(endPoint) - r, err := rest.API(req) - if err != nil { - return fmt.Errorf("status/%s API query: %s", subquery, err.Error()) + endPoint += "/" + op } - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data - err = json.Unmarshal([]byte(r.Body), &data) - if err != nil { - return fmt.Errorf("status/%s API: %s", subquery, err.Error()) - } - return nil + return g.apiCall(endPoint, rest.Get, nil, data) } // updateStatusData updates the statuses -// The subquery can be empty or "status" (to post a status), "delete" (for -// deleting a status), "reblog", "unreblog", "favourite", "unfavourite". +// The operation 'op' can be empty or "status" (to post a status), "delete" +// (for deleting a status), "reblog", "unreblog", "favourite", "unfavourite". // The data argument will receive the object(s) returned by the API server. -func (g *Client) updateStatusData(subquery string, opts updateStatusOptions, data interface{}) error { +func (g *Client) updateStatusData(op string, opts updateStatusOptions, data interface{}) error { method := rest.Post endPoint := "statuses" + params := make(apiCallParams) - switch subquery { + switch op { case "", "status": - subquery = "status" + op = "status" if opts.Status == "" { return ErrInvalidParameter } @@ -98,55 +79,33 @@ if opts.ID < 1 { return ErrInvalidID } - endPoint += "/" + strconv.Itoa(opts.ID) + "/" + subquery + endPoint += "/" + strconv.Itoa(opts.ID) + "/" + op default: return ErrInvalidParameter } - req := g.prepareRequest(endPoint) - req.Method = method - // Form items for a new toot - if subquery == "status" { - req.QueryParams["status"] = opts.Status + if op == "status" { + params["status"] = opts.Status if opts.InReplyToID > 0 { - req.QueryParams["in_reply_to_id"] = strconv.Itoa(opts.InReplyToID) + params["in_reply_to_id"] = strconv.Itoa(opts.InReplyToID) } for i, id := range opts.MediaIDs { qID := fmt.Sprintf("media_ids[%d]", i+1) - req.QueryParams[qID] = strconv.Itoa(id) + params[qID] = strconv.Itoa(id) } if opts.Sensitive { - req.QueryParams["sensitive"] = "true" + params["sensitive"] = "true" } if opts.SpoilerText != "" { - req.QueryParams["spoiler_text"] = opts.SpoilerText + params["spoiler_text"] = opts.SpoilerText } if opts.Visibility != "" { - req.QueryParams["visibility"] = opts.Visibility + params["visibility"] = opts.Visibility } } - r, err := rest.API(req) - if err != nil { - return fmt.Errorf("status/%s API query: %s", subquery, err.Error()) - } - - // Check for error reply - var errorResult Error - if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { - // The empty object is not an error - if errorResult.Text != "" { - return fmt.Errorf("%s", errorResult.Text) - } - } - - // Not an error reply; let's unmarshal the data - err = json.Unmarshal([]byte(r.Body), &data) - if err != nil { - return fmt.Errorf("status/%s API: %s", subquery, err.Error()) - } - return nil + return g.apiCall(endPoint, method, params, data) } // GetStatus returns a status diff -r 22c8c58ad61b -r 579912e9d0ef streams.go --- a/streams.go Sat Apr 15 00:39:43 2017 +0200 +++ b/streams.go Sat Apr 15 10:26:36 2017 +0200 @@ -28,7 +28,7 @@ // The stream name can be "user", "public" or "hashtag". // For "hashtag", the hashTag argument cannot be empty. func (g *Client) openStream(streamName, hashTag string) (*http.Response, error) { - req := g.prepareRequest("streaming/" + streamName) + params := make(apiCallParams) switch streamName { case "user", "public": @@ -36,15 +36,17 @@ if hashTag == "" { return nil, ErrInvalidParameter } - req.QueryParams["tag"] = hashTag + params["tag"] = hashTag default: return nil, ErrInvalidParameter } + req := g.prepareRequest("streaming/"+streamName, rest.Get, params) reqObj, err := rest.BuildRequestObject(req) if err != nil { return nil, fmt.Errorf("cannot build stream request: %s", err.Error()) } + resp, err := rest.MakeRequest(reqObj) if err != nil { return nil, fmt.Errorf("cannot open stream: %s", err.Error()) diff -r 22c8c58ad61b -r 579912e9d0ef timelines.go --- a/timelines.go Sat Apr 15 00:39:43 2017 +0200 +++ b/timelines.go Sat Apr 15 10:26:36 2017 +0200 @@ -1,7 +1,6 @@ package gondole import ( - "encoding/json" "fmt" "strings" @@ -14,7 +13,6 @@ // local instance. func (g *Client) GetTimelines(timeline string, local bool) ([]Status, error) { var endPoint string - var tl []Status switch { case timeline == "home", timeline == "public": @@ -22,33 +20,21 @@ case strings.HasPrefix(timeline, ":"): hashtag := timeline[1:] if hashtag == "" { - return tl, fmt.Errorf("timelines API: empty hashtag") + return nil, fmt.Errorf("timelines API: empty hashtag") } endPoint = "timelines/tag/" + hashtag default: - return tl, fmt.Errorf("GetTimelines: bad timelines argument") - } - - req := g.prepareRequest(endPoint) - - if timeline == "public" && local { - req.QueryParams["local"] = "true" + return nil, fmt.Errorf("GetTimelines: bad timelines argument") } - r, err := rest.API(req) - if err != nil { - return tl, fmt.Errorf("timelines API query: %s", err.Error()) + params := make(apiCallParams) + if timeline == "public" && local { + params["local"] = "true" } - err = json.Unmarshal([]byte(r.Body), &tl) - if err != nil { - var errorRes Error - err2 := json.Unmarshal([]byte(r.Body), &errorRes) - if err2 == nil { - return tl, fmt.Errorf("%s", errorRes.Text) - } - return tl, fmt.Errorf("timelines API: %s", err.Error()) + var tl []Status + if err := g.apiCall(endPoint, rest.Get, params, &tl); err != nil { + return nil, err } - return tl, nil }