Browse Source

add support for Release assets

Includes Repositories.ListReleaseAssets, Repositories.GetReleaseAsset,
Repositories.EditReleaseAsset, Repositories.DeleteReleaseAsset,
Repositories.UploadReleaseAsset.
Thomas Bruyelle 12 years ago
committed by Will Norris
parent
commit
3b87a64f0d
5 changed files with 278 additions and 15 deletions
  1. +32
    -1
      github/github.go
  2. +3
    -1
      github/github_test.go
  3. +122
    -3
      github/repos_releases.go
  4. +119
    -10
      github/repos_releases_test.go
  5. +2
    -0
      github/strings_test.go

+ 32
- 1
github/github.go View File

@ -10,6 +10,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@ -24,6 +25,7 @@ import (
const ( const (
libraryVersion = "0.1" libraryVersion = "0.1"
defaultBaseURL = "https://api.github.com/" defaultBaseURL = "https://api.github.com/"
uploadBaseURL = "https://uploads.github.com/"
userAgent = "go-github/" + libraryVersion userAgent = "go-github/" + libraryVersion
headerRateLimit = "X-RateLimit-Limit" headerRateLimit = "X-RateLimit-Limit"
@ -44,6 +46,9 @@ type Client struct {
// always be specified with a trailing slash. // always be specified with a trailing slash.
BaseURL *url.URL BaseURL *url.URL
// Base URL for uploading files.
UploadURL *url.URL
// User agent used when communicating with the GitHub API. // User agent used when communicating with the GitHub API.
UserAgent string UserAgent string
@ -75,6 +80,11 @@ type ListOptions struct {
PerPage int `url:"per_page,omitempty"` PerPage int `url:"per_page,omitempty"`
} }
// UploadOptions specifies the parameters to methods that support uploads.
type UploadOptions struct {
Name string `url:"name,omitempty"`
}
// addOptions adds the parameters in opt as URL query parameters to s. opt // addOptions adds the parameters in opt as URL query parameters to s. opt
// must be a struct whose fields may contain "url" tags. // must be a struct whose fields may contain "url" tags.
func addOptions(s string, opt interface{}) (string, error) { func addOptions(s string, opt interface{}) (string, error) {
@ -106,8 +116,9 @@ func NewClient(httpClient *http.Client) *Client {
httpClient = http.DefaultClient httpClient = http.DefaultClient
} }
baseURL, _ := url.Parse(defaultBaseURL) baseURL, _ := url.Parse(defaultBaseURL)
uploadURL, _ := url.Parse(uploadBaseURL)
c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent}
c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL}
c.Activity = &ActivityService{client: c} c.Activity = &ActivityService{client: c}
c.Gists = &GistsService{client: c} c.Gists = &GistsService{client: c}
c.Git = &GitService{client: c} c.Git = &GitService{client: c}
@ -150,6 +161,26 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
return req, nil return req, nil
} }
// 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) {
rel, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
u := c.UploadURL.ResolveReference(rel)
req, err := http.NewRequest("POST", u.String(), reader)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", contentType)
req.Header.Add("User-Agent", c.UserAgent)
return req, nil
}
// Response is a GitHub API response. This wraps the standard http.Response // Response is a GitHub API response. This wraps the standard http.Response
// returned from GitHub and provides convenient access to things like // returned from GitHub and provides convenient access to things like
// pagination links. // pagination links.


+ 3
- 1
github/github_test.go View File

@ -40,7 +40,9 @@ func setup() {
// github client configured to use test server // github client configured to use test server
client = NewClient(nil) client = NewClient(nil)
client.BaseURL, _ = url.Parse(server.URL)
url, _ := url.Parse(server.URL)
client.BaseURL = url
client.UploadURL = url
} }
// teardown closes the test HTTP server. // teardown closes the test HTTP server.


+ 122
- 3
github/repos_releases.go View File

