Git Product home page Git Product logo

go-github's Introduction

go-github

go-github release (latest SemVer) GoDoc Test Status Test Coverage Discuss at go-github@googlegroups.com CII Best Practices

go-github is a Go client library for accessing the GitHub API v3.

Currently, go-github requires Go version 1.13 or greater. go-github tracks Go's version support policy. We do our best not to break older versions of Go if we don't have to, but due to tooling constraints, we don't always test older versions.

If you're interested in using the GraphQL API v4, the recommended library is shurcooL/githubv4.

Installation

go-github is compatible with modern Go releases in module mode, with Go installed:

go get github.com/google/go-github/v60

will resolve and add the package to the current development module, along with its dependencies.

Alternatively the same can be achieved if you use import in a package:

import "github.com/google/go-github/v60/github"

and run go get without parameters.

Finally, to use the top-of-trunk version of this repo, use the following command:

go get github.com/google/go-github/v60@master

Usage

import "github.com/google/go-github/v60/github"	// with go modules enabled (GO111MODULE=on or outside GOPATH)
import "github.com/google/go-github/github" // with go modules disabled

Construct a new GitHub client, then use the various services on the client to access different parts of the GitHub API. For example:

client := github.NewClient(nil)

// list all organizations for user "willnorris"
orgs, _, err := client.Organizations.List(context.Background(), "willnorris", nil)

Some API methods have optional parameters that can be passed. For example:

client := github.NewClient(nil)

// list public repositories for org "github"
opt := &github.RepositoryListByOrgOptions{Type: "public"}
repos, _, err := client.Repositories.ListByOrg(context.Background(), "github", opt)

The services of a client divide the API into logical chunks and correspond to the structure of the GitHub API documentation at https://docs.github.com/en/rest .

NOTE: Using the context package, one can easily pass cancelation signals and deadlines to various services of the client for handling a request. In case there is no context available, then context.Background() can be used as a starting point.

For more sample code snippets, head over to the example directory.

Authentication

Use the WithAuthToken method to configure your client to authenticate using an OAuth token (for example, a personal access token). This is what is needed for a majority of use cases aside from GitHub Apps.

client := github.NewClient(nil).WithAuthToken("... your access token ...")

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.

For API methods that require HTTP Basic Authentication, use the BasicAuthTransport.

As a GitHub App

GitHub Apps authentication can be provided by the ghinstallation package.

Note: Most endpoints (ex. GET /rate_limit) require access token authentication while a few others (ex. GET /app/hook/deliveries) require JWT authentication.

import (
	"net/http"

	"github.com/bradleyfalzon/ghinstallation/v2"
	"github.com/google/go-github/v60/github"
)

func main() {
	// Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99.
	itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem")

	// Or for endpoints that require JWT authentication
	// itr, err := ghinstallation.NewAppsTransportKeyFromFile(http.DefaultTransport, 1, "2016-10-19.private-key.pem")

	if err != nil {
		// Handle error.
	}

	// Use installation transport with client.
	client := github.NewClient(&http.Client{Transport: itr})

	// Use client...
}

Note: In order to interact with certain APIs, for example writing a file to a repo, one must generate an installation token using the installation ID of the GitHub app and authenticate with the OAuth method mentioned above. See the examples.

Rate Limiting

GitHub imposes a rate limit on all API clients. Unauthenticated clients are limited to 60 requests per hour, while authenticated clients can make up to 5,000 requests per hour. The Search API has a custom rate limit. Unauthenticated clients are limited to 10 requests per minute, while authenticated clients can make up to 30 requests per minute. To receive the higher rate limit when making calls that are not issued on behalf of a user, use UnauthenticatedRateLimitedTransport.

The returned Response.Rate value contains the rate limit information from the most recent API call. If a recent enough response isn't available, you can use RateLimits to fetch the most up-to-date rate limit data for the client.

To detect an API rate limit error, you can check if its type is *github.RateLimitError:

repos, _, err := client.Repositories.List(ctx, "", nil)
if _, ok := err.(*github.RateLimitError); ok {
	log.Println("hit rate limit")
}

