From af673974307b2c425e9af30b2e0a20f1dfe7f6eb Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 17 Apr 2017 13:54:36 +0900 Subject: [PATCH] add UploadMedia --- mastodon.go | 47 ++++++++++++++++++++++++++++++++++++++++++----- status.go | 10 ++++++++++ status_test.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/mastodon.go b/mastodon.go index 277e269..399eac4 100644 --- a/mastodon.go +++ b/mastodon.go @@ -1,12 +1,17 @@ package mastodon import ( + "bytes" "context" "encoding/json" "fmt" + "io" + "mime/multipart" "net/http" "net/url" + "os" "path" + "path/filepath" "strings" ) @@ -24,21 +29,53 @@ type Client struct { config *Config } -func (c *Client) doAPI(ctx context.Context, method string, uri string, params url.Values, res interface{}) error { +func (c *Client) doAPI(ctx context.Context, method string, uri string, params interface{}, res interface{}) error { u, err := url.Parse(c.config.Server) if err != nil { return err } u.Path = path.Join(u.Path, uri) - req, err := http.NewRequest(method, u.String(), strings.NewReader(params.Encode())) - if err != nil { - return err + var req *http.Request + ct := "application/x-www-form-urlencoded" + if values, ok := params.(url.Values); ok { + req, err = http.NewRequest(method, u.String(), strings.NewReader(values.Encode())) + if err != nil { + return err + } + } else if file, ok := params.(string); ok { + f, err := os.Open(file) + if err != nil { + return err + } + defer f.Close() + + var buf bytes.Buffer + mw := multipart.NewWriter(&buf) + part, err := mw.CreateFormFile("file", filepath.Base(file)) + if err != nil { + return err + } + _, err = io.Copy(part, f) + if err != nil { + return err + } + err = mw.Close() + if err != nil { + return err + } + req, err = http.NewRequest(method, u.String(), &buf) + if err != nil { + return err + } + ct = mw.FormDataContentType() + } else { + req, err = http.NewRequest(method, u.String(), nil) } req.WithContext(ctx) req.Header.Set("Authorization", "Bearer "+c.config.AccessToken) if params != nil { - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Content-Type", ct) } resp, err := c.Do(req) diff --git a/status.go b/status.go index 104de1f..09dffac 100644 --- a/status.go +++ b/status.go @@ -201,3 +201,13 @@ func (c *Client) Search(ctx context.Context, q string, resolve bool) (*Results, } return &results, nil } + +// PostMedia 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) + if err != nil { + return nil, err + } + return &attachment, nil +} diff --git a/status_test.go b/status_test.go index 115082c..8251383 100644 --- a/status_test.go +++ b/status_test.go @@ -257,3 +257,33 @@ func TestUnfavourite(t *testing.T) { t.Fatalf("want %q but %q", "zzz", status.Content) } } + +func TestUploadMedia(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/api/v1/media" { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } + if r.Method != "POST" { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } + fmt.Fprintln(w, `{"ID": 123}`) + return + })) + defer ts.Close() + + client := NewClient(&Config{ + Server: ts.URL, + ClientID: "foo", + ClientSecret: "bar", + AccessToken: "zoo", + }) + attachment, err := client.UploadMedia(context.Background(), "testdata/logo.png") + if err != nil { + t.Fatalf("should not be fail: %v", err) + } + if attachment.ID != 123 { + t.Fatalf("want %q but %q", 123, attachment.ID) + } +}