// 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) Note that when using an authenticated Client, all calls made by the client will include the specified OAuth token. Therefore, authenticated clients should almost never be shared between different users. 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 } // ListOptions specifies the optional parameters to various List methods that // support pagination. type ListOptions struct { // For paginated result sets, page of results to retrieve. Page int } // 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. 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. func CheckResponse(r *http.Response) error { if c := r.StatusCode; 200 <= c && c <= 299 { return nil } errorResponse := &ErrorResponse{Response: r} data, err := ioutil.ReadAll(r.Body) if err == nil && data != nil { json.Unmarshal(data, errorResponse) } return errorResponse } // 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 }