Repository: google/go-github
Pull request #1117 (2019-02-11 16:10:13)

Fix Webhook creation.
## Changes

Attempting to create webhooks fail unless a name is specified. As per the documentation the only name that is valid is "web", so I haven't opted to allow modification of this but creation seems to fail without it.

googlebot: 
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

:memo: **Please visit <https://cla.developers.google.com/> to sign.**

Once you've signed (or fixed any issues), please reply here (e.g. `I signed it!`) and we'll verify it.

----

#### What to do if you already signed the CLA

##### Individual signers

*   It's possible we don't have your GitHub username or you're using a different email address on your commit. Check [your existing CLA data](https://cla.developers.google.com/clas) and verify that your [email is set on your git commits](https://help.github.com/articles/setting-your-email-in-git/).

##### Corporate signers

*   Your company has a Point of Contact who decides which employees are authorized to participate. Ask your POC to be added to the group of authorized contributors. If you don't know who your Point of Contact is, direct the Google project maintainer to [go/cla#troubleshoot](http://go/cla#troubleshoot) ([Public version](https://opensource.google.com/docs/cla/#troubleshoot)).
*   The email used to register you as an authorized contributor must be the email used for the Git commit. Check [your existing CLA data](https://cla.developers.google.com/clas) and verify that your [email is set on your git commits](https://help.github.com/articles/setting-your-email-in-git/).
*   The email used to register you as an authorized contributor must also be [attached to your GitHub account](https://github.com/settings/emails).
		

<!-- need_sender_cla -->
DominicGunn: I signed it!
googlebot: CLAs look good, thanks!

<!-- ok -->
gmlewis: The only documentation I found about this:
https://developer.github.com/v3/repos/hooks/#create-a-hook
says that the `name` field has a default value of `web`.

Do you want to contact GitHub (support@github.com) and ask them to clarify what is going on after describing the situation you discovered? I have found their support team to be incredibly responsive and helpful.

Patch:
diff --git a/README.md b/README.md
index 4c07dd8a..90cd8f6e 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ If you're interested in using the [GraphQL API v4][], the recommended library is
 ## Usage ##
 
 ```go
-import "github.com/google/go-github/v23/github"	// with go modules enabled (GO111MODULE=on or outside GOPATH)
+import "github.com/google/go-github/v24/github"	// with go modules enabled (GO111MODULE=on or outside GOPATH)
 import "github.com/google/go-github/github" // with go modules disabled
 ```
 
diff --git a/example/appengine/app.go b/example/appengine/app.go
index aa1d34e9..b9a66e0f 100644
--- a/example/appengine/app.go
+++ b/example/appengine/app.go
@@ -12,7 +12,7 @@ import (
 	"net/http"
 	"os"
 
-	"github.com/google/go-github/v23/github"
+	"github.com/google/go-github/v24/github"
 	"golang.org/x/oauth2"
 	"google.golang.org/appengine"
 	"google.golang.org/appengine/log"
diff --git a/example/basicauth/main.go b/example/basicauth/main.go
index f314cba8..a622d71d 100644
--- a/example/basicauth/main.go
+++ b/example/basicauth/main.go
@@ -16,7 +16,7 @@ import (
 	"strings"
 	"syscall"
 
-	"github.com/google/go-github/v23/github"
+	"github.com/google/go-github/v24/github"
 	"golang.org/x/crypto/ssh/terminal"
 )
 
diff --git a/example/commitpr/main.go b/example/commitpr/main.go
index f5d2991e..c96f4aa1 100644
--- a/example/commitpr/main.go
+++ b/example/commitpr/main.go
@@ -31,7 +31,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/google/go-github/v23/github"
+	"github.com/google/go-github/v24/github"
 	"golang.org/x/oauth2"
 )
 
diff --git a/example/migrations/main.go b/example/migrations/main.go
index e7612908..f53278b5 100644
--- a/example/migrations/main.go
+++ b/example/migrations/main.go
@@ -12,7 +12,7 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/google/go-github/v23/github"
+	"github.com/google/go-github/v24/github"
 	"golang.org/x/oauth2"
 )
 
diff --git a/example/newrepo/main.go b/example/newrepo/main.go
index d104a612..df4327cc 100644
--- a/example/newrepo/main.go
+++ b/example/newrepo/main.go
@@ -16,7 +16,7 @@ import (
 	"log"
 	"os"
 
-	"github.com/google/go-github/v23/github"
+	"github.com/google/go-github/v24/github"
 	"golang.org/x/oauth2"
 )
 
diff --git a/example/simple/main.go b/example/simple/main.go
index c55729b4..24e33bb2 100644
--- a/example/simple/main.go
+++ b/example/simple/main.go
@@ -12,7 +12,7 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/google/go-github/v23/github"
+	"github.com/google/go-github/v24/github"
 )
 
 // Fetch all the public organizations' membership of a user.
