update go-github dependency

closes #42
This commit is contained in:
Chris Hendrix
2017-05-23 17:21:04 -07:00
parent ebe605054f
commit 28d6890c8c
81 changed files with 12126 additions and 1633 deletions

View File

@@ -3,10 +3,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run gen-accessors.go
package github
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
@@ -24,12 +27,7 @@ import (
)
const (
// StatusUnprocessableEntity is the status code returned when sending a request with invalid fields.
StatusUnprocessableEntity = 422
)
const (
libraryVersion = "2"
libraryVersion = "7"
defaultBaseURL = "https://api.github.com/"
uploadBaseURL = "https://uploads.github.com/"
userAgent = "go-github/" + libraryVersion
@@ -42,6 +40,8 @@ const (
mediaTypeV3 = "application/vnd.github.v3+json"
defaultMediaType = "application/octet-stream"
mediaTypeV3SHA = "application/vnd.github.v3.sha"
mediaTypeV3Diff = "application/vnd.github.v3.diff"
mediaTypeV3Patch = "application/vnd.github.v3.patch"
mediaTypeOrgPermissionRepo = "application/vnd.github.v3.repository+json"
// Media Type values to access preview APIs
@@ -68,33 +68,44 @@ const (
mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview"
// https://developer.github.com/changes/2016-04-01-squash-api-preview/
// https://developer.github.com/changes/2016-09-26-pull-request-merge-api-update/
mediaTypeSquashPreview = "application/vnd.github.polaris-preview+json"
// https://developer.github.com/changes/2016-04-04-git-signing-api-preview/
mediaTypeGitSigningPreview = "application/vnd.github.cryptographer-preview+json"
// https://developer.github.com/changes/2016-5-27-multiple-assignees/
mediaTypeMultipleAssigneesPreview = "application/vnd.github.cerberus-preview+json"
// https://developer.github.com/changes/2016-05-23-timeline-preview-api/
mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json"
// https://developer.github.com/changes/2016-06-14-repository-invitations/
mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-preview+json"
// https://developer.github.com/changes/2016-04-21-oauth-authorizations-grants-api-preview/
mediaTypeOAuthGrantAuthorizationsPreview = "application/vnd.github.damage-preview+json"
// https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/
mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json"
// https://developer.github.com/changes/2016-09-14-projects-api/
mediaTypeProjectsPreview = "application/vnd.github.inertia-preview+json"
// https://developer.github.com/changes/2016-09-14-Integrations-Early-Access/
mediaTypeIntegrationPreview = "application/vnd.github.machine-man-preview+json"
// https://developer.github.com/changes/2017-01-05-commit-search-api/
mediaTypeCommitSearchPreview = "application/vnd.github.cloak-preview+json"
// https://developer.github.com/changes/2017-02-28-user-blocking-apis-and-webhook/
mediaTypeBlockUsersPreview = "application/vnd.github.giant-sentry-fist-preview+json"
// https://developer.github.com/changes/2017-02-09-community-health/
mediaTypeRepositoryCommunityHealthMetricsPreview = "application/vnd.github.black-panther-preview+json"
)
// A Client manages communication with the GitHub API.
type Client struct {
// HTTP client used to communicate with the API.
client *http.Client
// clientMu protects the client during calls that modify the CheckRedirect func.
clientMu sync.Mutex
clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func.
client *http.Client // HTTP client used to communicate with the API.
// Base URL for API requests. Defaults to the public GitHub API, but can be
// set to a domain endpoint to use with GitHub Enterprise. BaseURL should
// Base URL for API requests. Defaults to the public GitHub API, but can be
// set to a domain endpoint to use with GitHub Enterprise. BaseURL should
// always be specified with a trailing slash.
BaseURL *url.URL
@@ -106,18 +117,20 @@ type Client struct {
rateMu sync.Mutex
rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls.
mostRecent rateLimitCategory
common service // Reuse a single struct instead of allocating one for each service on the heap.
// Services used for talking to different parts of the GitHub API.
Activity *ActivityService
Admin *AdminService
Authorizations *AuthorizationsService
Gists *GistsService
Git *GitService
Gitignores *GitignoresService
Integrations *IntegrationsService
Issues *IssuesService
Organizations *OrganizationsService
Projects *ProjectsService
PullRequests *PullRequestsService
Repositories *RepositoriesService
Search *SearchService
@@ -146,7 +159,23 @@ type UploadOptions struct {
Name string `url:"name,omitempty"`
}
// addOptions adds the parameters in opt as URL query parameters to s. opt
// RawType represents type of raw format of a request instead of JSON.
type RawType uint8
const (
// Diff format.
Diff RawType = 1 + iota
// Patch format.
Patch
)
// RawOptions specifies parameters when user wants to get raw format of
// a response instead of JSON.
type RawOptions struct {
Type RawType
}
// addOptions adds the parameters in opt as URL query parameters to s. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(s string, opt interface{}) (string, error) {
v := reflect.ValueOf(opt)
@@ -168,8 +197,8 @@ func addOptions(s string, opt interface{}) (string, error) {
return u.String(), nil
}
// NewClient returns a new GitHub API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
// NewClient returns a new GitHub API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
// authentication, provide an http.Client that will perform the authentication
// for you (such as that provided by the golang.org/x/oauth2 library).
func NewClient(httpClient *http.Client) *Client {
@@ -182,14 +211,17 @@ func NewClient(httpClient *http.Client) *Client {
c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL}
c.common.client = c
c.Activity = (*ActivityService)(&c.common)
c.Admin = (*AdminService)(&c.common)
c.Authorizations = (*AuthorizationsService)(&c.common)
c.Gists = (*GistsService)(&c.common)
c.Git = (*GitService)(&c.common)
c.Gitignores = (*GitignoresService)(&c.common)
c.Integrations = (*IntegrationsService)(&c.common)
c.Issues = (*IssuesService)(&c.common)
c.Licenses = (*LicensesService)(&c.common)
c.Migrations = (*MigrationService)(&c.common)
c.Organizations = (*OrganizationsService)(&c.common)
c.Projects = (*ProjectsService)(&c.common)
c.PullRequests = (*PullRequestsService)(&c.common)
c.Reactions = (*ReactionsService)(&c.common)
c.Repositories = (*RepositoriesService)(&c.common)
@@ -200,7 +232,7 @@ func NewClient(httpClient *http.Client) *Client {
// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If
// Relative URLs should always be specified without a preceding slash. If
// specified, the value pointed to by body is JSON encoded and included as the
// request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
@@ -225,9 +257,12 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
return nil, err
}
req.Header.Add("Accept", mediaTypeV3)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
req.Header.Set("Accept", mediaTypeV3)
if c.UserAgent != "" {
req.Header.Add("User-Agent", c.UserAgent)
req.Header.Set("User-Agent", c.UserAgent)
}
return req, nil
}
@@ -248,23 +283,23 @@ func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, m
}
req.ContentLength = size
if len(mediaType) == 0 {
if mediaType == "" {
mediaType = defaultMediaType
}
req.Header.Add("Content-Type", mediaType)
req.Header.Add("Accept", mediaTypeV3)
req.Header.Add("User-Agent", c.UserAgent)
req.Header.Set("Content-Type", mediaType)
req.Header.Set("Accept", mediaTypeV3)
req.Header.Set("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
// pagination links.
type Response struct {
*http.Response
// These fields provide the page values for paginating through a set of
// results. Any or all of these may be set to the zero value for
// results. Any or all of these may be set to the zero value for
// responses that are not part of a paginated set, or for which there
// are no additional pages.
@@ -345,35 +380,46 @@ func parseRate(r *http.Response) Rate {
return rate
}
// Rate specifies the current rate limit for the client as determined by the
// most recent API call. If the client is used in a multi-user application,
// this rate may not always be up-to-date.
//
// Deprecated: Use the Response.Rate returned from most recent API call instead.
// Call RateLimits() to check the current rate.
func (c *Client) Rate() Rate {
c.rateMu.Lock()
rate := c.rateLimits[c.mostRecent]
c.rateMu.Unlock()
return rate
}
// Do sends an API request and returns the API response. The API response is
// Do sends an API request and returns the API response. The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred. If v implements the io.Writer
// error if an API error has occurred. If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// first decode it. If rate limit is exceeded and reset time is in the future,
// first decode it. If rate limit is exceeded and reset time is in the future,
// Do returns *RateLimitError immediately without making a network API call.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
//
// The provided ctx must be non-nil. If it is canceled or times out,
// ctx.Err() will be returned.
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
ctx, req = withContext(ctx, req)
rateLimitCategory := category(req.URL.Path)
// If we've hit rate limit, don't make further requests before Reset time.
if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil {
return nil, err
return &Response{
Response: err.Response,
Rate: err.Rate,
}, err
}
resp, err := c.client.Do(req)
if err != nil {
// If we got an error, and the context has been canceled,
// the context's error is probably more useful.
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
// If the error type is *url.Error, sanitize its URL before returning.
if e, ok := err.(*url.Error); ok {
if url, err := url.Parse(e.URL); err == nil {
e.URL = sanitizeURL(url).String()
return nil, e
}
}
return nil, err
}
@@ -387,7 +433,6 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
c.rateMu.Lock()
c.rateLimits[rateLimitCategory] = response.Rate
c.mostRecent = rateLimitCategory
c.rateMu.Unlock()
err = CheckResponse(resp)
@@ -413,9 +458,9 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
// checkRateLimitBeforeDo does not make any network calls, but uses existing knowledge from
// current client state in order to quickly check if *RateLimitError can be immediately returned
// from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unneccessarily.
// from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unnecessarily.
// Otherwise it returns nil, and Client.Do should proceed normally.
func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rateLimitCategory) error {
func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rateLimitCategory) *RateLimitError {
c.rateMu.Lock()
rate := c.rateLimits[rateLimitCategory]
c.rateMu.Unlock()
@@ -441,7 +486,7 @@ func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rat
/*
An ErrorResponse reports one or more errors caused by an API request.
GitHub API docs: http://developer.github.com/v3/#client-errors
GitHub API docs: https://developer.github.com/v3/#client-errors
*/
type ErrorResponse struct {
Response *http.Response // HTTP response that caused this error
@@ -467,7 +512,7 @@ func (r *ErrorResponse) Error() string {
}
// TwoFactorAuthError occurs when using HTTP Basic Authentication for a user
// that has two-factor authentication enabled. The request can be reattempted
// that has two-factor authentication enabled. The request can be reattempted
// by providing a one-time password in the request.
type TwoFactorAuthError ErrorResponse
@@ -487,8 +532,38 @@ func (r *RateLimitError) Error() string {
r.Response.StatusCode, r.Message, r.Rate.Reset.Time.Sub(time.Now()))
}
// AcceptedError occurs when GitHub returns 202 Accepted response with an
// empty body, which means a job was scheduled on the GitHub side to process
// the information needed and cache it.
// Technically, 202 Accepted is not a real error, it's just used to
// indicate that results are not ready yet, but should be available soon.
// The request can be repeated after some time.
type AcceptedError struct{}
func (*AcceptedError) Error() string {
return "job scheduled on GitHub side; try again later"
}
// AbuseRateLimitError occurs when GitHub returns 403 Forbidden response with the
// "documentation_url" field value equal to "https://developer.github.com/v3#abuse-rate-limits".
type AbuseRateLimitError struct {
Response *http.Response // HTTP response that caused this error
Message string `json:"message"` // error message
// RetryAfter is provided with some abuse rate limit errors. If present,
// it is the amount of time that the client should wait before retrying.
// Otherwise, the client should try again later (after an unspecified amount of time).
RetryAfter *time.Duration
}
func (r *AbuseRateLimitError) Error() string {
return fmt.Sprintf("%v %v: %d %v",
r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
r.Response.StatusCode, r.Message)
}
// sanitizeURL redacts the client_secret parameter from the URL which may be
// exposed to the user, specifically in the ErrorResponse error message.
// exposed to the user.
func sanitizeURL(uri *url.URL) *url.URL {
if uri == nil {
return nil
@@ -517,7 +592,7 @@ These are the possible validation error codes:
some resources return this (e.g. github.User.CreateKey()), additional
information is set in the Message field of the Error
GitHub API docs: http://developer.github.com/v3/#client-errors
GitHub API docs: https://developer.github.com/v3/#client-errors
*/
type Error struct {
Resource string `json:"resource"` // resource on which the error occurred
@@ -532,14 +607,19 @@ func (e *Error) Error() string {
}
// CheckResponse checks the API response for errors, and returns them if
// present. A response is considered an error if it has a status code outside
// the 200 range. API error responses are expected to have either no response
// body, or a JSON response body that maps to ErrorResponse. Any other
// present. A response is considered an error if it has a status code outside
// the 200 range or equal to 202 Accepted.
// API error responses are expected to have either no response
// body, or a JSON response body that maps to ErrorResponse. Any other
// response body will be silently ignored.
//
// The error type will be *RateLimitError for rate limit exceeded errors,
// *AcceptedError for 202 Accepted status codes,
// and *TwoFactorAuthError for two-factor authentication errors.
func CheckResponse(r *http.Response) error {
if r.StatusCode == http.StatusAccepted {
return &AcceptedError{}
}
if c := r.StatusCode; 200 <= c && c <= 299 {
return nil
}
@@ -557,6 +637,20 @@ func CheckResponse(r *http.Response) error {
Response: errorResponse.Response,
Message: errorResponse.Message,
}
case r.StatusCode == http.StatusForbidden && errorResponse.DocumentationURL == "https://developer.github.com/v3#abuse-rate-limits":
abuseRateLimitError := &AbuseRateLimitError{
Response: errorResponse.Response,
Message: errorResponse.Message,
}
if v := r.Header["Retry-After"]; len(v) > 0 {
// According to GitHub support, the "Retry-After" header value will be
// an integer which represents the number of seconds that one should
// wait before resuming making requests.
retryAfterSeconds, _ := strconv.ParseInt(v[0], 10, 64) // Error handling is noop.
retryAfter := time.Duration(retryAfterSeconds) * time.Second
abuseRateLimitError.RetryAfter = &retryAfter
}
return abuseRateLimitError
default:
return errorResponse
}
@@ -565,15 +659,15 @@ func CheckResponse(r *http.Response) error {
// parseBoolResponse determines the boolean result from a GitHub API response.
// Several GitHub API methods return boolean responses indicated by the HTTP
// status code in the response (true indicated by a 204, false indicated by a
// 404). This helper function will determine that result and hide the 404
// error if present. Any other error will be returned through as-is.
// 404). This helper function will determine that result and hide the 404
// error if present. Any other error will be returned through as-is.
func parseBoolResponse(err error) (bool, error) {
if err == nil {
return true, nil
}
if err, ok := err.(*ErrorResponse); ok && err.Response.StatusCode == http.StatusNotFound {
// Simply false. In this one case, we do not pass the error through.
// Simply false. In this one case, we do not pass the error through.
return false, nil
}
@@ -599,16 +693,16 @@ func (r Rate) String() string {
// RateLimits represents the rate limits for the current client.
type RateLimits struct {
// The rate limit for non-search API requests. Unauthenticated
// requests are limited to 60 per hour. Authenticated requests are
// The rate limit for non-search API requests. Unauthenticated
// requests are limited to 60 per hour. Authenticated requests are
// limited to 5,000 per hour.
//
// GitHub API docs: https://developer.github.com/v3/#rate-limiting
Core *Rate `json:"core"`
// The rate limit for search API requests. Unauthenticated requests
// are limited to 5 requests per minutes. Authenticated requests are
// limited to 20 per minute.
// The rate limit for search API requests. Unauthenticated requests
// are limited to 10 requests per minutes. Authenticated requests are
// limited to 30 per minute.
//
// GitHub API docs: https://developer.github.com/v3/search/#rate-limit
Search *Rate `json:"search"`
@@ -637,18 +731,8 @@ func category(path string) rateLimitCategory {
}
}
// Deprecated: RateLimit is deprecated, use RateLimits instead.
func (c *Client) RateLimit() (*Rate, *Response, error) {
limits, resp, err := c.RateLimits()
if limits == nil {
return nil, nil, err
}
return limits.Core, resp, err
}
// RateLimits returns the rate limits for the current client.
func (c *Client) RateLimits() (*RateLimits, *Response, error) {
func (c *Client) RateLimits(ctx context.Context) (*RateLimits, *Response, error) {
req, err := c.NewRequest("GET", "rate_limit", nil)
if err != nil {
return nil, nil, err
@@ -657,7 +741,7 @@ func (c *Client) RateLimits() (*RateLimits, *Response, error) {
response := new(struct {
Resources *RateLimits `json:"resources"`
})
resp, err := c.Do(req, response)
resp, err := c.Do(ctx, req, response)
if err != nil {
return nil, nil, err
}
@@ -673,7 +757,7 @@ func (c *Client) RateLimits() (*RateLimits, *Response, error) {
c.rateMu.Unlock()
}
return response.Resources, resp, err
return response.Resources, resp, nil
}
/*
@@ -689,7 +773,7 @@ that need to use a higher rate limit associated with your OAuth application.
This will append the querystring params client_id=xxx&client_secret=yyy to all
requests.
See http://developer.github.com/v3/#unauthenticated-rate-limited-requests for
See https://developer.github.com/v3/#unauthenticated-rate-limited-requests for
more information.
*/
type UnauthenticatedRateLimitedTransport struct {
@@ -743,7 +827,7 @@ func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper {
}
// BasicAuthTransport is an http.RoundTripper that authenticates all requests
// using HTTP Basic Authentication with the provided username and password. It
// using HTTP Basic Authentication with the provided username and password. It
// additionally supports users who have two-factor authentication enabled on
// their GitHub account.
type BasicAuthTransport struct {
@@ -761,7 +845,7 @@ func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error
req = cloneRequest(req) // per RoundTrip contract
req.SetBasicAuth(t.Username, t.Password)
if t.OTP != "" {
req.Header.Add(headerOTP, t.OTP)
req.Header.Set(headerOTP, t.OTP)
}
return t.transport().RoundTrip(req)
}