diff --git a/internal/exec/resolvable/packer.go b/internal/exec/packer/packer.go similarity index 81% rename from internal/exec/resolvable/packer.go rename to internal/exec/packer/packer.go index 5fb272bb306..02b9d832b71 100644 --- a/internal/exec/resolvable/packer.go +++ b/internal/exec/packer/packer.go @@ -1,4 +1,4 @@ -package resolvable +package packer import ( "fmt" @@ -15,7 +15,51 @@ type packer interface { Pack(value interface{}) (reflect.Value, error) } -func (b *execBuilder) assignPacker(target *packer, schemaType common.Type, reflectType reflect.Type) 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 { @@ -31,7 +75,7 @@ func (b *execBuilder) assignPacker(target *packer, schemaType common.Type, refle return nil } -func (b *execBuilder) makePacker(schemaType common.Type, reflectType reflect.Type) (packer, error) { +func (b *Builder) makePacker(schemaType common.Type, reflectType reflect.Type) (packer, error) { t, nonNull := unwrapNonNull(schemaType) if !nonNull { if reflectType.Kind() != reflect.Ptr { @@ -57,7 +101,7 @@ func (b *execBuilder) makePacker(schemaType common.Type, reflectType reflect.Typ return b.makeNonNullPacker(t, reflectType) } -func (b *execBuilder) makeNonNullPacker(schemaType common.Type, reflectType reflect.Type) (packer, error) { +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) @@ -83,7 +127,7 @@ func (b *execBuilder) makeNonNullPacker(schemaType common.Type, reflectType refl }, nil case *schema.InputObject: - e, err := b.makeStructPacker(t.Values, reflectType) + e, err := b.MakeStructPacker(t.Values, reflectType) if err != nil { return nil, err } @@ -109,7 +153,7 @@ func (b *execBuilder) makeNonNullPacker(schemaType common.Type, reflectType refl } } -func (b *execBuilder) makeStructPacker(values common.InputValueList, typ reflect.Type) (*StructPacker, error) { +func (b *Builder) MakeStructPacker(values common.InputValueList, typ reflect.Type) (*StructPacker, error) { structType := typ usePtr := false if typ.Kind() == reflect.Ptr { @@ -310,3 +354,14 @@ func unmarshalInput(typ reflect.Type, input interface{}) (interface{}, error) { 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) +} diff --git a/internal/exec/resolvable/resolvable.go b/internal/exec/resolvable/resolvable.go index 460542b77a2..c681cf208ab 100644 --- a/internal/exec/resolvable/resolvable.go +++ b/internal/exec/resolvable/resolvable.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/neelance/graphql-go/internal/common" + "github.com/neelance/graphql-go/internal/exec/packer" "github.com/neelance/graphql-go/internal/schema" ) @@ -32,7 +33,7 @@ type Field struct { TypeName string MethodIndex int HasContext bool - ArgsPacker *StructPacker + ArgsPacker *packer.StructPacker HasError bool ValueExec Resolvable TraceLabel string @@ -85,8 +86,7 @@ func ApplyResolver(s *schema.Schema, resolver interface{}) (*Schema, error) { type execBuilder struct { schema *schema.Schema resMap map[typePair]*resMapEntry - packerMap map[typePair]*packerMapEntry - structPackers []*StructPacker + packerBuilder *packer.Builder } type typePair struct { @@ -99,16 +99,11 @@ type resMapEntry struct { targets []*Resolvable } -type packerMapEntry struct { - packer packer - targets []*packer -} - func newBuilder(s *schema.Schema) *execBuilder { return &execBuilder{ - schema: s, - resMap: make(map[typePair]*resMapEntry), - packerMap: make(map[typePair]*packerMapEntry), + schema: s, + resMap: make(map[typePair]*resMapEntry), + packerBuilder: packer.NewBuilder(), } } @@ -119,26 +114,7 @@ func (b *execBuilder) 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 + return b.packerBuilder.Finish() } func (b *execBuilder) assignExec(target *Resolvable, t common.Type, resolverType reflect.Type) error { @@ -212,7 +188,7 @@ func makeScalarExec(t *schema.Scalar, resolverType reflect.Type) (Resolvable, er implementsType = (t.Name == "String") case *bool: implementsType = (t.Name == "Boolean") - case Unmarshaler: + case packer.Unmarshaler: implementsType = r.ImplementsGraphQLType(t.Name) } if !implementsType { @@ -291,13 +267,13 @@ func (b *execBuilder) makeFieldExec(typeName string, f *schema.Field, m reflect. in = in[1:] } - var argsPacker *StructPacker + 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.makeStructPacker(f.Args, in[0]) + argsPacker, err = b.packerBuilder.MakeStructPacker(f.Args, in[0]) if err != nil { return nil, err } diff --git a/internal/exec/selected/selected.go b/internal/exec/selected/selected.go index e76c75acf72..eecdcf387d8 100644 --- a/internal/exec/selected/selected.go +++ b/internal/exec/selected/selected.go @@ -7,6 +7,7 @@ import ( "github.com/neelance/graphql-go/errors" "github.com/neelance/graphql-go/internal/common" + "github.com/neelance/graphql-go/internal/exec/packer" "github.com/neelance/graphql-go/internal/exec/resolvable" "github.com/neelance/graphql-go/internal/query" "github.com/neelance/graphql-go/internal/schema" @@ -92,7 +93,7 @@ func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection) }) case "__type": - p := resolvable.ValuePacker{ValueType: reflect.TypeOf("")} + 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)) @@ -192,7 +193,7 @@ func applyField(r *Request, e resolvable.Resolvable, sels []query.Selection) []S func skipByDirective(r *Request, directives common.DirectiveList) bool { if d := directives.Get("skip"); d != nil { - p := resolvable.ValuePacker{ValueType: reflect.TypeOf(false)} + 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)) @@ -203,7 +204,7 @@ func skipByDirective(r *Request, directives common.DirectiveList) bool { } if d := directives.Get("include"); d != nil { - p := resolvable.ValuePacker{ValueType: reflect.TypeOf(false)} + 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))