Skip to content
This repository was archived by the owner on Jan 8, 2024. It is now read-only.

Add the ability to instrument a graphql request #8

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

edersonmf
Copy link

A Chain of Responsibility pattern is employed to allow
callers adding custom instrumentation targeting graphql
request and response objects.
A couple good use cases for this are request logging,
caching, and certain kinds of auth (e.g. bearer token renewal).

In order to proceed of this kind of support a small refactor
was required. So, there are somee changes that impact directly
the previous structure of this module.

chain.go Outdated
Comment on lines 57 to 71
ResponseParser interface {
parseResponse() (*GraphResponse, Error)
}

Action interface {
call() ResponseParser
}

Validated interface {
createRequest() Action
}

Init interface {
validate() Validated
}

Choose a reason for hiding this comment

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

Do we need all these different interfaces for defining private methods from a private struct? Defining public interfaces with private methods seems a bit off to me.

Maybe keeping this as different interfaces, but having the methods public and their implementations separated could be a better approach? This way we could allow the users of our lib to only substitute a part of the chain (e.g. they could annotate one of the steps with a custom error handler, counter, etc) instead of requiring them to re-implement the whole lib.

Copy link
Author

Choose a reason for hiding this comment

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

That's a good point and I was wondering that as well.
Let me submit another version of it. Let me know your thoughts.
Thanks!

A Chain of Responsibility pattern is employed to allow
callers adding custom instrumentation targeting graphql
request and response objects.
A couple good use cases for this are request logging,
caching, and certain kinds of auth (e.g. bearer token renewal).

In order to proceed of this kind of support a small refactor
was required. So, there are somee changes that impact directly
the previous structure of this module.
c.logf("<< %s", buf.String())
resp := c.operation.ResponseBodyAs()
var gr *GraphResponse
if c.operation.IsMutation() {
Copy link

@GiancarloJung GiancarloJung Feb 15, 2022

Choose a reason for hiding this comment

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

I created the Mutation struct to make the result.Successful field control if the request was of the Request (query) or Mutation type and based on the type of the request this flow decided what flow was gonna be used. If you think is easier to control this by a boolean flag we could also remove some of that logic and merge the Operation and Request interfaces and make the naming simpler, wdyt?

@@ -5,147 +5,154 @@ import (
"net/http"
)

// Request is a GraphQL request.
// queryOperation is a GraphQL defaultRequest.
Copy link

@GiancarloJung GiancarloJung Feb 15, 2022

Choose a reason for hiding this comment

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

Suggested change
// queryOperation is a GraphQL defaultRequest.

✂️

Comment on lines +53 to +55
)
// Deprecated: in favor of NewGraphOperation
func NewRequest(q string) Operation {

Choose a reason for hiding this comment

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

Suggested change
)
// Deprecated: in favor of NewGraphOperation
func NewRequest(q string) Operation {
)
// Deprecated: in favor of NewGraphOperation
func NewRequest(q string) Operation {

Mutation struct {
Req *Req
// queryOperation is the regular graphQL query operation
queryOperation struct {

Choose a reason for hiding this comment

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

A request could be either a query or a mutation if struct can be both my suggestion is to name it defaultOperation, the field queryName could also be just name. wdyt?

Copy link

@GiancarloJung GiancarloJung left a comment

Choose a reason for hiding this comment

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

The refactor looks great, and I like the chain principle, could you also provide some examples on how to use its benefits on some tests? I also got a little confused between the names on the request.go file between the interfaces and the structs, but I left a comment about the operation that could make this simpler =]

Comment on lines +60 to +61
// client.log = func(s string) { log.Println(s) }
log func(s string)

Choose a reason for hiding this comment

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

Could this have a setter function? I'm not sure we could do this since this is a private field 🤔

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants