summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/graph-gophers/graphql-go/internal/exec
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/graph-gophers/graphql-go/internal/exec')
-rw-r--r--vendor/github.com/graph-gophers/graphql-go/internal/exec/exec.go381
-rw-r--r--vendor/github.com/graph-gophers/graphql-go/internal/exec/packer/packer.go390
-rw-r--r--vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/meta.go70
-rw-r--r--vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/resolvable.go453
-rw-r--r--vendor/github.com/graph-gophers/graphql-go/internal/exec/selected/selected.go269
-rw-r--r--vendor/github.com/graph-gophers/graphql-go/internal/exec/subscribe.go179
6 files changed, 1742 insertions, 0 deletions
diff --git a/vendor/github.com/graph-gophers/graphql-go/internal/exec/exec.go b/vendor/github.com/graph-gophers/graphql-go/internal/exec/exec.go
new file mode 100644
index 00000000..6b478487
--- /dev/null
+++ b/vendor/github.com/graph-gophers/graphql-go/internal/exec/exec.go
@@ -0,0 +1,381 @@
+package exec
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "sync"
+ "time"
+
+ "github.com/graph-gophers/graphql-go/errors"
+ "github.com/graph-gophers/graphql-go/internal/exec/resolvable"
+ "github.com/graph-gophers/graphql-go/internal/exec/selected"
+ "github.com/graph-gophers/graphql-go/internal/query"
+ "github.com/graph-gophers/graphql-go/log"
+ "github.com/graph-gophers/graphql-go/trace"
+ "github.com/graph-gophers/graphql-go/types"
+)
+
+type Request struct {
+ selected.Request
+ Limiter chan struct{}
+ Tracer trace.Tracer
+ Logger log.Logger
+ PanicHandler errors.PanicHandler
+ SubscribeResolverTimeout time.Duration
+}
+
+func (r *Request) handlePanic(ctx context.Context) {
+ if value := recover(); value != nil {
+ r.Logger.LogPanic(ctx, value)
+ r.AddError(r.PanicHandler.MakePanicError(ctx, value))
+ }
+}
+
+type extensionser interface {
+ Extensions() map[string]interface{}
+}
+
+func (r *Request) Execute(ctx context.Context, s *resolvable.Schema, op *types.OperationDefinition) ([]byte, []*errors.QueryError) {
+ var out bytes.Buffer
+ func() {
+ defer r.handlePanic(ctx)
+ sels := selected.ApplyOperation(&r.Request, s, op)
+ r.execSelections(ctx, sels, nil, s, s.Resolver, &out, op.Type == query.Mutation)
+ }()
+
+ if err := ctx.Err(); err != nil {
+ return nil, []*errors.QueryError{errors.Errorf("%s", err)}
+ }
+
+ return out.Bytes(), r.Errs
+}
+
+type fieldToExec struct {
+ field *selected.SchemaField
+ sels []selected.Selection
+ resolver reflect.Value
+ out *bytes.Buffer
+}
+
+func resolvedToNull(b *bytes.Buffer) bool {
+ return bytes.Equal(b.Bytes(), []byte("null"))
+}
+
+func (r *Request) execSelections(ctx context.Context, sels []selected.Selection, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer, serially bool) {
+ async := !serially && selected.HasAsyncSel(sels)
+
+ var fields []*fieldToExec
+ collectFieldsToResolve(sels, s, resolver, &fields, make(map[string]*fieldToExec))
+
+ if async {
+ var wg sync.WaitGroup
+ wg.Add(len(fields))
+ for _, f := range fields {
+ go func(f *fieldToExec) {
+ defer wg.Done()
+ defer r.handlePanic(ctx)
+ f.out = new(bytes.Buffer)
+ execFieldSelection(ctx, r, s, f, &pathSegment{path, f.field.Alias}, true)
+ }(f)
+ }
+ wg.Wait()
+ } else {
+ for _, f := range fields {
+ f.out = new(bytes.Buffer)
+ execFieldSelection(ctx, r, s, f, &pathSegment{path, f.field.Alias}, true)
+ }
+ }
+
+ out.WriteByte('{')
+ for i, f := range fields {
+ // If a non-nullable child resolved to null, an error was added to the
+ // "errors" list in the response, so this field resolves to null.
+ // If this field is non-nullable, the error is propagated to its parent.
+ if _, ok := f.field.Type.(*types.NonNull); ok && resolvedToNull(f.out) {
+ out.Reset()
+ out.Write([]byte("null"))
+ return
+ }
+
+ if i > 0 {
+ out.WriteByte(',')
+ }
+ out.WriteByte('"')
+ out.WriteString(f.field.Alias)
+ out.WriteByte('"')
+ out.WriteByte(':')
+ out.Write(f.out.Bytes())
+ }
+ out.WriteByte('}')
+}
+
+func collectFieldsToResolve(sels []selected.Selection, s *resolvable.Schema, resolver reflect.Value, fields *[]*fieldToExec, fieldByAlias map[string]*fieldToExec) {
+ for _, sel := range sels {
+ switch sel := sel.(type) {
+ case *selected.SchemaField:
+ field, ok := fieldByAlias[sel.Alias]
+ if !ok { // validation already checked for conflict (TODO)
+ field = &fieldToExec{field: sel, resolver: resolver}
+ fieldByAlias[sel.Alias] = field
+ *fields = append(*fields, field)
+ }
+ field.sels = append(field.sels, sel.Sels...)
+
+ case *selected.TypenameField:
+ _, ok := fieldByAlias[sel.Alias]
+ if !ok {
+ res := reflect.ValueOf(typeOf(sel, resolver))
+ f := s.FieldTypename
+ f.TypeName = res.String()
+
+ sf := &selected.SchemaField{
+ Field: f,
+ Alias: sel.Alias,
+ FixedResult: res,
+ }
+
+ field := &fieldToExec{field: sf, resolver: resolver}
+ *fields = append(*fields, field)
+ fieldByAlias[sel.Alias] = field
+ }
+
+ case *selected.TypeAssertion:
+ out := resolver.Method(sel.MethodIndex).Call(nil)
+ if !out[1].Bool() {
+ continue
+ }
+ collectFieldsToResolve(sel.Sels, s, out[0], fields, fieldByAlias)
+
+ default:
+ panic("unreachable")
+ }
+ }
+}
+
+func typeOf(tf *selected.TypenameField, resolver reflect.Value) string {
+ if len(tf.TypeAssertions) == 0 {
+ return tf.Name
+ }
+ for name, a := range tf.TypeAssertions {
+ out := resolver.Method(a.MethodIndex).Call(nil)
+ if out[1].Bool() {
+ return name
+ }
+ }
+ return ""
+}
+
+func execFieldSelection(ctx context.Context, r *Request, s *resolvable.Schema, f *fieldToExec, path *pathSegment, applyLimiter bool) {
+ if applyLimiter {
+ r.Limiter <- struct{}{}
+ }
+
+ var result reflect.Value
+ var err *errors.QueryError
+
+ traceCtx, finish := r.Tracer.TraceField(ctx, f.field.TraceLabel, f.field.TypeName, f.field.Name, !f.field.Async, f.field.Args)
+ defer func() {
+ finish(err)
+ }()
+
+ err = func() (err *errors.QueryError) {
+ defer func() {
+ if panicValue := recover(); panicValue != nil {
+ r.Logger.LogPanic(ctx, panicValue)
+ err = r.PanicHandler.MakePanicError(ctx, panicValue)
+ err.Path = path.toSlice()
+ }
+ }()
+
+ if f.field.FixedResult.IsValid() {
+ result = f.field.FixedResult
+ return nil
+ }
+
+ if err := traceCtx.Err(); err != nil {
+ return errors.Errorf("%s", err) // don't execute any more resolvers if context got cancelled
+ }
+
+ res := f.resolver
+ if f.field.UseMethodResolver() {
+ var in []reflect.Value
+ if f.field.HasContext {
+ in = append(in, reflect.ValueOf(traceCtx))
+ }
+ if f.field.ArgsPacker != nil {
+ in = append(in, f.field.PackedArgs)
+ }
+ callOut := res.Method(f.field.MethodIndex).Call(in)
+ result = callOut[0]
+ if f.field.HasError && !callOut[1].IsNil() {
+ resolverErr := callOut[1].Interface().(error)
+ err := errors.Errorf("%s", resolverErr)
+ err.Path = path.toSlice()
+ err.ResolverError = resolverErr
+ if ex, ok := callOut[1].Interface().(extensionser); ok {
+ err.Extensions = ex.Extensions()
+ }
+ return err
+ }
+ } else {
+ // TODO extract out unwrapping ptr logic to a common place
+ if res.Kind() == reflect.Ptr {
+ res = res.Elem()
+ }
+ result = res.FieldByIndex(f.field.FieldIndex)
+ }
+ return nil
+ }()
+
+ if applyLimiter {
+ <-r.Limiter
+ }
+
+ if err != nil {
+ // If an error occurred while resolving a field, it should be treated as though the field
+ // returned null, and an error must be added to the "errors" list in the response.
+ r.AddError(err)
+ f.out.WriteString("null")
+ return
+ }
+
+ r.execSelectionSet(traceCtx, f.sels, f.field.Type, path, s, result, f.out)
+}
+
+func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selection, typ types.Type, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer) {
+ t, nonNull := unwrapNonNull(typ)
+
+ // a reflect.Value of a nil interface will show up as an Invalid value
+ if resolver.Kind() == reflect.Invalid || ((resolver.Kind() == reflect.Ptr || resolver.Kind() == reflect.Interface) && resolver.IsNil()) {
+ // If a field of a non-null type resolves to null (either because the
+ // function to resolve the field returned null or because an error occurred),
+ // add an error to the "errors" list in the response.
+ if nonNull {
+ err := errors.Errorf("graphql: got nil for non-null %q", t)
+ err.Path = path.toSlice()
+ r.AddError(err)
+ }
+ out.WriteString("null")
+ return
+ }
+
+ switch t.(type) {
+ case *types.ObjectTypeDefinition, *types.InterfaceTypeDefinition, *types.Union:
+ r.execSelections(ctx, sels, path, s, resolver, out, false)
+ return
+ }
+
+ // Any pointers or interfaces at this point should be non-nil, so we can get the actual value of them
+ // for serialization
+ if resolver.Kind() == reflect.Ptr || resolver.Kind() == reflect.Interface {
+ resolver = resolver.Elem()
+ }
+
+ switch t := t.(type) {
+ case *types.List:
+ r.execList(ctx, sels, t, path, s, resolver, out)
+
+ case *types.ScalarTypeDefinition:
+ v := resolver.Interface()
+ data, err := json.Marshal(v)
+ if err != nil {
+ panic(errors.Errorf("could not marshal %v: %s", v, err))
+ }
+ out.Write(data)
+
+ case *types.EnumTypeDefinition:
+ var stringer fmt.Stringer = resolver
+ if s, ok := resolver.Interface().(fmt.Stringer); ok {
+ stringer = s
+ }
+ name := stringer.String()
+ var valid bool
+ for _, v := range t.EnumValuesDefinition {
+ if v.EnumValue == name {
+ valid = true
+ break
+ }
+ }
+ if !valid {
+ err := errors.Errorf("Invalid value %s.\nExpected type %s, found %s.", name, t.Name, name)
+ err.Path = path.toSlice()
+ r.AddError(err)
+ out.WriteString("null")
+ return
+ }
+ out.WriteByte('"')
+ out.WriteString(name)
+ out.WriteByte('"')
+
+ default:
+ panic("unreachable")
+ }
+}
+
+func (r *Request) execList(ctx context.Context, sels []selected.Selection, typ *types.List, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer) {
+ l := resolver.Len()
+ entryouts := make([]bytes.Buffer, l)
+
+ if selected.HasAsyncSel(sels) {
+ // Limit the number of concurrent goroutines spawned as it can lead to large
+ // memory spikes for large lists.
+ concurrency := cap(r.Limiter)
+ sem := make(chan struct{}, concurrency)
+ for i := 0; i < l; i++ {
+ sem <- struct{}{}
+ go func(i int) {
+ defer func() { <-sem }()
+ defer r.handlePanic(ctx)
+ r.execSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, i}, s, resolver.Index(i), &entryouts[i])
+ }(i)
+ }
+ for i := 0; i < concurrency; i++ {
+ sem <- struct{}{}
+ }
+ } else {
+ for i := 0; i < l; i++ {
+ r.execSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, i}, s, resolver.Index(i), &entryouts[i])
+ }
+ }
+
+ _, listOfNonNull := typ.OfType.(*types.NonNull)
+
+ out.WriteByte('[')
+ for i, entryout := range entryouts {
+ // If the list wraps a non-null type and one of the list elements
+ // resolves to null, then the entire list resolves to null.
+ if listOfNonNull && resolvedToNull(&entryout) {
+ out.Reset()
+ out.WriteString("null")
+ return
+ }
+
+ if i > 0 {
+ out.WriteByte(',')
+ }
+ out.Write(entryout.Bytes())
+ }
+ out.WriteByte(']')
+}
+
+func unwrapNonNull(t types.Type) (types.Type, bool) {
+ if nn, ok := t.(*types.NonNull); ok {
+ return nn.OfType, true
+ }
+ return t, false
+}
+
+type pathSegment struct {
+ parent *pathSegment
+ value interface{}
+}
+
+func (p *pathSegment) toSlice() []interface{} {
+ if p == nil {
+ return nil
+ }
+ return append(p.parent.toSlice(), p.value)
+}
diff --git a/vendor/github.com/graph-gophers/graphql-go/internal/exec/packer/packer.go b/vendor/github.com/graph-gophers/graphql-go/internal/exec/packer/packer.go
new file mode 100644
index 00000000..c0bb7dc9
--- /dev/null
+++ b/vendor/github.com/graph-gophers/graphql-go/internal/exec/packer/packer.go
@@ -0,0 +1,390 @@
+package packer
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "strings"
+
+ "github.com/graph-gophers/graphql-go/decode"
+ "github.com/graph-gophers/graphql-go/errors"
+ "github.com/graph-gophers/graphql-go/types"
+)
+
+type packer interface {
+ Pack(value interface{}) (reflect.Value, error)
+}
+
+type Builder struct {
+ packerMap map[typePair]*packerMapEntry
+ structPackers []*StructPacker
+}
+
+type typePair struct {
+ graphQLType types.Type
+ resolverType reflect.Type
+}
+
+type packerMapEntry struct {
+ packer packer
+ targets []*packer
+}
+
+func NewBuilder() *Builder {
+ return &Builder{
+ packerMap: make(map[typePair]*packerMapEntry),
+ }
+}
+
+func (b *Builder) Finish() error {
+ for _, entry := range b.packerMap {
+ for _, target := range entry.targets {
+ *target = entry.packer
+ }
+ }
+
+ for _, p := range b.structPackers {
+ p.defaultStruct = reflect.New(p.structType).Elem()
+ for _, f := range p.fields {
+ if defaultVal := f.field.Default; defaultVal != nil {
+ v, err := f.fieldPacker.Pack(defaultVal.Deserialize(nil))
+ if err != nil {
+ return err
+ }
+ p.defaultStruct.FieldByIndex(f.fieldIndex).Set(v)
+ }
+ }
+ }
+
+ return nil
+}
+
+func (b *Builder) assignPacker(target *packer, schemaType types.Type, reflectType reflect.Type) error {
+ k := typePair{schemaType, reflectType}
+ ref, ok := b.packerMap[k]
+ if !ok {
+ ref = &packerMapEntry{}
+ b.packerMap[k] = ref
+ var err error
+ ref.packer, err = b.makePacker(schemaType, reflectType)
+ if err != nil {
+ return err
+ }
+ }
+ ref.targets = append(ref.targets, target)
+ return nil
+}
+
+func (b *Builder) makePacker(schemaType types.Type, reflectType reflect.Type) (packer, error) {
+ t, nonNull := unwrapNonNull(schemaType)
+ if !nonNull {
+ if reflectType.Kind() == reflect.Ptr {
+ elemType := reflectType.Elem()
+ addPtr := true
+ if _, ok := t.(*types.InputObject); ok {
+ elemType = reflectType // keep pointer for input objects
+ addPtr = false
+ }
+ elem, err := b.makeNonNullPacker(t, elemType)
+ if err != nil {
+ return nil, err
+ }
+ return &nullPacker{
+ elemPacker: elem,
+ valueType: reflectType,
+ addPtr: addPtr,
+ }, nil
+ } else if isNullable(reflectType) {
+ elemType := reflectType
+ addPtr := false
+ elem, err := b.makeNonNullPacker(t, elemType)
+ if err != nil {
+ return nil, err
+ }
+ return &nullPacker{
+ elemPacker: elem,
+ valueType: reflectType,
+ addPtr: addPtr,
+ }, nil
+ } else {
+ return nil, fmt.Errorf("%s is not a pointer or a nullable type", reflectType)
+ }
+ }
+
+ return b.makeNonNullPacker(t, reflectType)
+}
+
+func (b *Builder) makeNonNullPacker(schemaType types.Type, reflectType reflect.Type) (packer, error) {
+ if u, ok := reflect.New(reflectType).Interface().(decode.Unmarshaler); ok {
+ if !u.ImplementsGraphQLType(schemaType.String()) {
+ return nil, fmt.Errorf("can not unmarshal %s into %s", schemaType, reflectType)
+ }
+ return &unmarshalerPacker{
+ ValueType: reflectType,
+ }, nil
+ }
+
+ switch t := schemaType.(type) {
+ case *types.ScalarTypeDefinition:
+ return &ValuePacker{
+ ValueType: reflectType,
+ }, nil
+
+ case *types.EnumTypeDefinition:
+ if reflectType.Kind() != reflect.String {
+ return nil, fmt.Errorf("wrong type, expected %s", reflect.String)
+ }
+ return &ValuePacker{
+ ValueType: reflectType,
+ }, nil
+
+ case *types.InputObject:
+ e, err := b.MakeStructPacker(t.Values, reflectType)
+ if err != nil {
+ return nil, err
+ }
+ return e, nil
+
+ case *types.List:
+ if reflectType.Kind() != reflect.Slice {
+ return nil, fmt.Errorf("expected slice, got %s", reflectType)
+ }
+ p := &listPacker{
+ sliceType: reflectType,
+ }
+ if err := b.assignPacker(&p.elem, t.OfType, reflectType.Elem()); err != nil {
+ return nil, err
+ }
+ return p, nil
+
+ case *types.ObjectTypeDefinition, *types.InterfaceTypeDefinition, *types.Union:
+ return nil, fmt.Errorf("type of kind %s can not be used as input", t.Kind())
+
+ default:
+ panic("unreachable")
+ }
+}
+
+func (b *Builder) MakeStructPacker(values []*types.InputValueDefinition, typ reflect.Type) (*StructPacker, error) {
+ structType := typ
+ usePtr := false
+ if typ.Kind() == reflect.Ptr {
+ structType = typ.Elem()
+ usePtr = true
+ }
+ if structType.Kind() != reflect.Struct {
+ return nil, fmt.Errorf("expected struct or pointer to struct, got %s (hint: missing `args struct { ... }` wrapper for field arguments?)", typ)
+ }
+
+ var fields []*structPackerField
+ for _, v := range values {
+ fe := &structPackerField{field: v}
+ fx := func(n string) bool {
+ return strings.EqualFold(stripUnderscore(n), stripUnderscore(v.Name.Name))
+ }
+
+ sf, ok := structType.FieldByNameFunc(fx)
+ if !ok {
+ return nil, fmt.Errorf("%s does not define field %q (hint: missing `args struct { ... }` wrapper for field arguments, or missing field on input struct)", typ, v.Name.Name)
+ }
+ if sf.PkgPath != "" {
+ return nil, fmt.Errorf("field %q must be exported", sf.Name)
+ }
+ fe.fieldIndex = sf.Index
+
+ ft := v.Type
+ if v.Default != nil {
+ ft, _ = unwrapNonNull(ft)
+ ft = &types.NonNull{OfType: ft}
+ }
+
+ if err := b.assignPacker(&fe.fieldPacker, ft, sf.Type); err != nil {
+ return nil, fmt.Errorf("field %q: %s", sf.Name, err)
+ }
+
+ fields = append(fields, fe)
+ }
+
+ p := &StructPacker{
+ structType: structType,
+ usePtr: usePtr,
+ fields: fields,
+ }
+ b.structPackers = append(b.structPackers, p)
+ return p, nil
+}
+
+type StructPacker struct {
+ structType reflect.Type
+ usePtr bool
+ defaultStruct reflect.Value
+ fields []*structPackerField
+}
+
+type structPackerField struct {
+ field *types.InputValueDefinition
+ fieldIndex []int
+ fieldPacker packer
+}
+
+func (p *StructPacker) Pack(value interface{}) (reflect.Value, error) {
+ if value == nil {
+ return reflect.Value{}, errors.Errorf("got null for non-null")
+ }
+
+ values := value.(map[string]interface{})
+ v := reflect.New(p.structType)
+ v.Elem().Set(p.defaultStruct)
+ for _, f := range p.fields {
+ if value, ok := values[f.field.Name.Name]; ok {
+ packed, err := f.fieldPacker.Pack(value)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ v.Elem().FieldByIndex(f.fieldIndex).Set(packed)
+ }
+ }
+ if !p.usePtr {
+ return v.Elem(), nil
+ }
+ return v, nil
+}
+
+type listPacker struct {
+ sliceType reflect.Type
+ elem packer
+}
+
+func (e *listPacker) Pack(value interface{}) (reflect.Value, error) {
+ list, ok := value.([]interface{})
+ if !ok {
+ list = []interface{}{value}
+ }
+
+ v := reflect.MakeSlice(e.sliceType, len(list), len(list))
+ for i := range list {
+ packed, err := e.elem.Pack(list[i])
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ v.Index(i).Set(packed)
+ }
+ return v, nil
+}
+
+type nullPacker struct {
+ elemPacker packer
+ valueType reflect.Type
+ addPtr bool
+}
+
+func (p *nullPacker) Pack(value interface{}) (reflect.Value, error) {
+ if value == nil && !isNullable(p.valueType) {
+ return reflect.Zero(p.valueType), nil
+ }
+
+ v, err := p.elemPacker.Pack(value)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+
+ if p.addPtr {
+ ptr := reflect.New(p.valueType.Elem())
+ ptr.Elem().Set(v)
+ return ptr, nil
+ }
+
+ return v, nil
+}
+
+type ValuePacker struct {
+ ValueType reflect.Type
+}
+
+func (p *ValuePacker) Pack(value interface{}) (reflect.Value, error) {
+ if value == nil {
+ return reflect.Value{}, errors.Errorf("got null for non-null")
+ }
+
+ coerced, err := unmarshalInput(p.ValueType, value)
+ if err != nil {
+ return reflect.Value{}, fmt.Errorf("could not unmarshal %#v (%T) into %s: %s", value, value, p.ValueType, err)
+ }
+ return reflect.ValueOf(coerced), nil
+}
+
+type unmarshalerPacker struct {
+ ValueType reflect.Type
+}
+
+func (p *unmarshalerPacker) Pack(value interface{}) (reflect.Value, error) {
+ if value == nil && !isNullable(p.ValueType) {
+ return reflect.Value{}, errors.Errorf("got null for non-null")
+ }
+
+ v := reflect.New(p.ValueType)
+ if err := v.Interface().(decode.Unmarshaler).UnmarshalGraphQL(value); err != nil {
+ return reflect.Value{}, err
+ }
+ return v.Elem(), nil
+}
+
+func unmarshalInput(typ reflect.Type, input interface{}) (interface{}, error) {
+ if reflect.TypeOf(input) == typ {
+ return input, nil
+ }
+
+ switch typ.Kind() {
+ case reflect.Int32:
+ switch input := input.(type) {
+ case int:
+ if input < math.MinInt32 || input > math.MaxInt32 {
+ return nil, fmt.Errorf("not a 32-bit integer")
+ }
+ return int32(input), nil
+ case float64:
+ coerced := int32(input)
+ if input < math.MinInt32 || input > math.MaxInt32 || float64(coerced) != input {
+ return nil, fmt.Errorf("not a 32-bit integer")
+ }
+ return coerced, nil
+ }
+
+ case reflect.Float64:
+ switch input := input.(type) {
+ case int32:
+ return float64(input), nil
+ case int:
+ return float64(input), nil
+ }
+
+ case reflect.String:
+ if reflect.TypeOf(input).ConvertibleTo(typ) {
+ return reflect.ValueOf(input).Convert(typ).Interface(), nil
+ }
+ }
+
+ return nil, fmt.Errorf("incompatible type")
+}
+
+func unwrapNonNull(t types.Type) (types.Type, bool) {
+ if nn, ok := t.(*types.NonNull); ok {
+ return nn.OfType, true
+ }
+ return t, false
+}
+
+func stripUnderscore(s string) string {
+ return strings.Replace(s, "_", "", -1)
+}
+
+// NullUnmarshaller is an unmarshaller that can handle a nil input
+type NullUnmarshaller interface {
+ decode.Unmarshaler
+ Nullable()
+}
+
+func isNullable(t reflect.Type) bool {
+ _, ok := reflect.New(t).Interface().(NullUnmarshaller)
+ return ok
+}
diff --git a/vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/meta.go b/vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/meta.go
new file mode 100644
index 00000000..02d5e262
--- /dev/null
+++ b/vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/meta.go
@@ -0,0 +1,70 @@
+package resolvable
+
+import (
+ "reflect"
+
+ "github.com/graph-gophers/graphql-go/introspection"
+ "github.com/graph-gophers/graphql-go/types"
+)
+
+// Meta defines the details of the metadata schema for introspection.
+type Meta struct {
+ FieldSchema Field
+ FieldType Field
+ FieldTypename Field
+ Schema *Object
+ Type *Object
+}
+
+func newMeta(s *types.Schema) *Meta {
+ var err error
+ b := newBuilder(s)
+
+ metaSchema := s.Types["__Schema"].(*types.ObjectTypeDefinition)
+ so, err := b.makeObjectExec(metaSchema.Name, metaSchema.Fields, nil, false, reflect.TypeOf(&introspection.Schema{}))
+ if err != nil {
+ panic(err)
+ }
+
+ metaType := s.Types["__Type"].(*types.ObjectTypeDefinition)
+ t, err := b.makeObjectExec(metaType.Name, metaType.Fields, nil, false, reflect.TypeOf(&introspection.Type{}))
+ if err != nil {
+ panic(err)
+ }
+
+ if err := b.finish(); err != nil {
+ panic(err)
+ }
+
+ fieldTypename := Field{
+ FieldDefinition: types.FieldDefinition{
+ Name: "__typename",
+ Type: &types.NonNull{OfType: s.Types["String"]},
+ },
+ TraceLabel: "GraphQL field: __typename",
+ }
+
+ fieldSchema := Field{
+ FieldDefinition: types.FieldDefinition{
+ Name: "__schema",
+ Type: s.Types["__Schema"],
+ },
+ TraceLabel: "GraphQL field: __schema",
+ }
+
+ fieldType := Field{
+ FieldDefinition: types.FieldDefinition{
+ Name: "__type",
+ Type: s.Types["__Type"],
+ },
+ TraceLabel: "GraphQL field: __type",
+ }
+
+ return &Meta{
+ FieldSchema: fieldSchema,
+ FieldTypename: fieldTypename,
+ FieldType: fieldType,
+ Schema: so,
+ Type: t,
+ }
+}
diff --git a/vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/resolvable.go b/vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/resolvable.go
new file mode 100644
index 00000000..3410f557
--- /dev/null
+++ b/vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/resolvable.go
@@ -0,0 +1,453 @@
+package resolvable
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/graph-gophers/graphql-go/decode"
+ "github.com/graph-gophers/graphql-go/internal/exec/packer"
+ "github.com/graph-gophers/graphql-go/types"
+)
+
+type Schema struct {
+ *Meta
+ types.Schema
+ Query Resolvable
+ Mutation Resolvable
+ Subscription Resolvable
+ Resolver reflect.Value
+}
+
+type Resolvable interface {
+ isResolvable()
+}
+
+type Object struct {
+ Name string
+ Fields map[string]*Field
+ TypeAssertions map[string]*TypeAssertion
+}
+
+type Field struct {
+ types.FieldDefinition
+ TypeName string
+ MethodIndex int
+ FieldIndex []int
+ HasContext bool
+ HasError bool
+ ArgsPacker *packer.StructPacker
+ ValueExec Resolvable
+ TraceLabel string
+}
+
+func (f *Field) UseMethodResolver() bool {
+ return len(f.FieldIndex) == 0
+}
+
+type TypeAssertion struct {
+ MethodIndex int
+ TypeExec Resolvable
+}
+
+type List struct {
+ Elem Resolvable
+}
+
+type Scalar struct{}
+
+func (*Object) isResolvable() {}
+func (*List) isResolvable() {}
+func (*Scalar) isResolvable() {}
+
+func ApplyResolver(s *types.Schema, resolver interface{}) (*Schema, error) {
+ if resolver == nil {
+ return &Schema{Meta: newMeta(s), Schema: *s}, nil
+ }
+
+ b := newBuilder(s)
+
+ var query, mutation, subscription Resolvable
+
+ if t, ok := s.EntryPoints["query"]; ok {
+ if err := b.assignExec(&query, t, reflect.TypeOf(resolver)); err != nil {
+ return nil, err
+ }
+ }
+
+ if t, ok := s.EntryPoints["mutation"]; ok {
+ if err := b.assignExec(&mutation, t, reflect.TypeOf(resolver)); err != nil {
+ return nil, err
+ }
+ }
+
+ if t, ok := s.EntryPoints["subscription"]; ok {
+ if err := b.assignExec(&subscription, t, reflect.TypeOf(resolver)); err != nil {
+ return nil, err
+ }
+ }
+
+ if err := b.finish(); err != nil {
+ return nil, err
+ }
+
+ return &Schema{
+ Meta: newMeta(s),
+ Schema: *s,
+ Resolver: reflect.ValueOf(resolver),
+ Query: query,
+ Mutation: mutation,
+ Subscription: subscription,
+ }, nil
+}
+
+type execBuilder struct {
+ schema *types.Schema
+ resMap map[typePair]*resMapEntry
+ packerBuilder *packer.Builder
+}
+
+type typePair struct {
+ graphQLType types.Type
+ resolverType reflect.Type
+}
+
+type resMapEntry struct {
+ exec Resolvable
+ targets []*Resolvable
+}
+
+func newBuilder(s *types.Schema) *execBuilder {
+ return &execBuilder{
+ schema: s,
+ resMap: make(map[typePair]*resMapEntry),
+ packerBuilder: packer.NewBuilder(),
+ }
+}
+
+func (b *execBuilder) finish() error {
+ for _, entry := range b.resMap {
+ for _, target := range entry.targets {
+ *target = entry.exec
+ }
+ }
+
+ return b.packerBuilder.Finish()
+}
+
+func (b *execBuilder) assignExec(target *Resolvable, t types.Type, resolverType reflect.Type) error {
+ k := typePair{t, resolverType}
+ ref, ok := b.resMap[k]
+ if !ok {
+ ref = &resMapEntry{}
+ b.resMap[k] = ref
+ var err error
+ ref.exec, err = b.makeExec(t, resolverType)
+ if err != nil {
+ return err
+ }
+ }
+ ref.targets = append(ref.targets, target)
+ return nil
+}
+
+func (b *execBuilder) makeExec(t types.Type, resolverType reflect.Type) (Resolvable, error) {
+ var nonNull bool
+ t, nonNull = unwrapNonNull(t)
+
+ switch t := t.(type) {
+ case *types.ObjectTypeDefinition:
+ return b.makeObjectExec(t.Name, t.Fields, nil, nonNull, resolverType)
+
+ case *types.InterfaceTypeDefinition:
+ return b.makeObjectExec(t.Name, t.Fields, t.PossibleTypes, nonNull, resolverType)
+
+ case *types.Union:
+ return b.makeObjectExec(t.Name, nil, t.UnionMemberTypes, nonNull, resolverType)
+ }
+
+ if !nonNull {
+ if resolverType.Kind() != reflect.Ptr {
+ return nil, fmt.Errorf("%s is not a pointer", resolverType)
+ }
+ resolverType = resolverType.Elem()
+ }
+
+ switch t := t.(type) {
+ case *types.ScalarTypeDefinition:
+ return makeScalarExec(t, resolverType)
+
+ case *types.EnumTypeDefinition:
+ return &Scalar{}, nil
+
+ case *types.List:
+ if resolverType.Kind() != reflect.Slice {
+ return nil, fmt.Errorf("%s is not a slice", resolverType)
+ }
+ e := &List{}
+ if err := b.assignExec(&e.Elem, t.OfType, resolverType.Elem()); err != nil {
+ return nil, err
+ }
+ return e, nil
+
+ default:
+ panic("invalid type: " + t.String())
+ }
+}
+
+func makeScalarExec(t *types.ScalarTypeDefinition, resolverType reflect.Type) (Resolvable, error) {
+ implementsType := false
+ switch r := reflect.New(resolverType).Interface().(type) {
+ case *int32:
+ implementsType = t.Name == "Int"
+ case *float64:
+ implementsType = t.Name == "Float"
+ case *string:
+ implementsType = t.Name == "String"
+ case *bool:
+ implementsType = t.Name == "Boolean"
+ case decode.Unmarshaler:
+ implementsType = r.ImplementsGraphQLType(t.Name)
+ }
+
+ if !implementsType {
+ return nil, fmt.Errorf("can not use %s as %s", resolverType, t.Name)
+ }
+ return &Scalar{}, nil
+}
+
+func (b *execBuilder) makeObjectExec(typeName string, fields types.FieldsDefinition, possibleTypes []*types.ObjectTypeDefinition,
+ nonNull bool, resolverType reflect.Type) (*Object, error) {
+ if !nonNull {
+ if resolverType.Kind() != reflect.Ptr && resolverType.Kind() != reflect.Interface {
+ return nil, fmt.Errorf("%s is not a pointer or interface", resolverType)
+ }
+ }
+
+ methodHasReceiver := resolverType.Kind() != reflect.Interface
+
+ Fields := make(map[string]*Field)
+ rt := unwrapPtr(resolverType)
+ fieldsCount := fieldCount(rt, map[string]int{})
+ for _, f := range fields {
+ var fieldIndex []int
+ methodIndex := findMethod(resolverType, f.Name)
+ if b.schema.UseFieldResolvers && methodIndex == -1 {
+ if fieldsCount[strings.ToLower(stripUnderscore(f.Name))] > 1 {
+ return nil, fmt.Errorf("%s does not resolve %q: ambiguous field %q", resolverType, typeName, f.Name)
+ }
+ fieldIndex = findField(rt, f.Name, []int{})
+ }
+ if methodIndex == -1 && len(fieldIndex) == 0 {
+ hint := ""
+ if findMethod(reflect.PtrTo(resolverType), f.Name) != -1 {
+ hint = " (hint: the method exists on the pointer type)"
+ }
+ return nil, fmt.Errorf("%s does not resolve %q: missing method for field %q%s", resolverType, typeName, f.Name, hint)
+ }
+
+ var m reflect.Method
+ var sf reflect.StructField
+ if methodIndex != -1 {
+ m = resolverType.Method(methodIndex)
+ } else {
+ sf = rt.FieldByIndex(fieldIndex)
+ }
+ fe, err := b.makeFieldExec(typeName, f, m, sf, methodIndex, fieldIndex, methodHasReceiver)
+ if err != nil {
+ var resolverName string
+ if methodIndex != -1 {
+ resolverName = m.Name
+ } else {
+ resolverName = sf.Name
+ }
+ return nil, fmt.Errorf("%s\n\tused by (%s).%s", err, resolverType, resolverName)
+ }
+ Fields[f.Name] = fe
+ }
+
+ // Check type assertions when
+ // 1) using method resolvers
+ // 2) Or resolver is not an interface type
+ typeAssertions := make(map[string]*TypeAssertion)
+ if !b.schema.UseFieldResolvers || resolverType.Kind() != reflect.Interface {
+ for _, impl := range possibleTypes {
+ methodIndex := findMethod(resolverType, "To"+impl.Name)
+ if methodIndex == -1 {
+ return nil, fmt.Errorf("%s does not resolve %q: missing method %q to convert to %q", resolverType, typeName, "To"+impl.Name, impl.Name)
+ }
+ if resolverType.Method(methodIndex).Type.NumOut() != 2 {
+ return nil, fmt.Errorf("%s does not resolve %q: method %q should return a value and a bool indicating success", resolverType, typeName, "To"+impl.Name)
+ }
+ a := &TypeAssertion{
+ MethodIndex: methodIndex,
+ }
+ if err := b.assignExec(&a.TypeExec, impl, resolverType.Method(methodIndex).Type.Out(0)); err != nil {
+ return nil, err
+ }
+ typeAssertions[impl.Name] = a
+ }
+ }
+
+ return &Object{
+ Name: typeName,
+ Fields: Fields,
+ TypeAssertions: typeAssertions,
+ }, nil
+}
+
+var contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
+var errorType = reflect.TypeOf((*error)(nil)).Elem()
+
+func (b *execBuilder) makeFieldExec(typeName string, f *types.FieldDefinition, m reflect.Method, sf reflect.StructField,
+ methodIndex int, fieldIndex []int, methodHasReceiver bool) (*Field, error) {
+
+ var argsPacker *packer.StructPacker
+ var hasError bool
+ var hasContext bool
+
+ // Validate resolver method only when there is one
+ if methodIndex != -1 {
+ in := make([]reflect.Type, m.Type.NumIn())
+ for i := range in {
+ in[i] = m.Type.In(i)
+ }
+ if methodHasReceiver {
+ in = in[1:] // first parameter is receiver
+ }
+
+ hasContext = len(in) > 0 && in[0] == contextType
+ if hasContext {
+ in = in[1:]
+ }
+
+ if len(f.Arguments) > 0 {
+ if len(in) == 0 {
+ return nil, fmt.Errorf("must have parameter for field arguments")
+ }
+ var err error
+ argsPacker, err = b.packerBuilder.MakeStructPacker(f.Arguments, in[0])
+ if err != nil {
+ return nil, err
+ }
+ in = in[1:]
+ }
+
+ if len(in) > 0 {
+ return nil, fmt.Errorf("too many parameters")
+ }
+
+ maxNumOfReturns := 2
+ if m.Type.NumOut() < maxNumOfReturns-1 {
+ return nil, fmt.Errorf("too few return values")
+ }
+
+ if m.Type.NumOut() > maxNumOfReturns {
+ return nil, fmt.Errorf("too many return values")
+ }
+
+ hasError = m.Type.NumOut() == maxNumOfReturns
+ if hasError {
+ if m.Type.Out(maxNumOfReturns-1) != errorType {
+ return nil, fmt.Errorf(`must have "error" as its last return value`)
+ }
+ }
+ }
+
+ fe := &Field{
+ FieldDefinition: *f,
+ TypeName: typeName,
+ MethodIndex: methodIndex,
+ FieldIndex: fieldIndex,
+ HasContext: hasContext,
+ ArgsPacker: argsPacker,
+ HasError: hasError,
+ TraceLabel: fmt.Sprintf("GraphQL field: %s.%s", typeName, f.Name),
+ }
+
+ var out reflect.Type
+ if methodIndex != -1 {
+ out = m.Type.Out(0)
+ sub, ok := b.schema.EntryPoints["subscription"]
+ if ok && typeName == sub.TypeName() && out.Kind() == reflect.Chan {
+ out = m.Type.Out(0).Elem()
+ }
+ } else {
+ out = sf.Type
+ }
+ if err := b.assignExec(&fe.ValueExec, f.Type, out); err != nil {
+ return nil, err
+ }
+
+ return fe, nil
+}
+
+func findMethod(t reflect.Type, name string) int {
+ for i := 0; i < t.NumMethod(); i++ {
+ if strings.EqualFold(stripUnderscore(name), stripUnderscore(t.Method(i).Name)) {
+ return i
+ }
+ }
+ return -1
+}
+
+func findField(t reflect.Type, name string, index []int) []int {
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+
+ if field.Type.Kind() == reflect.Struct && field.Anonymous {
+ newIndex := findField(field.Type, name, []int{i})
+ if len(newIndex) > 1 {
+ return append(index, newIndex...)
+ }
+ }
+
+ if strings.EqualFold(stripUnderscore(name), stripUnderscore(field.Name)) {
+ return append(index, i)
+ }
+ }
+
+ return index
+}
+
+// fieldCount helps resolve ambiguity when more than one embedded struct contains fields with the same name.
+func fieldCount(t reflect.Type, count map[string]int) map[string]int {
+ if t.Kind() != reflect.Struct {
+ return nil
+ }
+
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+ fieldName := strings.ToLower(stripUnderscore(field.Name))
+
+ if field.Type.Kind() == reflect.Struct && field.Anonymous {
+ count = fieldCount(field.Type, count)
+ } else {
+ if _, ok := count[fieldName]; !ok {
+ count[fieldName] = 0
+ }
+ count[fieldName]++
+ }
+ }
+
+ return count
+}
+
+func unwrapNonNull(t types.Type) (types.Type, bool) {
+ if nn, ok := t.(*types.NonNull); ok {
+ return nn.OfType, true
+ }
+ return t, false
+}
+
+func stripUnderscore(s string) string {
+ return strings.Replace(s, "_", "", -1)
+}
+
+func unwrapPtr(t reflect.Type) reflect.Type {
+ if t.Kind() == reflect.Ptr {
+ return t.Elem()
+ }
+ return t
+}
diff --git a/vendor/github.com/graph-gophers/graphql-go/internal/exec/selected/selected.go b/vendor/github.com/graph-gophers/graphql-go/internal/exec/selected/selected.go
new file mode 100644
index 00000000..9b96d2b6
--- /dev/null
+++ b/vendor/github.com/graph-gophers/graphql-go/internal/exec/selected/selected.go
@@ -0,0 +1,269 @@
+package selected
+
+import (
+ "fmt"
+ "reflect"
+ "sync"
+
+ "github.com/graph-gophers/graphql-go/errors"
+ "github.com/graph-gophers/graphql-go/internal/exec/packer"
+ "github.com/graph-gophers/graphql-go/internal/exec/resolvable"
+ "github.com/graph-gophers/graphql-go/internal/query"
+ "github.com/graph-gophers/graphql-go/introspection"
+ "github.com/graph-gophers/graphql-go/types"
+)
+
+type Request struct {
+ Schema *types.Schema
+ Doc *types.ExecutableDefinition
+ Vars map[string]interface{}
+ Mu sync.Mutex
+ Errs []*errors.QueryError
+ DisableIntrospection bool
+}
+
+func (r *Request) AddError(err *errors.QueryError) {
+ r.Mu.Lock()
+ r.Errs = append(r.Errs, err)
+ r.Mu.Unlock()
+}
+
+func ApplyOperation(r *Request, s *resolvable.Schema, op *types.OperationDefinition) []Selection {
+ var obj *resolvable.Object
+ switch op.Type {
+ case query.Query:
+ obj = s.Query.(*resolvable.Object)
+ case query.Mutation:
+ obj = s.Mutation.(*resolvable.Object)
+ case query.Subscription:
+ obj = s.Subscription.(*resolvable.Object)
+ }
+ return applySelectionSet(r, s, obj, op.Selections)
+}
+
+type Selection interface {
+ isSelection()
+}
+
+type SchemaField struct {
+ resolvable.Field
+ Alias string
+ Args map[string]interface{}
+ PackedArgs reflect.Value
+ Sels []Selection
+ Async bool
+ FixedResult reflect.Value
+}
+
+type TypeAssertion struct {
+ resolvable.TypeAssertion
+ Sels []Selection
+}
+
+type TypenameField struct {
+ resolvable.Object
+ Alias string
+}
+
+func (*SchemaField) isSelection() {}
+func (*TypeAssertion) isSelection() {}
+func (*TypenameField) isSelection() {}
+
+func applySelectionSet(r *Request, s *resolvable.Schema, e *resolvable.Object, sels []types.Selection) (flattenedSels []Selection) {
+ for _, sel := range sels {
+ switch sel := sel.(type) {
+ case *types.Field:
+ field := sel
+ if skipByDirective(r, field.Directives) {
+ continue
+ }
+
+ switch field.Name.Name {
+ case "__typename":
+ // __typename is available even though r.DisableIntrospection == true
+ // because it is necessary when using union types and interfaces: https://graphql.org/learn/schema/#union-types
+ flattenedSels = append(flattenedSels, &TypenameField{
+ Object: *e,
+ Alias: field.Alias.Name,
+ })
+
+ case "__schema":
+ if !r.DisableIntrospection {
+ flattenedSels = append(flattenedSels, &SchemaField{
+ Field: s.Meta.FieldSchema,
+ Alias: field.Alias.Name,
+ Sels: applySelectionSet(r, s, s.Meta.Schema, field.SelectionSet),
+ Async: true,
+ FixedResult: reflect.ValueOf(introspection.WrapSchema(r.Schema)),
+ })
+ }
+
+ case "__type":
+ if !r.DisableIntrospection {
+ p := packer.ValuePacker{ValueType: reflect.TypeOf("")}
+ v, err := p.Pack(field.Arguments.MustGet("name").Deserialize(r.Vars))
+ if err != nil {
+ r.AddError(errors.Errorf("%s", err))
+ return nil
+ }
+
+ t, ok := r.Schema.Types[v.String()]
+ if !ok {
+ return nil
+ }
+
+ flattenedSels = append(flattenedSels, &SchemaField{
+ Field: s.Meta.FieldType,
+ Alias: field.Alias.Name,
+ Sels: applySelectionSet(r, s, s.Meta.Type, field.SelectionSet),
+ Async: true,
+ FixedResult: reflect.ValueOf(introspection.WrapType(t)),
+ })
+ }
+
+ default:
+ fe := e.Fields[field.Name.Name]
+
+ var args map[string]interface{}
+ var packedArgs reflect.Value
+ if fe.ArgsPacker != nil {
+ args = make(map[string]interface{})
+ for _, arg := range field.Arguments {
+ args[arg.Name.Name] = arg.Value.Deserialize(r.Vars)
+ }
+ var err error
+ packedArgs, err = fe.ArgsPacker.Pack(args)
+ if err != nil {
+ r.AddError(errors.Errorf("%s", err))
+ return
+ }
+ }
+
+ fieldSels := applyField(r, s, fe.ValueExec, field.SelectionSet)
+ flattenedSels = append(flattenedSels, &SchemaField{
+ Field: *fe,
+ Alias: field.Alias.Name,
+ Args: args,
+ PackedArgs: packedArgs,
+ Sels: fieldSels,
+ Async: fe.HasContext || fe.ArgsPacker != nil || fe.HasError || HasAsyncSel(fieldSels),
+ })
+ }
+
+ case *types.InlineFragment:
+ frag := sel
+ if skipByDirective(r, frag.Directives) {
+ continue
+ }
+ flattenedSels = append(flattenedSels, applyFragment(r, s, e, &frag.Fragment)...)
+
+ case *types.FragmentSpread:
+ spread := sel
+ if skipByDirective(r, spread.Directives) {
+ continue
+ }
+ flattenedSels = append(flattenedSels, applyFragment(r, s, e, &r.Doc.Fragments.Get(spread.Name.Name).Fragment)...)
+
+ default:
+ panic("invalid type")
+ }
+ }
+ return
+}
+
+func applyFragment(r *Request, s *resolvable.Schema, e *resolvable.Object, frag *types.Fragment) []Selection {
+ if frag.On.Name != e.Name {
+ t := r.Schema.Resolve(frag.On.Name)
+ face, ok := t.(*types.InterfaceTypeDefinition)
+ if !ok && frag.On.Name != "" {
+ a, ok2 := e.TypeAssertions[frag.On.Name]
+ if !ok2 {
+ panic(fmt.Errorf("%q does not implement %q", frag.On, e.Name)) // TODO proper error handling
+ }
+
+ return []Selection{&TypeAssertion{
+ TypeAssertion: *a,
+ Sels: applySelectionSet(r, s, a.TypeExec.(*resolvable.Object), frag.Selections),
+ }}
+ }
+ if ok && len(face.PossibleTypes) > 0 {
+ sels := []Selection{}
+ for _, t := range face.PossibleTypes {
+ if t.Name == e.Name {
+ return applySelectionSet(r, s, e, frag.Selections)
+ }
+
+ if a, ok := e.TypeAssertions[t.Name]; ok {
+ sels = append(sels, &TypeAssertion{
+ TypeAssertion: *a,
+ Sels: applySelectionSet(r, s, a.TypeExec.(*resolvable.Object), frag.Selections),
+ })
+ }
+ }
+ if len(sels) == 0 {
+ panic(fmt.Errorf("%q does not implement %q", e.Name, frag.On)) // TODO proper error handling
+ }
+ return sels
+ }
+ }
+ return applySelectionSet(r, s, e, frag.Selections)
+}
+
+func applyField(r *Request, s *resolvable.Schema, e resolvable.Resolvable, sels []types.Selection) []Selection {
+ switch e := e.(type) {
+ case *resolvable.Object:
+ return applySelectionSet(r, s, e, sels)
+ case *resolvable.List:
+ return applyField(r, s, e.Elem, sels)
+ case *resolvable.Scalar:
+ return nil
+ default:
+ panic("unreachable")
+ }
+}
+
+func skipByDirective(r *Request, directives types.DirectiveList) bool {
+ if d := directives.Get("skip"); d != nil {
+ p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
+ v, err := p.Pack(d.Arguments.MustGet("if").Deserialize(r.Vars))
+ if err != nil {
+ r.AddError(errors.Errorf("%s", err))
+ }
+ if err == nil && v.Bool() {
+ return true
+ }
+ }
+
+ if d := directives.Get("include"); d != nil {
+ p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
+ v, err := p.Pack(d.Arguments.MustGet("if").Deserialize(r.Vars))
+ if err != nil {
+ r.AddError(errors.Errorf("%s", err))
+ }
+ if err == nil && !v.Bool() {
+ return true
+ }
+ }
+
+ return false
+}
+
+func HasAsyncSel(sels []Selection) bool {
+ for _, sel := range sels {
+ switch sel := sel.(type) {
+ case *SchemaField:
+ if sel.Async {
+ return true
+ }
+ case *TypeAssertion:
+ if HasAsyncSel(sel.Sels) {
+ return true
+ }
+ case *TypenameField:
+ // sync
+ default:
+ panic("unreachable")
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/graph-gophers/graphql-go/internal/exec/subscribe.go b/vendor/github.com/graph-gophers/graphql-go/internal/exec/subscribe.go
new file mode 100644
index 00000000..37ebacbc
--- /dev/null
+++ b/vendor/github.com/graph-gophers/graphql-go/internal/exec/subscribe.go
@@ -0,0 +1,179 @@
+package exec
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "time"
+
+ "github.com/graph-gophers/graphql-go/errors"
+ "github.com/graph-gophers/graphql-go/internal/exec/resolvable"
+ "github.com/graph-gophers/graphql-go/internal/exec/selected"
+ "github.com/graph-gophers/graphql-go/types"
+)
+
+type Response struct {
+ Data json.RawMessage
+ Errors []*errors.QueryError
+}
+
+func (r *Request) Subscribe(ctx context.Context, s *resolvable.Schema, op *types.OperationDefinition) <-chan *Response {
+ var result reflect.Value
+ var f *fieldToExec
+ var err *errors.QueryError
+ func() {
+ defer r.handlePanic(ctx)
+
+ sels := selected.ApplyOperation(&r.Request, s, op)
+ var fields []*fieldToExec
+ collectFieldsToResolve(sels, s, s.Resolver, &fields, make(map[string]*fieldToExec))
+
+ // TODO: move this check into validation.Validate
+ if len(fields) != 1 {
+ err = errors.Errorf("%s", "can subscribe to at most one subscription at a time")
+ return
+ }
+ f = fields[0]
+
+ var in []reflect.Value
+ if f.field.HasContext {
+ in = append(in, reflect.ValueOf(ctx))
+ }
+ if f.field.ArgsPacker != nil {
+ in = append(in, f.field.PackedArgs)
+ }
+ callOut := f.resolver.Method(f.field.MethodIndex).Call(in)
+ result = callOut[0]
+
+ if f.field.HasError && !callOut[1].IsNil() {
+ switch resolverErr := callOut[1].Interface().(type) {
+ case *errors.QueryError:
+ err = resolverErr
+ case error:
+ err = errors.Errorf("%s", resolverErr)
+ err.ResolverError = resolverErr
+ default:
+ panic(fmt.Errorf("can only deal with *QueryError and error types, got %T", resolverErr))
+ }
+ }
+ }()
+
+ // Handles the case where the locally executed func above panicked
+ if len(r.Request.Errs) > 0 {
+ return sendAndReturnClosed(&Response{Errors: r.Request.Errs})
+ }
+
+ if f == nil {
+ return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{err}})
+ }
+
+ if err != nil {
+ if _, nonNullChild := f.field.Type.(*types.NonNull); nonNullChild {
+ return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{err}})
+ }
+ return sendAndReturnClosed(&Response{Data: []byte(fmt.Sprintf(`{"%s":null}`, f.field.Alias)), Errors: []*errors.QueryError{err}})
+ }
+
+ if ctxErr := ctx.Err(); ctxErr != nil {
+ return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{errors.Errorf("%s", ctxErr)}})
+ }
+
+ c := make(chan *Response)
+ // TODO: handle resolver nil channel better?
+ if result.IsZero() {
+ close(c)
+ return c
+ }
+
+ go func() {
+ for {
+ // Check subscription context
+ chosen, resp, ok := reflect.Select([]reflect.SelectCase{
+ {
+ Dir: reflect.SelectRecv,
+ Chan: reflect.ValueOf(ctx.Done()),
+ },
+ {
+ Dir: reflect.SelectRecv,
+ Chan: result,
+ },
+ })
+ switch chosen {
+ // subscription context done
+ case 0:
+ close(c)
+ return
+ // upstream received
+ case 1:
+ // upstream closed
+ if !ok {
+ close(c)
+ return
+ }
+
+ subR := &Request{
+ Request: selected.Request{
+ Doc: r.Request.Doc,
+ Vars: r.Request.Vars,
+ Schema: r.Request.Schema,
+ },
+ Limiter: r.Limiter,
+ Tracer: r.Tracer,
+ Logger: r.Logger,
+ }
+ var out bytes.Buffer
+ func() {
+ timeout := r.SubscribeResolverTimeout
+ if timeout == 0 {
+ timeout = time.Second
+ }
+
+ subCtx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ // resolve response
+ func() {
+ defer subR.handlePanic(subCtx)
+
+ var buf bytes.Buffer
+ subR.execSelectionSet(subCtx, f.sels, f.field.Type, &pathSegment{nil, f.field.Alias}, s, resp, &buf)
+
+ propagateChildError := false
+ if _, nonNullChild := f.field.Type.(*types.NonNull); nonNullChild && resolvedToNull(&buf) {
+ propagateChildError = true
+ }
+
+ if !propagateChildError {
+ out.WriteString(fmt.Sprintf(`{"%s":`, f.field.Alias))
+ out.Write(buf.Bytes())
+ out.WriteString(`}`)
+ }
+ }()
+
+ if err := subCtx.Err(); err != nil {
+ c <- &Response{Errors: []*errors.QueryError{errors.Errorf("%s", err)}}
+ return
+ }
+
+ // Send response within timeout
+ // TODO: maybe block until sent?
+ select {
+ case <-subCtx.Done():
+ case c <- &Response{Data: out.Bytes(), Errors: subR.Errs}:
+ }
+ }()
+ }
+ }
+ }()
+
+ return c
+}
+
+func sendAndReturnClosed(resp *Response) chan *Response {
+ c := make(chan *Response, 1)
+ c <- resp
+ close(c)
+ return c
+}