Skip to content

Commit

Permalink
add Start/EndOperationParsing & Start/EndOperationValidation methods …
Browse files Browse the repository at this point in the history
…to Tracer
  • Loading branch information
vvakame committed Oct 30, 2018
1 parent 0d5c65b commit 83c7b2c
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 37 deletions.
93 changes: 62 additions & 31 deletions codegen/testserver/generated_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,34 @@ type testTracer struct {
append func(string)
}

func (tt *testTracer) StartOperationParsing(ctx context.Context) context.Context {
line := fmt.Sprintf("op:p:start:%d", tt.id)

tracerLogs, _ := ctx.Value("tracer").([]string)
ctx = context.WithValue(ctx, "tracer", append(append([]string{}, tracerLogs...), line))
tt.append(line)
return ctx
}

func (tt *testTracer) EndOperationParsing(ctx context.Context) {
tt.append(fmt.Sprintf("op:p:end:%d", tt.id))
}

func (tt *testTracer) StartOperationValidation(ctx context.Context) context.Context {
line := fmt.Sprintf("op:v:start:%d", tt.id)

tracerLogs, _ := ctx.Value("tracer").([]string)
ctx = context.WithValue(ctx, "tracer", append(append([]string{}, tracerLogs...), line))
tt.append(line)
return ctx
}

func (tt *testTracer) EndOperationValidation(ctx context.Context) {
tt.append(fmt.Sprintf("op:v:end:%d", tt.id))
}

func (tt *testTracer) StartOperationExecution(ctx context.Context) context.Context {
line := fmt.Sprintf("op:start:%d", tt.id)
line := fmt.Sprintf("op:e:start:%d", tt.id)

tracerLogs, _ := ctx.Value("tracer").([]string)
ctx = context.WithValue(ctx, "tracer", append(append([]string{}, tracerLogs...), line))
Expand All @@ -49,7 +75,7 @@ func (tt *testTracer) StartOperationExecution(ctx context.Context) context.Conte
}

