diff --git a/.gitignore b/.gitignore index 10ed19500d..e726518406 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /internal/tests/testdata/graphql-js /vendor /docs/public +/example/chat/node_modules +/example/chat/package-lock.json diff --git a/.gometalinter.json b/.gometalinter.json index ab1c96e4fb..b8162c837a 100644 --- a/.gometalinter.json +++ b/.gometalinter.json @@ -1,6 +1,6 @@ { "sort": ["path"], - "Deadline": "2m", + "Deadline": "5m", "Linters": { "errcheck": { "Command": "errcheck -abspath -ignore '[rR]ead|[wW]rite|Close'", diff --git a/codegen/templates/data.go b/codegen/templates/data.go index 69c49f59ac..7e3bcd09f2 100644 --- a/codegen/templates/data.go +++ b/codegen/templates/data.go @@ -2,8 +2,8 @@ package templates var data = map[string]string{ "args.gotpl": "\t{{- if . }}args := map[string]interface{}{} {{end}}\n\t{{- range $i, $arg := . }}\n\t\tvar arg{{$i}} {{$arg.Signature }}\n\t\tif tmp, ok := field.Args[{{$arg.GQLName|quote}}]; ok {\n\t\t\tvar err error\n\t\t\t{{$arg.Unmarshal (print \"arg\" $i) \"tmp\" }}\n\t\t\tif err != nil {\n\t\t\t\tec.Error(err)\n\t\t\t\t{{- if $arg.Object.Stream }}\n\t\t\t\t\treturn nil\n\t\t\t\t{{- else }}\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t{{- end }}\n\t\t\t}\n\t\t} {{ if $arg.Default }} else {\n\t\t\tvar tmp interface{} = {{ $arg.Default | dump }}\n\t\t\tvar err error\n\t\t\t{{$arg.Unmarshal (print \"arg\" $i) \"tmp\" }}\n\t\t\tif err != nil {\n\t\t\t\tec.Error(err)\n\t\t\t\t{{- if $arg.Object.Stream }}\n\t\t\t\t\treturn nil\n\t\t\t\t{{- else }}\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t{{- end }}\n\t\t\t}\n\t\t}\n\t\t{{end }}\n\t\targs[{{$arg.GQLName|quote}}] = arg{{$i}}\n\t{{- end -}}\n", - "field.gotpl": "{{ $field := . }}\n{{ $object := $field.Object }}\n\n{{- if $object.Stream }}\n\tfunc (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField) func() graphql.Marshaler {\n\t\t{{- template \"args.gotpl\" $field.Args }}\n\t\trctx := graphql.WithResolverContext(ctx, &graphql.ResolverContext{Field: field})\n\t\tresults, err := ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }})\n\t\tif err != nil {\n\t\t\tec.Error(err)\n\t\t\treturn nil\n\t\t}\n\t\treturn func() graphql.Marshaler {\n\t\t\tres, ok := <-results\n\t\t\tif !ok {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tvar out graphql.OrderedMap\n\t\t\tout.Add(field.Alias, func() graphql.Marshaler { {{ $field.WriteJson }} }())\n\t\t\treturn &out\n\t\t}\n\t}\n{{ else }}\n\tfunc (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField, {{if not $object.Root}}obj *{{$object.FullName}}{{end}}) graphql.Marshaler {\n\t\t{{- template \"args.gotpl\" $field.Args }}\n\n\t\t{{- if $field.IsConcurrent }}\n\t\t\treturn graphql.Defer(func() (ret graphql.Marshaler) {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\tuserErr := ec.Recover(r)\n\t\t\t\t\t\tec.Error(userErr)\n\t\t\t\t\t\tret = graphql.Null\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t{{- end }}\n\n\t\t\t{{- if $field.GoVarName }}\n\t\t\t\tres := obj.{{$field.GoVarName}}\n\t\t\t{{- else if $field.GoMethodName }}\n\t\t\t\t{{- if $field.NoErr }}\n\t\t\t\t\tres := {{$field.GoMethodName}}({{ $field.CallArgs }})\n\t\t\t\t{{- else }}\n\t\t\t\t\tres, err := {{$field.GoMethodName}}({{ $field.CallArgs }})\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tec.Error(err)\n\t\t\t\t\t\treturn graphql.Null\n\t\t\t\t\t}\n\t\t\t\t{{- end }}\n\t\t\t{{- else }}\n\t\t\t\trctx := graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\t\t\t\tObject: {{$object.GQLType|quote}},\n\t\t\t\t\tArgs: {{if $field.Args }}args{{else}}nil{{end}},\n\t\t\t\t\tField: field,\n\t\t\t\t})\n\t\t\t\tresTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) {\n\t\t\t\t\treturn ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }})\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tec.Error(err)\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t}\n\t\t\t\tif resTmp == nil {\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t}\n\t\t\t\tres := resTmp.({{$field.Signature}})\n\t\t\t{{- end }}\n\t\t\t{{ $field.WriteJson }}\n\t\t{{- if $field.IsConcurrent }}\n\t\t\t})\n\t\t{{- end }}\n\t}\n{{ end }}\n", - "generated.gotpl": "// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\nfunc MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema {\n\treturn &executableSchema{resolvers: resolvers}\n}\n\ntype Resolvers interface {\n{{- range $object := .Objects -}}\n\t{{ range $field := $object.Fields -}}\n\t\t{{ $field.ResolverDeclaration }}\n\t{{ end }}\n{{- end }}\n}\n\ntype executableSchema struct {\n\tresolvers Resolvers\n}\n\nfunc (e *executableSchema) Schema() *schema.Schema {\n\treturn parsedSchema\n}\n\nfunc (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response {\n\t{{- if .QueryRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tdata := ec._{{.QueryRoot.GQLType}}(ctx, op.Selections)\n\t\tvar buf bytes.Buffer\n\t\tdata.MarshalGQL(&buf)\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf.Bytes(),\n\t\t\tErrors: ec.Errors,\n\t\t}\n\t{{- else }}\n\t\treturn &graphql.Response{Errors: []*errors.QueryError{ {Message: \"queries are not supported\"} }}\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Mutation(ctx context.Context, op *query.Operation) *graphql.Response {\n\t{{- if .MutationRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tdata := ec._{{.MutationRoot.GQLType}}(ctx, op.Selections)\n\t\tvar buf bytes.Buffer\n\t\tdata.MarshalGQL(&buf)\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf.Bytes(),\n\t\t\tErrors: ec.Errors,\n\t\t}\n\t{{- else }}\n\t\treturn &graphql.Response{Errors: []*errors.QueryError{ {Message: \"mutations are not supported\"} }}\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Subscription(ctx context.Context, op *query.Operation) func() *graphql.Response {\n\t{{- if .SubscriptionRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tnext := ec._{{.SubscriptionRoot.GQLType}}(ctx, op.Selections)\n\t\tif ec.Errors != nil {\n\t\t\treturn graphql.OneShot(&graphql.Response{Data: []byte(\"null\"), Errors: ec.Errors})\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\treturn func() *graphql.Response {\n\t\t\tbuf.Reset()\n\t\t\tdata := next()\n\t\t\tif data == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tdata.MarshalGQL(&buf)\n\n\t\t\terrs := ec.Errors\n\t\t\tec.Errors = nil\n\t\t\treturn &graphql.Response{\n\t\t\t\tData: buf.Bytes(),\n\t\t\t\tErrors: errs,\n\t\t\t}\n\t\t}\n\t{{- else }}\n\t\treturn graphql.OneShot(&graphql.Response{Errors: []*errors.QueryError{ {Message: \"subscriptions are not supported\"} }})\n\t{{- end }}\n}\n\ntype executionContext struct {\n\t*graphql.RequestContext\n\n\tresolvers Resolvers\n}\n\n{{- range $object := .Objects }}\n\t{{ template \"object.gotpl\" $object }}\n\n\t{{- range $field := $object.Fields }}\n\t\t{{ template \"field.gotpl\" $field }}\n\t{{ end }}\n{{- end}}\n\n{{- range $interface := .Interfaces }}\n\t{{ template \"interface.gotpl\" $interface }}\n{{- end }}\n\n{{- range $input := .Inputs }}\n\t{{ template \"input.gotpl\" $input }}\n{{- end }}\n\nfunc (ec *executionContext) introspectSchema() *introspection.Schema {\n\treturn introspection.WrapSchema(parsedSchema)\n}\n\nfunc (ec *executionContext) introspectType(name string) *introspection.Type {\n\tt := parsedSchema.Resolve(name)\n\tif t == nil {\n\t\treturn nil\n\t}\n\treturn introspection.WrapType(t)\n}\n\nvar parsedSchema = schema.MustParse({{.SchemaRaw|rawQuote}})\n", + "field.gotpl": "{{ $field := . }}\n{{ $object := $field.Object }}\n\n{{- if $object.Stream }}\n\tfunc (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField) func() graphql.Marshaler {\n\t\t{{- template \"args.gotpl\" $field.Args }}\n\t\trctx := graphql.WithResolverContext(ctx, &graphql.ResolverContext{Field: field})\n\t\tresults, err := ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }})\n\t\tif err != nil {\n\t\t\tec.Error(err)\n\t\t\treturn nil\n\t\t}\n\t\treturn func() graphql.Marshaler {\n\t\t\tres, ok := <-results\n\t\t\tif !ok {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tvar out graphql.OrderedMap\n\t\t\tout.Add(field.Alias, func() graphql.Marshaler { {{ $field.WriteJson }} }())\n\t\t\treturn &out\n\t\t}\n\t}\n{{ else }}\n\tfunc (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField, {{if not $object.Root}}obj *{{$object.FullName}}{{end}}) graphql.Marshaler {\n\t\t{{- template \"args.gotpl\" $field.Args }}\n\n\t\t{{- if $field.IsConcurrent }}\n\t\t\treturn graphql.Defer(func() (ret graphql.Marshaler) {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\tuserErr := ec.Recover(r)\n\t\t\t\t\t\tec.Error(userErr)\n\t\t\t\t\t\tret = graphql.Null\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t{{- end }}\n\n\t\t\t{{- if $field.GoVarName }}\n\t\t\t\tres := obj.{{$field.GoVarName}}\n\t\t\t{{- else if $field.GoMethodName }}\n\t\t\t\t{{- if $field.NoErr }}\n\t\t\t\t\tres := {{$field.GoMethodName}}({{ $field.CallArgs }})\n\t\t\t\t{{- else }}\n\t\t\t\t\tres, err := {{$field.GoMethodName}}({{ $field.CallArgs }})\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tec.Error(err)\n\t\t\t\t\t\treturn graphql.Null\n\t\t\t\t\t}\n\t\t\t\t{{- end }}\n\t\t\t{{- else }}\n\t\t\t\trctx := graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\t\t\t\tObject: {{$object.GQLType|quote}},\n\t\t\t\t\tArgs: {{if $field.Args }}args{{else}}nil{{end}},\n\t\t\t\t\tField: field,\n\t\t\t\t})\n\t\t\t\tresTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) {\n\t\t\t\t\treturn ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }})\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tec.Error(err)\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t}\n\t\t\t\tif resTmp == nil {\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t}\n\t\t\t\tres := resTmp.({{$field.Signature}})\n\t\t\t{{- end }}\n\t\t\t{{ $field.WriteJson }}\n\t\t{{- if $field.IsConcurrent }}\n\t\t\t})\n\t\t{{- end }}\n\t}\n{{ end }}\n", + "generated.gotpl": "// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\nfunc MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema {\n\treturn &executableSchema{resolvers: resolvers}\n}\n\ntype Resolvers interface {\n{{- range $object := .Objects -}}\n\t{{ range $field := $object.Fields -}}\n\t\t{{ $field.ResolverDeclaration }}\n\t{{ end }}\n{{- end }}\n}\n\ntype executableSchema struct {\n\tresolvers Resolvers\n}\n\nfunc (e *executableSchema) Schema() *schema.Schema {\n\treturn parsedSchema\n}\n\nfunc (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response {\n\t{{- if .QueryRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\tdata := ec._{{.QueryRoot.GQLType}}(ctx, op.Selections)\n\t\t\tvar buf bytes.Buffer\n\t\t\tdata.MarshalGQL(&buf)\n\t\t\treturn buf.Bytes()\n\t\t})\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf,\n\t\t\tErrors: ec.Errors,\n\t\t}\n\t{{- else }}\n\t\treturn &graphql.Response{Errors: []*errors.QueryError{ {Message: \"queries are not supported\"} }}\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Mutation(ctx context.Context, op *query.Operation) *graphql.Response {\n\t{{- if .MutationRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\tdata := ec._{{.MutationRoot.GQLType}}(ctx, op.Selections)\n\t\t\tvar buf bytes.Buffer\n\t\t\tdata.MarshalGQL(&buf)\n\t\t\treturn buf.Bytes()\n\t\t})\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf,\n\t\t\tErrors: ec.Errors,\n\t\t}\n\t{{- else }}\n\t\treturn &graphql.Response{Errors: []*errors.QueryError{ {Message: \"mutations are not supported\"} }}\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Subscription(ctx context.Context, op *query.Operation) func() *graphql.Response {\n\t{{- if .SubscriptionRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tnext := ec._{{.SubscriptionRoot.GQLType}}(ctx, op.Selections)\n\t\tif ec.Errors != nil {\n\t\t\treturn graphql.OneShot(&graphql.Response{Data: []byte(\"null\"), Errors: ec.Errors})\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\treturn func() *graphql.Response {\n\t\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\t\tbuf.Reset()\n\t\t\t\tdata := next()\n\n\t\t\t\tif data == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tdata.MarshalGQL(&buf)\n\t\t\t\treturn buf.Bytes()\n\t\t\t})\n\n\t\t\treturn &graphql.Response{\n\t\t\t\tData: buf,\n\t\t\t\tErrors: ec.Errors,\n\t\t\t}\n\t\t}\n\t{{- else }}\n\t\treturn graphql.OneShot(&graphql.Response{Errors: []*errors.QueryError{ {Message: \"subscriptions are not supported\"} }})\n\t{{- end }}\n}\n\ntype executionContext struct {\n\t*graphql.RequestContext\n\n\tresolvers Resolvers\n}\n\n{{- range $object := .Objects }}\n\t{{ template \"object.gotpl\" $object }}\n\n\t{{- range $field := $object.Fields }}\n\t\t{{ template \"field.gotpl\" $field }}\n\t{{ end }}\n{{- end}}\n\n{{- range $interface := .Interfaces }}\n\t{{ template \"interface.gotpl\" $interface }}\n{{- end }}\n\n{{- range $input := .Inputs }}\n\t{{ template \"input.gotpl\" $input }}\n{{- end }}\n\nfunc (ec *executionContext) introspectSchema() *introspection.Schema {\n\treturn introspection.WrapSchema(parsedSchema)\n}\n\nfunc (ec *executionContext) introspectType(name string) *introspection.Type {\n\tt := parsedSchema.Resolve(name)\n\tif t == nil {\n\t\treturn nil\n\t}\n\treturn introspection.WrapType(t)\n}\n\nvar parsedSchema = schema.MustParse({{.SchemaRaw|rawQuote}})\n", "input.gotpl": "\t{{- if .IsMarshaled }}\n\tfunc Unmarshal{{ .GQLType }}(v interface{}) ({{.FullName}}, error) {\n\t\tvar it {{.FullName}}\n\n\t\tfor k, v := range v.(map[string]interface{}) {\n\t\t\tswitch k {\n\t\t\t{{- range $field := .Fields }}\n\t\t\tcase {{$field.GQLName|quote}}:\n\t\t\t\tvar err error\n\t\t\t\t{{ $field.Unmarshal (print \"it.\" $field.GoVarName) \"v\" }}\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn it, err\n\t\t\t\t}\n\t\t\t{{- end }}\n\t\t\t}\n\t\t}\n\n\t\treturn it, nil\n\t}\n\t{{- end }}\n", "interface.gotpl": "{{- $interface := . }}\n\nfunc (ec *executionContext) _{{$interface.GQLType}}(ctx context.Context, sel []query.Selection, obj *{{$interface.FullName}}) graphql.Marshaler {\n\tswitch obj := (*obj).(type) {\n\tcase nil:\n\t\treturn graphql.Null\n\t{{- range $implementor := $interface.Implementors }}\n\t\t{{- if $implementor.ValueReceiver }}\n\t\t\tcase {{$implementor.FullName}}:\n\t\t\t\treturn ec._{{$implementor.GQLType}}(ctx, sel, &obj)\n\t\t{{- end}}\n\t\tcase *{{$implementor.FullName}}:\n\t\t\treturn ec._{{$implementor.GQLType}}(ctx, sel, obj)\n\t{{- end }}\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unexpected type %T\", obj))\n\t}\n}\n", "models.gotpl": "// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\n{{ range $model := .Models }}\n\t{{- if .IsInterface }}\n\t\ttype {{.GoType}} interface {}\n\t{{- else }}\n\t\ttype {{.GoType}} struct {\n\t\t\t{{- range $field := .Fields }}\n\t\t\t\t{{- if $field.GoVarName }}\n\t\t\t\t\t{{ $field.GoVarName }} {{$field.Signature}}\n\t\t\t\t{{- else }}\n\t\t\t\t\t{{ $field.GoFKName }} {{$field.GoFKType}}\n\t\t\t\t{{- end }}\n\t\t\t{{- end }}\n\t\t}\n\t{{- end }}\n{{- end}}\n\n{{ range $enum := .Enums }}\n\ttype {{.GoType}} string\n\tconst (\n\t{{ range $value := .Values }}\n\t\t{{$enum.GoType}}{{ .Name|toCamel }} {{$enum.GoType}} = {{.Name|quote}} {{with .Description}} // {{.}} {{end}}\n\t{{- end }}\n\t)\n\n\tfunc (e {{.GoType}}) IsValid() bool {\n\t\tswitch e {\n\t\tcase {{ range $index, $element := .Values}}{{if $index}},{{end}}{{ $enum.GoType }}{{ $element.Name|toCamel }}{{end}}:\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\tfunc (e {{.GoType}}) String() string {\n\t\treturn string(e)\n\t}\n\n\tfunc (e *{{.GoType}}) UnmarshalGQL(v interface{}) error {\n\t\tstr, ok := v.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"enums must be strings\")\n\t\t}\n\n\t\t*e = {{.GoType}}(str)\n\t\tif !e.IsValid() {\n\t\t\treturn fmt.Errorf(\"%s is not a valid {{.GQLType}}\", str)\n\t\t}\n\t\treturn nil\n\t}\n\n\tfunc (e {{.GoType}}) MarshalGQL(w io.Writer) {\n\t\tfmt.Fprint(w, strconv.Quote(e.String()))\n\t}\n\n{{- end }}\n", diff --git a/codegen/templates/field.gotpl b/codegen/templates/field.gotpl index 9ab270b572..e77e71986a 100644 --- a/codegen/templates/field.gotpl +++ b/codegen/templates/field.gotpl @@ -53,7 +53,7 @@ Args: {{if $field.Args }}args{{else}}nil{{end}}, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }}) }) if err != nil { diff --git a/codegen/templates/generated.gotpl b/codegen/templates/generated.gotpl index cc8a4ba92c..5ae2120cd7 100644 --- a/codegen/templates/generated.gotpl +++ b/codegen/templates/generated.gotpl @@ -32,12 +32,15 @@ func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *grap {{- if .QueryRoot }} ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._{{.QueryRoot.GQLType}}(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._{{.QueryRoot.GQLType}}(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } {{- else }} @@ -49,12 +52,15 @@ func (e *executableSchema) Mutation(ctx context.Context, op *query.Operation) *g {{- if .MutationRoot }} ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._{{.MutationRoot.GQLType}}(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._{{.MutationRoot.GQLType}}(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } {{- else }} @@ -73,18 +79,20 @@ func (e *executableSchema) Subscription(ctx context.Context, op *query.Operation var buf bytes.Buffer return func() *graphql.Response { - buf.Reset() - data := next() - if data == nil { - return nil - } - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + buf.Reset() + data := next() + + if data == nil { + return nil + } + data.MarshalGQL(&buf) + return buf.Bytes() + }) - errs := ec.Errors - ec.Errors = nil return &graphql.Response{ - Data: buf.Bytes(), - Errors: errs, + Data: buf, + Errors: ec.Errors, } } {{- else }} diff --git a/example/chat/generated.go b/example/chat/generated.go index 6bf8922d3b..d616f24ccd 100644 --- a/example/chat/generated.go +++ b/example/chat/generated.go @@ -35,12 +35,15 @@ func (e *executableSchema) Schema() *schema.Schema { func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._Query(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Query(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -48,12 +51,15 @@ func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *grap func (e *executableSchema) Mutation(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._Mutation(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Mutation(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -68,18 +74,20 @@ func (e *executableSchema) Subscription(ctx context.Context, op *query.Operation var buf bytes.Buffer return func() *graphql.Response { - buf.Reset() - data := next() - if data == nil { - return nil - } - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + buf.Reset() + data := next() + + if data == nil { + return nil + } + data.MarshalGQL(&buf) + return buf.Bytes() + }) - errs := ec.Errors - ec.Errors = nil return &graphql.Response{ - Data: buf.Bytes(), - Errors: errs, + Data: buf, + Errors: ec.Errors, } } } @@ -235,7 +243,7 @@ func (ec *executionContext) _Mutation_post(ctx context.Context, field graphql.Co Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Mutation_post(rctx, args["text"].(string), args["username"].(string), args["roomName"].(string)) }) if err != nil { @@ -300,7 +308,7 @@ func (ec *executionContext) _Query_room(ctx context.Context, field graphql.Colle Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_room(rctx, args["name"].(string)) }) if err != nil { diff --git a/example/chat/package.json b/example/chat/package.json index 6b3c0346d9..119c7ea2ed 100644 --- a/example/chat/package.json +++ b/example/chat/package.json @@ -5,6 +5,8 @@ "dependencies": { "apollo-cache-inmemory": "^1.1.9", "apollo-client": "^2.2.5", + "graphql-tag": "^2.9.1", + "graphql": "^0.13.2", "react": "^16.2.0", "react-apollo": "^2.1.0-beta.2", "react-dom": "^16.2.0", diff --git a/example/chat/server/server.go b/example/chat/server/server.go index 2bab154b85..1a16399637 100644 --- a/example/chat/server/server.go +++ b/example/chat/server/server.go @@ -3,15 +3,26 @@ package main import ( "log" "net/http" + "net/url" + "time" "github.com/gorilla/websocket" + "github.com/opentracing/opentracing-go" "github.com/vektah/gqlgen/example/chat" "github.com/vektah/gqlgen/handler" + gqlopentracing "github.com/vektah/gqlgen/opentracing" + "sourcegraph.com/sourcegraph/appdash" + appdashtracer "sourcegraph.com/sourcegraph/appdash/opentracing" + "sourcegraph.com/sourcegraph/appdash/traceapp" ) func main() { + startAppdashServer() + http.Handle("/", handler.Playground("Todo", "/query")) http.Handle("/query", handler.GraphQL(chat.MakeExecutableSchema(chat.New()), + handler.ResolverMiddleware(gqlopentracing.ResolverMiddleware()), + handler.RequestMiddleware(gqlopentracing.RequestMiddleware()), handler.WebsocketUpgrader(websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true @@ -20,3 +31,35 @@ func main() { ) log.Fatal(http.ListenAndServe(":8085", nil)) } + +func startAppdashServer() opentracing.Tracer { + memStore := appdash.NewMemoryStore() + store := &appdash.RecentStore{ + MinEvictAge: 5 * time.Minute, + DeleteStore: memStore, + } + + url, err := url.Parse("http://localhost:8700") + if err != nil { + log.Fatal(err) + } + tapp, err := traceapp.New(nil, url) + if err != nil { + log.Fatal(err) + } + tapp.Store = store + tapp.Queryer = memStore + + go func() { + log.Fatal(http.ListenAndServe(":8700", tapp)) + }() + tapp.Store = store + tapp.Queryer = memStore + + collector := appdash.NewLocalCollector(store) + tracer := appdashtracer.NewTracer(collector) + opentracing.InitGlobalTracer(tracer) + + log.Println("Appdash web UI running on HTTP :8700") + return tracer +} diff --git a/example/dataloader/generated.go b/example/dataloader/generated.go index b54d6524e7..ac5abd33d7 100644 --- a/example/dataloader/generated.go +++ b/example/dataloader/generated.go @@ -38,12 +38,15 @@ func (e *executableSchema) Schema() *schema.Schema { func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._Query(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Query(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -155,7 +158,7 @@ func (ec *executionContext) _Customer_address(ctx context.Context, field graphql Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Customer_address(rctx, obj) }) if err != nil { @@ -187,7 +190,7 @@ func (ec *executionContext) _Customer_orders(ctx context.Context, field graphql. Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Customer_orders(rctx, obj) }) if err != nil { @@ -290,7 +293,7 @@ func (ec *executionContext) _Order_items(ctx context.Context, field graphql.Coll Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Order_items(rctx, obj) }) if err != nil { @@ -351,7 +354,7 @@ func (ec *executionContext) _Query_customers(ctx context.Context, field graphql. Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_customers(rctx) }) if err != nil { @@ -403,7 +406,7 @@ func (ec *executionContext) _Query_torture(ctx context.Context, field graphql.Co Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_torture(rctx, args["customerIds"].([][]int)) }) if err != nil { diff --git a/example/dataloader/server/server.go b/example/dataloader/server/server.go index c016c5aebb..22b3b214e5 100644 --- a/example/dataloader/server/server.go +++ b/example/dataloader/server/server.go @@ -25,7 +25,11 @@ func main() { router.Use(dataloader.LoaderMiddleware) router.Handle("/", handler.Playground("Dataloader", "/query")) - router.Handle("/query", handler.GraphQL(dataloader.MakeExecutableSchema(&dataloader.Resolver{}), handler.Use(gqlopentracing.Middleware()))) + router.Handle("/query", handler.GraphQL( + dataloader.MakeExecutableSchema(&dataloader.Resolver{}), + handler.ResolverMiddleware(gqlopentracing.ResolverMiddleware()), + handler.RequestMiddleware(gqlopentracing.RequestMiddleware()), + )) log.Println("connect to http://localhost:8082/ for graphql playground") log.Fatal(http.ListenAndServe(":8082", router)) diff --git a/example/scalars/generated.go b/example/scalars/generated.go index d49ad2b1b2..2e5dbdc1a8 100644 --- a/example/scalars/generated.go +++ b/example/scalars/generated.go @@ -39,12 +39,15 @@ func (e *executableSchema) Schema() *schema.Schema { func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._Query(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Query(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -153,7 +156,7 @@ func (ec *executionContext) _Query_user(ctx context.Context, field graphql.Colle Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_user(rctx, args["id"].(external.ObjectID)) }) if err != nil { @@ -205,7 +208,7 @@ func (ec *executionContext) _Query_search(ctx context.Context, field graphql.Col Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_search(rctx, args["input"].(SearchArgs)) }) if err != nil { @@ -321,7 +324,7 @@ func (ec *executionContext) _User_primitiveResolver(ctx context.Context, field g Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.User_primitiveResolver(rctx, obj) }) if err != nil { @@ -350,7 +353,7 @@ func (ec *executionContext) _User_customResolver(ctx context.Context, field grap Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.User_customResolver(rctx, obj) }) if err != nil { diff --git a/example/selection/generated.go b/example/selection/generated.go index 16951bd8e9..a9f8ecb830 100644 --- a/example/selection/generated.go +++ b/example/selection/generated.go @@ -34,12 +34,15 @@ func (e *executableSchema) Schema() *schema.Schema { func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._Query(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Query(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -210,7 +213,7 @@ func (ec *executionContext) _Query_events(ctx context.Context, field graphql.Col Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_events(rctx) }) if err != nil { diff --git a/example/starwars/generated.go b/example/starwars/generated.go index 6b2d115e11..8228de453c 100644 --- a/example/starwars/generated.go +++ b/example/starwars/generated.go @@ -53,12 +53,15 @@ func (e *executableSchema) Schema() *schema.Schema { func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._Query(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Query(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -66,12 +69,15 @@ func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *grap func (e *executableSchema) Mutation(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._Mutation(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Mutation(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -142,7 +148,7 @@ func (ec *executionContext) _Droid_friends(ctx context.Context, field graphql.Co Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Droid_friends(rctx, obj) }) if err != nil { @@ -206,7 +212,7 @@ func (ec *executionContext) _Droid_friendsConnection(ctx context.Context, field Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Droid_friendsConnection(rctx, obj, args["first"].(*int), args["after"].(*string)) }) if err != nil { @@ -282,7 +288,7 @@ func (ec *executionContext) _FriendsConnection_edges(ctx context.Context, field Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.FriendsConnection_edges(rctx, obj) }) if err != nil { @@ -315,7 +321,7 @@ func (ec *executionContext) _FriendsConnection_friends(ctx context.Context, fiel Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.FriendsConnection_friends(rctx, obj) }) if err != nil { @@ -463,7 +469,7 @@ func (ec *executionContext) _Human_friends(ctx context.Context, field graphql.Co Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Human_friends(rctx, obj) }) if err != nil { @@ -527,7 +533,7 @@ func (ec *executionContext) _Human_friendsConnection(ctx context.Context, field Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Human_friendsConnection(rctx, obj, args["first"].(*int), args["after"].(*string)) }) if err != nil { @@ -565,7 +571,7 @@ func (ec *executionContext) _Human_starships(ctx context.Context, field graphql. Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Human_starships(rctx, obj) }) if err != nil { @@ -633,7 +639,7 @@ func (ec *executionContext) _Mutation_createReview(ctx context.Context, field gr Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Mutation_createReview(rctx, args["episode"].(Episode), args["review"].(Review)) }) if err != nil { @@ -763,7 +769,7 @@ func (ec *executionContext) _Query_hero(ctx context.Context, field graphql.Colle Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_hero(rctx, args["episode"].(Episode)) }) if err != nil { @@ -818,7 +824,7 @@ func (ec *executionContext) _Query_reviews(ctx context.Context, field graphql.Co Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_reviews(rctx, args["episode"].(Episode), args["since"].(*time.Time)) }) if err != nil { @@ -862,7 +868,7 @@ func (ec *executionContext) _Query_search(ctx context.Context, field graphql.Col Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_search(rctx, args["text"].(string)) }) if err != nil { @@ -906,7 +912,7 @@ func (ec *executionContext) _Query_character(ctx context.Context, field graphql. Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_character(rctx, args["id"].(string)) }) if err != nil { @@ -946,7 +952,7 @@ func (ec *executionContext) _Query_droid(ctx context.Context, field graphql.Coll Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_droid(rctx, args["id"].(string)) }) if err != nil { @@ -989,7 +995,7 @@ func (ec *executionContext) _Query_human(ctx context.Context, field graphql.Coll Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_human(rctx, args["id"].(string)) }) if err != nil { @@ -1032,7 +1038,7 @@ func (ec *executionContext) _Query_starship(ctx context.Context, field graphql.C Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_starship(rctx, args["id"].(string)) }) if err != nil { diff --git a/example/starwars/server/server.go b/example/starwars/server/server.go index dc2c57d2ae..86bdbdc546 100644 --- a/example/starwars/server/server.go +++ b/example/starwars/server/server.go @@ -14,7 +14,7 @@ import ( func main() { http.Handle("/", handler.Playground("Starwars", "/query")) http.Handle("/query", handler.GraphQL(starwars.MakeExecutableSchema(starwars.NewResolver()), - handler.Use(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { + handler.ResolverMiddleware(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { rc := graphql.GetResolverContext(ctx) fmt.Println("Entered", rc.Object, rc.Field.Name) res, err = next(ctx) diff --git a/example/todo/generated.go b/example/todo/generated.go index 8b815211d0..8b32fcb3fb 100644 --- a/example/todo/generated.go +++ b/example/todo/generated.go @@ -37,12 +37,15 @@ func (e *executableSchema) Schema() *schema.Schema { func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._MyQuery(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._MyQuery(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -50,12 +53,15 @@ func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *grap func (e *executableSchema) Mutation(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._MyMutation(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._MyMutation(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -111,7 +117,7 @@ func (ec *executionContext) _MyMutation_createTodo(ctx context.Context, field gr Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.MyMutation_createTodo(rctx, args["todo"].(TodoInput)) }) if err != nil { @@ -152,7 +158,7 @@ func (ec *executionContext) _MyMutation_updateTodo(ctx context.Context, field gr Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.MyMutation_updateTodo(rctx, args["id"].(int), args["changes"].(map[string]interface{})) }) if err != nil { @@ -224,7 +230,7 @@ func (ec *executionContext) _MyQuery_todo(ctx context.Context, field graphql.Col Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.MyQuery_todo(rctx, args["id"].(int)) }) if err != nil { @@ -256,7 +262,7 @@ func (ec *executionContext) _MyQuery_lastTodo(ctx context.Context, field graphql Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.MyQuery_lastTodo(rctx) }) if err != nil { @@ -288,7 +294,7 @@ func (ec *executionContext) _MyQuery_todos(ctx context.Context, field graphql.Co Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.MyQuery_todos(rctx) }) if err != nil { diff --git a/graphql/context.go b/graphql/context.go index 4f2dcfcef7..e5210a4e40 100644 --- a/graphql/context.go +++ b/graphql/context.go @@ -9,14 +9,17 @@ import ( type Resolver func(ctx context.Context) (res interface{}, err error) type ResolverMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error) +type RequestMiddleware func(ctx context.Context, next func(ctx context.Context) []byte) []byte type RequestContext struct { errors.Builder - Variables map[string]interface{} - Doc *query.Document - Recover RecoverFunc - Middleware ResolverMiddleware + RawQuery string + Variables map[string]interface{} + Doc *query.Document + Recover RecoverFunc + ResolverMiddleware ResolverMiddleware + RequestMiddleware RequestMiddleware } type key string diff --git a/handler/graphql.go b/handler/graphql.go index e15c47f35a..44ed1924b0 100644 --- a/handler/graphql.go +++ b/handler/graphql.go @@ -25,6 +25,7 @@ type Config struct { recover graphql.RecoverFunc formatError func(error) string resolverHook graphql.ResolverMiddleware + requestHook graphql.RequestMiddleware } type Option func(cfg *Config) @@ -47,7 +48,11 @@ func FormatErrorFunc(f func(error) string) Option { } } -func Use(middleware graphql.ResolverMiddleware) Option { +// ResolverMiddleware allows you to define a function that will be called around every resolver, +// useful for tracing and logging. +// It will only be called for user defined resolvers, any direct binding to models is assumed +// to cost nothing. +func ResolverMiddleware(middleware graphql.ResolverMiddleware) Option { return func(cfg *Config) { if cfg.resolverHook == nil { cfg.resolverHook = middleware @@ -63,6 +68,24 @@ func Use(middleware graphql.ResolverMiddleware) Option { } } +// RequestMiddleware allows you to define a function that will be called around the root request, +// after the query has been parsed. This is useful for logging and tracing +func RequestMiddleware(middleware graphql.RequestMiddleware) Option { + return func(cfg *Config) { + if cfg.requestHook == nil { + cfg.requestHook = middleware + return + } + + lastResolve := cfg.requestHook + cfg.requestHook = func(ctx context.Context, next func(ctx context.Context) []byte) []byte { + return lastResolve(ctx, func(ctx context.Context) []byte { + return middleware(ctx, next) + }) + } + } +} + func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc { cfg := Config{ recover: graphql.DefaultRecoverFunc, @@ -82,6 +105,12 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc } } + if cfg.requestHook == nil { + cfg.requestHook = func(ctx context.Context, next func(ctx context.Context) []byte) []byte { + return next(ctx) + } + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodOptions { w.Header().Set("Allow", "OPTIONS, GET, POST") @@ -136,10 +165,12 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc } ctx := graphql.WithRequestContext(r.Context(), &graphql.RequestContext{ - Doc: doc, - Variables: reqParams.Variables, - Recover: cfg.recover, - Middleware: cfg.resolverHook, + Doc: doc, + RawQuery: reqParams.Query, + Variables: reqParams.Variables, + Recover: cfg.recover, + ResolverMiddleware: cfg.resolverHook, + RequestMiddleware: cfg.requestHook, Builder: errors.Builder{ ErrorMessageFn: cfg.formatError, }, diff --git a/handler/websocket.go b/handler/websocket.go index 7662390be6..32eee24553 100644 --- a/handler/websocket.go +++ b/handler/websocket.go @@ -49,7 +49,7 @@ func connectWs(exec graphql.ExecutableSchema, w http.ResponseWriter, r *http.Req "Sec-Websocket-Protocol": []string{"graphql-ws"}, }) if err != nil { - log.Printf("unable to upgrade connection to websocket %s: ", err.Error()) + log.Printf("unable to upgrade %T to websocket %s: ", w, err.Error()) sendErrorf(w, http.StatusBadRequest, "unable to upgrade") return } @@ -156,10 +156,12 @@ func (c *wsConnection) subscribe(message *operationMessage) bool { } ctx := graphql.WithRequestContext(c.ctx, &graphql.RequestContext{ - Doc: doc, - Variables: reqParams.Variables, - Recover: c.cfg.recover, - Middleware: c.cfg.resolverHook, + Doc: doc, + Variables: reqParams.Variables, + RawQuery: reqParams.Query, + Recover: c.cfg.recover, + ResolverMiddleware: c.cfg.resolverHook, + RequestMiddleware: c.cfg.requestHook, Builder: errors.Builder{ ErrorMessageFn: c.cfg.formatError, }, diff --git a/opentracing/opentracing.go b/opentracing/opentracing.go index d7699547c1..ddb98f7ae0 100644 --- a/opentracing/opentracing.go +++ b/opentracing/opentracing.go @@ -10,16 +10,14 @@ import ( "github.com/vektah/gqlgen/graphql" ) -func Middleware() graphql.ResolverMiddleware { +func ResolverMiddleware() graphql.ResolverMiddleware { return func(ctx context.Context, next graphql.Resolver) (interface{}, error) { rctx := graphql.GetResolverContext(ctx) - span, ctx := opentracing.StartSpanFromContext(ctx, rctx.Object+"_"+rctx.Field.Name, opentracing.Tag{Key: "resolver.object", Value: rctx.Object}, opentracing.Tag{Key: "resolver.field", Value: rctx.Field.Name}, ) defer span.Finish() - ext.SpanKind.Set(span, "server") ext.Component.Set(span, "gqlgen") @@ -37,3 +35,17 @@ func Middleware() graphql.ResolverMiddleware { return res, err } } + +func RequestMiddleware() graphql.RequestMiddleware { + return func(ctx context.Context, next func(ctx context.Context) []byte) []byte { + requestContext := graphql.GetRequestContext(ctx) + span, ctx := opentracing.StartSpanFromContext(ctx, requestContext.RawQuery) + defer span.Finish() + ext.SpanKind.Set(span, "server") + ext.Component.Set(span, "gqlgen") + + res := next(ctx) + + return res + } +} diff --git a/test/generated.go b/test/generated.go index 73771099c4..d0ea1ae173 100644 --- a/test/generated.go +++ b/test/generated.go @@ -44,12 +44,15 @@ func (e *executableSchema) Schema() *schema.Schema { func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response { ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - data := ec._Query(ctx, op.Selections) - var buf bytes.Buffer - data.MarshalGQL(&buf) + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Query(ctx, op.Selections) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) return &graphql.Response{ - Data: buf.Bytes(), + Data: buf, Errors: ec.Errors, } } @@ -219,7 +222,7 @@ func (ec *executionContext) _OuterObject_inner(ctx context.Context, field graphq Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.OuterObject_inner(rctx, obj) }) if err != nil { @@ -322,7 +325,7 @@ func (ec *executionContext) _Query_nestedInputs(ctx context.Context, field graph Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_nestedInputs(rctx, args["input"].([][]models.OuterInput)) }) if err != nil { @@ -354,7 +357,7 @@ func (ec *executionContext) _Query_nestedOutputs(ctx context.Context, field grap Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_nestedOutputs(rctx) }) if err != nil { @@ -393,7 +396,7 @@ func (ec *executionContext) _Query_shapes(ctx context.Context, field graphql.Col Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_shapes(rctx) }) if err != nil { @@ -442,7 +445,7 @@ func (ec *executionContext) _Query_recursive(ctx context.Context, field graphql. Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_recursive(rctx, args["input"].(*RecursiveInputSlice)) }) if err != nil { @@ -490,7 +493,7 @@ func (ec *executionContext) _Query_mapInput(ctx context.Context, field graphql.C Args: args, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_mapInput(rctx, args["input"].(*map[string]interface{})) }) if err != nil { @@ -522,7 +525,7 @@ func (ec *executionContext) _Query_collision(ctx context.Context, field graphql. Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_collision(rctx) }) if err != nil { @@ -554,7 +557,7 @@ func (ec *executionContext) _Query_invalidIdentifier(ctx context.Context, field Args: nil, Field: field, }) - resTmp, err := ec.Middleware(rctx, func(rctx context.Context) (interface{}, error) { + resTmp, err := ec.ResolverMiddleware(rctx, func(rctx context.Context) (interface{}, error) { return ec.resolvers.Query_invalidIdentifier(rctx) }) if err != nil { diff --git a/test/resolvers_test.go b/test/resolvers_test.go index 06fffd3fd9..edf119e226 100644 --- a/test/resolvers_test.go +++ b/test/resolvers_test.go @@ -71,7 +71,10 @@ func TestErrorConverter(t *testing.T) { func mkctx(doc *query.Document, errFn func(e error) string) context.Context { return graphql.WithRequestContext(context.Background(), &graphql.RequestContext{ Doc: doc, - Middleware: func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { + ResolverMiddleware: func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { + return next(ctx) + }, + RequestMiddleware: func(ctx context.Context, next func(ctx context.Context) []byte) []byte { return next(ctx) }, Builder: gqlerrors.Builder{