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.
 
 
 

244 lines
7.0 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)
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
}