Learn more about GitHub rate limiting at https://docs.github.com/en/rest/rate-limit .

In addition to these rate limits, GitHub imposes a secondary rate limit on all API clients. This rate limit prevents clients from making too many concurrent requests.

To detect an API secondary rate limit error, you can check if its type is *github.AbuseRateLimitError:

repos, _, err := client.Repositories.List(ctx, "", nil)
if _, ok := err.(*github.AbuseRateLimitError); ok {
	log.Println("hit secondary rate limit")
}

You can use go-github-ratelimit to handle secondary rate limit sleep-and-retry for you.

Learn more about GitHub secondary rate limiting at https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits .

Accepted Status

Some endpoints may return a 202 Accepted status code, meaning that the information required is not yet ready and was scheduled to be gathered on the GitHub side. Methods known to behave like this are documented specifying this behavior.

To detect this condition of error, you can check if its type is *github.AcceptedError:

stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo)
if _, ok := err.(*github.AcceptedError); ok {
	log.Println("scheduled on GitHub side")
}

Conditional Requests

The GitHub API has good support for conditional requests which will help prevent you from burning through your rate limit, as well as help speed up your application. go-github does not handle conditional requests directly, but is instead designed to work with a caching http.Transport. We recommend using https://github.com/gregjones/httpcache for that. For example:

import "github.com/gregjones/httpcache"

	client := github.NewClient(
		httpcache.NewMemoryCacheTransport().Client()
    ).WithAuthToken(os.Getenv("GITHUB_TOKEN"))

Learn more about GitHub conditional requests at https://docs.github.com/en/rest/overview/resources-in-the-rest-api#conditional-requests.

Creating and Updating Resources

All structs for GitHub resources use pointer values for all non-repeated fields. This allows distinguishing between unset fields and those set to a zero-value. Helper functions have been provided to easily create these pointers for string, bool, and int values. For example:

// create a new private repository named "foo"
repo := &github.Repository{
	Name:    github.String("foo"),
	Private: github.Bool(true),
}
client.Repositories.Create(ctx, "", repo)

Users who have worked with protocol buffers should find this pattern familiar.

Pagination

All requests for resource collections (repos, pull requests, issues, etc.) support pagination. Pagination options are described in the github.ListOptions struct and passed to the list methods directly or as an embedded type of a more specific list options struct (for example github.PullRequestListOptions). Pages information is available via the github.Response struct.

client := github.NewClient(nil)

opt := &github.RepositoryListByOrgOptions{
	ListOptions: github.ListOptions{PerPage: 10},
}
// get all pages of results
var allRepos []*github.Repository
for {
	repos, resp, err := client.Repositories.ListByOrg(ctx, "github", opt)
	if err != nil {
		return err
	}
	allRepos = append(allRepos, repos...)
	if resp.NextPage == 0 {
		break
	}
	opt.Page = resp.NextPage
}

Webhooks

go-github provides structs for almost all GitHub webhook events as well as functions to validate them and unmarshal JSON payloads from http.Request structs.

func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	payload, err := github.ValidatePayload(r, s.webhookSecretKey)
	if err != nil { ... }
	event, err := github.ParseWebHook(github.WebHookType(r), payload)
	if err != nil { ... }
	switch event := event.(type) {
	case *github.CommitCommentEvent:
		processCommitCommentEvent(event)
	case *github.CreateEvent:
		processCreateEvent(event)
	...
	}
}

Furthermore, there are libraries like cbrgm/githubevents that build upon the example above and provide functions to subscribe callbacks to specific events.

For complete usage of go-github, see the full package docs.

Testing code that uses go-github

The repo migueleliasweb/go-github-mock provides a way to mock responses. Check the repo for more details.

Integration Tests

You can run integration tests from the test directory. See the integration tests README.

Contributing

I would like to cover the entire GitHub API and contributions are of course always welcome. The calling pattern is pretty well established, so adding new methods is relatively straightforward. See CONTRIBUTING.md for details.