@ -5,7 +5,10 @@
package github package github
import "fmt"
import (
"fmt"
"io"
)
// RepositoryRelease represents a GitHub release in a repository. // RepositoryRelease represents a GitHub release in a repository.
type RepositoryRelease struct { type RepositoryRelease struct {
@ -14,8 +17,8 @@ type RepositoryRelease struct {
TargetCommitish *string `json:"target_commitish,omitempty"` TargetCommitish *string `json:"target_commitish,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
Body *string `json:"body,omitempty"` Body *string `json:"body,omitempty"`
Draft *bool `json:"draft"`
Prerelease *bool `json:"prerelease"`
Draft *bool `json:"draft,omitempty"`
Prerelease *bool `json:"prerelease,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"` CreatedAt *Timestamp `json:"created_at,omitempty"`
PublishedAt *Timestamp `json:"published_at,omitempty"` PublishedAt *Timestamp `json:"published_at,omitempty"`
URL *string `json:"url,omitempty"` URL *string `json:"url,omitempty"`
@ -28,6 +31,24 @@ func (r RepositoryRelease) String() string {
return Stringify(r) return Stringify(r)
} }
// ReleaseAsset represents a Github release asset in a repository.
type ReleaseAsset struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Name *string `json:"name,omitempty"`
Label *string `json:"label,omitempty"`
State *string `json:"state,omitempty"`
ContentType *string `json:"content_type,omitempty"`
Size *int `json:"size,omitempty"`
DownloadCount *int `json:"download_count,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
func (r ReleaseAsset) String() string {
return Stringify(r)
}
// ListReleases lists the releases for a repository. // ListReleases lists the releases for a repository.
// //
// GitHub API docs: http://developer.github.com/v3/repos/releases/#list-releases-for-a-repository // GitHub API docs: http://developer.github.com/v3/repos/releases/#list-releases-for-a-repository
@ -121,3 +142,101 @@ func (s *RepositoriesService) DeleteRelease(owner, repo string, id int) (*Respon
req.Header.Add("Accept", mimeReleasePreview) req.Header.Add("Accept", mimeReleasePreview)
return s.client.Do(req, nil) return s.client.Do(req, nil)
} }
// ListReleaseAssets lists the release's assets.
//
// GitHub API docs : http://developer.github.com/v3/repos/releases/#list-assets-for-a-release
func (s *RepositoriesService) ListReleaseAssets(owner, repo string, id int) ([]ReleaseAsset, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Add("Accept", mimeReleasePreview)
assets := new([]ReleaseAsset)
resp, err := s.client.Do(req, assets)
if err != nil {
return nil, resp, nil
}
return *assets, resp, err
}
// GetReleaseAsset fetches a single release asset.
//
// GitHub API docs : http://developer.github.com/v3/repos/releases/#get-a-single-release-asset
func (s *RepositoriesService) GetReleaseAsset(owner, repo string, id int) (*ReleaseAsset, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Add("Accept", mimeReleasePreview)
asset := new(ReleaseAsset)
resp, err := s.client.Do(req, asset)
if err != nil {
return nil, resp, nil
}
return asset, resp, err
}
// EditReleaseAsset edits a repository release asset.
//
// GitHub API docs : http://developer.github.com/v3/repos/releases/#edit-a-release-asset
func (s *RepositoriesService) EditReleaseAsset(owner, repo string, id int, release *ReleaseAsset) (*ReleaseAsset, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
req, err := s.client.NewRequest("PATCH", u, release)
if err != nil {
return nil, nil, err
}
req.Header.Add("Accept", mimeReleasePreview)
asset := new(ReleaseAsset)
resp, err := s.client.Do(req, asset)
if err != nil {
return nil, resp, err
}
return asset, resp, err
}
// DeleteReleaseAsset delete a single release asset from a repository.
//
// GitHub API docs : http://developer.github.com/v3/repos/releases/#delete-a-release-asset
func (s *RepositoriesService) DeleteReleaseAsset(owner, repo string, id int) (*Response, error) {
u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", mimeReleasePreview)
return s.client.Do(req, nil)
}
// UploadReleaseAsset creates an asset by uploading a file into a release repository.
//
// 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) {
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)
if err != nil {
return nil, nil, err
}
req.Header.Add("Accept", mimeReleasePreview)
asset := new(ReleaseAsset)
resp, err := s.client.Do(req, asset)
if err != nil {
return nil, resp, err
}
return asset, resp, err
}