func (tt *testTracer) StartFieldExecution(ctx context.Context, field graphql.CollectedField) context.Context {
line := fmt.Sprintf("field'a:start:%d:%s", tt.id, field.Name)
line := fmt.Sprintf("field'a:e:start:%d:%s", tt.id, field.Name)

tracerLogs, _ := ctx.Value("tracer").([]string)
ctx = context.WithValue(ctx, "tracer", append(append([]string{}, tracerLogs...), line))
Expand All @@ -58,7 +84,7 @@ func (tt *testTracer) StartFieldExecution(ctx context.Context, field graphql.Col
}

func (tt *testTracer) StartFieldResolverExecution(ctx context.Context, rc *graphql.ResolverContext) context.Context {
line := fmt.Sprintf("field'b:start:%d:%v", tt.id, rc.Path())
line := fmt.Sprintf("field'b:e:start:%d:%v", tt.id, rc.Path())

tracerLogs, _ := ctx.Value("tracer").([]string)
ctx = context.WithValue(ctx, "tracer", append(append([]string{}, tracerLogs...), line))
Expand All @@ -67,7 +93,7 @@ func (tt *testTracer) StartFieldResolverExecution(ctx context.Context, rc *graph
}

func (tt *testTracer) StartFieldChildExecution(ctx context.Context) context.Context {
line := fmt.Sprintf("field'c:start:%d", tt.id)
line := fmt.Sprintf("field'c:e:start:%d", tt.id)

tracerLogs, _ := ctx.Value("tracer").([]string)
ctx = context.WithValue(ctx, "tracer", append(append([]string{}, tracerLogs...), line))
Expand All @@ -76,11 +102,11 @@ func (tt *testTracer) StartFieldChildExecution(ctx context.Context) context.Cont
}

func (tt *testTracer) EndFieldExecution(ctx context.Context) {
tt.append(fmt.Sprintf("field:end:%d", tt.id))
tt.append(fmt.Sprintf("field:e:end:%d", tt.id))
}

func (tt *testTracer) EndOperationExecution(ctx context.Context) {
tt.append(fmt.Sprintf("op:end:%d", tt.id))
tt.append(fmt.Sprintf("op:e:end:%d", tt.id))
}

func TestGeneratedServer(t *testing.T) {
Expand Down Expand Up @@ -205,12 +231,14 @@ func TestGeneratedServer(t *testing.T) {
called := false
resolvers.userFriends = func(ctx context.Context, obj *User) ([]User, error) {
assert.Equal(t, []string{
"op:start:1", "op:start:2",
"field'a:start:1:user", "field'a:start:2:user",
"field'b:start:1:[user]", "field'b:start:2:[user]",
"field'c:start:1", "field'c:start:2",
"field'a:start:1:friends", "field'a:start:2:friends",
"field'b:start:1:[user friends]", "field'b:start:2:[user friends]",
"op:p:start:1", "op:p:start:2",
"op:v:start:1", "op:v:start:2",
"op:e:start:1", "op:e:start:2",
"field'a:e:start:1:user", "field'a:e:start:2:user",
"field'b:e:start:1:[user]", "field'b:e:start:2:[user]",
"field'c:e:start:1", "field'c:e:start:2",
"field'a:e:start:1:friends", "field'a:e:start:2:friends",
"field'b:e:start:1:[user friends]", "field'b:e:start:2:[user friends]",
}, ctx.Value("tracer"))
called = true
return []User{}, nil
Expand All @@ -223,25 +251,28 @@ func TestGeneratedServer(t *testing.T) {
mu.Lock()
defer mu.Unlock()
assert.Equal(t, []string{
"op:start:1", "op:start:2",

"field'a:start:1:user", "field'a:start:2:user",
"field'b:start:1:[user]", "field'b:start:2:[user]",
"field'c:start:1", "field'c:start:2",

"field'a:start:1:id", "field'a:start:2:id",
"field'b:start:1:[user id]", "field'b:start:2:[user id]",
"field'c:start:1", "field'c:start:2",
"field:end:2", "field:end:1",

"field'a:start:1:friends", "field'a:start:2:friends",
"field'b:start:1:[user friends]", "field'b:start:2:[user friends]",
"field'c:start:1", "field'c:start:2",
"field:end:2", "field:end:1",

"field:end:2", "field:end:1",

"op:end:2", "op:end:1",
"op:p:start:1", "op:p:start:2",
"op:p:end:2", "op:p:end:1",

"op:v:start:1", "op:v:start:2",
"op:v:end:2", "op:v:end:1",

"op:e:start:1", "op:e:start:2",

"field'a:e:start:1:user", "field'a:e:start:2:user",
"field'b:e:start:1:[user]", "field'b:e:start:2:[user]",
"field'c:e:start:1", "field'c:e:start:2",
"field'a:e:start:1:id", "field'a:e:start:2:id",
"field'b:e:start:1:[user id]", "field'b:e:start:2:[user id]",
"field'c:e:start:1", "field'c:e:start:2",
"field:e:end:2", "field:e:end:1",
"field'a:e:start:1:friends", "field'a:e:start:2:friends",
"field'b:e:start:1:[user friends]", "field'b:e:start:2:[user friends]",
"field'c:e:start:1", "field'c:e:start:2",
"field:e:end:2", "field:e:end:1",
"field:e:end:2", "field:e:end:1",

"op:e:end:2", "op:e:end:1",
}, tracerLog)
})

Expand Down
18 changes: 18 additions & 0 deletions graphql/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (
var _ Tracer = (*NopTracer)(nil)

type Tracer interface {
StartOperationParsing(ctx context.Context) context.Context
EndOperationParsing(ctx context.Context)
StartOperationValidation(ctx context.Context) context.Context
EndOperationValidation(ctx context.Context)
StartOperationExecution(ctx context.Context) context.Context
StartFieldExecution(ctx context.Context, field CollectedField) context.Context
StartFieldResolverExecution(ctx context.Context, rc *ResolverContext) context.Context
Expand All @@ -17,6 +21,20 @@ type Tracer interface {

type NopTracer struct{}

func (NopTracer) StartOperationParsing(ctx context.Context) context.Context {
return ctx
}

func (NopTracer) EndOperationParsing(ctx context.Context) {
}

func (NopTracer) StartOperationValidation(ctx context.Context) context.Context {
return ctx
}

func (NopTracer) EndOperationValidation(ctx context.Context) {
}

func (NopTracer) StartOperationExecution(ctx context.Context) context.Context {
return ctx
}
Expand Down
62 changes: 56 additions & 6 deletions handler/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
"github.com/99designs/gqlgen/graphql"
"github.com/gorilla/websocket"
"github.com/hashicorp/golang-lru"
"github.com/vektah/gqlparser"
"github.com/vektah/gqlparser/ast"
"github.com/vektah/gqlparser/gqlerror"
"github.com/vektah/gqlparser/parser"
"github.com/vektah/gqlparser/validator"
)

Expand Down Expand Up @@ -160,6 +160,28 @@ type tracerWrapper struct {
tracer2 graphql.Tracer
}

func (tw *tracerWrapper) StartOperationParsing(ctx context.Context) context.Context {
ctx = tw.tracer1.StartOperationParsing(ctx)
ctx = tw.tracer2.StartOperationParsing(ctx)
return ctx
}

func (tw *tracerWrapper) EndOperationParsing(ctx context.Context) {
tw.tracer2.EndOperationParsing(ctx)
tw.tracer1.EndOperationParsing(ctx)
}

func (tw *tracerWrapper) StartOperationValidation(ctx context.Context) context.Context {
ctx = tw.tracer1.StartOperationValidation(ctx)
ctx = tw.tracer2.StartOperationValidation(ctx)
return ctx
}

func (tw *tracerWrapper) EndOperationValidation(ctx context.Context) {
tw.tracer2.EndOperationValidation(ctx)
tw.tracer1.EndOperationValidation(ctx)
}

func (tw *tracerWrapper) StartOperationExecution(ctx context.Context) context.Context {
ctx = tw.tracer1.StartOperationExecution(ctx)
ctx = tw.tracer2.StartOperationExecution(ctx)
Expand Down Expand Up @@ -227,6 +249,9 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc
panic("unexpected error creating cache: " + err.Error())
}
}
if cfg.tracer == nil {
cfg.tracer = &graphql.NopTracer{}
}

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
Expand Down Expand Up @@ -263,6 +288,8 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc
}
w.Header().Set("Content-Type", "application/json")

ctx := r.Context()

var doc *ast.QueryDocument
if cache != nil {
val, ok := cache.Get(reqParams.Query)
Expand All @@ -271,35 +298,58 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc
}
}
if doc == nil {
var qErr gqlerror.List
doc, qErr = gqlparser.LoadQuery(exec.Schema(), reqParams.Query)
if len(qErr) > 0 {
sendError(w, http.StatusUnprocessableEntity, qErr...)
var gqlErr *gqlerror.Error
var listErr gqlerror.List

ctx = cfg.tracer.StartOperationParsing(ctx)
doc, gqlErr = parser.ParseQuery(&ast.Source{Input: reqParams.Query})
if gqlErr != nil {
cfg.tracer.EndOperationParsing(ctx)
sendError(w, http.StatusUnprocessableEntity, gqlErr)
return
}
cfg.tracer.EndOperationParsing(ctx)

ctx = cfg.tracer.StartOperationValidation(ctx)
listErr = validator.Validate(exec.Schema(), doc)
if len(listErr) != 0 {
cfg.tracer.EndOperationValidation(ctx)
sendError(w, http.StatusUnprocessableEntity, listErr...)
return
}
// NOTE: call cfg.tracer.EndOperationValidation later

if cache != nil {
cache.Add(reqParams.Query, doc)
}
} else {
ctx = cfg.tracer.StartOperationParsing(ctx)
cfg.tracer.EndOperationParsing(ctx)
ctx = cfg.tracer.StartOperationValidation(ctx)
}

op := doc.Operations.ForName(reqParams.OperationName)
if op == nil {
cfg.tracer.EndOperationValidation(ctx)
sendErrorf(w, http.StatusUnprocessableEntity, "operation %s not found", reqParams.OperationName)
return
}

if op.Operation != ast.Query && r.Method == http.MethodGet {
cfg.tracer.EndOperationValidation(ctx)
sendErrorf(w, http.StatusUnprocessableEntity, "GET requests only allow query operations")
return
}

vars, err := validator.VariableValues(exec.Schema(), op, reqParams.Variables)
if err != nil {
cfg.tracer.EndOperationValidation(ctx)
sendError(w, http.StatusUnprocessableEntity, err)
return
}
cfg.tracer.EndOperationValidation(ctx)
reqCtx := cfg.newRequestContext(doc, reqParams.Query, vars)
ctx := graphql.WithRequestContext(r.Context(), reqCtx)
ctx = graphql.WithRequestContext(ctx, reqCtx)

defer func() {
if err := recover(); err != nil {
Expand Down

0 comments on commit 83c7b2c

Please sign in to comment.