Versioning

In general, go-github follows semver as closely as we can for tagging releases of the package. For self-contained libraries, the application of semantic versioning is relatively straightforward and generally understood. But because go-github is a client library for the GitHub API, which itself changes behavior, and because we are typically pretty aggressive about implementing preview features of the GitHub API, we've adopted the following versioning policy:

  • We increment the major version with any incompatible change to non-preview functionality, including changes to the exported Go API surface or behavior of the API.
  • We increment the minor version with any backwards-compatible changes to functionality, as well as any changes to preview functionality in the GitHub API. GitHub makes no guarantee about the stability of preview functionality, so neither do we consider it a stable part of the go-github API.
  • We increment the patch version with any backwards-compatible bug fixes.

Preview functionality may take the form of entire methods or simply additional data returned from an otherwise non-preview method. Refer to the GitHub API documentation for details on preview functionality.

Calendar Versioning

As of 2022-11-28, GitHub has announced that they are starting to version their v3 API based on "calendar-versioning".

In practice, our goal is to make per-method version overrides (at least in the core library) rare and temporary.

Our understanding of the GitHub docs is that they will be revving the entire API to each new date-based version, even if only a few methods have breaking changes. Other methods will accept the new version with their existing functionality. So when a new date-based version of the GitHub API is released, we (the repo maintainers) plan to:

  • update each method that had breaking changes, overriding their per-method API version header. This may happen in one or multiple commits and PRs, and is all done in the main branch.

  • once all of the methods with breaking changes have been updated, have a final commit that bumps the default API version, and remove all of the per-method overrides. That would now get a major version bump when the next go-github release is made.

Version Compatibility Table

The following table identifies which version of the GitHub API is supported by this (and past) versions of this repo (go-github). Versions prior to 48.2.0 are not listed.

go-github Version GitHub v3 API Version
60.0.0 2022-11-28
59.0.0 2022-11-28
58.0.0 2022-11-28
57.0.0 2022-11-28
56.0.0 2022-11-28
55.0.0 2022-11-28
54.0.0 2022-11-28
53.2.0 2022-11-28
53.1.0 2022-11-28
53.0.0 2022-11-28
52.0.0 2022-11-28
51.0.0 2022-11-28
50.2.0 2022-11-28
50.1.0 2022-11-28
50.0.0 2022-11-28
49.1.0 2022-11-28
49.0.0 2022-11-28
48.2.0 2022-11-28

License

This library is distributed under the BSD-style license found in the LICENSE file.

go-github's People

Contributors

alindeman avatar andygrunwald avatar bradleyfalzon avatar caarlos0 avatar cpanato avatar dependabot[bot] avatar dmitshur avatar fho avatar ganeshkumarsv avatar gmlewis avatar harikesh00 avatar haya14busa avatar joshuabezaleel avatar jporzucek avatar n1lesh avatar nightlark avatar nikpivkin avatar palash25 avatar parkr avatar pzurek avatar ravitezu avatar sagar23sj avatar sahildua2305 avatar shawnps avatar sqs avatar vaibhavsingh97 avatar varadarajana avatar willabides avatar willnorris avatar wlynch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-github's Issues

Make it easier to create commit statuses from pull requests

This just saves some code. The thing is that in the pull request object, there is a field called statuses_url, which can be directly used to create new commit statuses. The current API implements CreateStatus, which internally generates the same URL, the user just has to take care of getting the right ref, which is, however, already supplied in statuses_url. So it could make sense to implement PostStatus or so, which would take the whole URL path, e.g. octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e. So the API could be PostStatus(url string, status *RepoStatus). What do you think?

I will implement this anyway since I need it. Perhaps just let me know what do you think about the method name...

Getting a User object with null fields fails

The github API v3 declares that blank fields are included with a null as the value instead of being omitted.

For a user which did not specify an organisation, blog or bio the Get returns null as the field values.
In go-github this leads to an unmarshalling failure as type string is defined in the struct type User. Unfortunately, null can not be unmarshalled/mapped to string.

