pip compatible server to serve Python packages out of GitHub
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

236 lines
6.6 KiB

// 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
}