Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor handler package #885

Merged
merged 56 commits into from
Nov 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
249b602
Start drafting new handler interfaces
vektah Sep 30, 2019
da98618
port over the setter request context middleware
vektah Oct 1, 2019
311887d
convert APQ to middleware
vektah Oct 1, 2019
afe241b
port over tracing
vektah Oct 2, 2019
d0f6830
port json post
vektah Oct 2, 2019
b5089ca
Split transports into subpackage
vektah Oct 28, 2019
eed1515
Split middlware out of handler package
vektah Oct 28, 2019
cb99b42
Add websocket transport
vektah Oct 28, 2019
2e0c9ca
mark validation and parse errors separately to execution errors
vektah Oct 28, 2019
a7c5e66
build middleware graph once at startup
vektah Oct 28, 2019
f00e5fa
use plugins instead of middleware so multiple hooks can be configured
vektah Oct 29, 2019
c3dbcf8
Add apollo tracing
vektah Oct 30, 2019
ab5665a
Add result context
vektah Oct 30, 2019
4a69bcd
Bring operation middleware inline with other handler interfaces
vektah Oct 30, 2019
72c47c9
rename result handler to response handler
vektah Oct 30, 2019
9d1d77e
split context.go into 3 files
vektah Oct 31, 2019
a70e93b
consistently name transports
vektah Oct 31, 2019
64cfc9a
extract shared handler test server stubs
vektah Oct 31, 2019
aede7d1
Add multipart from transport
vektah Oct 31, 2019
0965420
Add query document caching
vektah Oct 31, 2019
8c5340c
Add complexity limit plugin
vektah Oct 31, 2019
e7e913d
Remove remains of old handler package
vektah Oct 31, 2019
c7bb03a
merge executable schema entrypoints
vektah Nov 1, 2019
631142c
move writer all the way back to the transport
vektah Nov 1, 2019
473a0d2
Implement bc shim for old handler package
vektah Nov 1, 2019
bc98156
Combine root handlers in ExecutableSchema into a single Exec method
vektah Nov 5, 2019
479abbe
update generated code
vektah Nov 5, 2019
3476ac4
fix linting issues
vektah Nov 5, 2019
1b57bc3
Rename RequestContext to OperationContext
vektah Nov 5, 2019
40f0886
add NewDefaultServer
vektah Nov 5, 2019
848c627
remove DirectiveMiddleware
vektah Nov 5, 2019
092ed95
collect field timing in generated code
vektah Nov 5, 2019
2898a62
rename ResolverContext to FieldContext
vektah Nov 5, 2019
f2ef5ec
more deprecations and more compat
vektah Nov 6, 2019
0a39ae2
add fixed complexity limit
vektah Nov 11, 2019
fc727c9
Add a signpost method to handler extension interface
vektah Nov 11, 2019
e9502ae
Make extensions validatable
vektah Nov 11, 2019
67fa210
allow extensions to declare their own stats
vektah Nov 11, 2019
7cbd75d
fix APQ signature
vektah Nov 11, 2019
0ee185b
fix duplicate header sends
vektah Nov 11, 2019
a77d9fc
Add generated stanzas back in
vektah Nov 11, 2019
631b48a
remove automatic field stat collection to reduce time calls
vektah Nov 11, 2019
c3f9381
fix benchmark
vektah Nov 11, 2019
a6f9462
Merge remote-tracking branch 'origin/master' into handler-refactor
vektah Nov 11, 2019
dc62234
Tune allocs for benchmarks
vektah Nov 11, 2019
572fb41
remove all references to deprecated handler package
vektah Nov 11, 2019
dc76d02
Merge remote-tracking branch 'origin/master' into handler-refactor
vektah Nov 12, 2019
d0836b7
Expose APQ stats
vektah Nov 28, 2019
458fa0d
Add more interface assertions
vektah Nov 28, 2019
a13a0f5
add docs on extension name conventions
vektah Nov 28, 2019
a339a04
panic if operation context is missing when requested
vektah Nov 28, 2019
14dbf1a
use new handler package in new test
vektah Nov 28, 2019
922db1e
always return OperationContext for postpone process
vvakame Nov 28, 2019
5106480
Merge pull request #947 from 99designs/handler-oc-handling
vektah Nov 28, 2019
dca9e4a
Add migration docs
vektah Nov 28, 2019
bac79c5
force clean git checkout
vektah Nov 28, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ linters:
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unused
- varcheck
Expand Down
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ build: false
deploy: false

test_script:
- git clean -ffdx
- go generate ./...
- go test -timeout 20m ./...
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/urfave/cli"

// Required since otherwise dep will prune away these unused packages before codegen has a chance to run
_ "github.com/99designs/gqlgen/graphql/handler"
_ "github.com/99designs/gqlgen/handler"
)

