// Copyright 2013 Google. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd /* Package github provides a client for using the GitHub API. Access different parts of the GitHub API using the various services on a GitHub Client: client := github.NewClient(nil) // list all organizations for user "willnorris" orgs, err := client.Organizations.List("willnorris", nil) Set optional parameters for an API method by passing an Options object. // list recently updated repositories for org "github" opt := &github.RepositoryListByOrgOptions{Sort: "updated"} repos, err := client.Repositories.ListByOrg("github", opt) Make authenticated API calls by constructing a GitHub client using an OAuth capable http.Client: import "code.google.com/p/goauth2/oauth" // simple OAuth transport if you already have an access token; // see goauth2 library for full usage t := &oauth.Transport{ Config: &oauth.Config{}, Token: &oauth.Token{AccessToken: "..."} } client := github.NewClient(t.Client()) // list all repositories for the authenticated user repos, err := client.Repositories.List(nil) The full GitHub API is documented at http://developer.github.com/v3/. */ package github import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" ) const ( libraryVersion = "0.1" defaultBaseURL = "https://api.github.com/" userAgent = "go-github/" + libraryVersion ) // A Client manages communication with the GitHub API. type Client struct { // HTTP client used to communicate with the API. client *http.Client // 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 // User agent used when communicating with the GitHub API. UserAgent string // Services used for talking to different parts of the API Organizations *OrganizationsService Repositories *RepositoriesService Users *UsersService } // 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 can handle that. func NewClient(httpClient *http.Client) *Client { if httpClient == nil { httpClient = http.DefaultClient } baseURL, _ := url.Parse(defaultBaseURL) c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent} c.Organizations = &OrganizationsService{client: c} c.Repositories = &RepositoriesService{client: c} c.Users = &UsersService{client: c} return c } // NewRequest creates an API request. A relative URL can be provided in urls, // in which case it is resolved relative to the BaseURL of the Client. // 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, urls string, body interface{}) (*http.Request, error) { rel, err := url.Parse(urls) if err != nil { return nil, err } url_ := c.BaseURL.ResolveReference(rel) buf := new(bytes.Buffer) if body != nil { err := json.NewEncoder(buf).Encode(body) if err != nil { return nil, err } } req, err := http.NewRequest(method, url_.String(), buf) if err != nil { return nil, err } req.Header.Add("User-Agent", c.UserAgent) return req, nil } // Do sends an API request and returns the API response. The API response is // decoded and stored in the value pointed to by v, or returned as an error if // an API error has occurred. func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) { resp, err := c.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() err = CheckResponse(resp) if err != nil { return resp, err } if v != nil { err = json.NewDecoder(resp.Body).Decode(v) } return resp, err } /* An ErrorResponse reports one or more errors caused by an API request. GitHub API docs: http://developer.github.com/v3/#client-errors */ type ErrorResponse struct { Response *http.Response // HTTP response that caused this error Message string `json:message` // error message Errors []Error `json:errors` // more detail on individual errors } func (r *ErrorResponse) Error() string { return fmt.Sprintf("%v %v: %d %v", r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message) } /* An Error reports more details on an individual error in an ErrorResponse. These are the possible validation error codes: missing: resource does not exist missing_field: a required field on a resource has not been set invalid: the formatting of a field is invalid already_exists: another resource has the same valid as this field GitHub API docs: http://developer.github.com/v3/#client-errors */ type Error struct { Resource string `json:resource` // resource on which the error occurred Field string `json:field` // field on which the error occurred Code string `json:code` // validation error code } func (e *Error) Error() string { return fmt.Sprintf("%v error caused by %v field on %v resource", e.Code, e.Field, e.Resource) } // CheckResponse checks the API response for errors, and returns them if // present. func CheckResponse(r *http.Response) error { if c := r.StatusCode; 200 <= c && c <= 299 { return nil } data, err := ioutil.ReadAll(r.Body) if err == nil { errorResponse := new(ErrorResponse) err = json.Unmarshal(data, errorResponse) if err == nil && errorResponse != nil { errorResponse.Response = r return errorResponse } } return fmt.Errorf("github: got HTTP response code %d and error reading body: %v", r.StatusCode, err) } // API response wrapper to a rate limit request. type rateResponse struct { Rate *Rate `json:rate` } // Rate represents the rate limit for the current client. Unauthenticated // requests are limited to 60 per hour. Authenticated requests are limited to // 5,000 per hour. type Rate struct { // The number of requests per hour the client is currently limited to. Limit int `json:limit` // The number of remaining requests the client can make this hour. Remaining int `json:remaining` } // RateLimit returns the rate limit for the current client. func (c *Client) RateLimit() (*Rate, error) { req, err := c.NewRequest("GET", "rate_limit", nil) if err != nil { return nil, err } response := new(rateResponse) _, err = c.Do(req, response) return response.Rate, err }