diff --git a/github/doc.go b/github/doc.go
index 5c89c049..21dc231d 100644
--- a/github/doc.go
+++ b/github/doc.go
@@ -8,7 +8,7 @@ Package github provides a client for using the GitHub API.
 
 Usage:
 
-	import "github.com/google/go-github/v23/github"	// with go modules enabled (GO111MODULE=on or outside GOPATH)
+	import "github.com/google/go-github/v24/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
diff --git a/github/git_commits.go b/github/git_commits.go
index 7638acbd..9dfd3afb 100644
--- a/github/git_commits.go
+++ b/github/git_commits.go
@@ -84,6 +84,7 @@ type createCommit struct {
 	Message   *string       `json:"message,omitempty"`
 	Tree      *string       `json:"tree,omitempty"`
 	Parents   []string      `json:"parents,omitempty"`
+	Signature *string       `json:"signature,omitempty"`
 }
 
 // CreateCommit creates a new commit in a repository.
@@ -115,6 +116,9 @@ func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string
 	if commit.Tree != nil {
 		body.Tree = commit.Tree.SHA
 	}
+	if commit.Verification != nil {
+		body.Signature = commit.Verification.Signature
+	}
 
 	req, err := s.client.NewRequest("POST", u, body)
 	if err != nil {
diff --git a/github/github-accessors.go b/github/github-accessors.go
index cb2f0284..b9deb7e1 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -7196,6 +7196,14 @@ func (p *PullRequest) GetDiffURL() string {
 	return *p.DiffURL
 }
 
+// GetDraft returns the Draft field if it's non-nil, zero value otherwise.
+func (p *PullRequest) GetDraft() bool {
+	if p == nil || p.Draft == nil {
+		return false
+	}
+	return *p.Draft
+}
+
 // GetHead returns the Head field.
 func (p *PullRequest) GetHead() *PullRequestBranch {
 	if p == nil {
diff --git a/github/github.go b/github/github.go
index 36ee5acb..4d86d76b 100644
--- a/github/github.go
+++ b/github/github.go
@@ -128,6 +128,9 @@ const (
 
 	// https://developer.github.com/changes/2018-12-18-interactions-preview/
 	mediaTypeInteractionRestrictionsPreview = "application/vnd.github.sombra-preview+json"
+
+	// https://developer.github.com/changes/2019-02-14-draft-pull-requests/
+	mediaTypeDraftPreview = "application/vnd.github.shadow-cat-preview+json"
 )
 
 // A Client manages communication with the GitHub API.
diff --git a/github/pulls.go b/github/pulls.go
index b3479dfe..067e362b 100644
--- a/github/pulls.go
+++ b/github/pulls.go
@@ -32,6 +32,7 @@ type PullRequest struct {
 	MergedAt            *time.Time `json:"merged_at,omitempty"`
 	Labels              []*Label   `json:"labels,omitempty"`
 	User                *User      `json:"user,omitempty"`
+	Draft               *bool      `json:"draft,omitempty"`
 	Merged              *bool      `json:"merged,omitempty"`
 	Mergeable           *bool      `json:"mergeable,omitempty"`
 	MergeableState      *string    `json:"mergeable_state,omitempty"`
@@ -146,7 +147,7 @@ func (s *PullRequestsService) List(ctx context.Context, owner string, repo strin
 	}
 
 	// TODO: remove custom Accept header when this API fully launches.
-	acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
+	acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview}
 	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
 
 	var pulls []*PullRequest
@@ -169,7 +170,7 @@ func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string
 	}
 
 	// TODO: remove custom Accept header when this API fully launches.
-	acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
+	acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview}
 	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
 
 	pull := new(PullRequest)
diff --git a/github/repos_hooks.go b/github/repos_hooks.go
index 43f70687..7674947d 100644
--- a/github/repos_hooks.go
+++ b/github/repos_hooks.go
@@ -92,6 +92,7 @@ func (h Hook) String() string {
 // information.
 type createHookRequest struct {
 	// Config is required.
+	Name   string                 `json:"name"`
 	Config map[string]interface{} `json:"config,omitempty"`
 	Events []string               `json:"events,omitempty"`
 	Active *bool                  `json:"active,omitempty"`
@@ -108,6 +109,7 @@ func (s *RepositoriesService) CreateHook(ctx context.Context, owner, repo string
 	u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo)
 
 	hookReq := &createHookRequest{
+		Name:   "web",
 		Events: hook.Events,
 		Active: hook.Active,
 		Config: hook.Config,
@@ -169,6 +171,24 @@ func (s *RepositoriesService) GetHook(ctx context.Context, owner, repo string, i
 	return h, resp, nil
 }
 
+// EditHook updates a specified Hook.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/hooks/#edit-a-hook
+func (s *RepositoriesService) EditHook(ctx context.Context, owner, repo string, id int64, hook *Hook) (*Hook, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id)
+	req, err := s.client.NewRequest("PATCH", u, hook)
+	if err != nil {
+		return nil, nil, err
+	}
+	h := new(Hook)
+	resp, err := s.client.Do(ctx, req, h)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return h, resp, nil
+}
+
 // DeleteHook deletes a specified Hook.
 //
 // GitHub API docs: https://developer.github.com/v3/repos/hooks/#delete-a-hook
@@ -180,3 +200,27 @@ func (s *RepositoriesService) DeleteHook(ctx context.Context, owner, repo string
 	}
 	return s.client.Do(ctx, req, nil)
 }
+
+// PingHook triggers a 'ping' event to be sent to the Hook.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/hooks/#ping-a-hook
+func (s *RepositoriesService) PingHook(ctx context.Context, owner, repo string, id int64) (*Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/hooks/%d/pings", owner, repo, id)
+	req, err := s.client.NewRequest("POST", u, nil)
+	if err != nil {
+		return nil, err
+	}
+	return s.client.Do(ctx, req, nil)
+}
+
+// TestHook triggers a test Hook by github.
+//
+// GitHub API docs: https://developer.github.com/v3/repos/hooks/#test-a-push-hook
+func (s *RepositoriesService) TestHook(ctx context.Context, owner, repo string, id int64) (*Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/hooks/%d/tests", owner, repo, id)
+	req, err := s.client.NewRequest("POST", u, nil)
+	if err != nil {
+		return nil, err
+	}
+	return s.client.Do(ctx, req, nil)
+}
diff --git a/go.mod b/go.mod
index 94cdcb37..9cd582a3 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/google/go-github/v23
+module github.com/google/go-github/v24
 
 require (
 	github.com/golang/protobuf v1.2.0 // indirect


Test patch:
diff --git a/github/examples_test.go b/github/examples_test.go
index a53d6865..7c0eda25 100644
--- a/github/examples_test.go
+++ b/github/examples_test.go
@@ -12,7 +12,7 @@ import (
 	"fmt"
 	"log"
 
-	"github.com/google/go-github/v23/github"
+	"github.com/google/go-github/v24/github"
 )
 
 func ExampleClient_Markdown() {
diff --git a/github/git_commits_test.go b/github/git_commits_test.go
index f3cf8099..83f56407 100644
--- a/github/git_commits_test.go
+++ b/github/git_commits_test.go
@@ -80,6 +80,50 @@ func TestGitService_CreateCommit(t *testing.T) {
 	}
 }
 
+func TestGitService_CreateSignedCommit(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	signature := "----- BEGIN PGP SIGNATURE -----\n\naaaa\naaaa\n----- END PGP SIGNATURE -----"
+
+	input := &Commit{
+		Message: String("m"),
+		Tree:    &Tree{SHA: String("t")},
+		Parents: []Commit{{SHA: String("p")}},
+		Verification: &SignatureVerification{
+			Signature: String(signature),
+		},
+	}
+
+	mux.HandleFunc("/repos/o/r/git/commits", func(w http.ResponseWriter, r *http.Request) {
+		v := new(createCommit)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "POST")
+
+		want := &createCommit{
+			Message:   input.Message,
+			Tree:      String("t"),
+			Parents:   []string{"p"},
+			Signature: String(signature),
+		}
+		if !reflect.DeepEqual(v, want) {
+			t.Errorf("Request body = %+v, want %+v", v, want)
+		}
+		fmt.Fprint(w, `{"sha":"s"}`)
+	})
+
+	commit, _, err := client.Git.CreateCommit(context.Background(), "o", "r", input)
+	if err != nil {
+		t.Errorf("Git.CreateCommit returned error: %v", err)
+	}
+
+	want := &Commit{SHA: String("s")}
+	if !reflect.DeepEqual(commit, want) {
+		t.Errorf("Git.CreateCommit returned %+v, want %+v", commit, want)
+	}
+}
+
 func TestGitService_CreateCommit_invalidOwner(t *testing.T) {
 	client, _, _, teardown := setup()
 	defer teardown()
diff --git a/github/pulls_test.go b/github/pulls_test.go
index f8533828..602f03be 100644
--- a/github/pulls_test.go
+++ b/github/pulls_test.go
@@ -20,7 +20,7 @@ func TestPullRequestsService_List(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
 
-	wantAcceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
+	wantAcceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview}
 	mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
 		testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", "))
@@ -59,7 +59,7 @@ func TestPullRequestsService_Get(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
 
-	wantAcceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
+	wantAcceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview}
 	mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
 		testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", "))