Expand Down
4 changes: 2 additions & 2 deletions codegen/directives.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ func (ec *executionContext) _subscriptionMiddleware(ctx context.Context, obj *as
{{ if .Directives.LocationDirectives "FIELD" }}
func (ec *executionContext) _fieldMiddleware(ctx context.Context, obj interface{}, next graphql.Resolver) interface{} {
{{- if .Directives.LocationDirectives "FIELD" }}
rctx := graphql.GetResolverContext(ctx)
for _, d := range rctx.Field.Directives {
fc := graphql.GetFieldContext(ctx)
for _, d := range fc.Field.Directives {
switch d.Name {
{{- range $directive := .Directives.LocationDirectives "FIELD" }}
case "{{$directive.Name}}":
Expand Down
15 changes: 6 additions & 9 deletions codegen/field.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,29 @@ func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Contex
{{- if $object.Stream }}
{{- $null = "nil" }}
{{- end }}
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func () {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = {{ $null }}
}
ec.Tracer.EndFieldExecution(ctx)
}()
rctx := &graphql.ResolverContext{
fc := &graphql.FieldContext{
Object: {{$object.Name|quote}},
Field: field,
Args: nil,
IsMethod: {{or $field.IsMethod $field.IsResolver}},
}
ctx = graphql.WithResolverContext(ctx, rctx)

ctx = graphql.WithFieldContext(ctx, fc)
{{- if $field.Args }}
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.{{ $field.ArgsFunc }}(ctx,rawArgs)
if err != nil {
ec.Error(ctx, err)
return {{ $null }}
}
rctx.Args = args
fc.Args = args
{{- end }}
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
{{- if $.Directives.LocationDirectives "FIELD" }}
resTmp := ec._fieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) {
{{ template "field" $field }}
Expand All @@ -45,7 +43,7 @@ func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Contex
{{- end }}
if resTmp == nil {
{{- if $field.TypeReference.GQL.NonNull }}
if !ec.HasError(rctx) {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
{{- end }}
Expand All @@ -67,8 +65,7 @@ func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Contex
}
{{- else }}
res := resTmp.({{$field.TypeReference.GO | ref}})
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
fc.Result = res
return ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res)
{{- end }}
}
Expand Down
124 changes: 51 additions & 73 deletions codegen/generated!.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -112,105 +112,83 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false
}

func (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinition) *graphql.Response {
{{- if .QueryRoot }}
ec := executionContext{graphql.GetRequestContext(ctx), e}

buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {
{{ if .Directives.LocationDirectives "QUERY" -}}
data := ec._queryMiddleware(ctx, op, func(ctx context.Context) (interface{}, error){
return ec._{{.QueryRoot.Name}}(ctx, op.SelectionSet), nil
})
{{- else -}}
data := ec._{{.QueryRoot.Name}}(ctx, op.SelectionSet)
{{- end }}
func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
rc := graphql.GetOperationContext(ctx)
ec := executionContext{rc, e}
first := true

switch rc.Operation.Operation {
{{- if .QueryRoot }} case ast.Query:
return func(ctx context.Context) *graphql.Response {
if !first { return nil }
first = false
{{ if .Directives.LocationDirectives "QUERY" -}}
data := ec._queryMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet), nil
})
{{- else -}}
data := ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet)
{{- end }}
var buf bytes.Buffer
data.MarshalGQL(&buf)
return buf.Bytes()
})

return &graphql.Response{
Data: buf,
Errors: ec.Errors,
Extensions: ec.Extensions,
return &graphql.Response{
Data: buf.Bytes(),
}
}
{{- else }}
return graphql.ErrorResponse(ctx, "queries are not supported")
{{- end }}
}

func (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefinition) *graphql.Response {
{{- if .MutationRoot }}
ec := executionContext{graphql.GetRequestContext(ctx), e}
{{ end }}

buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {
{{ if .Directives.LocationDirectives "MUTATION" -}}
data := ec._mutationMiddleware(ctx, op, func(ctx context.Context) (interface{}, error){
return ec._{{.MutationRoot.Name}}(ctx, op.SelectionSet), nil
})
{{- else -}}
data := ec._{{.MutationRoot.Name}}(ctx, op.SelectionSet)
{{- end }}
{{- if .MutationRoot }} case ast.Mutation:
return func(ctx context.Context) *graphql.Response {
if !first { return nil }
first = false
{{ if .Directives.LocationDirectives "MUTATION" -}}
data := ec._mutationMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.MutationRoot.Name}}(ctx, rc.Operation.SelectionSet), nil
})
{{- else -}}
data := ec._{{.MutationRoot.Name}}(ctx, rc.Operation.SelectionSet)
{{- end }}
var buf bytes.Buffer
data.MarshalGQL(&buf)
return buf.Bytes()
})