A potential solution may be to change the type from string to pointers, allowing nil.
But then this should probably be implemented as a general concept across the library, as the API docs state that generally empty fields are returned as null values - and does not seem to note which fields can be empty/null in the end.

Adding Repository Collaborators API

I am going to start adding the Repository APIs that are currently not implemented. First up is Collaborators.

I'll be making separate issues for these APIs so others can hop in on other Repository APIs if/when they want.

methods return non-nil resources on error

take for example:

repo, _, err := client.Repositories.Get("foo", "bar")

If foo/bar is not a valid repository, I would expect repo to be nil. Currently, it's set to an empty (but non-nil) Repository. I guess I just didn't really think about this when setting things up initially. This should be fixed throughout the library. It won't require changing any method signatures, just some behavior.

GitHub API docs now CC licensed

The GitHub API docs are now CC-BY licensed, so we can safely copy method and parameter descriptions. The LICENSE file should be updated to reflect the license.

go test fails with: cannot use "StatusBadRequest" (type string) as type *testing.T in function argument

On latest code on master

G15491-2:go-github sgaddipati$ g show head --name-only
3a1d485 - (HEAD, origin/master, origin/HEAD, master) Fix PushEvent JSON representation of size attribute (4 weeks ago) <Tobias Schmidt>
github/activity_events.go

I get test failures

G15491-2:go-github sgaddipati$ go test -v ./github
# go-github/github
github/orgs_members_test.go:42: cannot use "StatusBadRequest" (type string) as type *testing.T in function argument
github/orgs_members_test.go:42: not enough arguments in call to testURLParseError
FAIL    go-github/github [build failed]

Track rate limit on github.Client

I've seen a couple of other GitHub libraries do this, and it's almost certainly worth adding here as well. github.Client should add a Rate field which is updated after each request using the rate-related HTTP headers in the response. This can all be done inside the Do() func, so it should be a pretty minimal change.

If the OAuth credentials are being shared between multiple clients then this rate my actually get out of date, so the documentation for the field should also point to the RateLimit() method to retrieve the most up-to-date limit for the client.

Add PullRequest to Issue struct

If an issue has a pull request, a pull request JSON payload is returned. That JSON payload should be parsed into a PullRequest struct and attached to the Issue.

Determine how to handle partial resource representations

( original discussion on sqs/go-github@515d5d7 )

Resources in the GitHub API are often truncated when returned from certain API endpoints. For example, fetching a single repository returns the full representation including properties like source and parent, but fetching a list of repos returns a truncated view. This is even more apparent when comparing the view of a single user to a list of org members. The latter truncates about half of the resource properties; and yet even more properties are included if you make an authenticated request for your own account.

The question then becomes, how should we represent this in the library? The two basic options are:

  1. Define a type for each permutation of a resource and use embedded structs to build up the more defined types. Quinn has a more complete example of this in sqs/go-github@515d5d7, but it would amount to something like:

    type Repository struct {
        // common set of fields that every repository API endpoint returns
        ID          int        `json:"id,omitempty"`
        Owner       *User      `json:"owner,omitempty"`
        Name        string     `json:"name,omitempty"`
        // etc
    }
    
    type DetailedRepository struct {
        // fields returned when getting a user's or org's repositories
        Repository
        FullName     string     `json:"full_name,omitempty"`
        Private      bool       `json:"private"`
        Fork         bool       `json:"fork"`
        // etc
    }
    
    type FullRepository struct {
        // fields returned when getting a single repository
        DetailedRepository
        Parent Repository `json:"parent,omitempty"`
        Source Repository `json:"source,omitempty"`
    }

    Each function would returned the relevant type, based on how much data the GitHub API returns, making it very explicit how much information to expect. This comes at the cost of more types to deal with and to understand the hierarchy of.

  2. Define a single type which includes all possible fields, and document under which circumstances a truncated resource will be returned. This simplifies the library surface and interaction between different functions, at the cost of potential confusion as to why certain fields are missing in different cases.