diff --git a/github/repos_commits_test.go b/github/repos_commits_test.go
index 2cbcc5ae..e076febb 100644
--- a/github/repos_commits_test.go
+++ b/github/repos_commits_test.go
@@ -223,6 +223,45 @@ func TestRepositoriesService_GetCommitSHA1(t *testing.T) {
 	}
 }
 
+func TestRepositoriesService_NonAlphabetCharacter_GetCommitSHA1(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	const sha1 = "01234abcde"
+
+	mux.HandleFunc("/repos/o/r/commits/master%20hash", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeV3SHA)
+
+		fmt.Fprintf(w, sha1)
+	})
+
+	got, _, err := client.Repositories.GetCommitSHA1(context.Background(), "o", "r", "master%20hash", "")
+	if err != nil {
+		t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err)
+	}
+
+	if want := sha1; got != want {
+		t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want)
+	}
+
+	mux.HandleFunc("/repos/o/r/commits/tag", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeV3SHA)
+		testHeader(t, r, "If-None-Match", `"`+sha1+`"`)
+
+		w.WriteHeader(http.StatusNotModified)
+	})
+
+	got, _, err = client.Repositories.GetCommitSHA1(context.Background(), "o", "r", "tag", sha1)
+	if err == nil {
+		t.Errorf("Expected HTTP 304 response")
+	}
+
+	if want := ""; got != want {
+		t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want)
+	}
+}
+
 func TestRepositoriesService_CompareCommits(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/repos_hooks_test.go b/github/repos_hooks_test.go
index 1ab287ed..9e4691c3 100644
--- a/github/repos_hooks_test.go
+++ b/github/repos_hooks_test.go
@@ -25,7 +25,7 @@ func TestRepositoriesService_CreateHook(t *testing.T) {
 		json.NewDecoder(r.Body).Decode(v)
 
 		testMethod(t, r, "POST")
-		want := &createHookRequest{}
+		want := &createHookRequest{Name: "web"}
 		if !reflect.DeepEqual(v, want) {
 			t.Errorf("Request body = %+v, want %+v", v, want)
 		}
@@ -103,6 +103,43 @@ func TestRepositoriesService_GetHook_invalidOwner(t *testing.T) {
 	testURLParseError(t, err)
 }
 
+func TestRepositoriesService_EditHook(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &Hook{}
+
+	mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) {
+		v := new(Hook)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "PATCH")
+		if !reflect.DeepEqual(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `{"id":1}`)
+	})
+
+	hook, _, err := client.Repositories.EditHook(context.Background(), "o", "r", 1, input)
+	if err != nil {
+		t.Errorf("Repositories.EditHook returned error: %v", err)
+	}
+
+	want := &Hook{ID: Int64(1)}
+	if !reflect.DeepEqual(hook, want) {
+		t.Errorf("Repositories.EditHook returned %+v, want %+v", hook, want)
+	}
+}
+
+func TestRepositoriesService_EditHook_invalidOwner(t *testing.T) {
+	client, _, _, teardown := setup()
+	defer teardown()
+
+	_, _, err := client.Repositories.EditHook(context.Background(), "%", "%", 1, nil)
+	testURLParseError(t, err)
+}
+
 func TestRepositoriesService_DeleteHook(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
@@ -124,3 +161,39 @@ func TestRepositoriesService_DeleteHook_invalidOwner(t *testing.T) {
 	_, err := client.Repositories.DeleteHook(context.Background(), "%", "%", 1)
 	testURLParseError(t, err)
 }
+
+func TestRepositoriesService_PingHook(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/hooks/1/pings", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+	})
+
+	_, err := client.Repositories.PingHook(context.Background(), "o", "r", 1)
+	if err != nil {
+		t.Errorf("Repositories.PingHook returned error: %v", err)
+	}
+}
+
+func TestRepositoriesService_TestHook(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/hooks/1/tests", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+	})
+
+	_, err := client.Repositories.TestHook(context.Background(), "o", "r", 1)
+	if err != nil {
+		t.Errorf("Repositories.TestHook returned error: %v", err)
+	}
+}
+
+func TestRepositoriesService_TestHook_invalidOwner(t *testing.T) {
+	client, _, _, teardown := setup()
+	defer teardown()
+
+	_, err := client.Repositories.TestHook(context.Background(), "%", "%", 1)
+	testURLParseError(t, err)
+}
diff --git a/test/fields/fields.go b/test/fields/fields.go
index 9e3ef3d2..5df051e2 100644
--- a/test/fields/fields.go
+++ b/test/fields/fields.go
@@ -25,7 +25,7 @@ import (
 	"reflect"
 	"strings"
 
-	"github.com/google/go-github/v23/github"
+	"github.com/google/go-github/v24/github"
 	"golang.org/x/oauth2"
 )