return &graphql.Response{
Data: buf,
Errors: ec.Errors,
Extensions: ec.Extensions,
return &graphql.Response{
Data: buf.Bytes(),
}
}
{{- else }}
return graphql.ErrorResponse(ctx, "mutations are not supported")
{{- end }}
}

func (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDefinition) func() *graphql.Response {
{{- if .SubscriptionRoot }}
ec := executionContext{graphql.GetRequestContext(ctx), e}
{{ end }}

{{- if .SubscriptionRoot }} case ast.Subscription:
{{ if .Directives.LocationDirectives "SUBSCRIPTION" -}}
next := ec._subscriptionMiddleware(ctx, op, func(ctx context.Context) (interface{}, error){
return ec._{{.SubscriptionRoot.Name}}(ctx, op.SelectionSet),nil
next := ec._subscriptionMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.SubscriptionRoot.Name}}(ctx, rc.Operation.SelectionSet),nil
})
{{- else -}}
next := ec._{{.SubscriptionRoot.Name}}(ctx, op.SelectionSet)
next := ec._{{.SubscriptionRoot.Name}}(ctx, rc.Operation.SelectionSet)
{{- end }}
if ec.Errors != nil {
return graphql.OneShot(&graphql.Response{Data: []byte("null"), Errors: ec.Errors})
}

var buf bytes.Buffer
return func() *graphql.Response {
buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {
buf.Reset()
data := next()

if data == nil {
return nil
}
data.MarshalGQL(&buf)
return buf.Bytes()
})
return func(ctx context.Context) *graphql.Response {
buf.Reset()
data := next()

if buf == nil {
if data == nil {
return nil
}
data.MarshalGQL(&buf)

return &graphql.Response{
Data: buf,
Errors: ec.Errors,
Extensions: ec.Extensions,
Data: buf.Bytes(),
}
}
{{- else }}
return graphql.OneShot(graphql.ErrorResponse(ctx, "subscriptions are not supported"))
{{- end }}
{{ end }}
default:
return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation"))
}
}

type executionContext struct {
*graphql.RequestContext
*graphql.OperationContext
*executableSchema
}

Expand Down
8 changes: 4 additions & 4 deletions codegen/object.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ var {{ $object.Name|lcFirst}}Implementors = {{$object.Implementors}}

{{- if .Stream }}
func (ec *executionContext) _{{$object.Name}}(ctx context.Context, sel ast.SelectionSet) func() graphql.Marshaler {
fields := graphql.CollectFields(ec.RequestContext, sel, {{$object.Name|lcFirst}}Implementors)
ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
fields := graphql.CollectFields(ec.OperationContext, sel, {{$object.Name|lcFirst}}Implementors)
ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{
Object: {{$object.Name|quote}},
})
if len(fields) != 1 {
Expand All @@ -24,9 +24,9 @@ func (ec *executionContext) _{{$object.Name}}(ctx context.Context, sel ast.Selec
}
{{- else }}
func (ec *executionContext) _{{$object.Name}}(ctx context.Context, sel ast.SelectionSet{{ if not $object.Root }},obj {{$object.Reference | ref }}{{ end }}) graphql.Marshaler {
fields := graphql.CollectFields(ec.RequestContext, sel, {{$object.Name|lcFirst}}Implementors)
fields := graphql.CollectFields(ec.OperationContext, sel, {{$object.Name|lcFirst}}Implementors)
{{if $object.Root}}
ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{
Object: {{$object.Name|quote}},
})
{{end}}
Expand Down
11 changes: 8 additions & 3 deletions codegen/testserver/complexity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import (
"testing"

"github.com/99designs/gqlgen/client"
"github.com/99designs/gqlgen/handler"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/stretchr/testify/require"
)

func TestComplexityCollisions(t *testing.T) {
resolvers := &Stub{}

c := client.New(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolvers})))
srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))

c := client.New(srv)

resolvers.QueryResolver.Overlapping = func(ctx context.Context) (fields *OverlappingFields, e error) {
return &OverlappingFields{
Expand Down Expand Up @@ -48,7 +51,9 @@ func TestComplexityFuncs(t *testing.T) {
cfg.Complexity.OverlappingFields.Foo = func(childComplexity int) int { return 1000 }
cfg.Complexity.OverlappingFields.NewFoo = func(childComplexity int) int { return 5 }

c := client.New(handler.GraphQL(NewExecutableSchema(cfg), handler.ComplexityLimit(10)))
srv := handler.NewDefaultServer(NewExecutableSchema(cfg))
srv.Use(extension.FixedComplexityLimit(10))
c := client.New(srv)

resolvers.QueryResolver.Overlapping = func(ctx context.Context) (fields *OverlappingFields, e error) {
return &OverlappingFields{
Expand Down
Loading