+ 119
- 10
github/repos_releases_test.go View File

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"reflect" "reflect"
"strings"
"testing" "testing"
) )
@ -17,13 +18,13 @@ func TestRepositoriesService_ListReleases(t *testing.T) {
setup() setup()
defer teardown() defer teardown()
mux.HandleFunc("/repos/o/u/releases", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET") testMethod(t, r, "GET")
testHeader(t, r, "Accept", mimeReleasePreview) testHeader(t, r, "Accept", mimeReleasePreview)
fmt.Fprint(w, `[{"id":1}]`) fmt.Fprint(w, `[{"id":1}]`)
}) })
releases, _, err := client.Repositories.ListReleases("o", "u")
releases, _, err := client.Repositories.ListReleases("o", "r")
if err != nil { if err != nil {
t.Errorf("Repositories.ListReleases returned error: %v", err) t.Errorf("Repositories.ListReleases returned error: %v", err)
} }
@ -37,13 +38,13 @@ func TestRepositoriesService_GetRelease(t *testing.T) {
setup() setup()
defer teardown() defer teardown()
mux.HandleFunc("/repos/o/u/releases/1", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET") testMethod(t, r, "GET")
testHeader(t, r, "Accept", mimeReleasePreview) testHeader(t, r, "Accept", mimeReleasePreview)
fmt.Fprint(w, `{"id":1}`) fmt.Fprint(w, `{"id":1}`)
}) })
release, resp, err := client.Repositories.GetRelease("o", "u", 1)
release, resp, err := client.Repositories.GetRelease("o", "r", 1)
if err != nil { if err != nil {
t.Errorf("Repositories.GetRelease returned error: %v\n%v", err, resp.Body) t.Errorf("Repositories.GetRelease returned error: %v\n%v", err, resp.Body)
} }
@ -60,7 +61,7 @@ func TestRepositoriesService_CreateRelease(t *testing.T) {
input := &RepositoryRelease{Name: String("v1.0")} input := &RepositoryRelease{Name: String("v1.0")}
mux.HandleFunc("/repos/o/u/releases", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) {
v := new(RepositoryRelease) v := new(RepositoryRelease)
json.NewDecoder(r.Body).Decode(v) json.NewDecoder(r.Body).Decode(v)
@ -72,7 +73,7 @@ func TestRepositoriesService_CreateRelease(t *testing.T) {
fmt.Fprint(w, `{"id":1}`) fmt.Fprint(w, `{"id":1}`)
}) })
release, _, err := client.Repositories.CreateRelease("o", "u", input)
release, _, err := client.Repositories.CreateRelease("o", "r", input)
if err != nil { if err != nil {
t.Errorf("Repositories.CreateRelease returned error: %v", err) t.Errorf("Repositories.CreateRelease returned error: %v", err)
} }
@ -89,7 +90,7 @@ func TestRepositoriesService_EditRelease(t *testing.T) {
input := &RepositoryRelease{Name: String("n")} input := &RepositoryRelease{Name: String("n")}
mux.HandleFunc("/repos/o/u/releases/1", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) {
v := new(RepositoryRelease) v := new(RepositoryRelease)
json.NewDecoder(r.Body).Decode(v) json.NewDecoder(r.Body).Decode(v)
@ -101,7 +102,7 @@ func TestRepositoriesService_EditRelease(t *testing.T) {
fmt.Fprint(w, `{"id":1}`) fmt.Fprint(w, `{"id":1}`)
}) })
release, _, err := client.Repositories.EditRelease("o", "u", 1, input)
release, _, err := client.Repositories.EditRelease("o", "r", 1, input)
if err != nil { if err != nil {
t.Errorf("Repositories.EditRelease returned error: %v", err) t.Errorf("Repositories.EditRelease returned error: %v", err)
} }
@ -115,13 +116,121 @@ func TestRepositoriesService_DeleteRelease(t *testing.T) {
setup() setup()
defer teardown() defer teardown()
mux.HandleFunc("/repos/o/u/releases/1", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE") testMethod(t, r, "DELETE")
testHeader(t, r, "Accept", mimeReleasePreview) testHeader(t, r, "Accept", mimeReleasePreview)
}) })
_, err := client.Repositories.DeleteRelease("o", "u", 1)
_, err := client.Repositories.DeleteRelease("o", "r", 1)
if err != nil { if err != nil {
t.Errorf("Repositories.DeleteRelease returned error: %v", err) t.Errorf("Repositories.DeleteRelease returned error: %v", err)
} }
} }
func TestRepositoriesService_ListReleaseAssets(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mimeReleasePreview)
fmt.Fprint(w, `[{"id":1}]`)
})
assets, _, err := client.Repositories.ListReleaseAssets("o", "r", 1)
if err != nil {
t.Errorf("Repositories.ListReleaseAssets returned error: %v", err)
}
want := []ReleaseAsset{{ID: Int(1)}}
if !reflect.DeepEqual(assets, want) {
t.Errorf("Repositories.ListReleaseAssets returned %+v, want %+v", assets, want)
}
}
func TestRepositoriesService_GetReleaseAsset(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mimeReleasePreview)
fmt.Fprint(w, `{"id":1}`)
})
asset, _, err := client.Repositories.GetReleaseAsset("o", "r", 1)
if err != nil {
t.Errorf("Repositories.GetReleaseAsset returned error: %v", err)
}
want := &ReleaseAsset{ID: Int(1)}
if !reflect.DeepEqual(asset, want) {
t.Errorf("Repositories.GetReleaseAsset returned %+v, want %+v", asset, want)
}
}
func TestRepositoriesService_EditReleaseAsset(t *testing.T) {
setup()
defer teardown()
input := &ReleaseAsset{Name: String("n")}
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
v := new(ReleaseAsset)
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "PATCH")
testHeader(t, r, "Accept", mimeReleasePreview)
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
fmt.Fprint(w, `{"id":1}`)
})
asset, _, err := client.Repositories.EditReleaseAsset("o", "r", 1, input)
if err != nil {
t.Errorf("Repositories.EditReleaseAsset returned error: %v", err)
}
want := &ReleaseAsset{ID: Int(1)}
if !reflect.DeepEqual(asset, want) {
t.Errorf("Repositories.EditReleaseAsset returned = %+v, want %+v", asset, want)
}
}
func TestRepositoriesService_DeleteReleaseAsset(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testHeader(t, r, "Accept", mimeReleasePreview)
})
_, err := client.Repositories.DeleteReleaseAsset("o", "r", 1)
if err != nil {
t.Errorf("Repositories.DeleteReleaseAsset returned error: %v", err)
}
}
func TestRepositoriesService_UploadReleaseAsset(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mimeReleasePreview)
testHeader(t, r, "Content-Type", "application/zip")
testFormValues(t, r, values{"name": "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")
if err != nil {
t.Errorf("Repositories.UploadReleaseAssert returned error: %v", err)
}
want := &ReleaseAsset{ID: Int(1)}
if !reflect.DeepEqual(asset, want) {
t.Errorf("Repositories.UploadReleaseAssert returned %+v, want %+v", asset, want)
}
}

+ 2
- 0
github/strings_test.go View File

@ -120,6 +120,8 @@ func TestString(t *testing.T) {
{WebHookAuthor{Name: String("n")}, `github.WebHookAuthor{Name:"n"}`}, {WebHookAuthor{Name: String("n")}, `github.WebHookAuthor{Name:"n"}`},
{WebHookCommit{ID: String("1")}, `github.WebHookCommit{ID:"1"}`}, {WebHookCommit{ID: String("1")}, `github.WebHookCommit{ID:"1"}`},
{WebHookPayload{Ref: String("r")}, `github.WebHookPayload{Ref:"r"}`}, {WebHookPayload{Ref: String("r")}, `github.WebHookPayload{Ref:"r"}`},
{RepositoryRelease{ID: Int(1)}, `github.RepositoryRelease{ID:1}`},
{ReleaseAsset{ID: Int(1)},`github.ReleaseAsset{ID:1}`},
} }
for i, tt := range tests { for i, tt := range tests {


Loading…
Cancel
Save