It's worth pointing out that the source code used to generate the GitHub API documentation uses the former approach, whereas the Objective-C Octokit library appears to use the latter. For what it's worth, the ruby Octokit library punts entirely be returning untyped hashes.

Missing Repository APIs

Might as well make this one large issue and cross things off as we go instead of making 1 issue per API. There are still a few APIs missing for the repository service:

  • Repository
  • Collaborators
  • Comments : @wlynch92
  • Commits: @ktoso
  • Contents
  • Forks
  • Keys: @willnorris
  • Hooks
  • Merging
  • Releases
  • Statistics
  • Statuses

I'll edit this post as the APIs are assigned/implemented. If any one wants to help in implementing any of these APIs feel free to comment saying which you'll be working on.

Support conditional requests

GitHub API endpoints accept conditional requests using ETag and/or If-Modified-Since (http://developer.github.com/v3/#conditional-requests).

This library could support conditional requests by modifying API methods to take a struct parameter containing those fields (or the fields could be added to, e.g., ListOptions, as in https://github.com/sqs/go-github/compare/b01324...User_events__xpr).

Or there could be a more serious refactor where the current API becomes the high-level API and there is a new lower level API that separates the construction of abstract API requests from the HTTP headers and client. You would first create the abstract API request (and each existing API method would correspond to a helper method for creating the abstract API request), and then pass it to an executor along with any arbitrary set of HTTP headers you want to use for that particular request.

I'm interested in support for conditional requests and wanted to get feedback on the right approach. (Let me know if it's outside the scope of what you want in the library.)

add missing struct fields

With the new fields tool, it's now quite easy to identify which JSON fields are not being mapped into our resource structs. Most of these are API URLs, but there are also a handful of other fields that got overlooked.

Originally, I had pushed back a bit on adding the API URLs since this library doesn't use them at all. I've long since given up on that since a few use-cases have been mentioned, so we might as well go ahead and add them all. I also have a few ideas for how the library could actually use these URLs or ones like them.

Related, the API Versions page documents some changes in API resources that we should make sure are addressed.

Add integration tests

This is an ongoing work item to add integration tests for the entire library. Unlike the existing unit tests that spin up a local HTTP server that mimics certain behavior of the API, these integration tests will call against the live GitHub API. As such, they will likely take a lot longer to execute and will not be run near as often. I've set things up with aff0a13 and will continue working from there.

I'm not 100% settled yet on how extensive these tests need to be. For example, we don't need to test the value of every field in a response. The biggest goal is to have a test suite that can be run when things like the default media type changes to make sure the library is still working properly. I also have a separate test suite that I'm working on to identify fields in responses that we aren't mapping into our Go structs.

Provide access to text match metadata option in search

Text match metadata returns context on what matched in code search.

It's the same API that GitHub is using when you search on the site (or so I suspect by how they wrote their documentation).

To get this metadata, we just have to supply the text-match media type in the Accept Header.

application/vnd.github.v3.text-match+json

This also provides another field in the search results, a text_matches array.

Name Description
object_url The URL for the resource that contains a string property matching one of the search terms.
object_type The name for the type of resource that exists at the given object_url.
property The name of a property of the resource that exists at object_url. That property is a string that matches one of the search terms. (In the JSON returned from object_url, the full content for the fragment will be found in the property with this name.)
fragment A subset of the value of property. This is the text fragment that matches one or more of the search terms.
matches An array of one or more search terms that are present in fragment. The indices (i.e., “offsets”) are relative to the fragment. (They are not relative to the full content of property.)

I have blatantly stolen from their own documentation for this issue, check it out for more details.

TeamId missing from create organization repo

When trying to create a private repo for an organization I got the following error form github:

POST https://api.github.com/orgs/<organization_name>/repos: 422 Validation Failed [{Resource:Repo Field:team_id Code:invalid}]

While it isn't documented as required for organizations the error message seams to indicate that it is. https://developer.github.com/v3/repos/#create

I have a PR ready if this is indeed a bug and I am not missing something.

https://github.com/mattlandis/go-github/compare/add-team-id

Easy way to patch an issue?

Given an Issue, I want to comment or patch on it. However, to do that I need owner, repo and number, which are buried in the *issue.URL. It would be nice if either issues had CreateComment and Edit methods which received a *Client, or if they had a method to obtain those three fields easily.

watcher and star APIs

There's no rush on this, I'm just leaving a note for whoever ends up implementing the watcher and star APIs, since I just noticed this. These APIs are going through a 3 phase transition, during which the meaning of the term "watcher" is changing.

I haven't actually read though the transition plan closely enough to know which phase we're currently in, but my point here was to make sure that we use the terms "watch" and "star" correctly in the function names. I'm not too concerned which endpoint we hit (it looks like /subscribers is a temporary stopgap) or if we use the beta mime type... whichever way we do it, we should just make sure we pick the function names that will be stable, even if we switch to use a different API URL at some point.

Spreadsheet updates

  • Repos -> List Branches can be marked as "Done" - merged PR #86

Also the following are deprecated API's:

  • Downloads (replaced with Releases, not on spreadsheet) -> Replace w/ Releases?
  • Legacy search (replaced with completed "Search" spreadsheet group) -> Remove?

Creating an Issue with an Assignee fails with '422 Validation Failed'

The Assignee field in the create request is of type 'string', while the Issue struct has a full User struct. If you do something like this (note: sp() creates a string pointer):

issue := &github.Issue{
Title: sp("My new bug."),
Body: sp("Some text describing that bug..."),
Assignee: &github.User{Login: sp("fluffle")},
}

when this is serialized to JSON the 'assignee' field will contain {'login': 'fluffle'}.

I suspect (but haven't yet tested) that similar things will happen with Labels too. I don't think you can get away with using the same types for both API input and output; you may well need a type IssueCreate struct {} &c unless you want to teach all your types how to serialize themselves for specific uses.

I can send you a pull req for IssueCreate if you're interested. Nice API otherwise! Also, you can find me at who/abramley if you need to :-)

Cheers,
--alex

Channel interface

Hi,
I'm writing a similar library to this one for a different project. I'm curious about how you'd choose to implement paging via channels, if you wanted to do this.

Particularly I am wondering about how you'd pass errors through. Consider if the API goes down or exceeds the timeout while you are paging. It would be bad to not handle this case - I suppose you could just close the channel in the event of failure, but that doesn't seem good either.

I was thinking about something like this

type OrganizationPagingResult struct {
    Orgs []Organization
    // or some object here to hold errors.
    Err Error
}

for pagingResult := range myChannel {
    if pagingResult.Err != nil {
        return nil, pagingResult.Err
    }
    for i := 0; i < len(pagingResult.Orgs); i++ {
        org := pagingResult.Orgs[i];
        fmt.Println(org.ID);
    }
}

Then the channel returns one page at a time. Compared with the channel yielding one organization at a time, this simplifies the logic in the channel as the channel only has to fetch the next page, instead of sometimes yielding results and sometimes fetching and yielding a new page. Also the error is likely to come from the paging request failing, not the iterating over individual results.

You have more experience with Go and you've also used the library internally, so I was wondering if you had feedback on this or a proposed implementation for this library.

issue setting empty values

Depending on how our types are defined, we run into various interesting problems with setting empty values.

Option 1

This is the current behavior throughout the library.

As currently setup, we can't set empty values at all on Create or Update methods. For example, this won't work to remove the description from an existing repository:

// remove the repository description
r := &github.Repository{Description:""}
client.Repositories.Edit("user", "repo", r)

That is actually a no-op because the empty Description field gets dropped, since it is currently defined with omitempty. Fortunately, there are only a handful of mutable values where the zero value is actually meaningful, most notably bool values.

Option 2

We could then instead drop the omitempty, but that has potentially really bad side-effects, particularly on edit methods. Take the above code sample. Without omitempty on any of our fields, this would work as intended. However, it would also have the unintended side-effect of wiping out the repo homepage, [and once we add the additional fields...] making it public, and disabling issues and the wiki for the repo, since all of those fields would be passing their zero values. The solution there is to first call Get, update the response, and then call Edit with the updated object:

// remove the repository description
r, _ := client.Repositories.Get("user", "repo")
r.Description = ""
client.Repositories.Edit("user", "repo", r)

If you forget to follow that flow, you're gonna have a bad time.

Option 3

The third option is to do what goprotobuf does and actually use pointers for all non-repeated fields, since that allows a clear distinction between "unset" (nil) and "empty" (non-nil, zero value). That would result in types like:

type Repository struct {
    ID          *int        `json:"id,omitempty"`
    Owner       *User       `json:"owner,omitempty"`
    Name        *string     `json:"name,omitempty"`
    Description *string     `json:"description,omitempty"`
    CreatedAt   *time.Time  `json:"created_at,omitempty"`
    PushedAt    *time.Time  `json:"pushed_at,omitempty"`
    UpdatedAt   *time.Time  `json:"updated_at,omitempty"`
}

This is by far the safest approach, but does make working with the library a bit cumbersome, since creating pointers to primitive types takes a little extra work (multiplied by the number of fields you are setting). For example, the above code sample now becomes:

// remove the repository description
d = ""
r := &github.Repository{Description:&d}
client.Repositories.Edit("user", "repo", r)

The goprotobuf library makes this a little simpler by providing helper functions for creating pointers to primitives. Using those methods would result in:

// remove the repository description
r := &github.Repository{Description:proto.String("")}
client.Repositories.Edit("user", "repo", r)

Additionally, when working with these values, developers will always have to remember to dereference them where appropriate. While this is common for anyone used to working with protocol buffers in go, I'm not sure how unexpected it will be for the general community.

simplify construction of request URLs

opening this bug to track implementation of using URI templates to simplify construction of request URLs. Original description at jtacoma/uritemplates#3

The gist of it is that I'd like to go from this:

type ListOptions struct {
        Page int
}

func (s *OrganizationsService) List(user string, opt *ListOptions) ([]Organization, error) {
        u := fmt.Sprintf("users/%v/orgs", user)
        if opt != nil {
                params := url.Values{
                        "page": []string{strconv.Itoa(opt.Page)},
                }
                u += "?" + params.Encode()
        }
        ...
}

to something more like this:

type ListOptions struct {
        Page int `url:"page"`
}

func (s *OrganizationsService) List(user string, opt *ListOptions) ([]Organization, error) {
        u := fmt.Sprintf("users/%v/orgs", user)
        u += uritemplates.Parse("{?page}").Expand(ListOptions)
        ...
}

or possibly something even simpler with a little more work.

Issue struct is missing the URL field

This is particularly important if you want to process issue events coming from GitHub webhooks. There is no other way how to detect the source repository other than parsing the issue URL.

I will personally take care of this if there are no issues with simply adding URL field to the Issue struct.

reorganize code into separate files per API

@wlynch92's comment:

I was thinking about adopting some sort of naming scheme that would easily allow us to separate and distinguish source files and the APIs they provide. Instead of looking through repos.go (which after adding all 12 APIs, might be very lengthy) for the ListHook function, why not separate the hook API into something like repos_hooks.go. This lets us sort APIs into separate files while still keeping everything in a single package and making a clear relation to the service they are a part of. It also is a close mapping to the overall layout of the Github API (i.e. http://developer.github.com/v3/repos/ -> repos.go and http://developer.github.com/v3/repos/hooks/ -> repos_hooks.go).

I think this is something we should definitely consider for a large services such as RepositoryService, and apply to smaller services as a matter of consistency.

Mocking go-github

I would like to write tests for an app that uses a lot go-github.

For instance I would like to mock some methods in the client.*Services structs. Is this possible with the current usage of structs ? Wouldn't it be preferable that the client struct holds some interfaces rather than some structs ? With interfaces, mocking is very easy with struct embedding.

Change Check*() and other related functions to Is*()

As discussed in issue #34, the convention that has been previously used to name functions that check if something exists in a given state has been Check*(). For example, CheckAssignee().

In order to make these more consistent to the style of the go stdlib functions (see math and os), these should be changed to Is*() (i.e. CheckAssignee() -> IsAssignee()).

Some of these functions are not always in the Check*() form, such as Starred().

A good place to start would be to search for parseBoolResponse(). Most of these functions use this to determine their result.

Using Github Search Api does not work with complex search string

When i try to search against the Github api using the github example i get an 422 error.

In go code this looks like:

opt := &github.SearchOptions{Sort: "updated"}
client.Search.Code("addClass+in:file+language:js+repo:jquery/jquery", opt)

The error looks like:

error: GET https://api.github.com/search/code?q=addClass%2Bin%3Afile%2Blanguage%3Ajs%2Brepo%3Ajquery%2Fjquery&sort=updated: 422 Validation Failed [{Resource:Search Field:q Code:invalid}]

The Problem is that go encodes the query string and not only the params.
The Github Api isn't able to deal with such queries.

Go generated url:

$ curl https://api.github.com/search/code?q=addClass%2Bin%3Afile%2Blanguage%3Ajs%2Brepo%3Ajquery%2Fjquery&sort=updated

=> does not work

Same curl without encoding:

$ curl https://api.github.com/search/code?addClass+in:file+language:js+repo:jquery/jquery&sort=updated

=> works

We will prepare a pullrequest for this issue.

add Getter methods for data structures

as part of resolving #19, all data structures now use pointer field values. To ease the burden on developers to constantly perform nil checks, we should generate Get*() funcs on all our data structures, similar to what goprotobuf does. If a field is non-nil, return it. Otherwise, return the zero value for that type. So for example,

func (m *User) GetLogin() string {
    if m != nil && m.Login != nil {
        return *m.Login
    }
    return ""
}

Go provides good capabilities to read and manipulate go source code, so this can be completely generated. We might need to move all these types into a single data.go file, I'm not sure.

handling custom media types

A handful of API methods (like issues) support the use of custom mime types to return additional fields in the response, using different formats.

As currently designed, go-github doesn't have a simple way to expose this, since most HTTP-specific details are hidden from the caller. We could probably just add it to the Options parameter for the given method, but that feels a little weird. Additionally, I don't yet have a real use-case for needing to use these custom mime-types. Therefore, I'm currently leaning toward punting on this until someone actually needs it.

So if you need it, please comment here, and we can look at how to work that into the library.

Support pagination for listing gists

The required change is trivial:

diff --git a/github/gists.go b/github/gists.go
index ec662b5..48a0eec 100644
--- a/github/gists.go
+++ b/github/gists.go
@@ -56,6 +56,8 @@ func (g GistFile) String() string {
 type GistListOptions struct {
        // Since filters Gists by time.
        Since time.Time `url:"since,omitempty"`
+
+       ListOptions
 }

 // List gists for a user. Passing the empty string will list

Paginated client.Issues.List

The github api documentation shows that the /issues api endpoints are paginated.

The pagination is information is returned in the header like this:

Link:[<https://api.github.com/issues?direc
tion=desc&filter=all&labels=&sort=updated&state=open&page=2>; rel="next",

Unfortunately as far as I can tell the go-github package do not expose this information.
listIssues does not return the Headers nor the response. What is in your opinion the best approach to fix this issue ?

  • Changing the method signature to return the response.Headers.
  • Changing the method signature to return the response.
  • Changing the method signature to return a new struct that exposes some of the information returned in the response.Header

To put it in a nutshell I can't think of an easy way to make a backward compatible changes to fix this bug.

Accessing the attribute "login" from GetBranch

Hi,

I'm trying to get at the login attribute within author that's nestled between commit and parents. I don't see any of the structs supporting that. I'm more than happy to submit a patch to enable this, I'd just like a bit of input before I go hacking like crazy. If you happen to know a method by which you can access it, I'd be very grateful.

Here's what github's API specs say is returned: https://developer.github.com/v3/repos/#response-10

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.