From 4d35b2a5bd4c410df95638c0104f391aa92c4782 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 8 Oct 2019 12:04:58 -0700 Subject: [PATCH 01/14] Add simple graphql function --- graphql/client.go | 126 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 graphql/client.go diff --git a/graphql/client.go b/graphql/client.go new file mode 100644 index 000000000..41cb8cdcd --- /dev/null +++ b/graphql/client.go @@ -0,0 +1,126 @@ +package graphql + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os/user" + "regexp" +) + +type graphQLResponse struct { + Data interface{} + Errors []struct { + Message string + } +} + +/* +GraphQL usage + +type repoResponse struct { + repository struct { + createdAt string + } +} + +query := `query { + repository(owner: "golang", name: "go") { + createdAt + } +}` + +variables := map[string]string{} + +var resp repoResponse +err := graphql(query, map[string]string{}, &resp) +if err != nil { + panic(err) +} + +fmt.Printf("%+v\n", resp) +*/ +func GraphQL(query string, variables map[string]string, v interface{}) error { + url := "https://api.github.com/graphql" + reqBody, err := json.Marshal(map[string]interface{}{"query": query, "variables": variables}) + if err != nil { + panic(err) + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody)) + if err != nil { + panic(err) + } + + req.Header.Set("Authorization", "token "+getToken()) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + return handleResponse(resp, v) +} + +func handleResponse(resp *http.Response, v interface{}) error { + success := resp.StatusCode >= 200 && resp.StatusCode < 300 + if success { + gr := &graphQLResponse{Data: v} + err := json.NewDecoder(resp.Body).Decode(gr) + if err != nil { + return err + } + if len(gr.Errors) > 0 { + errorMessages := gr.Errors[0].Message + for _, e := range gr.Errors[1:] { + errorMessages += ", " + e.Message + } + return fmt.Errorf("graphql error: '%s'", errorMessages) + } + return nil + } + + return handleHTTPError(resp) +} + +func handleHTTPError(resp *http.Response) error { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + var message string + var parsedBody struct { + Message string `json:"message"` + } + err = json.Unmarshal(body, &parsedBody) + if err != nil { + message = string(body) + } else { + message = parsedBody.Message + } + + return fmt.Errorf("http error, '%s' failed (%d): '%s'", resp.Request.URL, resp.StatusCode, message) +} + +// THIS IS A BULLSHIT FUNCTION THAT SHOULD BE REMOVED +func getToken() string { + usr, err := user.Current() + if err != nil { + panic(err) + } + + content, err := ioutil.ReadFile(usr.HomeDir + "/.config/hub") + if err != nil { + panic(err) + } + + r := regexp.MustCompile(`oauth_token: (\w+)`) + token := r.FindStringSubmatch(string(content)) + return token[1] +} From 212df1725df7f17191fdffbcbb2c322883e37f28 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Tue, 8 Oct 2019 16:40:10 -0700 Subject: [PATCH 02/14] Pull out queries --- command/pr.go | 93 ++------------------------ graphql/client.go | 8 ++- graphql/queries.go | 159 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 89 deletions(-) create mode 100644 graphql/queries.go diff --git a/command/pr.go b/command/pr.go index e21d66e74..78557c999 100644 --- a/command/pr.go +++ b/command/pr.go @@ -3,8 +3,8 @@ package command import ( "fmt" - "github.com/github/gh-cli/git" - "github.com/github/gh-cli/github" + "github.com/github/gh-cli/graphql" + "github.com/spf13/cobra" ) @@ -32,89 +32,10 @@ var prListCmd = &cobra.Command{ }, } -type prFilter int - -const ( - createdByViewer prFilter = iota - reviewRequested -) - func ExecutePr() { - // prsForCurrentBranch := pullRequestsForCurrentBranch() - prsCreatedByViewer := pullRequests(createdByViewer) - // prsRequestingReview := pullRequests(reviewRequested) - - fmt.Printf("🌭 count! %d\n", len(prsCreatedByViewer)) -} - -type searchBody struct { - Items []github.PullRequest `json:"items"` -} - -func pullRequestsForCurrentBranch() []github.PullRequest { - project := project() - client := github.NewClient(project.Host) - currentBranch, error := git.Head() - if error != nil { - panic(error) - } - - headWithOwner := fmt.Sprintf("%s:%s", project.Owner, currentBranch) - filterParams := map[string]interface{}{"headWithOwner": headWithOwner} - prs, error := client.FetchPullRequests(&project, filterParams, 10, nil) - if error != nil { - panic(error) - } - - return prs -} - -func pullRequests(filter prFilter) []github.PullRequest { - project := project() - client := github.NewClient(project.Host) - owner := project.Owner - name := project.Name - user, error := client.CurrentUser() - if error != nil { - panic(error) - } - - var headers map[string]string - var q string - if filter == createdByViewer { - q = fmt.Sprintf("user:%s repo:%s state:open is:pr author:%s", owner, name, user.Login) - } else if filter == reviewRequested { - q = fmt.Sprintf("user:%s repo:%s state:open review-requested:%s", owner, name, user.Login) - } else { - panic("This is not a fitler") - } - - data := map[string]interface{}{"q": q} - - response, error := client.GenericAPIRequest("GET", "search/issues", data, headers, 60) - if error != nil { - panic(fmt.Sprintf("GenericAPIRequest failed %+v", error)) - } - searchBody := searchBody{} - error = response.Unmarshal(&searchBody) - if error != nil { - panic(fmt.Sprintf("Unmarshal failed %+v", error)) - } - - return searchBody.Items -} - -func project() github.Project { - remotes, error := github.Remotes() - if error != nil { - panic(error) - } - - for _, remote := range remotes { - if project, error := remote.Project(); error == nil { - return *project - } - } - - panic("Could not get the project. What is a project? I don't know, it's kind of like a git repository I think?") + prPayload, err := graphql.PullRequests() + if err != nil { + panic(err) + } + fmt.Printf("%+v!\n", prPayload) } diff --git a/graphql/client.go b/graphql/client.go index 41cb8cdcd..5c154a453 100644 --- a/graphql/client.go +++ b/graphql/client.go @@ -18,7 +18,7 @@ type graphQLResponse struct { } /* -GraphQL usage +graphQL usage type repoResponse struct { repository struct { @@ -42,7 +42,7 @@ if err != nil { fmt.Printf("%+v\n", resp) */ -func GraphQL(query string, variables map[string]string, v interface{}) error { +func graphQL(query string, variables map[string]string, v interface{}) error { url := "https://api.github.com/graphql" reqBody, err := json.Marshal(map[string]interface{}{"query": query, "variables": variables}) if err != nil { @@ -108,7 +108,9 @@ func handleHTTPError(resp *http.Response) error { return fmt.Errorf("http error, '%s' failed (%d): '%s'", resp.Request.URL, resp.StatusCode, message) } -// THIS IS A BULLSHIT FUNCTION THAT SHOULD BE REMOVED +// TODO: THIS IS NO GOOD. I need to figure out if the GraphQL function has direct +// access to the token, or if we should pass the token into the GraphQL function. For +// now I'm asuming that this has direct access so I'm simulating that with this function. func getToken() string { usr, err := user.Current() if err != nil { diff --git a/graphql/queries.go b/graphql/queries.go new file mode 100644 index 000000000..dec455f71 --- /dev/null +++ b/graphql/queries.go @@ -0,0 +1,159 @@ +package graphql + +import ( + "fmt" + "strings" + + "github.com/github/gh-cli/git" + "github.com/github/gh-cli/github" +) + +type PullRequestsPayload struct { + viewerCreated []PullRequest + reviewRequested []PullRequest + currentPR *PullRequest +} + +type PullRequest struct { + Number int + Title string + URL string + HeadRefName string +} + +func PullRequests() (PullRequestsPayload, error) { + type edges struct { + Edges []PullRequest + PageInfo struct { + HasNextPage bool + EndCursor string + } + } + + type response struct { + Repository struct { + PullRequests edges + } + ViewerCreated edges + ReviewRequested edges + } + + query := ` + fragment pr on PullRequest { + number + title + url + } + + query($owner: String!, $repo: String!, $headRefName: String!, $viewerQuery: String!, $reviewerQuery: String!, $per_page: Int = 10) { + repository(owner: $owner, name: $repo) { + pullRequests(headRefName: $headRefName, first: 1) { + edges { + node { + ...pr + } + } + } + } + viewerCreated: search(query: $viewerQuery, type: ISSUE, first: $per_page) { + edges { + node { + ...pr + } + } + pageInfo { + hasNextPage + } + } + reviewRequested: search(query: $reviewerQuery, type: ISSUE, first: $per_page) { + edges { + node { + ...pr + } + } + pageInfo { + hasNextPage + } + } + } + ` + + project := project() + owner := project.Owner + repo := project.Name + currentBranch := currentBranch() + + viewerQuery := fmt.Sprintf("repo:%s/%s state:open is:pr author:%s", owner, repo, currentUsername()) + reviewerQuery := fmt.Sprintf("repo:%s/%s state:open review-requested:%s", owner, repo, currentUsername()) + + variables := map[string]string{ + "viewerQuery": viewerQuery, + "reviewerQuery": reviewerQuery, + "owner": owner, + "repo": repo, + "headRefName": currentBranch, + } + + var resp response + err := graphQL(query, variables, &resp) + if err != nil { + return PullRequestsPayload{}, err + } + + var viewerCreated []PullRequest + for _, pr := range resp.ViewerCreated.Edges { + viewerCreated = append(viewerCreated, pr) + } + + var reviewRequested []PullRequest + for _, pr := range resp.ReviewRequested.Edges { + reviewRequested = append(reviewRequested, pr) + } + + var currentPR *PullRequest + for _, pr := range resp.Repository.PullRequests.Edges { + currentPR = &pr + } + + payload := PullRequestsPayload{ + viewerCreated, + reviewRequested, + currentPR, + } + + return payload, nil +} + +// These will be replaced by nate's context stuff +// 💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥 +func project() github.Project { + remotes, error := github.Remotes() + if error != nil { + panic(error) + } + + for _, remote := range remotes { + if project, error := remote.Project(); error == nil { + return *project + } + } + + panic("Could not get the project. What is a project? I don't know, it's kind of like a git repository I think?") +} + +func currentBranch() string { + currentBranch, err := git.Head() + if err != nil { + panic(err) + } + + return strings.Replace(currentBranch, "refs/heads/", "", 1) +} + +func currentUsername() string { + host, err := github.CurrentConfig().DefaultHost() + if err != nil { + panic(err) + } + return host.User +} From bcf3eb75c85d97e9d5568d51e1bc7dfe317b0708 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 9 Oct 2019 10:23:47 -0700 Subject: [PATCH 03/14] Add debug output --- command/pr.go | 18 +++++++++++++++++- graphql/client.go | 41 ++++++++++++++++++++++++++++++----------- graphql/queries.go | 6 +++--- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/command/pr.go b/command/pr.go index 78557c999..67cc58468 100644 --- a/command/pr.go +++ b/command/pr.go @@ -37,5 +37,21 @@ func ExecutePr() { if err != nil { panic(err) } - fmt.Printf("%+v!\n", prPayload) + + if prPayload.CurrentPR != nil { + fmt.Printf("Current Pr\n") + printPr(*prPayload.CurrentPR) + } + for _, pr := range prPayload.ViewerCreated { + fmt.Printf("Your Prs\n") + printPr(pr) + } + for _, pr := range prPayload.ReviewRequested { + fmt.Printf("Prs you need to review\n") + printPr(pr) + } +} + +func printPr(pr graphql.PullRequest) { + fmt.Printf("%d %s [%s]\n", pr.Number, pr.Title, pr.HeadRefName) } diff --git a/graphql/client.go b/graphql/client.go index 5c154a453..71ee8ce13 100644 --- a/graphql/client.go +++ b/graphql/client.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "os/user" "regexp" ) @@ -53,6 +54,7 @@ func graphQL(query string, variables map[string]string, v interface{}) error { if err != nil { panic(err) } + debugRequest(req, reqBody) req.Header.Set("Authorization", "token "+getToken()) req.Header.Set("Content-Type", "application/json; charset=utf-8") @@ -64,14 +66,20 @@ func graphQL(query string, variables map[string]string, v interface{}) error { } defer resp.Body.Close() - return handleResponse(resp, v) + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + debugResponse(resp, string(body)) + return handleResponse(resp, body, v) } -func handleResponse(resp *http.Response, v interface{}) error { +func handleResponse(resp *http.Response, body []byte, v interface{}) error { success := resp.StatusCode >= 200 && resp.StatusCode < 300 + if success { gr := &graphQLResponse{Data: v} - err := json.NewDecoder(resp.Body).Decode(gr) + err := json.Unmarshal(body, &gr) if err != nil { return err } @@ -85,20 +93,15 @@ func handleResponse(resp *http.Response, v interface{}) error { return nil } - return handleHTTPError(resp) + return handleHTTPError(resp, body) } -func handleHTTPError(resp *http.Response) error { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - +func handleHTTPError(resp *http.Response, body []byte) error { var message string var parsedBody struct { Message string `json:"message"` } - err = json.Unmarshal(body, &parsedBody) + err := json.Unmarshal(body, &parsedBody) if err != nil { message = string(body) } else { @@ -126,3 +129,19 @@ func getToken() string { token := r.FindStringSubmatch(string(content)) return token[1] } + +func debugRequest(req *http.Request, body string) { + if _, ok := os.LookupEnv("DEBUG"); !ok { + return + } + + fmt.Printf("DEBUG: GraphQL query to %s:\n %s\n\n", req.URL, body) +} + +func debugResponse(resp *http.Response, body string) { + if _, ok := os.LookupEnv("DEBUG"); !ok { + return + } + + fmt.Printf("DEBUG: GraphQL response:\n%+v\n\n%s\n\n", resp, body) +} diff --git a/graphql/queries.go b/graphql/queries.go index dec455f71..a885e5c5a 100644 --- a/graphql/queries.go +++ b/graphql/queries.go @@ -9,9 +9,9 @@ import ( ) type PullRequestsPayload struct { - viewerCreated []PullRequest - reviewRequested []PullRequest - currentPR *PullRequest + ViewerCreated []PullRequest + ReviewRequested []PullRequest + CurrentPR *PullRequest } type PullRequest struct { From 8b69ca39196f5adb98ba8626b51238dbc8cbc264 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 9 Oct 2019 11:34:35 -0700 Subject: [PATCH 04/14] Rename package to api --- {graphql => api}/client.go | 6 +++--- {graphql => api}/queries.go | 19 +++++++++++-------- command/pr.go | 11 ++++++----- 3 files changed, 20 insertions(+), 16 deletions(-) rename {graphql => api}/client.go (96%) rename {graphql => api}/queries.go (89%) diff --git a/graphql/client.go b/api/client.go similarity index 96% rename from graphql/client.go rename to api/client.go index 71ee8ce13..ec5f0dbcd 100644 --- a/graphql/client.go +++ b/api/client.go @@ -1,4 +1,4 @@ -package graphql +package api import ( "bytes" @@ -54,7 +54,7 @@ func graphQL(query string, variables map[string]string, v interface{}) error { if err != nil { panic(err) } - debugRequest(req, reqBody) + debugRequest(req, string(reqBody)) req.Header.Set("Authorization", "token "+getToken()) req.Header.Set("Content-Type", "application/json; charset=utf-8") @@ -135,7 +135,7 @@ func debugRequest(req *http.Request, body string) { return } - fmt.Printf("DEBUG: GraphQL query to %s:\n %s\n\n", req.URL, body) + fmt.Printf("DEBUG: GraphQL request to %s:\n %s\n\n", req.URL, body) } func debugResponse(resp *http.Response, body string) { diff --git a/graphql/queries.go b/api/queries.go similarity index 89% rename from graphql/queries.go rename to api/queries.go index a885e5c5a..928b40585 100644 --- a/graphql/queries.go +++ b/api/queries.go @@ -1,4 +1,4 @@ -package graphql +package api import ( "fmt" @@ -23,7 +23,9 @@ type PullRequest struct { func PullRequests() (PullRequestsPayload, error) { type edges struct { - Edges []PullRequest + Edges []struct { + Node PullRequest + } PageInfo struct { HasNextPage bool EndCursor string @@ -43,6 +45,7 @@ func PullRequests() (PullRequestsPayload, error) { number title url + headRefName } query($owner: String!, $repo: String!, $headRefName: String!, $viewerQuery: String!, $reviewerQuery: String!, $per_page: Int = 10) { @@ -101,18 +104,18 @@ func PullRequests() (PullRequestsPayload, error) { } var viewerCreated []PullRequest - for _, pr := range resp.ViewerCreated.Edges { - viewerCreated = append(viewerCreated, pr) + for _, edge := range resp.ViewerCreated.Edges { + viewerCreated = append(viewerCreated, edge.Node) } var reviewRequested []PullRequest - for _, pr := range resp.ReviewRequested.Edges { - reviewRequested = append(reviewRequested, pr) + for _, edge := range resp.ReviewRequested.Edges { + reviewRequested = append(reviewRequested, edge.Node) } var currentPR *PullRequest - for _, pr := range resp.Repository.PullRequests.Edges { - currentPR = &pr + for _, edge := range resp.Repository.PullRequests.Edges { + currentPR = &edge.Node } payload := PullRequestsPayload{ diff --git a/command/pr.go b/command/pr.go index 67cc58468..9522b2a75 100644 --- a/command/pr.go +++ b/command/pr.go @@ -3,6 +3,7 @@ package command import ( "fmt" + "github.com/github/gh-cli/api" "github.com/github/gh-cli/graphql" "github.com/spf13/cobra" @@ -38,20 +39,20 @@ func ExecutePr() { panic(err) } + fmt.Printf("Current Pr\n") if prPayload.CurrentPR != nil { - fmt.Printf("Current Pr\n") printPr(*prPayload.CurrentPR) } + fmt.Printf("Your Prs\n") for _, pr := range prPayload.ViewerCreated { - fmt.Printf("Your Prs\n") printPr(pr) } + fmt.Printf("Prs you need to review\n") for _, pr := range prPayload.ReviewRequested { - fmt.Printf("Prs you need to review\n") printPr(pr) } } -func printPr(pr graphql.PullRequest) { - fmt.Printf("%d %s [%s]\n", pr.Number, pr.Title, pr.HeadRefName) +func printPr(pr api.PullRequest) { + fmt.Printf(" #%d %s [%s]\n", pr.Number, pr.Title, pr.HeadRefName) } From b7dda5ef25aac89339229924588e85cbdf97e6e8 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 9 Oct 2019 11:39:26 -0700 Subject: [PATCH 05/14] tweaks --- api/client.go | 45 ++++++++++++++++++++++----------------------- api/queries.go | 3 +-- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/api/client.go b/api/client.go index ec5f0dbcd..3ca9cc25d 100644 --- a/api/client.go +++ b/api/client.go @@ -22,8 +22,8 @@ type graphQLResponse struct { graphQL usage type repoResponse struct { - repository struct { - createdAt string + Repository struct { + CreatedAt string } } @@ -54,11 +54,11 @@ func graphQL(query string, variables map[string]string, v interface{}) error { if err != nil { panic(err) } - debugRequest(req, string(reqBody)) - req.Header.Set("Authorization", "token "+getToken()) req.Header.Set("Content-Type", "application/json; charset=utf-8") + debugRequest(req, string(reqBody)) + client := &http.Client{} resp, err := client.Do(req) if err != nil { @@ -71,6 +71,7 @@ func graphQL(query string, variables map[string]string, v interface{}) error { return err } debugResponse(resp, string(body)) + return handleResponse(resp, body, v) } @@ -111,25 +112,6 @@ func handleHTTPError(resp *http.Response, body []byte) error { return fmt.Errorf("http error, '%s' failed (%d): '%s'", resp.Request.URL, resp.StatusCode, message) } -// TODO: THIS IS NO GOOD. I need to figure out if the GraphQL function has direct -// access to the token, or if we should pass the token into the GraphQL function. For -// now I'm asuming that this has direct access so I'm simulating that with this function. -func getToken() string { - usr, err := user.Current() - if err != nil { - panic(err) - } - - content, err := ioutil.ReadFile(usr.HomeDir + "/.config/hub") - if err != nil { - panic(err) - } - - r := regexp.MustCompile(`oauth_token: (\w+)`) - token := r.FindStringSubmatch(string(content)) - return token[1] -} - func debugRequest(req *http.Request, body string) { if _, ok := os.LookupEnv("DEBUG"); !ok { return @@ -145,3 +127,20 @@ func debugResponse(resp *http.Response, body string) { fmt.Printf("DEBUG: GraphQL response:\n%+v\n\n%s\n\n", resp, body) } + +// TODO: Everything below this line will be removed when Nate's context work is complete +func getToken() string { + usr, err := user.Current() + if err != nil { + panic(err) + } + + content, err := ioutil.ReadFile(usr.HomeDir + "/.config/hub") + if err != nil { + panic(err) + } + + r := regexp.MustCompile(`oauth_token: (\w+)`) + token := r.FindStringSubmatch(string(content)) + return token[1] +} diff --git a/api/queries.go b/api/queries.go index 928b40585..aa26c0be0 100644 --- a/api/queries.go +++ b/api/queries.go @@ -127,8 +127,7 @@ func PullRequests() (PullRequestsPayload, error) { return payload, nil } -// These will be replaced by nate's context stuff -// 💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥 +// TODO: Everything below this line will be removed when Nate's context work is complete func project() github.Project { remotes, error := github.Remotes() if error != nil { From 80dc17778ea71d2e6e825d2c186c7124955eed19 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 9 Oct 2019 11:54:37 -0700 Subject: [PATCH 06/14] Use correct package name --- command/pr.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/command/pr.go b/command/pr.go index 9522b2a75..4b9c02732 100644 --- a/command/pr.go +++ b/command/pr.go @@ -4,8 +4,6 @@ import ( "fmt" "github.com/github/gh-cli/api" - "github.com/github/gh-cli/graphql" - "github.com/spf13/cobra" ) @@ -34,7 +32,7 @@ var prListCmd = &cobra.Command{ } func ExecutePr() { - prPayload, err := graphql.PullRequests() + prPayload, err := api.PullRequests() if err != nil { panic(err) } From a35441cb6901b04ea248a0c68df5769d7e8d8b71 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 9 Oct 2019 13:15:03 -0700 Subject: [PATCH 07/14] Add version --- api/client.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/client.go b/api/client.go index 3ca9cc25d..b58786366 100644 --- a/api/client.go +++ b/api/client.go @@ -9,6 +9,8 @@ import ( "os" "os/user" "regexp" + + "github.com/github/gh-cli/version" ) type graphQLResponse struct { @@ -56,6 +58,7 @@ func graphQL(query string, variables map[string]string, v interface{}) error { } req.Header.Set("Authorization", "token "+getToken()) req.Header.Set("Content-Type", "application/json; charset=utf-8") + req.Header.Set("User-Agent", "GitHub CLI "+version.Version) debugRequest(req, string(reqBody)) From 3a931ea83ec6e4f9fb56cd16f3528b710cc611c4 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 9 Oct 2019 13:15:08 -0700 Subject: [PATCH 08/14] spacing --- api/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/client.go b/api/client.go index b58786366..82c72d9e1 100644 --- a/api/client.go +++ b/api/client.go @@ -73,8 +73,8 @@ func graphQL(query string, variables map[string]string, v interface{}) error { if err != nil { return err } - debugResponse(resp, string(body)) + debugResponse(resp, string(body)) return handleResponse(resp, body, v) } From 0f6daa0310694527ca8186e8847a32f6ba096b0d Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 9 Oct 2019 15:32:41 -0700 Subject: [PATCH 09/14] Don't panic --- api/client.go | 22 ++++++++++++++-------- command/pr.go | 11 ++++++++--- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/api/client.go b/api/client.go index 82c72d9e1..fc6895ca1 100644 --- a/api/client.go +++ b/api/client.go @@ -49,14 +49,20 @@ func graphQL(query string, variables map[string]string, v interface{}) error { url := "https://api.github.com/graphql" reqBody, err := json.Marshal(map[string]interface{}{"query": query, "variables": variables}) if err != nil { - panic(err) + return err } req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody)) if err != nil { - panic(err) + return err } - req.Header.Set("Authorization", "token "+getToken()) + + token, err := getToken() + if err != nil { + return err + } + + req.Header.Set("Authorization", "token "+token) req.Header.Set("Content-Type", "application/json; charset=utf-8") req.Header.Set("User-Agent", "GitHub CLI "+version.Version) @@ -65,7 +71,7 @@ func graphQL(query string, variables map[string]string, v interface{}) error { client := &http.Client{} resp, err := client.Do(req) if err != nil { - panic(err) + return err } defer resp.Body.Close() @@ -132,18 +138,18 @@ func debugResponse(resp *http.Response, body string) { } // TODO: Everything below this line will be removed when Nate's context work is complete -func getToken() string { +func getToken() (string, error) { usr, err := user.Current() if err != nil { - panic(err) + return "", err } content, err := ioutil.ReadFile(usr.HomeDir + "/.config/hub") if err != nil { - panic(err) + return "", err } r := regexp.MustCompile(`oauth_token: (\w+)`) token := r.FindStringSubmatch(string(content)) - return token[1] + return token[1], nil } diff --git a/command/pr.go b/command/pr.go index 4b9c02732..79d176e03 100644 --- a/command/pr.go +++ b/command/pr.go @@ -27,14 +27,17 @@ var prListCmd = &cobra.Command{ Use: "list", Short: "List pull requests", Run: func(cmd *cobra.Command, args []string) { - ExecutePr() + err := ExecutePr() + if err != nil { + panic(err) // In the future this should handle the error better, but for now panic seems like a valid reaction + } }, } -func ExecutePr() { +func ExecutePr() error { prPayload, err := api.PullRequests() if err != nil { - panic(err) + return error } fmt.Printf("Current Pr\n") @@ -49,6 +52,8 @@ func ExecutePr() { for _, pr := range prPayload.ReviewRequested { printPr(pr) } + + return nil } func printPr(pr api.PullRequest) { From ddb49557d82fb45cacccff59ac4467b380643f5f Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Wed, 9 Oct 2019 15:42:57 -0700 Subject: [PATCH 10/14] Use the correct variable name :) --- command/pr.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/pr.go b/command/pr.go index 79d176e03..d1caadeae 100644 --- a/command/pr.go +++ b/command/pr.go @@ -37,7 +37,7 @@ var prListCmd = &cobra.Command{ func ExecutePr() error { prPayload, err := api.PullRequests() if err != nil { - return error + return err } fmt.Printf("Current Pr\n") From 2dc521d589ecc26a10de11ac21d353887dbbff06 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Fri, 11 Oct 2019 14:23:46 -0700 Subject: [PATCH 11/14] A non-successful response returns early --- api/client.go | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/api/client.go b/api/client.go index fc6895ca1..6a9644284 100644 --- a/api/client.go +++ b/api/client.go @@ -87,23 +87,25 @@ func graphQL(query string, variables map[string]string, v interface{}) error { func handleResponse(resp *http.Response, body []byte, v interface{}) error { success := resp.StatusCode >= 200 && resp.StatusCode < 300 - if success { - gr := &graphQLResponse{Data: v} - err := json.Unmarshal(body, &gr) - if err != nil { - return err - } - if len(gr.Errors) > 0 { - errorMessages := gr.Errors[0].Message - for _, e := range gr.Errors[1:] { - errorMessages += ", " + e.Message - } - return fmt.Errorf("graphql error: '%s'", errorMessages) - } - return nil + if !success { + return handleHTTPError(resp, body) } - return handleHTTPError(resp, body) + gr := &graphQLResponse{Data: v} + err := json.Unmarshal(body, &gr) + if err != nil { + return err + } + + if len(gr.Errors) > 0 { + errorMessages := gr.Errors[0].Message + for _, e := range gr.Errors[1:] { + errorMessages += ", " + e.Message + } + return fmt.Errorf("graphql error: '%s'", errorMessages) + } + return nil + } func handleHTTPError(resp *http.Response, body []byte) error { From 994e9eee1081e707d47713a6e2f3a58a3252c060 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Fri, 11 Oct 2019 14:28:26 -0700 Subject: [PATCH 12/14] Use RunE instead of panicking --- command/pr.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/command/pr.go b/command/pr.go index d1caadeae..3dc8821c8 100644 --- a/command/pr.go +++ b/command/pr.go @@ -26,11 +26,8 @@ work with pull requests.`, var prListCmd = &cobra.Command{ Use: "list", Short: "List pull requests", - Run: func(cmd *cobra.Command, args []string) { - err := ExecutePr() - if err != nil { - panic(err) // In the future this should handle the error better, but for now panic seems like a valid reaction - } + RunE: func(cmd *cobra.Command, args []string) error { + return ExecutePr() }, } From ed4f59fe306da5ff5184685fe504f6a39163c48b Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Fri, 11 Oct 2019 14:37:28 -0700 Subject: [PATCH 13/14] rename v to data --- api/client.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/client.go b/api/client.go index 6a9644284..0e45c31f1 100644 --- a/api/client.go +++ b/api/client.go @@ -45,7 +45,7 @@ if err != nil { fmt.Printf("%+v\n", resp) */ -func graphQL(query string, variables map[string]string, v interface{}) error { +func graphQL(query string, variables map[string]string, data interface{}) error { url := "https://api.github.com/graphql" reqBody, err := json.Marshal(map[string]interface{}{"query": query, "variables": variables}) if err != nil { @@ -81,17 +81,17 @@ func graphQL(query string, variables map[string]string, v interface{}) error { } debugResponse(resp, string(body)) - return handleResponse(resp, body, v) + return handleResponse(resp, body, data) } -func handleResponse(resp *http.Response, body []byte, v interface{}) error { +func handleResponse(resp *http.Response, body []byte, data interface{}) error { success := resp.StatusCode >= 200 && resp.StatusCode < 300 if !success { return handleHTTPError(resp, body) } - gr := &graphQLResponse{Data: v} + gr := &graphQLResponse{Data: data} err := json.Unmarshal(body, &gr) if err != nil { return err From 90b0a6c55a62084fcf93426e6aef27e138bc09e4 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Fri, 11 Oct 2019 14:43:04 -0700 Subject: [PATCH 14/14] use spaces --- api/queries.go | 76 +++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/api/queries.go b/api/queries.go index aa26c0be0..a4c023375 100644 --- a/api/queries.go +++ b/api/queries.go @@ -41,45 +41,45 @@ func PullRequests() (PullRequestsPayload, error) { } query := ` - fragment pr on PullRequest { - number - title - url - headRefName - } + fragment pr on PullRequest { + number + title + url + headRefName + } - query($owner: String!, $repo: String!, $headRefName: String!, $viewerQuery: String!, $reviewerQuery: String!, $per_page: Int = 10) { - repository(owner: $owner, name: $repo) { - pullRequests(headRefName: $headRefName, first: 1) { - edges { - node { - ...pr - } - } - } - } - viewerCreated: search(query: $viewerQuery, type: ISSUE, first: $per_page) { - edges { - node { - ...pr - } - } - pageInfo { - hasNextPage - } - } - reviewRequested: search(query: $reviewerQuery, type: ISSUE, first: $per_page) { - edges { - node { - ...pr - } - } - pageInfo { - hasNextPage - } - } - } - ` + query($owner: String!, $repo: String!, $headRefName: String!, $viewerQuery: String!, $reviewerQuery: String!, $per_page: Int = 10) { + repository(owner: $owner, name: $repo) { + pullRequests(headRefName: $headRefName, first: 1) { + edges { + node { + ...pr + } + } + } + } + viewerCreated: search(query: $viewerQuery, type: ISSUE, first: $per_page) { + edges { + node { + ...pr + } + } + pageInfo { + hasNextPage + } + } + reviewRequested: search(query: $reviewerQuery, type: ISSUE, first: $per_page) { + edges { + node { + ...pr + } + } + pageInfo { + hasNextPage + } + } + } + ` project := project() owner := project.Owner