Repository: google/go-github (go)

Issue:
Bug with how AcceptedError is handled
Computing stats, `ListContributorsStats`, will return an `AcceptedError` and a body that is a `JSON` object and not a `JSON` array. However, it seems this will try to decode it as a `JSON` array causing a serialization error.

Commit that caused the bug... this seems to be a breaking change as well; I guess it is now obvious that it is:
https://github.com/google/go-github/commit/88eb4e9dc91cb2db1bbc56c936f1ee39bbe72ba5

My suggestion would be to internally type alias the `[]ContributorStats` and add a `ImmediateAcceptedErrorReturn` interface that can be casted during the `client.Do` function. If it is castable to an `ImmediateAcceptedErrorReturn`, it will immediately return the `AcceptedError` and not try to unmarshal the body. This makes it completely backwards compatible. I'll see if I can get some time to do this myself! :)

Pull request:
fixes AcceptedError unmarshaling
Fixes #1022 

`AcceptedError` had a change in behavior in https://github.com/google/go-github/commit/88eb4e9dc91cb2db1bbc56c936f1ee39bbe72ba5

This change adds a `Raw` field to `AcceptedError`s. This allows for per API handling of the accepted error.

Patch:
diff --git a/github/github.go b/github/github.go
index af1f1b23..320b8104 100644
--- a/github/github.go
+++ b/github/github.go
@@ -502,13 +502,23 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
 
 	err = CheckResponse(resp)
 	if err != nil {
-		// Even though there was an error, we still return the response
-		// in case the caller wants to inspect it further.
-		// However, if the error is AcceptedError, decode it below before
-		// returning from this function and closing the response body.
-		if _, ok := err.(*AcceptedError); !ok {
-			return response, err
+		// Special case for AcceptedErrors. If an AcceptedError
+		// has been encountered, the response's payload will be
+		// added to the AcceptedError and returned.
+		//
+		// Issue #1022
+		aerr, ok := err.(*AcceptedError)
+		if ok {
+			b, readErr := ioutil.ReadAll(resp.Body)
+			if readErr != nil {
+				return response, readErr
+			}
+
+			aerr.Raw = b
+			return response, aerr
 		}
+
+		return response, err
 	}
 
 	if v != nil {
@@ -610,7 +620,10 @@ func (r *RateLimitError) Error() string {
 // Technically, 202 Accepted is not a real error, it's just used to
 // indicate that results are not ready yet, but should be available soon.
 // The request can be repeated after some time.
-type AcceptedError struct{}
+type AcceptedError struct {
+	// Raw contains the response body.
+	Raw []byte
+}
 
 func (*AcceptedError) Error() string {
 	return "job scheduled on GitHub side; try again later"
diff --git a/github/repos_forks.go b/github/repos_forks.go
index d0bff544..bfff27bb 100644
--- a/github/repos_forks.go
+++ b/github/repos_forks.go
@@ -8,6 +8,8 @@ package github
 import (
 	"context"
 	"fmt"
+
+	"encoding/json"
 )
 
 // RepositoryListForksOptions specifies the optional parameters to the
@@ -78,10 +80,15 @@ func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string
 
 	fork := new(Repository)
 	resp, err := s.client.Do(ctx, req, fork)
-	if _, ok := err.(*AcceptedError); ok {
-		return fork, resp, err
-	}
 	if err != nil {
+		// Persist AcceptedError's metadata to the Repository object.
+		if aerr, ok := err.(*AcceptedError); ok {
+			if err := json.Unmarshal(aerr.Raw, fork); err != nil {
+				return fork, resp, err
+			}
+
+			return fork, resp, err
+		}
 		return nil, resp, err
 	}
 


Test patch:
diff --git a/github/repos_stats_test.go b/github/repos_stats_test.go
index e0d7be77..055504d7 100644
--- a/github/repos_stats_test.go
+++ b/github/repos_stats_test.go
@@ -209,3 +209,28 @@ func TestRepositoriesService_ListPunchCard(t *testing.T) {
 		t.Errorf("RepositoriesService.ListPunchCard returned %+v, want %+v", card, want)
 	}
 }
+
+func TestRepositoriesService_AcceptedError(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		// This response indicates the fork will happen asynchronously.
+		w.WriteHeader(http.StatusAccepted)
+		fmt.Fprint(w, `{"id":1}`)
+	})
+
+	stats, _, err := client.Repositories.ListContributorsStats(context.Background(), "o", "r")
+	if err == nil {
+		t.Errorf("RepositoriesService.AcceptedError should have returned an error")
+	}
+
+	if _, ok := err.(*AcceptedError); !ok {
+		t.Errorf("RepositoriesService.AcceptedError returned an AcceptedError: %v", err)
+	}
+
+	if stats != nil {
+		t.Errorf("RepositoriesService.AcceptedError expected stats to be nil: %v", stats)
+	}
+}