diff --git a/github/github.go b/github/github.go index f92b1eb..0e8c1ce 100644 --- a/github/github.go +++ b/github/github.go @@ -32,7 +32,8 @@ const ( headerRateRemaining = "X-RateLimit-Remaining" headerRateReset = "X-RateLimit-Reset" - mediaTypeV3 = "application/vnd.github.v3+json" + mediaTypeV3 = "application/vnd.github.v3+json" + defaultMediaType = "application/octet-stream" ) // A Client manages communication with the GitHub API. @@ -166,7 +167,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ // NewUploadRequest creates an upload request. A relative URL can be provided in // urlStr, in which case it is resolved relative to the UploadURL of the Client. // Relative URLs should always be specified without a preceding slash. -func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, contentType string) (*http.Request, error) { +func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) { rel, err := url.Parse(urlStr) if err != nil { return nil, err @@ -177,9 +178,13 @@ func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, contentType s if err != nil { return nil, err } + req.ContentLength = size + if len(mediaType) == 0 { + mediaType = defaultMediaType + } + req.Header.Add("Content-Type", mediaType) req.Header.Add("Accept", mediaTypeV3) - req.Header.Add("Content-Type", contentType) req.Header.Add("User-Agent", c.UserAgent) return req, nil } diff --git a/github/github_test.go b/github/github_test.go index 76d943e..835592b 100644 --- a/github/github_test.go +++ b/github/github_test.go @@ -85,6 +85,18 @@ func testURLParseError(t *testing.T, err error) { } } +func testBody(t *testing.T, r *http.Request, want string){ +b, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Unable to read body") + } + str := string(b) + if want != str { + t.Errorf("Body = %s, want: %s", str, want) + } + } + + // Helper function to test that a value is marshalled to JSON as expected. func testJSONMarshal(t *testing.T, v interface{}, want string) { j, err := json.Marshal(v) diff --git a/github/repos_releases.go b/github/repos_releases.go index 0477ada..bfeafd8 100644 --- a/github/repos_releases.go +++ b/github/repos_releases.go @@ -6,8 +6,11 @@ package github import ( + "errors" "fmt" - "io" + "mime" + "os" + "path/filepath" ) // RepositoryRelease represents a GitHub release in a repository. @@ -209,16 +212,26 @@ func (s *RepositoriesService) DeleteReleaseAsset(owner, repo string, id int) (*R } // UploadReleaseAsset creates an asset by uploading a file into a release repository. +// To upload assets that cannot be represented by an os.File, call NewUploadRequest directly. // // GitHub API docs : http://developer.github.com/v3/repos/releases/#upload-a-release-asset -func (s *RepositoriesService) UploadReleaseAsset(owner, repo string, id int, opt *UploadOptions, reader io.Reader, contentType string) (*ReleaseAsset, *Response, error) { +func (s *RepositoriesService) UploadReleaseAsset(owner, repo string, id int, opt *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) { u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) u, err := addOptions(u, opt) if err != nil { return nil, nil, err } - req, err := s.client.NewUploadRequest(u, reader, contentType) + stat, err := file.Stat() + if err != nil { + return nil, nil, err + } + if stat.IsDir() { + return nil, nil, errors.New("The asset to upload can't be a directory") + } + + mediaType := mime.TypeByExtension(filepath.Ext(file.Name())) + req, err := s.client.NewUploadRequest(u, file, stat.Size(), mediaType) if err != nil { return nil, nil, err } diff --git a/github/repos_releases_test.go b/github/repos_releases_test.go index bfb9c26..6ae08b1 100644 --- a/github/repos_releases_test.go +++ b/github/repos_releases_test.go @@ -9,8 +9,8 @@ import ( "encoding/json" "fmt" "net/http" + "os" "reflect" - "strings" "testing" ) @@ -207,15 +207,20 @@ func TestRepositoriesService_UploadReleaseAsset(t *testing.T) { mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testHeader(t, r, "Content-Type", "application/zip") + testHeader(t, r, "Content-Type", "text/plain; charset=utf-8") + testHeader(t, r, "Content-Length", "12") testFormValues(t, r, values{"name": "n"}) + testBody(t, r, "Upload me !\n") fmt.Fprintf(w, `{"id":1}`) }) opt := &UploadOptions{Name: "n"} - data := strings.NewReader("data") - asset, _, err := client.Repositories.UploadReleaseAsset("o", "r", 1, opt, data, "application/zip") + file, err := os.Open("testdata/upload.txt") + if err != nil { + t.Errorf("Unable to open file testdata/upload.txt") + } + asset, _, err := client.Repositories.UploadReleaseAsset("o", "r", 1, opt, file) if err != nil { t.Errorf("Repositories.UploadReleaseAssert returned error: %v", err) } diff --git a/github/testdata/upload.txt b/github/testdata/upload.txt new file mode 100644 index 0000000..5863dc7 --- /dev/null +++ b/github/testdata/upload.txt @@ -0,0 +1 @@ +Upload me !