Rework the API wrappers to handle arrays of parameters
This make some API calls work better (reports with several statuses,
statuses with several attachments, relationships for multiple
accounts...).
package gondole
import (
"fmt"
"strconv"
"github.com/sendgrid/rest"
)
// updateStatusOptions contains option fields for POST and DELETE API calls
type updateStatusOptions struct {
// The ID is used for most commands
ID int
// The following fields are used for posting a new status
Status string
InReplyToID int
MediaIDs []int
Sensitive bool
SpoilerText string
Visibility string // "direct", "private", "unlisted" or "public"
}
// queryStatusData queries the statuses API
// 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, op string, data interface{}) error {
if statusID < 1 {
return ErrInvalidID
}
endPoint := "statuses/" + strconv.Itoa(statusID)
if op != "" && op != "status" {
switch op {
case "context", "card", "reblogged_by", "favourited_by":
default:
return ErrInvalidParameter
}
endPoint += "/" + op
}
return g.apiCall(endPoint, rest.Get, nil, data)
}
// updateStatusData updates the statuses
// 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(op string, opts updateStatusOptions, data interface{}) error {
method := rest.Post
endPoint := "statuses"
params := make(apiCallParams)
switch op {
case "", "status":
op = "status"
if opts.Status == "" {
return ErrInvalidParameter
}
switch opts.Visibility {
case "", "direct", "private", "unlisted", "public":
// Okay
default:
return ErrInvalidParameter
}
if len(opts.MediaIDs) > 4 {
return fmt.Errorf("too many (>4) media IDs")
}
case "delete":
method = rest.Delete
if opts.ID < 1 {
return ErrInvalidID
}
endPoint += "/" + strconv.Itoa(opts.ID)
case "reblog", "unreblog", "favourite", "unfavourite":
if opts.ID < 1 {
return ErrInvalidID
}
endPoint += "/" + strconv.Itoa(opts.ID) + "/" + op
default:
return ErrInvalidParameter
}
// Form items for a new toot
if op == "status" {
params["status"] = opts.Status
if opts.InReplyToID > 0 {
params["in_reply_to_id"] = strconv.Itoa(opts.InReplyToID)
}
for i, id := range opts.MediaIDs {
qID := fmt.Sprintf("media_ids[%d]", i+1)
params[qID] = strconv.Itoa(id)
}
if opts.Sensitive {
params["sensitive"] = "true"
}
if opts.SpoilerText != "" {
params["spoiler_text"] = opts.SpoilerText
}
if opts.Visibility != "" {
params["visibility"] = opts.Visibility
}
}
return g.apiCall(endPoint, method, params, data)
}
// GetStatus returns a status
// The returned status can be nil if there is an error or if the
// requested ID does not exist.
func (g *Client) GetStatus(id int) (*Status, error) {
var status Status
if err := g.queryStatusData(id, "status", &status); err != nil {
return nil, err
}
if status.ID == 0 {
return nil, ErrEntityNotFound
}
return &status, nil
}
// GetStatusContext returns a status context
func (g *Client) GetStatusContext(id int) (*Context, error) {
var context Context
if err := g.queryStatusData(id, "context", &context); err != nil {
return nil, err
}
return &context, nil
}
// GetStatusCard returns a status card
func (g *Client) GetStatusCard(id int) (*Card, error) {
var card Card
if err := g.queryStatusData(id, "card", &card); err != nil {
return nil, err
}
return &card, nil
}
// GetStatusRebloggedBy returns a list of the accounts who reblogged a status
func (g *Client) GetStatusRebloggedBy(id int) ([]Account, error) {
var accounts []Account
err := g.queryStatusData(id, "reblogged_by", &accounts)
return accounts, err
}
// GetStatusFavouritedBy returns a list of the accounts who favourited a status
func (g *Client) GetStatusFavouritedBy(id int) ([]Account, error) {
var accounts []Account
err := g.queryStatusData(id, "favourited_by", &accounts)
return accounts, err
}
// PostStatus posts a new "toot"
// All parameters but "text" can be empty.
// Visibility must be empty, or one of "direct", "private", "unlisted" and "public".
func (g *Client) PostStatus(text string, inReplyTo int, mediaIDs []int, sensitive bool, spoilerText string, visibility string) (*Status, error) {
var status Status
o := updateStatusOptions{
Status: text,
InReplyToID: inReplyTo,
MediaIDs: mediaIDs,
Sensitive: sensitive,
SpoilerText: spoilerText,
Visibility: visibility,
}
err := g.updateStatusData("status", o, &status)
if err != nil {
return nil, err
}
if status.ID == 0 {
return nil, ErrEntityNotFound // TODO Change error message
}
return &status, err
}
// DeleteStatus deletes a status
func (g *Client) DeleteStatus(id int) error {
var status Status
o := updateStatusOptions{ID: id}
err := g.updateStatusData("delete", o, &status)
return err
}
// ReblogStatus reblogs a status
func (g *Client) ReblogStatus(id int) error {
var status Status
o := updateStatusOptions{ID: id}
err := g.updateStatusData("reblog", o, &status)
return err
}
// UnreblogStatus unreblogs a status
func (g *Client) UnreblogStatus(id int) error {
var status Status
o := updateStatusOptions{ID: id}
err := g.updateStatusData("unreblog", o, &status)
return err
}
// FavouriteStatus favourites a status
func (g *Client) FavouriteStatus(id int) error {
var status Status
o := updateStatusOptions{ID: id}
err := g.updateStatusData("favourite", o, &status)
return err
}
// UnfavouriteStatus unfavourites a status
func (g *Client) UnfavouriteStatus(id int) error {
var status Status
o := updateStatusOptions{ID: id}
err := g.updateStatusData("unfavourite", o, &status)
return err
}