Skip to content

Pull in the GraphQL API functionality#8

Merged
probablycorey merged 14 commits intomasterfrom
graphql
Oct 11, 2019
Merged

Pull in the GraphQL API functionality#8
probablycorey merged 14 commits intomasterfrom
graphql

Conversation

@probablycorey
Copy link
Contributor

This does a few things:

  1. I added an api package and a PullRequests function. I didn't expose a more generic GraphQL function because it seems like an implementation detail. Also, it keeps all the graphql queries and error handling out of our command files.
  2. I attempted to have good error messages. It should give us descriptive errors for HTTP, GitHub API, and GraphQL errors. I hate when API calls fail for unknown reasons 💢
  3. Use the DEBUG env to get log debug info from api calls. The output is not pretty and I have regret that I didn't make it easier to read. But a life without regrets is no life at all.

img

  1. I didn't add tests. I want to work on that in a follow up PR because this was already getting big.
  2. I did a shitty job with the output of the gh pr list command because the point of this PR is to use GraphQL, not to implement the gh pr list command.

I've left some inline comments below.

@probablycorey probablycorey requested a review from a team October 9, 2019 18:46
@probablycorey probablycorey self-assigned this Oct 9, 2019
api/client.go Outdated
success := resp.StatusCode >= 200 && resp.StatusCode < 300

if success {
gr := &graphQLResponse{Data: v}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still getting used to go's variable naming patterns. Is gr a bad name? Is resp better than response? I use v above because I've notice that pattern for interface{} values, but I don't understand why it is v.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think v is just short for value. i may not agree with go's terseness, but i think gr is a fine variable name in this context. when in rome.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think golang's "official" stance on guidelines for variable naming is that 1–2-letter (abbreviated) variable names are fine as long as the variable is used close to the place of its assignment.

PullRequests edges
}
ViewerCreated edges
ReviewRequested edges
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I was using the json tag syntax like json:"edges", but when I removed it everything still worked. I can't find any documentation but it seems like go handles this automatically.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default go tries to auto-guess field names for serializing. if it works, it works.

Copy link
Contributor

@vilmibm vilmibm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given that we're in master now i'd like to see us consistently avoiding panic in favor of go-style errors.

api/client.go Outdated
success := resp.StatusCode >= 200 && resp.StatusCode < 300

if success {
gr := &graphQLResponse{Data: v}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think v is just short for value. i may not agree with go's terseness, but i think gr is a fine variable name in this context. when in rome.

PullRequests edges
}
ViewerCreated edges
ReviewRequested edges
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default go tries to auto-guess field names for serializing. if it works, it works.

currentBranch, error := git.Head()
if error != nil {
panic(error)
prPayload, err := api.PullRequests()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes perfect sense in the context of the PR and shouldn't change. but for discussion's sake, i've been working on something like this (ignoring errors for brevity's sake):

ghRepo, _ := context.CurrentGitHubRepository()
prs, _ := ghRepo.GetPullRequestsByCurrentBranch()

the GitHubRepository struct has methods that would work with api. Does that seem like a good direction?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it ❤️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yess love the verbosity of it ✨

panic(error)
fmt.Printf("Current Pr\n")
if prPayload.CurrentPR != nil {
printPr(*prPayload.CurrentPR)
Copy link
Contributor

@vilmibm vilmibm Oct 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the CurrentPR and similar members are so specific; I wonder if it's worth putting those under some more specific function name like UserPullRequests or something to distinguish between a general list of PRs and the user-oriented lists we've been doing?

i don't know this is probably not urgent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem I ran into is how to have specific functions but keep it as one graphql request.

Copy link
Contributor

@vilmibm vilmibm Oct 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah, I meant keep the one single query but just name the collective output something more specific than "PullRequests" to indicate that it's a particular bundling / organization of PRs

@tierninho
Copy link
Contributor

@probablycorey What is the best way to trigger HTTP, GitHub API, and GraphQL errors? Thank you.

@probablycorey
Copy link
Contributor Author

@tierninho In the future the errors will pre presented better (now they say "panic: blah blah blah")

  • HTTP errors: turn off your wifi and run the command
  • GitHub API errors: add some garbage to the front of your token in ~/.config/hub
  • GraphQL errors: these only happen if you write the query wrong, but if you add some random characters to the query variable in api/queries.go you'll see the errors

Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how clean is the API client implementation that supports basically only one endpoint!

api/client.go Outdated
success := resp.StatusCode >= 200 && resp.StatusCode < 300

if success {
gr := &graphQLResponse{Data: v}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think golang's "official" stance on guidelines for variable naming is that 1–2-letter (abbreviated) variable names are fine as long as the variable is used close to the place of its assignment.

api/client.go Outdated
func handleResponse(resp *http.Response, body []byte, v interface{}) error {
success := resp.StatusCode >= 200 && resp.StatusCode < 300

if success {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was surprised that the "success" path is indented here and returns early, while the error path (handleHTTPError) continues in the main function body. The typical golang style is the other way around: error paths are indented and return early, and the most common (expected case) remains at the top level of a function body.

Would such a general guideline make sense for this logic as well?

command/pr.go Outdated
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the approach we've taken in the prototype branch re: cobra commands that potentially return an error:

RunE: func(cmd *cobra.Command, args []string) error {
  return ExecutePr()
}

I think it's easier to follow that here as well than to panic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I didn't know about the RunE method! Perfect!

currentBranch, error := git.Head()
if error != nil {
panic(error)
prPayload, err := api.PullRequests()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yess love the verbosity of it ✨

api/client.go Outdated
return handleResponse(resp, body, v)
}

func handleResponse(resp *http.Response, body []byte, v interface{}) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to use more descriptive argument names than "v" because function signatures can be shown in text editors/IDEs, and them having human-readable argument names helps understand the purpose of those arguments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent too long coming up with a new name and settled on "data". I ended up choosing this because it is what the go documentation uses for json.Unmarshal, which has very similar behavior.


func PullRequests() (PullRequestsPayload, error) {
type edges struct {
Edges []struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL:

  1. that one can define nested struct types directly in the parent struct definition
  2. that structs can also be defined within a function call 😲

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that structs can also be defined within a function call

I wasn't sure about this pattern. But since the struct is only used in the PullRequests function I figured it keeps everything more contained.

api/queries.go Outdated
pullRequests(headRefName: $headRefName, first: 1) {
edges {
node {
...pr
Copy link
Contributor

@mislav mislav Oct 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I observe here is how deeply some of these queries can be indented due to nesting. I'm predicting that we will even go deeper. Perhaps indenting by tab is not the best approach for GraphQL, since tabs are shown so wide by default (8 spaces in GitHub UI), and changes around GraphQL queries might be tricky to review due to online diff formatting. Not a huge issue right now; just something to keep in mind!

Copy link
Contributor Author

@probablycorey probablycorey Oct 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preferred solution would be to use tabs everywhere and make GitHub show tabs as two spaces, but I don't think that's an option. So spaces it is!

Copy link
Contributor

@vilmibm vilmibm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errors are consolidated and handling has been moved up, thanks! i'm a fan of mislav's suggestion of putting the panic even higher but i don't think that needs to be addressed here in this PR; i just didn't want a proliferation of new panics

@probablycorey probablycorey merged commit 2b75cd8 into master Oct 11, 2019
@mislav mislav deleted the graphql branch October 31, 2019 10:14
mislav pushed a commit that referenced this pull request Sep 28, 2021
handle errors in port forwarding
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants