GraphQL master FF for review (#18445)
* Initial work on a graphql API * Added receipts, and more transaction fields. * Finish receipts, add logs * Add transactionCount to block * Add types and . * Update Block type to be compatible with ethql * Rename nonce to transactionCount in Account, to be compatible with ethql * Update transaction, receipt and log to match ethql * Add query operator, for a range of blocks * Added ommerCount to Block * Add transactionAt and ommerAt to Block * Added sendRawTransaction mutation * Add Call and EstimateGas to graphQL API * Refactored to use hexutil.Bytes instead of HexBytes * Replace BigNum with hexutil.Big * Refactor call and estimateGas to use ethapi struct type * Replace ethgraphql.Address with common.Address * Replace ethgraphql.Hash with common.Hash * Converted most quantities to Long instead of Int * Add support for logs * Fix bug in runFilter * Restructured Transaction to work primarily with headers, so uncle data is reported properly * Add gasPrice API * Add protocolVersion API * Add syncing API * Moved schema into its own source file * Move some single use args types into anonymous structs * Add doc-comments * Fixed backend fetching to use context * Added (very) basic tests * Add documentation to the graphql schema * Fix reversion for formatting of big numbers * Correct spelling error * s/BigInt/Long/ * Update common/types.go * Fixes in response to review * Fix lint error * Updated calls on private functions * Fix typo in graphql.go * Rollback ethapi breaking changes for graphql support Co-Authored-By: Arachnid <arachnid@notdot.net>
This commit is contained in:
committed by
Guillaume Ballet
parent
105008b6a1
commit
f91312dbdb
305
vendor/github.com/graph-gophers/graphql-go/internal/exec/exec.go
generated
vendored
Normal file
305
vendor/github.com/graph-gophers/graphql-go/internal/exec/exec.go
generated
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/graph-gophers/graphql-go/errors"
|
||||
"github.com/graph-gophers/graphql-go/internal/common"
|
||||
"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/internal/schema"
|
||||
"github.com/graph-gophers/graphql-go/log"
|
||||
"github.com/graph-gophers/graphql-go/trace"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
selected.Request
|
||||
Limiter chan struct{}
|
||||
Tracer trace.Tracer
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
func (r *Request) handlePanic(ctx context.Context) {
|
||||
if value := recover(); value != nil {
|
||||
r.Logger.LogPanic(ctx, value)
|
||||
r.AddError(makePanicError(value))
|
||||
}
|
||||
}
|
||||
|
||||
func makePanicError(value interface{}) *errors.QueryError {
|
||||
return errors.Errorf("graphql: panic occurred: %v", value)
|
||||
}
|
||||
|
||||
func (r *Request) Execute(ctx context.Context, s *resolvable.Schema, op *query.Operation) ([]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.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 (r *Request) execSelections(ctx context.Context, sels []selected.Selection, path *pathSegment, resolver reflect.Value, out *bytes.Buffer, serially bool) {
|
||||
async := !serially && selected.HasAsyncSel(sels)
|
||||
|
||||
var fields []*fieldToExec
|
||||
collectFieldsToResolve(sels, 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, f, &pathSegment{path, f.field.Alias}, true)
|
||||
}(f)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
out.WriteByte('{')
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
out.WriteByte(',')
|
||||
}
|
||||
out.WriteByte('"')
|
||||
out.WriteString(f.field.Alias)
|
||||
out.WriteByte('"')
|
||||
out.WriteByte(':')
|
||||
if async {
|
||||
out.Write(f.out.Bytes())
|
||||
continue
|
||||
}
|
||||
f.out = out
|
||||
execFieldSelection(ctx, r, f, &pathSegment{path, f.field.Alias}, false)
|
||||
}
|
||||
out.WriteByte('}')
|
||||
}
|
||||
|
||||
func collectFieldsToResolve(sels []selected.Selection, 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:
|
||||
sf := &selected.SchemaField{
|
||||
Field: resolvable.MetaFieldTypename,
|
||||
Alias: sel.Alias,
|
||||
FixedResult: reflect.ValueOf(typeOf(sel, resolver)),
|
||||
}
|
||||
*fields = append(*fields, &fieldToExec{field: sf, resolver: resolver})
|
||||
|
||||
case *selected.TypeAssertion:
|
||||
out := resolver.Method(sel.MethodIndex).Call(nil)
|
||||
if !out[1].Bool() {
|
||||
continue
|
||||
}
|
||||
collectFieldsToResolve(sel.Sels, 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, 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 = makePanicError(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
|
||||
}
|
||||
|
||||
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 := f.resolver.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
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
if applyLimiter {
|
||||
<-r.Limiter
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
r.AddError(err)
|
||||
f.out.WriteString("null") // TODO handle non-nil
|
||||
return
|
||||
}
|
||||
|
||||
r.execSelectionSet(traceCtx, f.sels, f.field.Type, path, result, f.out)
|
||||
}
|
||||
|
||||
func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selection, typ common.Type, path *pathSegment, resolver reflect.Value, out *bytes.Buffer) {
|
||||
t, nonNull := unwrapNonNull(typ)
|
||||
switch t := t.(type) {
|
||||
case *schema.Object, *schema.Interface, *schema.Union:
|
||||
// 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 nonNull {
|
||||
panic(errors.Errorf("got nil for non-null %q", t))
|
||||
}
|
||||
out.WriteString("null")
|
||||
return
|
||||
}
|
||||
|
||||
r.execSelections(ctx, sels, path, resolver, out, false)
|
||||
return
|
||||
}
|
||||
|
||||
if !nonNull {
|
||||
if resolver.IsNil() {
|
||||
out.WriteString("null")
|
||||
return
|
||||
}
|
||||
resolver = resolver.Elem()
|
||||
}
|
||||
|
||||
switch t := t.(type) {
|
||||
case *common.List:
|
||||
l := resolver.Len()
|
||||
|
||||
if selected.HasAsyncSel(sels) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(l)
|
||||
entryouts := make([]bytes.Buffer, l)
|
||||
for i := 0; i < l; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
defer r.handlePanic(ctx)
|
||||
r.execSelectionSet(ctx, sels, t.OfType, &pathSegment{path, i}, resolver.Index(i), &entryouts[i])
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
out.WriteByte('[')
|
||||
for i, entryout := range entryouts {
|
||||
if i > 0 {
|
||||
out.WriteByte(',')
|
||||
}
|
||||
out.Write(entryout.Bytes())
|
||||
}
|
||||
out.WriteByte(']')
|
||||
return
|
||||
}
|
||||
|
||||
out.WriteByte('[')
|
||||
for i := 0; i < l; i++ {
|
||||
if i > 0 {
|
||||
out.WriteByte(',')
|
||||
}
|
||||
r.execSelectionSet(ctx, sels, t.OfType, &pathSegment{path, i}, resolver.Index(i), out)
|
||||
}
|
||||
out.WriteByte(']')
|
||||
|
||||
case *schema.Scalar:
|
||||
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 *schema.Enum:
|
||||
out.WriteByte('"')
|
||||
out.WriteString(resolver.String())
|
||||
out.WriteByte('"')
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func unwrapNonNull(t common.Type) (common.Type, bool) {
|
||||
if nn, ok := t.(*common.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)
|
||||
}
|
371
vendor/github.com/graph-gophers/graphql-go/internal/exec/packer/packer.go
generated
vendored
Normal file
371
vendor/github.com/graph-gophers/graphql-go/internal/exec/packer/packer.go
generated
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
package packer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/graph-gophers/graphql-go/errors"
|
||||
"github.com/graph-gophers/graphql-go/internal/common"
|
||||
"github.com/graph-gophers/graphql-go/internal/schema"
|
||||
)
|
||||
|
||||
type packer interface {
|
||||
Pack(value interface{}) (reflect.Value, error)
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
packerMap map[typePair]*packerMapEntry
|
||||
structPackers []*StructPacker
|
||||
}
|
||||
|
||||
type typePair struct {
|
||||
graphQLType common.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.Value(nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.defaultStruct.FieldByIndex(f.fieldIndex).Set(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) assignPacker(target *packer, schemaType common.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 common.Type, reflectType reflect.Type) (packer, error) {
|
||||
t, nonNull := unwrapNonNull(schemaType)
|
||||
if !nonNull {
|
||||
if reflectType.Kind() != reflect.Ptr {
|
||||
return nil, fmt.Errorf("%s is not a pointer", reflectType)
|
||||
}
|
||||
elemType := reflectType.Elem()
|
||||
addPtr := true
|
||||
if _, ok := t.(*schema.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
|
||||
}
|
||||
|
||||
return b.makeNonNullPacker(t, reflectType)
|
||||
}
|
||||
|
||||
func (b *Builder) makeNonNullPacker(schemaType common.Type, reflectType reflect.Type) (packer, error) {
|
||||
if u, ok := reflect.New(reflectType).Interface().(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 *schema.Scalar:
|
||||
return &ValuePacker{
|
||||
ValueType: reflectType,
|
||||
}, nil
|
||||
|
||||
case *schema.Enum:
|
||||
if reflectType.Kind() != reflect.String {
|
||||
return nil, fmt.Errorf("wrong type, expected %s", reflect.String)
|
||||
}
|
||||
return &ValuePacker{
|
||||
ValueType: reflectType,
|
||||
}, nil
|
||||
|
||||
case *schema.InputObject:
|
||||
e, err := b.MakeStructPacker(t.Values, reflectType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e, nil
|
||||
|
||||
case *common.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 *schema.Object, *schema.Interface, *schema.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 common.InputValueList, 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", 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("missing argument %q", v.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 = &common.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 *common.InputValue
|
||||
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 {
|
||||
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 {
|
||||
return reflect.Value{}, errors.Errorf("got null for non-null")
|
||||
}
|
||||
|
||||
v := reflect.New(p.ValueType)
|
||||
if err := v.Interface().(Unmarshaler).UnmarshalGraphQL(value); err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
return v.Elem(), nil
|
||||
}
|
||||
|
||||
type Unmarshaler interface {
|
||||
ImplementsGraphQLType(name string) bool
|
||||
UnmarshalGraphQL(input interface{}) error
|
||||
}
|
||||
|
||||
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 common.Type) (common.Type, bool) {
|
||||
if nn, ok := t.(*common.NonNull); ok {
|
||||
return nn.OfType, true
|
||||
}
|
||||
return t, false
|
||||
}
|
||||
|
||||
func stripUnderscore(s string) string {
|
||||
return strings.Replace(s, "_", "", -1)
|
||||
}
|
58
vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/meta.go
generated
vendored
Normal file
58
vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/meta.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package resolvable
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/graph-gophers/graphql-go/internal/common"
|
||||
"github.com/graph-gophers/graphql-go/internal/schema"
|
||||
"github.com/graph-gophers/graphql-go/introspection"
|
||||
)
|
||||
|
||||
var MetaSchema *Object
|
||||
var MetaType *Object
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
b := newBuilder(schema.Meta)
|
||||
|
||||
metaSchema := schema.Meta.Types["__Schema"].(*schema.Object)
|
||||
MetaSchema, err = b.makeObjectExec(metaSchema.Name, metaSchema.Fields, nil, false, reflect.TypeOf(&introspection.Schema{}))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
metaType := schema.Meta.Types["__Type"].(*schema.Object)
|
||||
MetaType, 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)
|
||||
}
|
||||
}
|
||||
|
||||
var MetaFieldTypename = Field{
|
||||
Field: schema.Field{
|
||||
Name: "__typename",
|
||||
Type: &common.NonNull{OfType: schema.Meta.Types["String"]},
|
||||
},
|
||||
TraceLabel: fmt.Sprintf("GraphQL field: __typename"),
|
||||
}
|
||||
|
||||
var MetaFieldSchema = Field{
|
||||
Field: schema.Field{
|
||||
Name: "__schema",
|
||||
Type: schema.Meta.Types["__Schema"],
|
||||
},
|
||||
TraceLabel: fmt.Sprintf("GraphQL field: __schema"),
|
||||
}
|
||||
|
||||
var MetaFieldType = Field{
|
||||
Field: schema.Field{
|
||||
Name: "__type",
|
||||
Type: schema.Meta.Types["__Type"],
|
||||
},
|
||||
TraceLabel: fmt.Sprintf("GraphQL field: __type"),
|
||||
}
|
331
vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/resolvable.go
generated
vendored
Normal file
331
vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/resolvable.go
generated
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
package resolvable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/graph-gophers/graphql-go/internal/common"
|
||||
"github.com/graph-gophers/graphql-go/internal/exec/packer"
|
||||
"github.com/graph-gophers/graphql-go/internal/schema"
|
||||
)
|
||||
|
||||
type Schema struct {
|
||||
schema.Schema
|
||||
Query Resolvable
|
||||
Mutation Resolvable
|
||||
Resolver reflect.Value
|
||||
}
|
||||
|
||||
type Resolvable interface {
|
||||
isResolvable()
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
Name string
|
||||
Fields map[string]*Field
|
||||
TypeAssertions map[string]*TypeAssertion
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
schema.Field
|
||||
TypeName string
|
||||
MethodIndex int
|
||||
HasContext bool
|
||||
HasError bool
|
||||
ArgsPacker *packer.StructPacker
|
||||
ValueExec Resolvable
|
||||
TraceLabel string
|
||||
}
|
||||
|
||||
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 *schema.Schema, resolver interface{}) (*Schema, error) {
|
||||
b := newBuilder(s)
|
||||
|
||||
var query, mutation 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 err := b.finish(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Schema{
|
||||
Schema: *s,
|
||||
Resolver: reflect.ValueOf(resolver),
|
||||
Query: query,
|
||||
Mutation: mutation,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type execBuilder struct {
|
||||
schema *schema.Schema
|
||||
resMap map[typePair]*resMapEntry
|
||||
packerBuilder *packer.Builder
|
||||
}
|
||||
|
||||
type typePair struct {
|
||||
graphQLType common.Type
|
||||
resolverType reflect.Type
|
||||
}
|
||||
|
||||
type resMapEntry struct {
|
||||
exec Resolvable
|
||||
targets []*Resolvable
|
||||
}
|
||||
|
||||
func newBuilder(s *schema.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 common.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 common.Type, resolverType reflect.Type) (Resolvable, error) {
|
||||
var nonNull bool
|
||||
t, nonNull = unwrapNonNull(t)
|
||||
|
||||
switch t := t.(type) {
|
||||
case *schema.Object:
|
||||
return b.makeObjectExec(t.Name, t.Fields, nil, nonNull, resolverType)
|
||||
|
||||
case *schema.Interface:
|
||||
return b.makeObjectExec(t.Name, t.Fields, t.PossibleTypes, nonNull, resolverType)
|
||||
|
||||
case *schema.Union:
|
||||
return b.makeObjectExec(t.Name, nil, t.PossibleTypes, 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 *schema.Scalar:
|
||||
return makeScalarExec(t, resolverType)
|
||||
|
||||
case *schema.Enum:
|
||||
return &Scalar{}, nil
|
||||
|
||||
case *common.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 *schema.Scalar, 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 packer.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 schema.FieldList, possibleTypes []*schema.Object, 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)
|
||||
for _, f := range fields {
|
||||
methodIndex := findMethod(resolverType, f.Name)
|
||||
if methodIndex == -1 {
|
||||
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)
|
||||
}
|
||||
|
||||
m := resolverType.Method(methodIndex)
|
||||
fe, err := b.makeFieldExec(typeName, f, m, methodIndex, methodHasReceiver)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s\n\treturned by (%s).%s", err, resolverType, m.Name)
|
||||
}
|
||||
Fields[f.Name] = fe
|
||||
}
|
||||
|
||||
typeAssertions := make(map[string]*TypeAssertion)
|
||||
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 *schema.Field, m reflect.Method, methodIndex int, methodHasReceiver bool) (*Field, error) {
|
||||
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:]
|
||||
}
|
||||
|
||||
var argsPacker *packer.StructPacker
|
||||
if len(f.Args) > 0 {
|
||||
if len(in) == 0 {
|
||||
return nil, fmt.Errorf("must have parameter for field arguments")
|
||||
}
|
||||
var err error
|
||||
argsPacker, err = b.packerBuilder.MakeStructPacker(f.Args, in[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in = in[1:]
|
||||
}
|
||||
|
||||
if len(in) > 0 {
|
||||
return nil, fmt.Errorf("too many parameters")
|
||||
}
|
||||
|
||||
if m.Type.NumOut() > 2 {
|
||||
return nil, fmt.Errorf("too many return values")
|
||||
}
|
||||
|
||||
hasError := m.Type.NumOut() == 2
|
||||
if hasError {
|
||||
if m.Type.Out(1) != errorType {
|
||||
return nil, fmt.Errorf(`must have "error" as its second return value`)
|
||||
}
|
||||
}
|
||||
|
||||
fe := &Field{
|
||||
Field: *f,
|
||||
TypeName: typeName,
|
||||
MethodIndex: methodIndex,
|
||||
HasContext: hasContext,
|
||||
ArgsPacker: argsPacker,
|
||||
HasError: hasError,
|
||||
TraceLabel: fmt.Sprintf("GraphQL field: %s.%s", typeName, f.Name),
|
||||
}
|
||||
if err := b.assignExec(&fe.ValueExec, f.Type, m.Type.Out(0)); 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 unwrapNonNull(t common.Type) (common.Type, bool) {
|
||||
if nn, ok := t.(*common.NonNull); ok {
|
||||
return nn.OfType, true
|
||||
}
|
||||
return t, false
|
||||
}
|
||||
|
||||
func stripUnderscore(s string) string {
|
||||
return strings.Replace(s, "_", "", -1)
|
||||
}
|
238
vendor/github.com/graph-gophers/graphql-go/internal/exec/selected/selected.go
generated
vendored
Normal file
238
vendor/github.com/graph-gophers/graphql-go/internal/exec/selected/selected.go
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
package selected
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/graph-gophers/graphql-go/errors"
|
||||
"github.com/graph-gophers/graphql-go/internal/common"
|
||||
"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/internal/schema"
|
||||
"github.com/graph-gophers/graphql-go/introspection"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Schema *schema.Schema
|
||||
Doc *query.Document
|
||||
Vars map[string]interface{}
|
||||
Mu sync.Mutex
|
||||
Errs []*errors.QueryError
|
||||
}
|
||||
|
||||
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 *query.Operation) []Selection {
|
||||
var obj *resolvable.Object
|
||||
switch op.Type {
|
||||
case query.Query:
|
||||
obj = s.Query.(*resolvable.Object)
|
||||
case query.Mutation:
|
||||
obj = s.Mutation.(*resolvable.Object)
|
||||
}
|
||||
return applySelectionSet(r, 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, e *resolvable.Object, sels []query.Selection) (flattenedSels []Selection) {
|
||||
for _, sel := range sels {
|
||||
switch sel := sel.(type) {
|
||||
case *query.Field:
|
||||
field := sel
|
||||
if skipByDirective(r, field.Directives) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch field.Name.Name {
|
||||
case "__typename":
|
||||
flattenedSels = append(flattenedSels, &TypenameField{
|
||||
Object: *e,
|
||||
Alias: field.Alias.Name,
|
||||
})
|
||||
|
||||
case "__schema":
|
||||
flattenedSels = append(flattenedSels, &SchemaField{
|
||||
Field: resolvable.MetaFieldSchema,
|
||||
Alias: field.Alias.Name,
|
||||
Sels: applySelectionSet(r, resolvable.MetaSchema, field.Selections),
|
||||
Async: true,
|
||||
FixedResult: reflect.ValueOf(introspection.WrapSchema(r.Schema)),
|
||||
})
|
||||
|
||||
case "__type":
|
||||
p := packer.ValuePacker{ValueType: reflect.TypeOf("")}
|
||||
v, err := p.Pack(field.Arguments.MustGet("name").Value(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: resolvable.MetaFieldType,
|
||||
Alias: field.Alias.Name,
|
||||
Sels: applySelectionSet(r, resolvable.MetaType, field.Selections),
|
||||
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.Value(r.Vars)
|
||||
}
|
||||
var err error
|
||||
packedArgs, err = fe.ArgsPacker.Pack(args)
|
||||
if err != nil {
|
||||
r.AddError(errors.Errorf("%s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fieldSels := applyField(r, fe.ValueExec, field.Selections)
|
||||
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 *query.InlineFragment:
|
||||
frag := sel
|
||||
if skipByDirective(r, frag.Directives) {
|
||||
continue
|
||||
}
|
||||
flattenedSels = append(flattenedSels, applyFragment(r, e, &frag.Fragment)...)
|
||||
|
||||
case *query.FragmentSpread:
|
||||
spread := sel
|
||||
if skipByDirective(r, spread.Directives) {
|
||||
continue
|
||||
}
|
||||
flattenedSels = append(flattenedSels, applyFragment(r, e, &r.Doc.Fragments.Get(spread.Name.Name).Fragment)...)
|
||||
|
||||
default:
|
||||
panic("invalid type")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func applyFragment(r *Request, e *resolvable.Object, frag *query.Fragment) []Selection {
|
||||
if frag.On.Name != "" && frag.On.Name != e.Name {
|
||||
a, ok := e.TypeAssertions[frag.On.Name]
|
||||
if !ok {
|
||||
panic(fmt.Errorf("%q does not implement %q", frag.On, e.Name)) // TODO proper error handling
|
||||
}
|
||||
|
||||
return []Selection{&TypeAssertion{
|
||||
TypeAssertion: *a,
|
||||
Sels: applySelectionSet(r, a.TypeExec.(*resolvable.Object), frag.Selections),
|
||||
}}
|
||||
}
|
||||
return applySelectionSet(r, e, frag.Selections)
|
||||
}
|
||||
|
||||
func applyField(r *Request, e resolvable.Resolvable, sels []query.Selection) []Selection {
|
||||
switch e := e.(type) {
|
||||
case *resolvable.Object:
|
||||
return applySelectionSet(r, e, sels)
|
||||
case *resolvable.List:
|
||||
return applyField(r, e.Elem, sels)
|
||||
case *resolvable.Scalar:
|
||||
return nil
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func skipByDirective(r *Request, directives common.DirectiveList) bool {
|
||||
if d := directives.Get("skip"); d != nil {
|
||||
p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
|
||||
v, err := p.Pack(d.Args.MustGet("if").Value(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.Args.MustGet("if").Value(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
|
||||
}
|
Reference in New Issue
Block a user