Repository: google/go-github
Pull request #1179 (2019-05-31 00:12:21)

github.App: Use *Timestamp instead of *time.Time
This is a similar issue to #965 - the `created_at` and `updated_at` fields in the `app` field of webhooks are formatted as Unix epoch integers, rather than RFC3339 for the CRUD APIs.

codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/1179?src=pr&el=h1) Report
> Merging [#1179](https://codecov.io/gh/google/go-github/pull/1179?src=pr&el=desc) into [master](https://codecov.io/gh/google/go-github/commit/8354c0739a78b30158359e98fe14ca3bb823159c?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/google/go-github/pull/1179/graphs/tree.svg?width=650&token=EgYjGjZlI0&height=150&src=pr)](https://codecov.io/gh/google/go-github/pull/1179?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master    #1179   +/-   ##
=======================================
  Coverage   70.25%   70.25%           
=======================================
  Files          84       84           
  Lines        5867     5867           
=======================================
  Hits         4122     4122           
  Misses        956      956           
  Partials      789      789
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/1179?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [github/apps.go](https://codecov.io/gh/google/go-github/pull/1179/diff?src=pr&el=tree#diff-Z2l0aHViL2FwcHMuZ28=) | `65.51% <ø> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-github/pull/1179?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/google/go-github/pull/1179?src=pr&el=footer). Last update [8354c07...d77805f](https://codecov.io/gh/google/go-github/pull/1179?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/github/apps.go b/github/apps.go
index 1ad9a981..80dfb3c8 100644
--- a/github/apps.go
+++ b/github/apps.go
@@ -26,8 +26,8 @@ type App struct {
 	Description *string    `json:"description,omitempty"`
 	ExternalURL *string    `json:"external_url,omitempty"`
 	HTMLURL     *string    `json:"html_url,omitempty"`
-	CreatedAt   *time.Time `json:"created_at,omitempty"`
-	UpdatedAt   *time.Time `json:"updated_at,omitempty"`
+	CreatedAt   *Timestamp `json:"created_at,omitempty"`
+	UpdatedAt   *Timestamp `json:"updated_at,omitempty"`
 }
 
 // InstallationToken represents an installation token.
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 9129909a..9cd33103 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -117,9 +117,9 @@ func (a *APIMeta) GetVerifiablePasswordAuthentication() bool {
 }
 
 // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
-func (a *App) GetCreatedAt() time.Time {
+func (a *App) GetCreatedAt() Timestamp {
 	if a == nil || a.CreatedAt == nil {
-		return time.Time{}
+		return Timestamp{}
 	}
 	return *a.CreatedAt
 }
@@ -181,9 +181,9 @@ func (a *App) GetOwner() *User {
 }
 
 // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
-func (a *App) GetUpdatedAt() time.Time {
+func (a *App) GetUpdatedAt() Timestamp {
 	if a == nil || a.UpdatedAt == nil {
-		return time.Time{}
+		return Timestamp{}
 	}
 	return *a.UpdatedAt
 }
@@ -484,6 +484,30 @@ func (b *Branch) GetProtected() bool {
 	return *b.Protected
 }
 
+// GetCommit returns the Commit field.
+func (b *BranchCommit) GetCommit() *Commit {
+	if b == nil {
+		return nil
+	}
+	return b.Commit
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (b *BranchCommit) GetName() string {
+	if b == nil || b.Name == nil {
+		return ""
+	}
+	return *b.Name
+}
+
+// GetProtected returns the Protected field if it's non-nil, zero value otherwise.
+func (b *BranchCommit) GetProtected() string {
+	if b == nil || b.Protected == nil {
+		return ""
+	}
+	return *b.Protected
+}
+
 // GetApp returns the App field.
 func (c *CheckRun) GetApp() *App {
 	if c == nil {
@@ -7228,6 +7252,14 @@ func (p *PublicEvent) GetSender() *User {
 	return p.Sender
 }
 
+// GetExpectedHeadSHA returns the ExpectedHeadSHA field if it's non-nil, zero value otherwise.
+func (p *PullReqestBranchUpdateOptions) GetExpectedHeadSHA() string {
+	if p == nil || p.ExpectedHeadSHA == nil {
+		return ""
+	}
+	return *p.ExpectedHeadSHA
+}
+
 // GetActiveLockReason returns the ActiveLockReason field if it's non-nil, zero value otherwise.
 func (p *PullRequest) GetActiveLockReason() string {
 	if p == nil || p.ActiveLockReason == nil {
@@ -7596,6 +7628,22 @@ func (p *PullRequestBranch) GetUser() *User {
 	return p.User
 }
 
+// GetMessage returns the Message field if it's non-nil, zero value otherwise.
+func (p *PullRequestBranchUpdateResponse) GetMessage() string {
+	if p == nil || p.Message == nil {
+		return ""
+	}
+	return *p.Message
+}
+
+// GetURL returns the URL field if it's non-nil, zero value otherwise.
+func (p *PullRequestBranchUpdateResponse) GetURL() string {
+	if p == nil || p.URL == nil {
+		return ""
+	}
+	return *p.URL
+}
+
 // GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise.
 func (p *PullRequestComment) GetAuthorAssociation() string {
 	if p == nil || p.AuthorAssociation == nil {
diff --git a/github/github.go b/github/github.go
index 6bef3a64..63f8a966 100644
--- a/github/github.go
+++ b/github/github.go
@@ -137,6 +137,12 @@ const (
 
 	// https://developer.github.com/changes/2019-04-24-vulnerability-alerts/
 	mediaTypeRequiredVulnerabilityAlertsPreview = "application/vnd.github.dorian-preview+json"
+
+	// https://developer.github.com/changes/2019-05-29-update-branch-api/
+	mediaTypeUpdatePullRequestBranchPreview = "application/vnd.github.lydian-preview+json"
+
+	// https://developer.github.com/changes/2019-04-11-pulls-branches-for-commit/
+	mediaTypeListPullsOrBranchesForCommitPreview = "application/vnd.github.groot-preview+json"
 )
 
 // A Client manages communication with the GitHub API.
diff --git a/github/pulls.go b/github/pulls.go
index b26f6c5c..a06f97bc 100644
--- a/github/pulls.go
+++ b/github/pulls.go
@@ -159,6 +159,35 @@ func (s *PullRequestsService) List(ctx context.Context, owner string, repo strin
 	return pulls, resp, nil
 }
 
+// ListPullRequestsWithCommit returns pull requests associated with a commit SHA.
+//
+// The results will include open and closed pull requests.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/commits/#list-pull-requests-associated-with-commit
+func (s *PullRequestsService) ListPullRequestsWithCommit(ctx context.Context, owner, repo, sha string, opt *PullRequestListOptions) ([]*PullRequest, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/commits/%v/pulls", owner, repo, sha)
+	u, err := addOptions(u, opt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// TODO: remove custom Accept header when this API fully launches.
+	acceptHeaders := []string{mediaTypeListPullsOrBranchesForCommitPreview, mediaTypeDraftPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
+	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
+	var pulls []*PullRequest
+	resp, err := s.client.Do(ctx, req, &pulls)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pulls, resp, nil
+}
+
 // Get a single pull request.
 //
 // GitHub API docs: https://developer.github.com/v3/pulls/#get-a-single-pull-request
@@ -242,6 +271,49 @@ func (s *PullRequestsService) Create(ctx context.Context, owner string, repo str
 	return p, resp, nil
 }
 
+// PullReqestBranchUpdateOptions specifies the optional parameters to the
+// PullRequestsService.UpdateBranch method.
+type PullReqestBranchUpdateOptions struct {
+	// ExpectedHeadSHA specifies the most recent commit on the pull request's branch.
+	// Default value is the SHA of the pull request's current HEAD ref.
+	ExpectedHeadSHA *string `json:"expected_head_sha,omitempty"`
+}
+
+// PullRequestBranchUpdateResponse specifies the response of pull request branch update.
+type PullRequestBranchUpdateResponse struct {
+	Message *string `json:"message,omitempty"`
+	URL     *string `json:"url,omitempty"`
+}
+
+// UpdateBranch updates the pull request branch with latest upstream changes.
+//
+// This method might return an AcceptedError and a status code of
+// 202. This is because this is the status that GitHub returns to signify that
+// it has now scheduled the update of the pull request branch in a background task.
+// A follow up request, after a delay of a second or so, should result
+// in a successful request.
+//
+// GitHub API docs: https://developer.github.com/v3/pulls/#update-a-pull-request-branch
+func (s *PullRequestsService) UpdateBranch(ctx context.Context, owner, repo string, number int, opts *PullReqestBranchUpdateOptions) (*PullRequestBranchUpdateResponse, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/pulls/%d/update-branch", owner, repo, number)
+
+	req, err := s.client.NewRequest("PUT", u, opts)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// TODO: remove custom Accept header when this API fully launches.
+	req.Header.Set("Accept", mediaTypeUpdatePullRequestBranchPreview)
+
+	p := new(PullRequestBranchUpdateResponse)
+	resp, err := s.client.Do(ctx, req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, nil
+}
+
 type pullRequestUpdate struct {
 	Title               *string `json:"title,omitempty"`
 	Body                *string `json:"body,omitempty"`
diff --git a/github/repos_commits.go b/github/repos_commits.go
index 83cdf5b3..95096923 100644
--- a/github/repos_commits.go
+++ b/github/repos_commits.go
@@ -115,6 +115,13 @@ type CommitsListOptions struct {
 	ListOptions
 }
 
+// BranchCommit is the result of listing branches with commit SHA.
+type BranchCommit struct {
+	Name      *string `json:"name,omitempty"`
+	Commit    *Commit `json:"commit,omitempty"`
+	Protected *string `json:"protected,omitempty"`
+}
+
 // ListCommits lists the commits of a repository.
 //
 // GitHub API docs: https://developer.github.com/v3/repos/commits/#list
@@ -232,3 +239,26 @@ func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo st
 
 	return comp, resp, nil
 }
+
+// ListBranchesHeadCommit gets all branches where the given commit SHA is the HEAD,
+// or latest commit for the branch.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/commits/#list-branches-for-head-commit
+func (s *RepositoriesService) ListBranchesHeadCommit(ctx context.Context, owner, repo, sha string) ([]*BranchCommit, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/commits/%v/branches-where-head", owner, repo, sha)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// TODO: remove custom Accept header when this API fully launches.
+	req.Header.Set("Accept", mediaTypeListPullsOrBranchesForCommitPreview)
+	var branchCommits []*BranchCommit
+	resp, err := s.client.Do(ctx, req, &branchCommits)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return branchCommits, resp, nil
+}


Test patch:
diff --git a/github/pulls_test.go b/github/pulls_test.go
index 87e00108..09d60e3a 100644
--- a/github/pulls_test.go
+++ b/github/pulls_test.go
@@ -47,6 +47,37 @@ func TestPullRequestsService_List(t *testing.T) {
 	}
 }
 
+func TestPullRequestsService_ListPullRequestsWithCommit(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	wantAcceptHeaders := []string{mediaTypeListPullsOrBranchesForCommitPreview, mediaTypeDraftPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
+	mux.HandleFunc("/repos/o/r/commits/sha/pulls", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", "))
+		testFormValues(t, r, values{
+			"state":     "closed",
+			"head":      "h",
+			"base":      "b",
+			"sort":      "created",
+			"direction": "desc",
+			"page":      "2",
+		})
+		fmt.Fprint(w, `[{"number":1}]`)
+	})
+
+	opt := &PullRequestListOptions{"closed", "h", "b", "created", "desc", ListOptions{Page: 2}}
+	pulls, _, err := client.PullRequests.ListPullRequestsWithCommit(context.Background(), "o", "r", "sha", opt)
+	if err != nil {
+		t.Errorf("PullRequests.ListPullRequestsWithCommit returned error: %v", err)
+	}
+
+	want := []*PullRequest{{Number: Int(1)}}
+	if !reflect.DeepEqual(pulls, want) {
+		t.Errorf("PullRequests.ListPullRequestsWithCommit returned %+v, want %+v", pulls, want)
+	}
+}
+
 func TestPullRequestsService_List_invalidOwner(t *testing.T) {
 	client, _, _, teardown := setup()
 	defer teardown()
@@ -303,6 +334,39 @@ func TestPullRequestsService_Create_invalidOwner(t *testing.T) {
 	testURLParseError(t, err)
 }
 
+func TestPullRequestsService_UpdateBranch(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/pulls/1/update-branch", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+		testHeader(t, r, "Accept", mediaTypeUpdatePullRequestBranchPreview)
+		fmt.Fprint(w, `
+			{
+			  "message": "Updating pull request branch.",
+			  "url": "https://github.com/repos/o/r/pulls/1"
+			}`)
+	})
+
+	opts := &PullReqestBranchUpdateOptions{
+		ExpectedHeadSHA: String("s"),
+	}
+
+	pull, _, err := client.PullRequests.UpdateBranch(context.Background(), "o", "r", 1, opts)
+	if err != nil {
+		t.Errorf("PullRequests.UpdateBranch returned error: %v", err)
+	}
+
+	want := &PullRequestBranchUpdateResponse{
+		Message: String("Updating pull request branch."),
+		URL:     String("https://github.com/repos/o/r/pulls/1"),
+	}
+
+	if !reflect.DeepEqual(pull, want) {
+		t.Errorf("PullRequests.UpdateBranch returned %+v, want %+v", pull, want)
+	}
+}
+
 func TestPullRequestsService_Edit(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/repos_commits_test.go b/github/repos_commits_test.go
index e076febb..feed2b95 100644
--- a/github/repos_commits_test.go
+++ b/github/repos_commits_test.go
@@ -360,3 +360,23 @@ func TestRepositoriesService_CompareCommits(t *testing.T) {
 		t.Errorf("Repositories.CompareCommits returned \n%+v, want \n%+v", got, want)
 	}
 }
+
+func TestRepositoriesService_ListBranchesHeadCommit(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/commits/s/branches-where-head", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprintf(w, `[{"name": "b"}]`)
+	})
+
+	branches, _, err := client.Repositories.ListBranchesHeadCommit(context.Background(), "o", "r", "s")
+	if err != nil {
+		t.Errorf("Repositories.ListBranchesHeadCommit returned error: %v", err)
+	}
+
+	want := []*BranchCommit{{Name: String("b")}}
+	if !reflect.DeepEqual(branches, want) {
+		t.Errorf("Repositories.ListBranchesHeadCommit returned %+v, want %+v", branches, want)
+	}
+}