From 402e073076977744699cdf3ab961bd9967125561 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Fri, 16 Feb 2018 18:26:20 +1100 Subject: [PATCH 1/8] rename jsonw to graphql --- graphql/bool.go | 30 ++++++++ graphql/float.go | 26 +++++++ graphql/id.go | 33 +++++++++ graphql/int.go | 26 +++++++ graphql/jsonw.go | 79 +++++++++++++++++++++ {jsonw => graphql}/jsonw_test.go | 8 +-- graphql/string.go | 33 +++++++++ graphql/time.go | 21 ++++++ graphql/unmarshal.go | 25 +++++++ jsonw/jsonw.go | 115 ------------------------------- neelance/errors/errors.go | 20 +++--- templates/file.go | 6 +- templates/interface.go | 4 +- templates/object.go | 6 +- 14 files changed, 295 insertions(+), 137 deletions(-) create mode 100644 graphql/bool.go create mode 100644 graphql/float.go create mode 100644 graphql/id.go create mode 100644 graphql/int.go create mode 100644 graphql/jsonw.go rename {jsonw => graphql}/jsonw_test.go (89%) create mode 100644 graphql/string.go create mode 100644 graphql/time.go create mode 100644 graphql/unmarshal.go delete mode 100644 jsonw/jsonw.go diff --git a/graphql/bool.go b/graphql/bool.go new file mode 100644 index 00000000000..6a3cfc4363b --- /dev/null +++ b/graphql/bool.go @@ -0,0 +1,30 @@ +package graphql + +import ( + "fmt" + "io" + "strings" +) + +func Boolean(b bool) Marshaler { + return WriterFunc(func(w io.Writer) { + if b { + w.Write(trueLit) + } else { + w.Write(falseLit) + } + }) +} + +func UnmarshalBoolean(v interface{}) (bool, error) { + switch v := v.(type) { + case string: + return "true" == strings.ToLower(v), nil + case int: + return v != 0, nil + case bool: + return v, nil + default: + return false, fmt.Errorf("%T is not a bool", v) + } +} diff --git a/graphql/float.go b/graphql/float.go new file mode 100644 index 00000000000..6c5703b5fdc --- /dev/null +++ b/graphql/float.go @@ -0,0 +1,26 @@ +package graphql + +import ( + "fmt" + "io" + "strconv" +) + +func Float(f float64) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, fmt.Sprintf("%f", f)) + }) +} + +func UnmarshalFloat(v interface{}) (float64, error) { + switch v := v.(type) { + case string: + return strconv.ParseFloat(v, 64) + case int: + return float64(v), nil + case float64: + return v, nil + default: + return 0, fmt.Errorf("%T is not an float", v) + } +} diff --git a/graphql/id.go b/graphql/id.go new file mode 100644 index 00000000000..77804e11d40 --- /dev/null +++ b/graphql/id.go @@ -0,0 +1,33 @@ +package graphql + +import ( + "fmt" + "io" + "strconv" +) + +func ID(s string) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, strconv.Quote(s)) + }) +} +func UnmarshalID(v interface{}) (string, error) { + switch v := v.(type) { + case string: + return v, nil + case int: + return strconv.Itoa(v), nil + case float64: + return fmt.Sprintf("%f", v), nil + case bool: + if v { + return "true", nil + } else { + return "false", nil + } + case nil: + return "null", nil + default: + return "", fmt.Errorf("%T is not a string", v) + } +} diff --git a/graphql/int.go b/graphql/int.go new file mode 100644 index 00000000000..13f736917dc --- /dev/null +++ b/graphql/int.go @@ -0,0 +1,26 @@ +package graphql + +import ( + "fmt" + "io" + "strconv" +) + +func Int(i int) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, strconv.Itoa(i)) + }) +} + +func UnmarshalInt(v interface{}) (int, error) { + switch v := v.(type) { + case string: + return strconv.Atoi(v) + case int: + return v, nil + case float64: + return int(v), nil + default: + return 0, fmt.Errorf("%T is not an int", v) + } +} diff --git a/graphql/jsonw.go b/graphql/jsonw.go new file mode 100644 index 00000000000..04ffaae31d6 --- /dev/null +++ b/graphql/jsonw.go @@ -0,0 +1,79 @@ +package graphql + +import ( + "io" + "strconv" +) + +var nullLit = []byte(`null`) +var trueLit = []byte(`true`) +var falseLit = []byte(`false`) +var openBrace = []byte(`{`) +var closeBrace = []byte(`}`) +var openBracket = []byte(`[`) +var closeBracket = []byte(`]`) +var colon = []byte(`:`) +var comma = []byte(`,`) + +var Null = lit(nullLit) +var True = lit(trueLit) +var False = lit(falseLit) + +type Marshaler interface { + MarshalGQL(w io.Writer) +} + +type OrderedMap struct { + Keys []string + Values []Marshaler +} + +type WriterFunc func(writer io.Writer) + +func (f WriterFunc) MarshalGQL(w io.Writer) { + f(w) +} + +func NewOrderedMap(len int) *OrderedMap { + return &OrderedMap{ + Keys: make([]string, len), + Values: make([]Marshaler, len), + } +} + +func (m *OrderedMap) Add(key string, value Marshaler) { + m.Keys = append(m.Keys, key) + m.Values = append(m.Values, value) +} + +func (m *OrderedMap) MarshalGQL(writer io.Writer) { + writer.Write(openBrace) + for i, key := range m.Keys { + if i != 0 { + writer.Write(comma) + } + io.WriteString(writer, strconv.Quote(key)) + writer.Write(colon) + m.Values[i].MarshalGQL(writer) + } + writer.Write(closeBrace) +} + +type Array []Marshaler + +func (a Array) MarshalGQL(writer io.Writer) { + writer.Write(openBracket) + for i, val := range a { + if i != 0 { + writer.Write(comma) + } + val.MarshalGQL(writer) + } + writer.Write(closeBracket) +} + +func lit(b []byte) Marshaler { + return WriterFunc(func(w io.Writer) { + w.Write(b) + }) +} diff --git a/jsonw/jsonw_test.go b/graphql/jsonw_test.go similarity index 89% rename from jsonw/jsonw_test.go rename to graphql/jsonw_test.go index 512a04724b0..4e4110e2d37 100644 --- a/jsonw/jsonw_test.go +++ b/graphql/jsonw_test.go @@ -1,4 +1,4 @@ -package jsonw +package graphql import ( "bytes" @@ -14,10 +14,10 @@ func TestJsonWriter(t *testing.T) { obj.Add("array", &Array{ Int(1), String("2"), - Bool(true), + Boolean(true), False, Null, - Float64(1.3), + Float(1.3), True, }) @@ -32,7 +32,7 @@ func TestJsonWriter(t *testing.T) { obj.Add("child", child1) b := &bytes.Buffer{} - obj.WriteJson(b) + obj.MarshalGQL(b) require.Equal(t, `{"test":10,"array":[1,"2",true,false,null,1.300000,true],"emptyArray":[],"child":{"child":{"child":null}}}`, b.String()) } diff --git a/graphql/string.go b/graphql/string.go new file mode 100644 index 00000000000..024f14adae7 --- /dev/null +++ b/graphql/string.go @@ -0,0 +1,33 @@ +package graphql + +import ( + "fmt" + "io" + "strconv" +) + +func String(s string) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, strconv.Quote(s)) + }) +} +func UnmarshalString(v interface{}) (string, error) { + switch v := v.(type) { + case string: + return v, nil + case int: + return strconv.Itoa(v), nil + case float64: + return fmt.Sprintf("%f", v), nil + case bool: + if v { + return "true", nil + } else { + return "false", nil + } + case nil: + return "null", nil + default: + return "", fmt.Errorf("%T is not a string", v) + } +} diff --git a/graphql/time.go b/graphql/time.go new file mode 100644 index 00000000000..a7c73c20bc0 --- /dev/null +++ b/graphql/time.go @@ -0,0 +1,21 @@ +package graphql + +import ( + "errors" + "io" + "strconv" + "time" +) + +func Time(t time.Time) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, strconv.Quote(t.Format(time.RFC3339))) + }) +} + +func UnmarshalTime(v interface{}) (time.Time, error) { + if tmpStr, ok := v.(string); ok { + return time.Parse(time.RFC3339, tmpStr) + } + return time.Time{}, errors.New("time should be RFC3339 formatted string") +} diff --git a/graphql/unmarshal.go b/graphql/unmarshal.go new file mode 100644 index 00000000000..82a35b1ad8a --- /dev/null +++ b/graphql/unmarshal.go @@ -0,0 +1,25 @@ +package graphql + +import ( + "reflect" + + "github.com/mitchellh/mapstructure" +) + +func UnmarshalComplexArg(result interface{}, data interface{}) error { + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + TagName: "graphql", + ErrorUnused: true, + Result: result, + DecodeHook: decodeHook, + }) + if err != nil { + panic(err) + } + + return decoder.Decode(data) +} + +func decodeHook(sourceType reflect.Type, destType reflect.Type, value interface{}) (interface{}, error) { + return value, nil +} diff --git a/jsonw/jsonw.go b/jsonw/jsonw.go deleted file mode 100644 index 234a1ba2b64..00000000000 --- a/jsonw/jsonw.go +++ /dev/null @@ -1,115 +0,0 @@ -package jsonw - -import ( - "fmt" - "io" - "strconv" - "time" -) - -var nullLit = []byte(`null`) -var trueLit = []byte(`true`) -var falseLit = []byte(`false`) -var openBrace = []byte(`{`) -var closeBrace = []byte(`}`) -var openBracket = []byte(`[`) -var closeBracket = []byte(`]`) -var colon = []byte(`:`) -var comma = []byte(`,`) - -var Null = lit(nullLit) -var True = lit(trueLit) -var False = lit(falseLit) - -type Writer interface { - WriteJson(w io.Writer) -} - -type OrderedMap struct { - Keys []string - Values []Writer -} - -type writerFunc func(writer io.Writer) - -func (f writerFunc) WriteJson(w io.Writer) { - f(w) -} - -func NewOrderedMap(len int) *OrderedMap { - return &OrderedMap{ - Keys: make([]string, len), - Values: make([]Writer, len), - } -} - -func (m *OrderedMap) Add(key string, value Writer) { - m.Keys = append(m.Keys, key) - m.Values = append(m.Values, value) -} - -func (m *OrderedMap) WriteJson(writer io.Writer) { - writer.Write(openBrace) - for i, key := range m.Keys { - if i != 0 { - writer.Write(comma) - } - io.WriteString(writer, strconv.Quote(key)) - writer.Write(colon) - m.Values[i].WriteJson(writer) - } - writer.Write(closeBrace) -} - -type Array []Writer - -func (a Array) WriteJson(writer io.Writer) { - writer.Write(openBracket) - for i, val := range a { - if i != 0 { - writer.Write(comma) - } - val.WriteJson(writer) - } - writer.Write(closeBracket) -} - -func lit(b []byte) Writer { - return writerFunc(func(w io.Writer) { - w.Write(b) - }) -} - -func Int(i int) Writer { - return writerFunc(func(w io.Writer) { - io.WriteString(w, strconv.Itoa(i)) - }) -} - -func Float64(f float64) Writer { - return writerFunc(func(w io.Writer) { - io.WriteString(w, fmt.Sprintf("%f", f)) - }) -} - -func String(s string) Writer { - return writerFunc(func(w io.Writer) { - io.WriteString(w, strconv.Quote(s)) - }) -} - -func Bool(b bool) Writer { - return writerFunc(func(w io.Writer) { - if b { - w.Write(trueLit) - } else { - w.Write(falseLit) - } - }) -} - -func Time(t time.Time) Writer { - return writerFunc(func(w io.Writer) { - io.WriteString(w, strconv.Quote(t.Format(time.RFC3339))) - }) -} diff --git a/neelance/errors/errors.go b/neelance/errors/errors.go index ffc0d5de7ea..8c1464705ec 100644 --- a/neelance/errors/errors.go +++ b/neelance/errors/errors.go @@ -3,7 +3,7 @@ package errors import ( "fmt" - "github.com/vektah/gqlgen/jsonw" + "github.com/vektah/gqlgen/graphql" ) type QueryError struct { @@ -54,25 +54,25 @@ func (c *Builder) Error(err error) { c.Errors = append(c.Errors, Errorf("%s", err.Error())) } -func ErrorWriter(errs []*QueryError) jsonw.Writer { - res := jsonw.Array{} +func ErrorWriter(errs []*QueryError) graphql.Marshaler { + res := graphql.Array{} for _, err := range errs { if err == nil { - res = append(res, jsonw.Null) + res = append(res, graphql.Null) continue } - errObj := &jsonw.OrderedMap{} + errObj := &graphql.OrderedMap{} - errObj.Add("message", jsonw.String(err.Message)) + errObj.Add("message", graphql.String(err.Message)) if len(err.Locations) > 0 { - locations := jsonw.Array{} + locations := graphql.Array{} for _, location := range err.Locations { - locationObj := &jsonw.OrderedMap{} - locationObj.Add("line", jsonw.Int(location.Line)) - locationObj.Add("column", jsonw.Int(location.Column)) + locationObj := &graphql.OrderedMap{} + locationObj.Add("line", graphql.Int(location.Line)) + locationObj.Add("column", graphql.Int(location.Column)) locations = append(locations, locationObj) } diff --git a/templates/file.go b/templates/file.go index d8d0aee53b2..70aa7039e5e 100644 --- a/templates/file.go +++ b/templates/file.go @@ -44,7 +44,7 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ ctx: ctx, } - var data jsonw.Writer + var data graphql.Marshaler if op.Type == query.Query { data = c._{{.QueryRoot.GQLType|lcFirst}}(op.Selections, nil) {{- if .MutationRoot}} @@ -57,14 +57,14 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ c.wg.Wait() - result := &jsonw.OrderedMap{} + result := &graphql.OrderedMap{} result.Add("data", data) if len(c.Errors) > 0 { result.Add("errors", errors.ErrorWriter(c.Errors)) } - result.WriteJson(w) + result.MarshalGQL(w) return nil } } diff --git a/templates/interface.go b/templates/interface.go index 3ff68849dd3..3268dd01b4b 100644 --- a/templates/interface.go +++ b/templates/interface.go @@ -4,10 +4,10 @@ const interfaceTpl = ` {{- define "interface"}} {{- $interface := . }} -func (ec *executionContext) _{{$interface.GQLType|lcFirst}}(sel []query.Selection, it *{{$interface.FullName}}) jsonw.Writer { +func (ec *executionContext) _{{$interface.GQLType|lcFirst}}(sel []query.Selection, it *{{$interface.FullName}}) graphql.Marshaler { switch it := (*it).(type) { case nil: - return jsonw.Null + return graphql.Null {{- range $implementor := $interface.Implementors }} case {{$implementor.FullName}}: return ec._{{$implementor.GQLType|lcFirst}}(sel, &it) diff --git a/templates/object.go b/templates/object.go index cc3042d6c74..24be6f000c8 100644 --- a/templates/object.go +++ b/templates/object.go @@ -7,12 +7,12 @@ const objectTpl = ` var {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, it *{{$object.FullName}}) jsonw.Writer { +func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, it *{{$object.FullName}}) graphql.Marshaler { fields := ec.collectFields(sel, {{$object.GQLType|lcFirst}}Implementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": From d94cfb1f8654a42c8ab21e80b54ee2a50dc744e3 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Fri, 16 Feb 2018 18:28:52 +1100 Subject: [PATCH 2/8] allow primitive scalars to be redefined --- codegen/build.go | 2 + codegen/import_build.go | 2 +- codegen/object.go | 8 +-- codegen/object_build.go | 46 ++-------------- codegen/type.go | 45 +++++++++++++--- codegen/type_build.go | 33 +++++++++++- codegen/util.go | 47 ++++++++++++++++ example/dataloader/schema.graphql | 1 + example/starwars/schema.graphql | 1 + main.go | 12 ++--- neelance/schema/meta.go | 3 -- neelance/validation/validation.go | 2 +- templates/args.go | 18 +------ templates/file.go | 90 ------------------------------- 14 files changed, 138 insertions(+), 172 deletions(-) create mode 100644 codegen/util.go diff --git a/codegen/build.go b/codegen/build.go index 1e4256eaf46..0ffd341adae 100644 --- a/codegen/build.go +++ b/codegen/build.go @@ -29,6 +29,8 @@ func Bind(schema *schema.Schema, userTypes map[string]string, destDir string) (* return nil, err } + bindTypes(imports, namedTypes, prog) + b := &Build{ PackageName: filepath.Base(destDir), Objects: buildObjects(namedTypes, schema, prog), diff --git a/codegen/import_build.go b/codegen/import_build.go index df1ead2d089..b683a1373fa 100644 --- a/codegen/import_build.go +++ b/codegen/import_build.go @@ -22,7 +22,7 @@ func buildImports(types NamedTypes, destDir string) Imports { {"query", "github.com/vektah/gqlgen/neelance/query"}, {"schema", "github.com/vektah/gqlgen/neelance/schema"}, {"validation", "github.com/vektah/gqlgen/neelance/validation"}, - {"jsonw", "github.com/vektah/gqlgen/jsonw"}, + {"graphql", "github.com/vektah/gqlgen/graphql"}, } for _, t := range types { diff --git a/codegen/object.go b/codegen/object.go index 84bedd62f17..e1069f9ff6a 100644 --- a/codegen/object.go +++ b/codegen/object.go @@ -105,7 +105,7 @@ func (f *Field) doWriteJson(res string, val string, remainingMods []string, isPt case len(remainingMods) > 0 && remainingMods[0] == modPtr: return tpl(` if {{.val}} == nil { - {{.res}} = jsonw.Null + {{.res}} = graphql.Null } else { {{.next}} }`, map[string]interface{}{ @@ -123,9 +123,9 @@ func (f *Field) doWriteJson(res string, val string, remainingMods []string, isPt var index = "idx" + strconv.Itoa(depth) return tpl(` - {{.arr}} := jsonw.Array{} + {{.arr}} := graphql.Array{} for {{.index}} := range {{.val}} { - var {{.tmp}} jsonw.Writer + var {{.tmp}} graphql.Marshaler {{.next}} {{.arr}} = append({{.arr}}, {{.tmp}}) } @@ -142,7 +142,7 @@ func (f *Field) doWriteJson(res string, val string, remainingMods []string, isPt if isPtr { val = "*" + val } - return fmt.Sprintf("%s = jsonw.%s(%s)", res, ucFirst(f.GoType), val) + return f.Marshal(res, val) default: if !isPtr { diff --git a/codegen/object_build.go b/codegen/object_build.go index 8c619f778be..e96fa193432 100644 --- a/codegen/object_build.go +++ b/codegen/object_build.go @@ -20,7 +20,10 @@ func buildObjects(types NamedTypes, s *schema.Schema, prog *loader.Program) Obje switch typ := typ.(type) { case *schema.Object: obj := buildObject(types, typ) - bindObject(prog, obj) + + if def := findGoType(prog, obj.Package, obj.GoType); def != nil { + findBindTargets(def.Type(), obj) + } objects = append(objects, obj) } @@ -76,33 +79,6 @@ func buildObject(types NamedTypes, typ *schema.Object) *Object { return obj } -func bindObject(prog *loader.Program, obj *Object) { - if obj.Package == "" { - return - } - pkgName, err := resolvePkg(obj.Package) - if err != nil { - fmt.Fprintf(os.Stderr, "unable to resolve package for %s: %s\n", obj.GQLType, err.Error()) - return - } - - pkg := prog.Imported[pkgName] - if pkg == nil { - fmt.Fprintf(os.Stderr, "required package was not loaded: %s", pkgName) - return - } - - for astNode, object := range pkg.Defs { - if astNode.Name != obj.GoType { - continue - } - - if findBindTargets(object.Type(), obj) { - return - } - } -} - func findBindTargets(t types.Type, object *Object) bool { switch t := t.(type) { case *types.Named: @@ -168,20 +144,6 @@ func findBindTargets(t types.Type, object *Object) bool { return false } -func mutationRoot(schema *schema.Schema) string { - if mu, ok := schema.EntryPoints["mutation"]; ok { - return mu.TypeName() - } - return "" -} - -func queryRoot(schema *schema.Schema) string { - if mu, ok := schema.EntryPoints["mutation"]; ok { - return mu.TypeName() - } - return "" -} - func modifiersFromGoType(t types.Type) []string { var modifiers []string for { diff --git a/codegen/type.go b/codegen/type.go index 4605b4fe38a..a927f27e07e 100644 --- a/codegen/type.go +++ b/codegen/type.go @@ -1,16 +1,23 @@ package codegen -import "strings" +import ( + "strings" +) type NamedTypes map[string]*NamedType type NamedType struct { + Ref IsScalar bool IsInterface bool GQLType string // Name of the graphql type - GoType string // Name of the go type - Package string // the package the go type lives in - Import *Import + Marshaler *Ref // If this type has an external marshaler this will be set +} + +type Ref struct { + GoType string // Name of the go type + Package string // the package the go type lives in + Import *Import // the resolved import with alias } type Type struct { @@ -24,11 +31,15 @@ const ( modPtr = "*" ) -func (t NamedType) FullName() string { +func (t Ref) FullName() string { + return t.pkgDot() + t.GoType +} + +func (t Ref) pkgDot() string { if t.Import == nil || t.Import.Name == "" { - return t.GoType + return "" } - return t.Import.Name + "." + t.GoType + return t.Import.Name + "." } func (t Type) Signature() string { @@ -42,3 +53,23 @@ func (t Type) IsPtr() bool { func (t Type) IsSlice() bool { return len(t.Modifiers) > 0 && t.Modifiers[0] == modList } + +func (t Type) Unmarshal(result, raw string) string { + if t.Marshaler != nil { + return result + ", err := " + t.Marshaler.pkgDot() + "Unmarshal" + t.Marshaler.GoType + "(" + raw + ")" + } + return tpl(`var {{.result}} {{.type}} + err := (&{{.result}}).Unmarshal({{.raw}})`, map[string]interface{}{ + "result": result, + "raw": raw, + "type": t.FullName(), + }) +} + +func (t Type) Marshal(result, val string) string { + if t.Marshaler != nil { + return result + " = " + t.Marshaler.pkgDot() + "" /* Marshal */ + t.Marshaler.GoType + "(" + val + ")" + } + + return result + " = " + val +} diff --git a/codegen/type_build.go b/codegen/type_build.go index b49968dbd47..bff39f2df6b 100644 --- a/codegen/type_build.go +++ b/codegen/type_build.go @@ -2,10 +2,12 @@ package codegen import ( "fmt" + "go/types" "strings" "github.com/vektah/gqlgen/neelance/common" "github.com/vektah/gqlgen/neelance/schema" + "golang.org/x/tools/go/loader" ) // namedTypeFromSchema objects for every graphql type, including scalars. There should only be one instance of Type for each thing @@ -17,10 +19,9 @@ func buildNamedTypes(s *schema.Schema, userTypes map[string]string) NamedTypes { userType := userTypes[t.GQLType] if userType == "" { if t.IsScalar { - userType = "string" + userType = "github.com/vektah/gqlgen/graphql.String" } else { userType = "interface{}" - userTypes[t.GQLType] = "interface{}" } } t.Package, t.GoType = pkgAndType(userType) @@ -30,6 +31,34 @@ func buildNamedTypes(s *schema.Schema, userTypes map[string]string) NamedTypes { return types } +func bindTypes(imports Imports, namedTypes NamedTypes, prog *loader.Program) { + fmt.Println(namedTypes) + for _, t := range namedTypes { + if t.Package == "" { + fmt.Println("NO PKG", t) + continue + } + + def := findGoType(prog, t.Package, t.GoType) + if def == nil { + + } + fmt.Println("Looking at " + t.FullName()) + switch def := def.(type) { + case *types.Func: + + fmt.Println(def.String()) + sig := def.Type().(*types.Signature) + cpy := t.Ref + t.Marshaler = &cpy + + fmt.Println("sig: " + sig.Params().At(0).Type().String()) + t.Package, t.GoType = pkgAndType(sig.Params().At(0).Type().String()) + t.Import = imports.findByName(t.Package) + } + } +} + // namedTypeFromSchema objects for every graphql type, including primitives. // don't recurse into object fields or interfaces yet, lets make sure we have collected everything first. func namedTypeFromSchema(schemaType schema.NamedType) *NamedType { diff --git a/codegen/util.go b/codegen/util.go new file mode 100644 index 00000000000..661d20fa513 --- /dev/null +++ b/codegen/util.go @@ -0,0 +1,47 @@ +package codegen + +import ( + "fmt" + "go/types" + "os" + + "golang.org/x/tools/go/loader" +) + +func findGoType(prog *loader.Program, pkgName string, typeName string) types.Object { + fullName := typeName + if pkgName != "" { + fullName = pkgName + "." + typeName + } + + pkgName, err := resolvePkg(pkgName) + if err != nil { + fmt.Fprintf(os.Stderr, "unable to resolve package for %s: %s\n", fullName, err.Error()) + return nil + } + + pkg := prog.Imported[pkgName] + if pkg == nil { + fmt.Fprintf(os.Stderr, "required package was not loaded: %s", fullName) + return nil + } + + for astNode, def := range pkg.Defs { + if astNode.Name != typeName || isMethod(def) { + continue + } + + return def + } + fmt.Fprintf(os.Stderr, "unable to find type %s\n", fullName) + return nil +} + +func isMethod(t types.Object) bool { + f, isFunc := t.(*types.Func) + if !isFunc { + return false + } + + return f.Type().(*types.Signature).Recv() != nil +} diff --git a/example/dataloader/schema.graphql b/example/dataloader/schema.graphql index 32314d2afe2..9bbf970d0ff 100644 --- a/example/dataloader/schema.graphql +++ b/example/dataloader/schema.graphql @@ -29,3 +29,4 @@ type Order { type Item { name: String } +scalar Time diff --git a/example/starwars/schema.graphql b/example/starwars/schema.graphql index 5cffd957e91..32b13a8e44a 100644 --- a/example/starwars/schema.graphql +++ b/example/starwars/schema.graphql @@ -132,3 +132,4 @@ type Starship { history: [[Int]] } union SearchResult = Human | Droid | Starship +scalar Time diff --git a/main.go b/main.go index c97c7ac93f3..5fa8b5911be 100644 --- a/main.go +++ b/main.go @@ -113,12 +113,12 @@ func loadTypeMap() map[string]string { "__EnumValue": "github.com/vektah/gqlgen/neelance/introspection.EnumValue", "__InputValue": "github.com/vektah/gqlgen/neelance/introspection.InputValue", "__Schema": "github.com/vektah/gqlgen/neelance/introspection.Schema", - "Int": "int", - "Float": "float64", - "String": "string", - "Boolean": "bool", - "ID": "string", - "Time": "time.Time", + "Int": "github.com/vektah/gqlgen/graphql.Int", + "Float": "github.com/vektah/gqlgen/graphql.Float", + "String": "github.com/vektah/gqlgen/graphql.String", + "Boolean": "github.com/vektah/gqlgen/graphql.Boolean", + "ID": "github.com/vektah/gqlgen/graphql.ID", + "Time": "github.com/vektah/gqlgen/graphql.Time", } b, err := ioutil.ReadFile(*typemap) if err != nil { diff --git a/neelance/schema/meta.go b/neelance/schema/meta.go index fc8f7410e9f..b48bf7acf28 100644 --- a/neelance/schema/meta.go +++ b/neelance/schema/meta.go @@ -26,9 +26,6 @@ var metaSrc = ` # The ` + "`" + `ID` + "`" + ` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as ` + "`" + `"4"` + "`" + `) or integer (such as ` + "`" + `4` + "`" + `) input value will be accepted as an ID. scalar ID - # The ` + "`" + `Time` + "`" + ` scalar type represents an RFC3339 encoded date time object - scalar Time - # Directs the executor to include this field or fragment only when the ` + "`" + `if` + "`" + ` argument is true. directive @include( # Included when true. diff --git a/neelance/validation/validation.go b/neelance/validation/validation.go index 7c9c2742c23..be94131acc5 100644 --- a/neelance/validation/validation.go +++ b/neelance/validation/validation.go @@ -743,7 +743,7 @@ func validateBasicLit(v *common.BasicLit, t common.Type) bool { case "ID": return v.Type == scanner.Int || v.Type == scanner.String default: - //TODO: Type-check against expected type by Unmarshalling + //TODO: Type-check against expected type by Unmarshaling return true } diff --git a/templates/args.go b/templates/args.go index ca3472f8420..c72694888ed 100644 --- a/templates/args.go +++ b/templates/args.go @@ -4,21 +4,7 @@ var argsTpl = ` {{- define "args" }} {{- range $i, $arg := . }} var arg{{$i}} {{$arg.Signature }} - {{- if eq $arg.FullName "time.Time" }} - if tmp, ok := field.Args[{{$arg.GQLName|quote}}]; ok { - if tmpStr, ok := tmp.(string); ok { - tmpDate, err := time.Parse(time.RFC3339, tmpStr) - if err != nil { - ec.Error(err) - continue - } - arg{{$i}} = {{if $arg.Type.IsPtr}}&{{end}}tmpDate - } else { - ec.Errorf("Time '{{$arg.GQLName}}' should be RFC3339 formatted string") - continue - } - } - {{- else if eq $arg.GoType "map[string]interface{}" }} + {{- if eq $arg.GoType "map[string]interface{}" }} if tmp, ok := field.Args[{{$arg.GQLName|quote}}]; ok { {{- if $arg.Type.IsPtr }} tmp2 := tmp.({{$arg.GoType}}) @@ -29,7 +15,7 @@ var argsTpl = ` } {{- else if $arg.IsScalar }} if tmp, ok := field.Args[{{$arg.GQLName|quote}}]; ok { - tmp2, err := coerce{{$arg.GoType|ucFirst}}(tmp) + {{$arg.Unmarshal "tmp2" "tmp" }} if err != nil { ec.Error(err) continue diff --git a/templates/file.go b/templates/file.go index 70aa7039e5e..6dcbf4d4380 100644 --- a/templates/file.go +++ b/templates/file.go @@ -177,31 +177,6 @@ type collectedField struct { Selections []query.Selection } -func decodeHook(sourceType reflect.Type, destType reflect.Type, value interface{}) (interface{}, error) { - if destType.PkgPath() == "time" && destType.Name() == "Time" { - if dateStr, ok := value.(string); ok { - return time.Parse(time.RFC3339, dateStr) - } - return nil, errors.Errorf("time should be an RFC3339 formatted string") - } - return value, nil -} - -// nolint: deadcode, megacheck -func unpackComplexArg(result interface{}, data interface{}) error { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - TagName: "graphql", - ErrorUnused: true, - Result: result, - DecodeHook: decodeHook, - }) - if err != nil { - panic(err) - } - - return decoder.Decode(data) -} - func getOrCreateField(c *[]collectedField, name string, creator func() collectedField) *collectedField { for i, cf := range *c { if cf.Alias == name { @@ -215,70 +190,5 @@ func getOrCreateField(c *[]collectedField, name string, creator func() collected return &(*c)[len(*c)-1] } -// nolint: deadcode, megacheck -func coerceString(v interface{}) (string, error) { - switch v := v.(type) { - case string: - return v, nil - case int: - return strconv.Itoa(v), nil - case float64: - return fmt.Sprintf("%f", v), nil - case bool: - if v { - return "true", nil - } else { - return "false", nil - } - case nil: - return "null", nil - default: - return "", fmt.Errorf("%T is not a string", v) - } -} - -// nolint: deadcode, megacheck -func coerceBool(v interface{}) (bool, error) { - switch v := v.(type) { - case string: - return "true" == strings.ToLower(v), nil - case int: - return v != 0, nil - case bool: - return v, nil - default: - return false, fmt.Errorf("%T is not a bool", v) - } -} - -// nolint: deadcode, megacheck -func coerceInt(v interface{}) (int, error) { - switch v := v.(type) { - case string: - return strconv.Atoi(v) - case int: - return v, nil - case float64: - return int(v), nil - default: - return 0, fmt.Errorf("%T is not an int", v) - } -} - -// nolint: deadcode, megacheck -func coercefloat64(v interface{}) (float64, error) { - switch v := v.(type) { - case string: - return strconv.ParseFloat(v, 64) - case int: - return float64(v), nil - case float64: - return v, nil - default: - return 0, fmt.Errorf("%T is not an float", v) - } -} - - {{end}} ` From 146c65380cec9e5f5bf59641d84faafecb31c07b Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Fri, 16 Feb 2018 19:35:28 +1100 Subject: [PATCH 3/8] generate input object unpackers --- codegen/build.go | 2 + codegen/input_build.go | 64 +++++++++++++++++++++++++ codegen/object.go | 20 ++++---- codegen/object_build.go | 100 +--------------------------------------- codegen/type.go | 4 ++ codegen/util.go | 85 ++++++++++++++++++++++++++++++++++ graphql/unmarshal.go | 25 ---------- templates.go | 2 +- templates/args.go | 10 +--- templates/file.go | 4 ++ templates/input.go | 26 +++++++++++ templates/object.go | 2 +- templates/templates.go | 4 +- 13 files changed, 202 insertions(+), 146 deletions(-) create mode 100644 codegen/input_build.go delete mode 100644 graphql/unmarshal.go create mode 100644 templates/input.go diff --git a/codegen/build.go b/codegen/build.go index 0ffd341adae..797c7a58596 100644 --- a/codegen/build.go +++ b/codegen/build.go @@ -12,6 +12,7 @@ import ( type Build struct { PackageName string Objects Objects + Inputs Objects Interfaces []*Interface Imports Imports QueryRoot *Object @@ -35,6 +36,7 @@ func Bind(schema *schema.Schema, userTypes map[string]string, destDir string) (* PackageName: filepath.Base(destDir), Objects: buildObjects(namedTypes, schema, prog), Interfaces: buildInterfaces(namedTypes, schema), + Inputs: buildInputs(namedTypes, schema, prog), Imports: imports, } diff --git a/codegen/input_build.go b/codegen/input_build.go new file mode 100644 index 00000000000..344c8a28210 --- /dev/null +++ b/codegen/input_build.go @@ -0,0 +1,64 @@ +package codegen + +import ( + "go/types" + "sort" + "strings" + + "github.com/vektah/gqlgen/neelance/schema" + "golang.org/x/tools/go/loader" +) + +func buildInputs(namedTypes NamedTypes, s *schema.Schema, prog *loader.Program) Objects { + var inputs Objects + + for _, typ := range s.Types { + switch typ := typ.(type) { + case *schema.InputObject: + input := buildInput(namedTypes, typ) + + if def := findGoType(prog, input.Package, input.GoType); def != nil { + input.Marshaler = buildInputMarshaler(typ, def) + bindObject(def.Type(), input) + } + + inputs = append(inputs, input) + } + } + + sort.Slice(inputs, func(i, j int) bool { + return strings.Compare(inputs[i].GQLType, inputs[j].GQLType) == -1 + }) + + return inputs +} + +func buildInput(types NamedTypes, typ *schema.InputObject) *Object { + obj := &Object{NamedType: types[typ.TypeName()]} + + for _, field := range typ.Values { + obj.Fields = append(obj.Fields, Field{ + GQLName: field.Name.Name, + Type: types.getType(field.Type), + Object: obj, + }) + } + return obj +} + +// if user has implemented an UnmarshalGQL method on the input type manually, use it +// otherwise we will generate one. +func buildInputMarshaler(typ *schema.InputObject, def types.Object) *Ref { + switch def := def.(type) { + case *types.TypeName: + namedType := def.Type().(*types.Named) + for i := 0; i < namedType.NumMethods(); i++ { + method := namedType.Method(i) + if method.Name() == "UnmarshalGQL" { + return nil + } + } + } + + return &Ref{GoType: typ.Name} +} diff --git a/codegen/object.go b/codegen/object.go index e1069f9ff6a..64a7d1858fc 100644 --- a/codegen/object.go +++ b/codegen/object.go @@ -35,6 +35,8 @@ type FieldArgument struct { GQLName string // The name of the argument in graphql } +type Objects []*Object + func (o *Object) GetField(name string) *Field { for i, field := range o.Fields { if strings.EqualFold(field.GQLName, name) { @@ -152,21 +154,21 @@ func (f *Field) doWriteJson(res string, val string, remainingMods []string, isPt } } +func (os Objects) ByName(name string) *Object { + for i, o := range os { + if strings.EqualFold(o.GQLType, name) { + return os[i] + } + } + return nil +} + func tpl(tpl string, vars map[string]interface{}) string { b := &bytes.Buffer{} template.Must(template.New("inline").Parse(tpl)).Execute(b, vars) return b.String() } -func ucFirst(s string) string { - if s == "" { - return "" - } - r := []rune(s) - r[0] = unicode.ToUpper(r[0]) - return string(r) -} - func lcFirst(s string) string { if s == "" { return "" diff --git a/codegen/object_build.go b/codegen/object_build.go index e96fa193432..a3812c51d99 100644 --- a/codegen/object_build.go +++ b/codegen/object_build.go @@ -1,9 +1,6 @@ package codegen import ( - "fmt" - "go/types" - "os" "sort" "strings" @@ -11,8 +8,6 @@ import ( "golang.org/x/tools/go/loader" ) -type Objects []*Object - func buildObjects(types NamedTypes, s *schema.Schema, prog *loader.Program) Objects { var objects Objects @@ -22,7 +17,7 @@ func buildObjects(types NamedTypes, s *schema.Schema, prog *loader.Program) Obje obj := buildObject(types, typ) if def := findGoType(prog, obj.Package, obj.GoType); def != nil { - findBindTargets(def.Type(), obj) + bindObject(def.Type(), obj) } objects = append(objects, obj) @@ -44,15 +39,6 @@ func buildObjects(types NamedTypes, s *schema.Schema, prog *loader.Program) Obje return objects } -func (os Objects) ByName(name string) *Object { - for i, o := range os { - if strings.EqualFold(o.GQLType, name) { - return os[i] - } - } - return nil -} - func buildObject(types NamedTypes, typ *schema.Object) *Object { obj := &Object{NamedType: types[typ.TypeName()]} @@ -78,87 +64,3 @@ func buildObject(types NamedTypes, typ *schema.Object) *Object { } return obj } - -func findBindTargets(t types.Type, object *Object) bool { - switch t := t.(type) { - case *types.Named: - for i := 0; i < t.NumMethods(); i++ { - method := t.Method(i) - if !method.Exported() { - continue - } - - if methodField := object.GetField(method.Name()); methodField != nil { - methodField.GoMethodName = "it." + method.Name() - sig := method.Type().(*types.Signature) - - methodField.Type.Modifiers = modifiersFromGoType(sig.Results().At(0).Type()) - - // check arg order matches code, not gql - - var newArgs []FieldArgument - l2: - for j := 0; j < sig.Params().Len(); j++ { - param := sig.Params().At(j) - for _, oldArg := range methodField.Args { - if strings.EqualFold(oldArg.GQLName, param.Name()) { - oldArg.Type.Modifiers = modifiersFromGoType(param.Type()) - newArgs = append(newArgs, oldArg) - continue l2 - } - } - fmt.Fprintln(os.Stderr, "cannot match argument "+param.Name()+" to any argument in "+t.String()) - } - methodField.Args = newArgs - - if sig.Results().Len() == 1 { - methodField.NoErr = true - } else if sig.Results().Len() != 2 { - fmt.Fprintf(os.Stderr, "weird number of results on %s. expected either (result), or (result, error)\n", method.Name()) - } - } - } - - findBindTargets(t.Underlying(), object) - return true - - case *types.Struct: - for i := 0; i < t.NumFields(); i++ { - field := t.Field(i) - // Todo: struct tags, name and - at least - - if !field.Exported() { - continue - } - - // Todo: check for type matches before binding too? - if objectField := object.GetField(field.Name()); objectField != nil { - objectField.GoVarName = "it." + field.Name() - objectField.Type.Modifiers = modifiersFromGoType(field.Type()) - } - } - t.Underlying() - return true - } - - return false -} - -func modifiersFromGoType(t types.Type) []string { - var modifiers []string - for { - switch val := t.(type) { - case *types.Pointer: - modifiers = append(modifiers, modPtr) - t = val.Elem() - case *types.Array: - modifiers = append(modifiers, modList) - t = val.Elem() - case *types.Slice: - modifiers = append(modifiers, modList) - t = val.Elem() - default: - return modifiers - } - } -} diff --git a/codegen/type.go b/codegen/type.go index a927f27e07e..507d2a4dca0 100644 --- a/codegen/type.go +++ b/codegen/type.go @@ -54,6 +54,10 @@ func (t Type) IsSlice() bool { return len(t.Modifiers) > 0 && t.Modifiers[0] == modList } +func (t NamedType) IsMarshaled() bool { + return t.Marshaler != nil +} + func (t Type) Unmarshal(result, raw string) string { if t.Marshaler != nil { return result + ", err := " + t.Marshaler.pkgDot() + "Unmarshal" + t.Marshaler.GoType + "(" + raw + ")" diff --git a/codegen/util.go b/codegen/util.go index 661d20fa513..36969b32083 100644 --- a/codegen/util.go +++ b/codegen/util.go @@ -4,6 +4,7 @@ import ( "fmt" "go/types" "os" + "strings" "golang.org/x/tools/go/loader" ) @@ -45,3 +46,87 @@ func isMethod(t types.Object) bool { return f.Type().(*types.Signature).Recv() != nil } + +func bindObject(t types.Type, object *Object) bool { + switch t := t.(type) { + case *types.Named: + for i := 0; i < t.NumMethods(); i++ { + method := t.Method(i) + if !method.Exported() { + continue + } + + if methodField := object.GetField(method.Name()); methodField != nil { + methodField.GoMethodName = "it." + method.Name() + sig := method.Type().(*types.Signature) + + methodField.Type.Modifiers = modifiersFromGoType(sig.Results().At(0).Type()) + + // check arg order matches code, not gql + + var newArgs []FieldArgument + l2: + for j := 0; j < sig.Params().Len(); j++ { + param := sig.Params().At(j) + for _, oldArg := range methodField.Args { + if strings.EqualFold(oldArg.GQLName, param.Name()) { + oldArg.Type.Modifiers = modifiersFromGoType(param.Type()) + newArgs = append(newArgs, oldArg) + continue l2 + } + } + fmt.Fprintln(os.Stderr, "cannot match argument "+param.Name()+" to any argument in "+t.String()) + } + methodField.Args = newArgs + + if sig.Results().Len() == 1 { + methodField.NoErr = true + } else if sig.Results().Len() != 2 { + fmt.Fprintf(os.Stderr, "weird number of results on %s. expected either (result), or (result, error)\n", method.Name()) + } + } + } + + bindObject(t.Underlying(), object) + return true + + case *types.Struct: + for i := 0; i < t.NumFields(); i++ { + field := t.Field(i) + // Todo: struct tags, name and - at least + + if !field.Exported() { + continue + } + + // Todo: check for type matches before binding too? + if objectField := object.GetField(field.Name()); objectField != nil { + objectField.GoVarName = "it." + field.Name() + objectField.Type.Modifiers = modifiersFromGoType(field.Type()) + } + } + t.Underlying() + return true + } + + return false +} + +func modifiersFromGoType(t types.Type) []string { + var modifiers []string + for { + switch val := t.(type) { + case *types.Pointer: + modifiers = append(modifiers, modPtr) + t = val.Elem() + case *types.Array: + modifiers = append(modifiers, modList) + t = val.Elem() + case *types.Slice: + modifiers = append(modifiers, modList) + t = val.Elem() + default: + return modifiers + } + } +} diff --git a/graphql/unmarshal.go b/graphql/unmarshal.go deleted file mode 100644 index 82a35b1ad8a..00000000000 --- a/graphql/unmarshal.go +++ /dev/null @@ -1,25 +0,0 @@ -package graphql - -import ( - "reflect" - - "github.com/mitchellh/mapstructure" -) - -func UnmarshalComplexArg(result interface{}, data interface{}) error { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - TagName: "graphql", - ErrorUnused: true, - Result: result, - DecodeHook: decodeHook, - }) - if err != nil { - panic(err) - } - - return decoder.Decode(data) -} - -func decodeHook(sourceType reflect.Type, destType reflect.Type, value interface{}) (interface{}, error) { - return value, nil -} diff --git a/templates.go b/templates.go index a64cc060089..b16faf33728 100644 --- a/templates.go +++ b/templates.go @@ -15,7 +15,7 @@ func runTemplate(e *codegen.Build) (*bytes.Buffer, error) { "ucFirst": ucFirst, "lcFirst": lcFirst, "quote": strconv.Quote, - }).Parse(templates.String()) + }).Parse(templates.All) if err != nil { return nil, err } diff --git a/templates/args.go b/templates/args.go index c72694888ed..4e2b365425a 100644 --- a/templates/args.go +++ b/templates/args.go @@ -1,6 +1,6 @@ package templates -var argsTpl = ` +const argsTpl = ` {{- define "args" }} {{- range $i, $arg := . }} var arg{{$i}} {{$arg.Signature }} @@ -13,7 +13,7 @@ var argsTpl = ` arg{{$i}} = tmp.({{$arg.GoType}}) {{- end }} } - {{- else if $arg.IsScalar }} + {{- else}} if tmp, ok := field.Args[{{$arg.GQLName|quote}}]; ok { {{$arg.Unmarshal "tmp2" "tmp" }} if err != nil { @@ -22,12 +22,6 @@ var argsTpl = ` } arg{{$i}} = {{if $arg.Type.IsPtr}}&{{end}}tmp2 } - {{- else }} - err := unpackComplexArg(&arg{{$i}}, field.Args[{{$arg.GQLName|quote}}]) - if err != nil { - ec.Error(err) - continue - } {{- end}} {{- end }} {{- end }} diff --git a/templates/file.go b/templates/file.go index 6dcbf4d4380..d893453df84 100644 --- a/templates/file.go +++ b/templates/file.go @@ -86,6 +86,10 @@ type executionContext struct { {{ template "interface" $interface }} {{- end }} +{{- range $input := .Inputs }} + {{ template "input" $input }} +{{- end }} + var parsedSchema = schema.MustParse({{.SchemaRaw|quote}}) func (ec *executionContext) introspectSchema() *introspection.Schema { diff --git a/templates/input.go b/templates/input.go new file mode 100644 index 00000000000..6f6f81361d5 --- /dev/null +++ b/templates/input.go @@ -0,0 +1,26 @@ +package templates + +const inputTpl = ` +{{- define "input" }} + {{- if .IsMarshaled }} + func Unmarshal{{ .GQLType }}(v interface{}) ({{.FullName}}, error) { + var it {{.FullName}} + + for k, v := range v.(map[string]interface{}) { + switch k { + {{- range $field := .Fields }} + case {{$field.GQLName|quote}}: + {{$field.Unmarshal "val" "v" }} + if err != nil { + return it, err + } + {{$field.GoVarName}} = {{if $field.Type.IsPtr}}&{{end}}val + {{- end }} + } + } + + return it, nil + } + {{- end }} +{{- end }} +` diff --git a/templates/object.go b/templates/object.go index 24be6f000c8..a6c217f6f52 100644 --- a/templates/object.go +++ b/templates/object.go @@ -16,7 +16,7 @@ func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, switch field.Name { case "__typename": - out.Values[i] = jsonw.String({{$object.GQLType|quote}}) + out.Values[i] = graphql.String({{$object.GQLType|quote}}) {{- range $field := $object.Fields }} case "{{$field.GQLName}}": {{- template "args" $field.Args }} diff --git a/templates/templates.go b/templates/templates.go index aa28ccff401..23ee30363e7 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -1,5 +1,3 @@ package templates -func String() string { - return argsTpl + fileTpl + interfaceTpl + objectTpl -} +const All = argsTpl + fileTpl + interfaceTpl + objectTpl + inputTpl From e0b7c25f10b0b089109ede0edb5ac55c56e91272 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Fri, 16 Feb 2018 19:47:33 +1100 Subject: [PATCH 4/8] move collectFields out of generated code --- graphql/error.go | 36 ++++++++++++++ graphql/exec.go | 101 ++++++++++++++++++++++++++++++++++++++ neelance/errors/errors.go | 33 ------------- templates/file.go | 92 +--------------------------------- templates/object.go | 4 +- 5 files changed, 140 insertions(+), 126 deletions(-) create mode 100644 graphql/error.go create mode 100644 graphql/exec.go diff --git a/graphql/error.go b/graphql/error.go new file mode 100644 index 00000000000..84fbdde31dd --- /dev/null +++ b/graphql/error.go @@ -0,0 +1,36 @@ +package graphql + +import ( + "github.com/vektah/gqlgen/neelance/errors" +) + +func Errors(errs []*errors.QueryError) Marshaler { + res := Array{} + + for _, err := range errs { + if err == nil { + res = append(res, Null) + continue + } + + errObj := &OrderedMap{} + + errObj.Add("message", String(err.Message)) + + if len(err.Locations) > 0 { + locations := Array{} + for _, location := range err.Locations { + locationObj := &OrderedMap{} + locationObj.Add("line", Int(location.Line)) + locationObj.Add("column", Int(location.Column)) + + locations = append(locations, locationObj) + } + + errObj.Add("locations", locations) + } + res = append(res, errObj) + } + + return res +} diff --git a/graphql/exec.go b/graphql/exec.go new file mode 100644 index 00000000000..df99b96d76e --- /dev/null +++ b/graphql/exec.go @@ -0,0 +1,101 @@ +package graphql + +import ( + "fmt" + + "github.com/vektah/gqlgen/neelance/query" +) + +func CollectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}) []CollectedField { + return collectFields(doc, selSet, satisfies, variables, map[string]bool{}) +} + +func collectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}, visited map[string]bool) []CollectedField { + var groupedFields []CollectedField + + for _, sel := range selSet { + switch sel := sel.(type) { + case *query.Field: + f := getOrCreateField(&groupedFields, sel.Name.Name, func() CollectedField { + f := CollectedField{ + Alias: sel.Alias.Name, + Name: sel.Name.Name, + } + if len(sel.Arguments) > 0 { + f.Args = map[string]interface{}{} + for _, arg := range sel.Arguments { + f.Args[arg.Name.Name] = arg.Value.Value(variables) + } + } + return f + }) + + f.Selections = append(f.Selections, sel.Selections...) + case *query.InlineFragment: + if !instanceOf(sel.On.Ident.Name, satisfies) { + continue + } + + for _, childField := range collectFields(doc, sel.Selections, satisfies, variables, visited) { + f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField }) + f.Selections = append(f.Selections, childField.Selections...) + } + + case *query.FragmentSpread: + fragmentName := sel.Name.Name + if _, seen := visited[fragmentName]; seen { + continue + } + visited[fragmentName] = true + + fragment := doc.Fragments.Get(fragmentName) + if fragment == nil { + // should never happen, validator has already run + panic(fmt.Errorf("missing fragment %s", fragmentName)) + } + + if !instanceOf(fragment.On.Ident.Name, satisfies) { + continue + } + + for _, childField := range collectFields(doc, fragment.Selections, satisfies, variables, visited) { + f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField }) + f.Selections = append(f.Selections, childField.Selections...) + } + + default: + panic(fmt.Errorf("unsupported %T", sel)) + } + } + + return groupedFields +} + +type CollectedField struct { + Alias string + Name string + Args map[string]interface{} + Selections []query.Selection +} + +func instanceOf(val string, satisfies []string) bool { + for _, s := range satisfies { + if val == s { + return true + } + } + return false +} + +func getOrCreateField(c *[]CollectedField, name string, creator func() CollectedField) *CollectedField { + for i, cf := range *c { + if cf.Alias == name { + return &(*c)[i] + } + } + + f := creator() + + *c = append(*c, f) + return &(*c)[len(*c)-1] +} diff --git a/neelance/errors/errors.go b/neelance/errors/errors.go index 8c1464705ec..d4a4d3275ff 100644 --- a/neelance/errors/errors.go +++ b/neelance/errors/errors.go @@ -2,8 +2,6 @@ package errors import ( "fmt" - - "github.com/vektah/gqlgen/graphql" ) type QueryError struct { @@ -53,34 +51,3 @@ func (c *Builder) Errorf(format string, args ...interface{}) { func (c *Builder) Error(err error) { c.Errors = append(c.Errors, Errorf("%s", err.Error())) } - -func ErrorWriter(errs []*QueryError) graphql.Marshaler { - res := graphql.Array{} - - for _, err := range errs { - if err == nil { - res = append(res, graphql.Null) - continue - } - - errObj := &graphql.OrderedMap{} - - errObj.Add("message", graphql.String(err.Message)) - - if len(err.Locations) > 0 { - locations := graphql.Array{} - for _, location := range err.Locations { - locationObj := &graphql.OrderedMap{} - locationObj.Add("line", graphql.Int(location.Line)) - locationObj.Add("column", graphql.Int(location.Column)) - - locations = append(locations, locationObj) - } - - errObj.Add("locations", locations) - } - res = append(res, errObj) - } - - return res -} diff --git a/templates/file.go b/templates/file.go index d893453df84..146a63f5214 100644 --- a/templates/file.go +++ b/templates/file.go @@ -61,7 +61,7 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ result.Add("data", data) if len(c.Errors) > 0 { - result.Add("errors", errors.ErrorWriter(c.Errors)) + result.Add("errors", graphql.Errors(c.Errors)) } result.MarshalGQL(w) @@ -104,95 +104,5 @@ func (ec *executionContext) introspectType(name string) *introspection.Type { return introspection.WrapType(t) } -func instanceOf(val string, satisfies []string) bool { - for _, s := range satisfies { - if val == s { - return true - } - } - return false -} - -func (ec *executionContext) collectFields(selSet []query.Selection, satisfies []string, visited map[string]bool) []collectedField { - var groupedFields []collectedField - - for _, sel := range selSet { - switch sel := sel.(type) { - case *query.Field: - f := getOrCreateField(&groupedFields, sel.Name.Name, func() collectedField { - f := collectedField{ - Alias: sel.Alias.Name, - Name: sel.Name.Name, - } - if len(sel.Arguments) > 0 { - f.Args = map[string]interface{}{} - for _, arg := range sel.Arguments { - f.Args[arg.Name.Name] = arg.Value.Value(ec.variables) - } - } - return f - }) - - f.Selections = append(f.Selections, sel.Selections...) - case *query.InlineFragment: - if !instanceOf(sel.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range ec.collectFields(sel.Selections, satisfies, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) - } - - case *query.FragmentSpread: - fragmentName := sel.Name.Name - if _, seen := visited[fragmentName]; seen { - continue - } - visited[fragmentName] = true - - fragment := ec.doc.Fragments.Get(fragmentName) - if fragment == nil { - ec.Errorf("missing fragment %s", fragmentName) - continue - } - - if !instanceOf(fragment.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range ec.collectFields(fragment.Selections, satisfies, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) - } - - default: - panic(fmt.Errorf("unsupported %T", sel)) - } - } - - return groupedFields -} - -type collectedField struct { - Alias string - Name string - Args map[string]interface{} - Selections []query.Selection -} - -func getOrCreateField(c *[]collectedField, name string, creator func() collectedField) *collectedField { - for i, cf := range *c { - if cf.Alias == name { - return &(*c)[i] - } - } - - f := creator() - - *c = append(*c, f) - return &(*c)[len(*c)-1] -} - {{end}} ` diff --git a/templates/object.go b/templates/object.go index a6c217f6f52..15de17d1bd2 100644 --- a/templates/object.go +++ b/templates/object.go @@ -8,7 +8,7 @@ var {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}} // nolint: gocyclo, errcheck, gas, goconst func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, it *{{$object.FullName}}) graphql.Marshaler { - fields := ec.collectFields(sel, {{$object.GQLType|lcFirst}}Implementors, map[string]bool{}) + fields := graphql.CollectFields(ec.doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.variables) out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias @@ -23,7 +23,7 @@ func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, {{- if $field.IsConcurrent }} ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() {{- end }} From 83b001aeb00773681279bc489197c08fff6794c2 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Fri, 16 Feb 2018 20:13:06 +1100 Subject: [PATCH 5/8] rename marshaler methods --- codegen/input_build.go | 8 +++++++- codegen/object_build.go | 8 +++++++- codegen/type.go | 2 +- codegen/type_build.go | 11 +---------- codegen/util.go | 16 ++++++++-------- graphql/bool.go | 2 +- graphql/error.go | 8 ++++---- graphql/float.go | 2 +- graphql/id.go | 2 +- graphql/int.go | 2 +- graphql/string.go | 2 +- graphql/time.go | 2 +- templates/file.go | 2 +- templates/object.go | 2 +- 14 files changed, 36 insertions(+), 33 deletions(-) diff --git a/codegen/input_build.go b/codegen/input_build.go index 344c8a28210..f42aa88058b 100644 --- a/codegen/input_build.go +++ b/codegen/input_build.go @@ -1,7 +1,9 @@ package codegen import ( + "fmt" "go/types" + "os" "sort" "strings" @@ -17,7 +19,11 @@ func buildInputs(namedTypes NamedTypes, s *schema.Schema, prog *loader.Program) case *schema.InputObject: input := buildInput(namedTypes, typ) - if def := findGoType(prog, input.Package, input.GoType); def != nil { + def, err := findGoType(prog, input.Package, input.GoType) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + } + if def != nil { input.Marshaler = buildInputMarshaler(typ, def) bindObject(def.Type(), input) } diff --git a/codegen/object_build.go b/codegen/object_build.go index a3812c51d99..fbe9cb0771d 100644 --- a/codegen/object_build.go +++ b/codegen/object_build.go @@ -1,6 +1,8 @@ package codegen import ( + "fmt" + "os" "sort" "strings" @@ -16,7 +18,11 @@ func buildObjects(types NamedTypes, s *schema.Schema, prog *loader.Program) Obje case *schema.Object: obj := buildObject(types, typ) - if def := findGoType(prog, obj.Package, obj.GoType); def != nil { + def, err := findGoType(prog, obj.Package, obj.GoType) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + } + if def != nil { bindObject(def.Type(), obj) } diff --git a/codegen/type.go b/codegen/type.go index 507d2a4dca0..71e44023714 100644 --- a/codegen/type.go +++ b/codegen/type.go @@ -72,7 +72,7 @@ func (t Type) Unmarshal(result, raw string) string { func (t Type) Marshal(result, val string) string { if t.Marshaler != nil { - return result + " = " + t.Marshaler.pkgDot() + "" /* Marshal */ + t.Marshaler.GoType + "(" + val + ")" + return result + " = " + t.Marshaler.pkgDot() + "Marshal" + t.Marshaler.GoType + "(" + val + ")" } return result + " = " + val diff --git a/codegen/type_build.go b/codegen/type_build.go index bff39f2df6b..93e817cec28 100644 --- a/codegen/type_build.go +++ b/codegen/type_build.go @@ -32,27 +32,18 @@ func buildNamedTypes(s *schema.Schema, userTypes map[string]string) NamedTypes { } func bindTypes(imports Imports, namedTypes NamedTypes, prog *loader.Program) { - fmt.Println(namedTypes) for _, t := range namedTypes { if t.Package == "" { - fmt.Println("NO PKG", t) continue } - def := findGoType(prog, t.Package, t.GoType) - if def == nil { - - } - fmt.Println("Looking at " + t.FullName()) + def, _ := findGoType(prog, t.Package, "Marshal"+t.GoType) switch def := def.(type) { case *types.Func: - - fmt.Println(def.String()) sig := def.Type().(*types.Signature) cpy := t.Ref t.Marshaler = &cpy - fmt.Println("sig: " + sig.Params().At(0).Type().String()) t.Package, t.GoType = pkgAndType(sig.Params().At(0).Type().String()) t.Import = imports.findByName(t.Package) } diff --git a/codegen/util.go b/codegen/util.go index 36969b32083..fa53c7dcc0e 100644 --- a/codegen/util.go +++ b/codegen/util.go @@ -9,7 +9,10 @@ import ( "golang.org/x/tools/go/loader" ) -func findGoType(prog *loader.Program, pkgName string, typeName string) types.Object { +func findGoType(prog *loader.Program, pkgName string, typeName string) (types.Object, error) { + if pkgName == "" { + return nil, nil + } fullName := typeName if pkgName != "" { fullName = pkgName + "." + typeName @@ -17,14 +20,12 @@ func findGoType(prog *loader.Program, pkgName string, typeName string) types.Obj pkgName, err := resolvePkg(pkgName) if err != nil { - fmt.Fprintf(os.Stderr, "unable to resolve package for %s: %s\n", fullName, err.Error()) - return nil + return nil, fmt.Errorf("unable to resolve package for %s: %s\n", fullName, err.Error()) } pkg := prog.Imported[pkgName] if pkg == nil { - fmt.Fprintf(os.Stderr, "required package was not loaded: %s", fullName) - return nil + return nil, fmt.Errorf("required package was not loaded: %s", fullName) } for astNode, def := range pkg.Defs { @@ -32,10 +33,9 @@ func findGoType(prog *loader.Program, pkgName string, typeName string) types.Obj continue } - return def + return def, nil } - fmt.Fprintf(os.Stderr, "unable to find type %s\n", fullName) - return nil + return nil, fmt.Errorf("unable to find type %s\n", fullName) } func isMethod(t types.Object) bool { diff --git a/graphql/bool.go b/graphql/bool.go index 6a3cfc4363b..7053bbcaa32 100644 --- a/graphql/bool.go +++ b/graphql/bool.go @@ -6,7 +6,7 @@ import ( "strings" ) -func Boolean(b bool) Marshaler { +func MarshalBoolean(b bool) Marshaler { return WriterFunc(func(w io.Writer) { if b { w.Write(trueLit) diff --git a/graphql/error.go b/graphql/error.go index 84fbdde31dd..ca1d2bf77fc 100644 --- a/graphql/error.go +++ b/graphql/error.go @@ -4,7 +4,7 @@ import ( "github.com/vektah/gqlgen/neelance/errors" ) -func Errors(errs []*errors.QueryError) Marshaler { +func MarshalErrors(errs []*errors.QueryError) Marshaler { res := Array{} for _, err := range errs { @@ -15,14 +15,14 @@ func Errors(errs []*errors.QueryError) Marshaler { errObj := &OrderedMap{} - errObj.Add("message", String(err.Message)) + errObj.Add("message", MarshalString(err.Message)) if len(err.Locations) > 0 { locations := Array{} for _, location := range err.Locations { locationObj := &OrderedMap{} - locationObj.Add("line", Int(location.Line)) - locationObj.Add("column", Int(location.Column)) + locationObj.Add("line", MarshalInt(location.Line)) + locationObj.Add("column", MarshalInt(location.Column)) locations = append(locations, locationObj) } diff --git a/graphql/float.go b/graphql/float.go index 6c5703b5fdc..c08b490a4f1 100644 --- a/graphql/float.go +++ b/graphql/float.go @@ -6,7 +6,7 @@ import ( "strconv" ) -func Float(f float64) Marshaler { +func MarshalFloat(f float64) Marshaler { return WriterFunc(func(w io.Writer) { io.WriteString(w, fmt.Sprintf("%f", f)) }) diff --git a/graphql/id.go b/graphql/id.go index 77804e11d40..7958670cdc7 100644 --- a/graphql/id.go +++ b/graphql/id.go @@ -6,7 +6,7 @@ import ( "strconv" ) -func ID(s string) Marshaler { +func MarshalID(s string) Marshaler { return WriterFunc(func(w io.Writer) { io.WriteString(w, strconv.Quote(s)) }) diff --git a/graphql/int.go b/graphql/int.go index 13f736917dc..b63b4c2aa66 100644 --- a/graphql/int.go +++ b/graphql/int.go @@ -6,7 +6,7 @@ import ( "strconv" ) -func Int(i int) Marshaler { +func MarshalInt(i int) Marshaler { return WriterFunc(func(w io.Writer) { io.WriteString(w, strconv.Itoa(i)) }) diff --git a/graphql/string.go b/graphql/string.go index 024f14adae7..d2bcea0b96f 100644 --- a/graphql/string.go +++ b/graphql/string.go @@ -6,7 +6,7 @@ import ( "strconv" ) -func String(s string) Marshaler { +func MarshalString(s string) Marshaler { return WriterFunc(func(w io.Writer) { io.WriteString(w, strconv.Quote(s)) }) diff --git a/graphql/time.go b/graphql/time.go index a7c73c20bc0..4f448560274 100644 --- a/graphql/time.go +++ b/graphql/time.go @@ -7,7 +7,7 @@ import ( "time" ) -func Time(t time.Time) Marshaler { +func MarshalTime(t time.Time) Marshaler { return WriterFunc(func(w io.Writer) { io.WriteString(w, strconv.Quote(t.Format(time.RFC3339))) }) diff --git a/templates/file.go b/templates/file.go index 146a63f5214..84597a664fd 100644 --- a/templates/file.go +++ b/templates/file.go @@ -61,7 +61,7 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ result.Add("data", data) if len(c.Errors) > 0 { - result.Add("errors", graphql.Errors(c.Errors)) + result.Add("errors", graphql.MarshalErrors(c.Errors)) } result.MarshalGQL(w) diff --git a/templates/object.go b/templates/object.go index 15de17d1bd2..bc13c2fdb28 100644 --- a/templates/object.go +++ b/templates/object.go @@ -16,7 +16,7 @@ func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, switch field.Name { case "__typename": - out.Values[i] = graphql.String({{$object.GQLType|quote}}) + out.Values[i] = graphql.MarshalString({{$object.GQLType|quote}}) {{- range $field := $object.Fields }} case "{{$field.GQLName}}": {{- template "args" $field.Args }} From 4722a855959112565548f5b2fc48891afd8d641a Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Fri, 16 Feb 2018 21:08:43 +1100 Subject: [PATCH 6/8] add scalar example --- example/scalars/generated.go | 686 +++++++++++++++++++++++++++++++ example/scalars/model.go | 74 ++++ example/scalars/readme.md | 60 +++ example/scalars/resolvers.go | 47 +++ example/scalars/scalar_test.go | 49 +++ example/scalars/schema.graphql | 23 ++ example/scalars/server/server.go | 16 + example/scalars/types.json | 6 + 8 files changed, 961 insertions(+) create mode 100644 example/scalars/generated.go create mode 100644 example/scalars/model.go create mode 100644 example/scalars/readme.md create mode 100644 example/scalars/resolvers.go create mode 100644 example/scalars/scalar_test.go create mode 100644 example/scalars/schema.graphql create mode 100644 example/scalars/server/server.go create mode 100644 example/scalars/types.json diff --git a/example/scalars/generated.go b/example/scalars/generated.go new file mode 100644 index 00000000000..0e01d44f584 --- /dev/null +++ b/example/scalars/generated.go @@ -0,0 +1,686 @@ +// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT + +package scalars + +import ( + context "context" + io "io" + strconv "strconv" + sync "sync" + + graphql "github.com/vektah/gqlgen/graphql" + errors "github.com/vektah/gqlgen/neelance/errors" + introspection "github.com/vektah/gqlgen/neelance/introspection" + query "github.com/vektah/gqlgen/neelance/query" + schema "github.com/vektah/gqlgen/neelance/schema" + validation "github.com/vektah/gqlgen/neelance/validation" +) + +type Resolvers interface { + Query_user(ctx context.Context, id string) (*User, error) + Query_search(ctx context.Context, input SearchArgs) ([]User, error) +} + +func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[string]interface{}, io.Writer) []*errors.QueryError { + return func(ctx context.Context, document string, operationName string, variables map[string]interface{}, w io.Writer) []*errors.QueryError { + doc, qErr := query.Parse(document) + if qErr != nil { + return []*errors.QueryError{qErr} + } + + errs := validation.Validate(parsedSchema, doc) + if len(errs) != 0 { + return errs + } + + op, err := doc.GetOperation(operationName) + if err != nil { + return []*errors.QueryError{errors.Errorf("%s", err)} + } + + c := executionContext{ + resolvers: resolvers, + variables: variables, + doc: doc, + ctx: ctx, + } + + var data graphql.Marshaler + if op.Type == query.Query { + data = c._query(op.Selections, nil) + } else { + return []*errors.QueryError{errors.Errorf("unsupported operation type")} + } + + c.wg.Wait() + + result := &graphql.OrderedMap{} + result.Add("data", data) + + if len(c.Errors) > 0 { + result.Add("errors", graphql.MarshalErrors(c.Errors)) + } + + result.MarshalGQL(w) + return nil + } +} + +type executionContext struct { + errors.Builder + resolvers Resolvers + variables map[string]interface{} + doc *query.Document + ctx context.Context + wg sync.WaitGroup +} + +var queryImplementors = []string{"Query"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, queryImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) + for i, field := range fields { + out.Keys[i] = field.Alias + out.Values[i] = graphql.Null + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Query") + case "user": + var arg0 string + if tmp, ok := field.Args["id"]; ok { + tmp2, err := graphql.UnmarshalID(tmp) + if err != nil { + ec.Error(err) + continue + } + arg0 = tmp2 + } + ec.wg.Add(1) + go func(i int, field graphql.CollectedField) { + defer ec.wg.Done() + res, err := ec.resolvers.Query_user(ec.ctx, arg0) + if err != nil { + ec.Error(err) + return + } + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec._user(field.Selections, res) + } + }(i, field) + case "search": + var arg0 SearchArgs + if tmp, ok := field.Args["input"]; ok { + tmp2, err := UnmarshalSearchArgs(tmp) + if err != nil { + ec.Error(err) + continue + } + arg0 = tmp2 + } + ec.wg.Add(1) + go func(i int, field graphql.CollectedField) { + defer ec.wg.Done() + res, err := ec.resolvers.Query_search(ec.ctx, arg0) + if err != nil { + ec.Error(err) + return + } + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + tmp1 = ec._user(field.Selections, &res[idx1]) + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + }(i, field) + case "__schema": + res := ec.introspectSchema() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec.___Schema(field.Selections, res) + } + case "__type": + var arg0 string + if tmp, ok := field.Args["name"]; ok { + tmp2, err := graphql.UnmarshalString(tmp) + if err != nil { + ec.Error(err) + continue + } + arg0 = tmp2 + } + res := ec.introspectType(arg0) + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec.___Type(field.Selections, res) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + return out +} + +var userImplementors = []string{"User"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) _user(sel []query.Selection, it *User) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, userImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) + for i, field := range fields { + out.Keys[i] = field.Alias + out.Values[i] = graphql.Null + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("User") + case "id": + res := it.ID + + out.Values[i] = graphql.MarshalID(res) + case "name": + res := it.Name + + out.Values[i] = graphql.MarshalString(res) + case "created": + res := it.Created + + out.Values[i] = MarshalTimestamp(res) + case "location": + res := it.Location + + out.Values[i] = res + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + return out +} + +var __DirectiveImplementors = []string{"__Directive"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) ___Directive(sel []query.Selection, it *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __DirectiveImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) + for i, field := range fields { + out.Keys[i] = field.Alias + out.Values[i] = graphql.Null + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Directive") + case "name": + res := it.Name() + + out.Values[i] = graphql.MarshalString(res) + case "description": + res := it.Description() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + case "locations": + res := it.Locations() + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + tmp1 = graphql.MarshalString(res[idx1]) + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + case "args": + res := it.Args() + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___InputValue(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + return out +} + +var __EnumValueImplementors = []string{"__EnumValue"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __EnumValueImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) + for i, field := range fields { + out.Keys[i] = field.Alias + out.Values[i] = graphql.Null + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__EnumValue") + case "name": + res := it.Name() + + out.Values[i] = graphql.MarshalString(res) + case "description": + res := it.Description() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + case "isDeprecated": + res := it.IsDeprecated() + + out.Values[i] = graphql.MarshalBoolean(res) + case "deprecationReason": + res := it.DeprecationReason() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + return out +} + +var __FieldImplementors = []string{"__Field"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __FieldImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) + for i, field := range fields { + out.Keys[i] = field.Alias + out.Values[i] = graphql.Null + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Field") + case "name": + res := it.Name() + + out.Values[i] = graphql.MarshalString(res) + case "description": + res := it.Description() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + case "args": + res := it.Args() + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___InputValue(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + case "type": + res := it.Type() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec.___Type(field.Selections, res) + } + case "isDeprecated": + res := it.IsDeprecated() + + out.Values[i] = graphql.MarshalBoolean(res) + case "deprecationReason": + res := it.DeprecationReason() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + return out +} + +var __InputValueImplementors = []string{"__InputValue"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __InputValueImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) + for i, field := range fields { + out.Keys[i] = field.Alias + out.Values[i] = graphql.Null + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__InputValue") + case "name": + res := it.Name() + + out.Values[i] = graphql.MarshalString(res) + case "description": + res := it.Description() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + case "type": + res := it.Type() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec.___Type(field.Selections, res) + } + case "defaultValue": + res := it.DefaultValue() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + return out +} + +var __SchemaImplementors = []string{"__Schema"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __SchemaImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) + for i, field := range fields { + out.Keys[i] = field.Alias + out.Values[i] = graphql.Null + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Schema") + case "types": + res := it.Types() + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___Type(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + case "queryType": + res := it.QueryType() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec.___Type(field.Selections, res) + } + case "mutationType": + res := it.MutationType() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec.___Type(field.Selections, res) + } + case "subscriptionType": + res := it.SubscriptionType() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec.___Type(field.Selections, res) + } + case "directives": + res := it.Directives() + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___Directive(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + return out +} + +var __TypeImplementors = []string{"__Type"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __TypeImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) + for i, field := range fields { + out.Keys[i] = field.Alias + out.Values[i] = graphql.Null + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Type") + case "kind": + res := it.Kind() + + out.Values[i] = graphql.MarshalString(res) + case "name": + res := it.Name() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + case "description": + res := it.Description() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = graphql.MarshalString(*res) + } + case "fields": + var arg0 bool + if tmp, ok := field.Args["includeDeprecated"]; ok { + tmp2, err := graphql.UnmarshalBoolean(tmp) + if err != nil { + ec.Error(err) + continue + } + arg0 = tmp2 + } + res := it.Fields(arg0) + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___Field(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + case "interfaces": + res := it.Interfaces() + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___Type(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + case "possibleTypes": + res := it.PossibleTypes() + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___Type(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + case "enumValues": + var arg0 bool + if tmp, ok := field.Args["includeDeprecated"]; ok { + tmp2, err := graphql.UnmarshalBoolean(tmp) + if err != nil { + ec.Error(err) + continue + } + arg0 = tmp2 + } + res := it.EnumValues(arg0) + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___EnumValue(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + case "inputFields": + res := it.InputFields() + + arr1 := graphql.Array{} + for idx1 := range res { + var tmp1 graphql.Marshaler + + if res[idx1] == nil { + tmp1 = graphql.Null + } else { + tmp1 = ec.___InputValue(field.Selections, res[idx1]) + } + arr1 = append(arr1, tmp1) + } + out.Values[i] = arr1 + case "ofType": + res := it.OfType() + + if res == nil { + out.Values[i] = graphql.Null + } else { + out.Values[i] = ec.___Type(field.Selections, res) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + + return out +} + +func UnmarshalSearchArgs(v interface{}) (SearchArgs, error) { + var it SearchArgs + + for k, v := range v.(map[string]interface{}) { + switch k { + case "location": + var val Point + err := (&val).UnmarshalGQL(v) + if err != nil { + return it, err + } + it.Location = &val + case "createdAfter": + val, err := UnmarshalTimestamp(v) + if err != nil { + return it, err + } + it.CreatedAfter = &val + } + } + + return it, nil +} + +var parsedSchema = schema.MustParse("schema {\n query: Query\n}\n\ntype Query {\n user(id: ID!): User\n search(input: SearchArgs!): [User!]!\n}\n\ntype User {\n id: ID!\n name: String!\n created: Timestamp\n location: Point\n}\n\ninput SearchArgs {\n location: Point\n createdAfter: Timestamp\n}\n\nscalar Timestamp\nscalar Point\n") + +func (ec *executionContext) introspectSchema() *introspection.Schema { + return introspection.WrapSchema(parsedSchema) +} + +func (ec *executionContext) introspectType(name string) *introspection.Type { + t := parsedSchema.Resolve(name) + if t == nil { + return nil + } + return introspection.WrapType(t) +} diff --git a/example/scalars/model.go b/example/scalars/model.go new file mode 100644 index 00000000000..2569932ebce --- /dev/null +++ b/example/scalars/model.go @@ -0,0 +1,74 @@ +package scalars + +import ( + "errors" + "fmt" + "io" + "strconv" + "strings" + "time" + + "github.com/vektah/gqlgen/graphql" +) + +type User struct { + ID string + Name string + Location Point // custom scalar types + Created time.Time // direct binding to builtin types with external Marshal/Unmarshal methods +} + +// Point is serialized as a simple array, eg [1, 2] +type Point struct { + X int + Y int +} + +func (p *Point) UnmarshalGQL(v interface{}) error { + pointStr, ok := v.(string) + if !ok { + return fmt.Errorf("points must be strings") + } + + parts := strings.Split(pointStr, ",") + + if len(parts) != 2 { + return fmt.Errorf("points must have 2 parts") + } + + var err error + if p.X, err = strconv.Atoi(parts[0]); err != nil { + return err + } + if p.Y, err = strconv.Atoi(parts[1]); err != nil { + return err + } + return nil +} + +// MarshalGQL implements the graphql.Marshaler interface +func (p Point) MarshalGQL(w io.Writer) { + fmt.Fprintf(w, `"%d,%d"`, p.X, p.Y) +} + +// if the type referenced in types.json is a function that returns a marshaller we can use it to encode and decode +// onto any existing go type. +func MarshalTimestamp(t time.Time) graphql.Marshaler { + return graphql.WriterFunc(func(w io.Writer) { + io.WriteString(w, strconv.FormatInt(t.Unix(), 10)) + }) +} + +// Unmarshal{Typename} is only required if the scalar appears as an input. The raw values have already been decoded +// from json into int/float64/bool/nil/map[string]interface/[]interface +func UnmarshalTimestamp(v interface{}) (time.Time, error) { + if tmpStr, ok := v.(int); ok { + return time.Unix(int64(tmpStr), 0), nil + } + return time.Time{}, errors.New("time should be RFC3339 formatted string") +} + +type SearchArgs struct { + Location *Point + CreatedAfter *time.Time +} diff --git a/example/scalars/readme.md b/example/scalars/readme.md new file mode 100644 index 00000000000..ad1862c1ef9 --- /dev/null +++ b/example/scalars/readme.md @@ -0,0 +1,60 @@ +### Custom scalars + +There are two different ways to implement scalars in gqlgen, depending on your need. + + +#### With user defined types +For user defined types you can implement the graphql.Marshal and graphql.Unmarshal interfaces and they will be called, +then add the type to your types.json + + +#### With types you don't control + +If the type isn't owned by you (time.Time), or you want to represent it as a builtin type (string) you can implement +some magic marshaling hooks. + + +```go +package mypkg + +import ( + "fmt" + "io" + "strings" + + "github.com/vektah/gqlgen/graphql" +) + + +func MarshalMyCustomBooleanScalar(b bool) graphql.Marshaler { + return graphql.WriterFunc(func(w io.Writer) { + if b { + w.Write([]byte("true")) + } else { + w.Write([]byte("false")) + } + }) +} + +func UnmarshalMyCustomBooleanScalar(v interface{}) (bool, error) { + switch v := v.(type) { + case string: + return "true" == strings.ToLower(v), nil + case int: + return v != 0, nil + case bool: + return v, nil + default: + return false, fmt.Errorf("%T is not a bool", v) + } +} +``` + +and then in types.json point to the name without the Marshal|Unmarshal in front: +```json +{ + "MyCustomBooleanScalar": "github.com/me/mypkg.MyCustomBooleanScalar" +} +``` + +see the `graphql` package for more examples. diff --git a/example/scalars/resolvers.go b/example/scalars/resolvers.go new file mode 100644 index 00000000000..8ccc12a3857 --- /dev/null +++ b/example/scalars/resolvers.go @@ -0,0 +1,47 @@ +//go:generate gorunpkg github.com/vektah/gqlgen -out generated.go + +package scalars + +import ( + context "context" + "time" +) + +type Resolver struct { +} + +func (r *Resolver) Query_user(ctx context.Context, id string) (*User, error) { + return &User{ + ID: id, + Name: "Test User " + id, + Created: time.Now(), + Location: Point{1, 2}, + }, nil +} + +func (r *Resolver) Query_search(ctx context.Context, input SearchArgs) ([]User, error) { + location := Point{1, 2} + if input.Location != nil { + location = *input.Location + } + + created := time.Now() + if input.CreatedAfter != nil { + created = *input.CreatedAfter + } + + return []User{ + { + ID: "1", + Name: "Test User 1", + Created: created, + Location: location, + }, + { + ID: "2", + Name: "Test User 2", + Created: created, + Location: location, + }, + }, nil +} diff --git a/example/scalars/scalar_test.go b/example/scalars/scalar_test.go new file mode 100644 index 00000000000..340e8aa527d --- /dev/null +++ b/example/scalars/scalar_test.go @@ -0,0 +1,49 @@ +package scalars + +import ( + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/vektah/gqlgen/client" + "github.com/vektah/gqlgen/handler" + introspection "github.com/vektah/gqlgen/neelance/introspection" +) + +type RawUser struct { + ID string + Name string + Created int64 + Location string +} + +func TestScalars(t *testing.T) { + srv := httptest.NewServer(handler.GraphQL(NewExecutor(&Resolver{}))) + c := client.New(srv.URL) + + var resp struct { + User RawUser + Search []RawUser + } + c.MustPost(`{ + user(id:"1") { + ...UserData + } + search(input:{location:"6,66", createdAfter:666}) { + ...UserData + } + } + fragment UserData on User { id name created location }`, &resp) + + require.Equal(t, "1,2", resp.User.Location) + require.Equal(t, time.Now().Unix(), resp.User.Created) + require.Equal(t, "6,66", resp.Search[0].Location) + require.Equal(t, int64(666), resp.Search[0].Created) + + t.Run("introspection", func(t *testing.T) { + // Make sure we can run the graphiql introspection query without errors + var resp interface{} + c.MustPost(introspection.Query, &resp) + }) +} diff --git a/example/scalars/schema.graphql b/example/scalars/schema.graphql new file mode 100644 index 00000000000..27d44f54536 --- /dev/null +++ b/example/scalars/schema.graphql @@ -0,0 +1,23 @@ +schema { + query: Query +} + +type Query { + user(id: ID!): User + search(input: SearchArgs!): [User!]! +} + +type User { + id: ID! + name: String! + created: Timestamp + location: Point +} + +input SearchArgs { + location: Point + createdAfter: Timestamp +} + +scalar Timestamp +scalar Point diff --git a/example/scalars/server/server.go b/example/scalars/server/server.go new file mode 100644 index 00000000000..df2c1a24eab --- /dev/null +++ b/example/scalars/server/server.go @@ -0,0 +1,16 @@ +package main + +import ( + "log" + "net/http" + + "github.com/vektah/gqlgen/example/scalars" + "github.com/vektah/gqlgen/handler" +) + +func main() { + http.Handle("/", handler.GraphiQL("Starwars", "/query")) + http.Handle("/query", handler.GraphQL(scalars.NewExecutor(&scalars.Resolver{}))) + + log.Fatal(http.ListenAndServe(":8084", nil)) +} diff --git a/example/scalars/types.json b/example/scalars/types.json new file mode 100644 index 00000000000..60f347c6055 --- /dev/null +++ b/example/scalars/types.json @@ -0,0 +1,6 @@ +{ + "User": "github.com/vektah/gqlgen/example/scalars.User", + "Timestamp": "github.com/vektah/gqlgen/example/scalars.Timestamp", + "SearchArgs": "github.com/vektah/gqlgen/example/scalars.SearchArgs", + "Point": "github.com/vektah/gqlgen/example/scalars.Point" +} From 944ee0884b565d82c2f567dc64f01bff1f6b2806 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Fri, 16 Feb 2018 21:13:54 +1100 Subject: [PATCH 7/8] regenerate --- codegen/import_build.go | 3 - codegen/type.go | 2 +- example/dataloader/generated.go | 478 +++++++--------------- example/starwars/generated.go | 685 ++++++++++++-------------------- example/todo/generated.go | 444 ++++++--------------- graphql/jsonw.go | 4 + 6 files changed, 547 insertions(+), 1069 deletions(-) diff --git a/codegen/import_build.go b/codegen/import_build.go index b683a1373fa..57395a69f0f 100644 --- a/codegen/import_build.go +++ b/codegen/import_build.go @@ -13,10 +13,7 @@ func buildImports(types NamedTypes, destDir string) Imports { {"io", "io"}, {"strconv", "strconv"}, {"time", "time"}, - {"reflect", "reflect"}, - {"strings", "strings"}, {"sync", "sync"}, - {"mapstructure", "github.com/mitchellh/mapstructure"}, {"introspection", "github.com/vektah/gqlgen/neelance/introspection"}, {"errors", "github.com/vektah/gqlgen/neelance/errors"}, {"query", "github.com/vektah/gqlgen/neelance/query"}, diff --git a/codegen/type.go b/codegen/type.go index 71e44023714..16ad100743a 100644 --- a/codegen/type.go +++ b/codegen/type.go @@ -63,7 +63,7 @@ func (t Type) Unmarshal(result, raw string) string { return result + ", err := " + t.Marshaler.pkgDot() + "Unmarshal" + t.Marshaler.GoType + "(" + raw + ")" } return tpl(`var {{.result}} {{.type}} - err := (&{{.result}}).Unmarshal({{.raw}})`, map[string]interface{}{ + err := (&{{.result}}).UnmarshalGQL({{.raw}})`, map[string]interface{}{ "result": result, "raw": raw, "type": t.FullName(), diff --git a/example/dataloader/generated.go b/example/dataloader/generated.go index 2a2d6fe7021..707b0e1919b 100644 --- a/example/dataloader/generated.go +++ b/example/dataloader/generated.go @@ -4,16 +4,11 @@ package dataloader import ( context "context" - fmt "fmt" io "io" - reflect "reflect" strconv "strconv" - strings "strings" sync "sync" - time "time" - mapstructure "github.com/mitchellh/mapstructure" - jsonw "github.com/vektah/gqlgen/jsonw" + graphql "github.com/vektah/gqlgen/graphql" errors "github.com/vektah/gqlgen/neelance/errors" introspection "github.com/vektah/gqlgen/neelance/introspection" query "github.com/vektah/gqlgen/neelance/query" @@ -53,7 +48,7 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ ctx: ctx, } - var data jsonw.Writer + var data graphql.Marshaler if op.Type == query.Query { data = c._query(op.Selections, nil) } else { @@ -62,14 +57,14 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ c.wg.Wait() - result := &jsonw.OrderedMap{} + result := &graphql.OrderedMap{} result.Add("data", data) if len(c.Errors) > 0 { - result.Add("errors", errors.ErrorWriter(c.Errors)) + result.Add("errors", graphql.MarshalErrors(c.Errors)) } - result.WriteJson(w) + result.MarshalGQL(w) return nil } } @@ -86,28 +81,28 @@ type executionContext struct { var addressImplementors = []string{"Address"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _address(sel []query.Selection, it *Address) jsonw.Writer { - fields := ec.collectFields(sel, addressImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _address(sel []query.Selection, it *Address) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, addressImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Address") + out.Values[i] = graphql.MarshalString("Address") case "id": res := it.ID - out.Values[i] = jsonw.Int(res) + out.Values[i] = graphql.MarshalInt(res) case "street": res := it.Street - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "country": res := it.Country - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -119,27 +114,27 @@ func (ec *executionContext) _address(sel []query.Selection, it *Address) jsonw.W var customerImplementors = []string{"Customer"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _customer(sel []query.Selection, it *Customer) jsonw.Writer { - fields := ec.collectFields(sel, customerImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _customer(sel []query.Selection, it *Customer) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, customerImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Customer") + out.Values[i] = graphql.MarshalString("Customer") case "id": res := it.ID - out.Values[i] = jsonw.Int(res) + out.Values[i] = graphql.MarshalInt(res) case "name": res := it.Name - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "address": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Customer_address(ec.ctx, it) if err != nil { @@ -148,14 +143,14 @@ func (ec *executionContext) _customer(sel []query.Selection, it *Customer) jsonw } if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec._address(field.Selections, res) } }(i, field) case "orders": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Customer_orders(ec.ctx, it) if err != nil { @@ -163,9 +158,9 @@ func (ec *executionContext) _customer(sel []query.Selection, it *Customer) jsonw return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._order(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -182,20 +177,20 @@ func (ec *executionContext) _customer(sel []query.Selection, it *Customer) jsonw var itemImplementors = []string{"Item"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _item(sel []query.Selection, it *Item) jsonw.Writer { - fields := ec.collectFields(sel, itemImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _item(sel []query.Selection, it *Item) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, itemImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Item") + out.Values[i] = graphql.MarshalString("Item") case "name": res := it.Name - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -207,31 +202,31 @@ func (ec *executionContext) _item(sel []query.Selection, it *Item) jsonw.Writer var orderImplementors = []string{"Order"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _order(sel []query.Selection, it *Order) jsonw.Writer { - fields := ec.collectFields(sel, orderImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _order(sel []query.Selection, it *Order) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, orderImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Order") + out.Values[i] = graphql.MarshalString("Order") case "id": res := it.ID - out.Values[i] = jsonw.Int(res) + out.Values[i] = graphql.MarshalInt(res) case "date": res := it.Date - out.Values[i] = jsonw.Time(res) + out.Values[i] = graphql.MarshalTime(res) case "amount": res := it.Amount - out.Values[i] = jsonw.Float64(res) + out.Values[i] = graphql.MarshalFloat(res) case "items": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Order_items(ec.ctx, it) if err != nil { @@ -239,9 +234,9 @@ func (ec *executionContext) _order(sel []query.Selection, it *Order) jsonw.Write return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._item(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -258,19 +253,19 @@ func (ec *executionContext) _order(sel []query.Selection, it *Order) jsonw.Write var queryImplementors = []string{"Query"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw.Writer { - fields := ec.collectFields(sel, queryImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, queryImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Query") + out.Values[i] = graphql.MarshalString("Query") case "customers": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Query_customers(ec.ctx) if err != nil { @@ -278,9 +273,9 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._customer(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -290,14 +285,14 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw res := ec.introspectSchema() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Schema(field.Selections, res) } case "__type": var arg0 string if tmp, ok := field.Args["name"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -307,7 +302,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw res := ec.introspectType(arg0) if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -322,47 +317,47 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw var __DirectiveImplementors = []string{"__Directive"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Directive(sel []query.Selection, it *introspection.Directive) jsonw.Writer { - fields := ec.collectFields(sel, __DirectiveImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Directive(sel []query.Selection, it *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __DirectiveImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Directive") + out.Values[i] = graphql.MarshalString("__Directive") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "locations": res := it.Locations() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer - tmp1 = jsonw.String(res[idx1]) + var tmp1 graphql.Marshaler + tmp1 = graphql.MarshalString(res[idx1]) arr1 = append(arr1, tmp1) } out.Values[i] = arr1 case "args": res := it.Args() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -380,39 +375,39 @@ func (ec *executionContext) ___Directive(sel []query.Selection, it *introspectio var __EnumValueImplementors = []string{"__EnumValue"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspection.EnumValue) jsonw.Writer { - fields := ec.collectFields(sel, __EnumValueImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __EnumValueImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__EnumValue") + out.Values[i] = graphql.MarshalString("__EnumValue") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "isDeprecated": res := it.IsDeprecated() - out.Values[i] = jsonw.Bool(res) + out.Values[i] = graphql.MarshalBoolean(res) case "deprecationReason": res := it.DeprecationReason() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -425,37 +420,37 @@ func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspectio var __FieldImplementors = []string{"__Field"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Field) jsonw.Writer { - fields := ec.collectFields(sel, __FieldImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __FieldImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Field") + out.Values[i] = graphql.MarshalString("__Field") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "args": res := it.Args() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -466,21 +461,21 @@ func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Fi res := it.Type() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } case "isDeprecated": res := it.IsDeprecated() - out.Values[i] = jsonw.Bool(res) + out.Values[i] = graphql.MarshalBoolean(res) case "deprecationReason": res := it.DeprecationReason() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -493,33 +488,33 @@ func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Fi var __InputValueImplementors = []string{"__InputValue"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspection.InputValue) jsonw.Writer { - fields := ec.collectFields(sel, __InputValueImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __InputValueImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__InputValue") + out.Values[i] = graphql.MarshalString("__InputValue") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "type": res := it.Type() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -527,9 +522,9 @@ func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspecti res := it.DefaultValue() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -542,25 +537,25 @@ func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspecti var __SchemaImplementors = []string{"__Schema"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.Schema) jsonw.Writer { - fields := ec.collectFields(sel, __SchemaImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __SchemaImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Schema") + out.Values[i] = graphql.MarshalString("__Schema") case "types": res := it.Types() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -571,7 +566,7 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.QueryType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -579,7 +574,7 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.MutationType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -587,19 +582,19 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.SubscriptionType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } case "directives": res := it.Directives() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Directive(field.Selections, res[idx1]) } @@ -617,40 +612,40 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S var __TypeImplementors = []string{"__Type"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Type) jsonw.Writer { - fields := ec.collectFields(sel, __TypeImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __TypeImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Type") + out.Values[i] = graphql.MarshalString("__Type") case "kind": res := it.Kind() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "name": res := it.Name() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "fields": var arg0 bool if tmp, ok := field.Args["includeDeprecated"]; ok { - tmp2, err := coerceBool(tmp) + tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { ec.Error(err) continue @@ -659,12 +654,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ } res := it.Fields(arg0) - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Field(field.Selections, res[idx1]) } @@ -674,12 +669,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "interfaces": res := it.Interfaces() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -689,12 +684,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "possibleTypes": res := it.PossibleTypes() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -704,7 +699,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "enumValues": var arg0 bool if tmp, ok := field.Args["includeDeprecated"]; ok { - tmp2, err := coerceBool(tmp) + tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { ec.Error(err) continue @@ -713,12 +708,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ } res := it.EnumValues(arg0) - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___EnumValue(field.Selections, res[idx1]) } @@ -728,12 +723,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "inputFields": res := it.InputFields() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -744,7 +739,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ res := it.OfType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -756,7 +751,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ return out } -var parsedSchema = schema.MustParse("schema {\n query: Query\n}\n\ntype Query {\n customers: [Customer!]\n}\n\ntype Customer {\n id: Int!\n name: String!\n address: Address\n orders: [Order!]\n}\n\ntype Address {\n id: Int!\n street: String\n country: String\n}\n\ntype Order {\n id: Int!\n date: Time\n amount: Float!\n items: [Item!]\n}\n\ntype Item {\n name: String\n}\n") +var parsedSchema = schema.MustParse("schema {\n query: Query\n}\n\ntype Query {\n customers: [Customer!]\n}\n\ntype Customer {\n id: Int!\n name: String!\n address: Address\n orders: [Order!]\n}\n\ntype Address {\n id: Int!\n street: String\n country: String\n}\n\ntype Order {\n id: Int!\n date: Time\n amount: Float!\n items: [Item!]\n}\n\ntype Item {\n name: String\n}\nscalar Time\n") func (ec *executionContext) introspectSchema() *introspection.Schema { return introspection.WrapSchema(parsedSchema) @@ -769,182 +764,3 @@ func (ec *executionContext) introspectType(name string) *introspection.Type { } return introspection.WrapType(t) } - -func instanceOf(val string, satisfies []string) bool { - for _, s := range satisfies { - if val == s { - return true - } - } - return false -} - -func (ec *executionContext) collectFields(selSet []query.Selection, satisfies []string, visited map[string]bool) []collectedField { - var groupedFields []collectedField - - for _, sel := range selSet { - switch sel := sel.(type) { - case *query.Field: - f := getOrCreateField(&groupedFields, sel.Name.Name, func() collectedField { - f := collectedField{ - Alias: sel.Alias.Name, - Name: sel.Name.Name, - } - if len(sel.Arguments) > 0 { - f.Args = map[string]interface{}{} - for _, arg := range sel.Arguments { - f.Args[arg.Name.Name] = arg.Value.Value(ec.variables) - } - } - return f - }) - - f.Selections = append(f.Selections, sel.Selections...) - case *query.InlineFragment: - if !instanceOf(sel.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range ec.collectFields(sel.Selections, satisfies, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) - } - - case *query.FragmentSpread: - fragmentName := sel.Name.Name - if _, seen := visited[fragmentName]; seen { - continue - } - visited[fragmentName] = true - - fragment := ec.doc.Fragments.Get(fragmentName) - if fragment == nil { - ec.Errorf("missing fragment %s", fragmentName) - continue - } - - if !instanceOf(fragment.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range ec.collectFields(fragment.Selections, satisfies, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) - } - - default: - panic(fmt.Errorf("unsupported %T", sel)) - } - } - - return groupedFields -} - -type collectedField struct { - Alias string - Name string - Args map[string]interface{} - Selections []query.Selection -} - -func decodeHook(sourceType reflect.Type, destType reflect.Type, value interface{}) (interface{}, error) { - if destType.PkgPath() == "time" && destType.Name() == "Time" { - if dateStr, ok := value.(string); ok { - return time.Parse(time.RFC3339, dateStr) - } - return nil, errors.Errorf("time should be an RFC3339 formatted string") - } - return value, nil -} - -// nolint: deadcode, megacheck -func unpackComplexArg(result interface{}, data interface{}) error { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - TagName: "graphql", - ErrorUnused: true, - Result: result, - DecodeHook: decodeHook, - }) - if err != nil { - panic(err) - } - - return decoder.Decode(data) -} - -func getOrCreateField(c *[]collectedField, name string, creator func() collectedField) *collectedField { - for i, cf := range *c { - if cf.Alias == name { - return &(*c)[i] - } - } - - f := creator() - - *c = append(*c, f) - return &(*c)[len(*c)-1] -} - -// nolint: deadcode, megacheck -func coerceString(v interface{}) (string, error) { - switch v := v.(type) { - case string: - return v, nil - case int: - return strconv.Itoa(v), nil - case float64: - return fmt.Sprintf("%f", v), nil - case bool: - if v { - return "true", nil - } else { - return "false", nil - } - case nil: - return "null", nil - default: - return "", fmt.Errorf("%T is not a string", v) - } -} - -// nolint: deadcode, megacheck -func coerceBool(v interface{}) (bool, error) { - switch v := v.(type) { - case string: - return "true" == strings.ToLower(v), nil - case int: - return v != 0, nil - case bool: - return v, nil - default: - return false, fmt.Errorf("%T is not a bool", v) - } -} - -// nolint: deadcode, megacheck -func coerceInt(v interface{}) (int, error) { - switch v := v.(type) { - case string: - return strconv.Atoi(v) - case int: - return v, nil - case float64: - return int(v), nil - default: - return 0, fmt.Errorf("%T is not an int", v) - } -} - -// nolint: deadcode, megacheck -func coercefloat64(v interface{}) (float64, error) { - switch v := v.(type) { - case string: - return strconv.ParseFloat(v, 64) - case int: - return float64(v), nil - case float64: - return v, nil - default: - return 0, fmt.Errorf("%T is not an float", v) - } -} diff --git a/example/starwars/generated.go b/example/starwars/generated.go index 6705acce03a..5270f3e0d7a 100644 --- a/example/starwars/generated.go +++ b/example/starwars/generated.go @@ -6,14 +6,11 @@ import ( context "context" fmt "fmt" io "io" - reflect "reflect" strconv "strconv" - strings "strings" sync "sync" time "time" - mapstructure "github.com/mitchellh/mapstructure" - jsonw "github.com/vektah/gqlgen/jsonw" + graphql "github.com/vektah/gqlgen/graphql" errors "github.com/vektah/gqlgen/neelance/errors" introspection "github.com/vektah/gqlgen/neelance/introspection" query "github.com/vektah/gqlgen/neelance/query" @@ -67,7 +64,7 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ ctx: ctx, } - var data jsonw.Writer + var data graphql.Marshaler if op.Type == query.Query { data = c._query(op.Selections, nil) } else if op.Type == query.Mutation { @@ -78,14 +75,14 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ c.wg.Wait() - result := &jsonw.OrderedMap{} + result := &graphql.OrderedMap{} result.Add("data", data) if len(c.Errors) > 0 { - result.Add("errors", errors.ErrorWriter(c.Errors)) + result.Add("errors", graphql.MarshalErrors(c.Errors)) } - result.WriteJson(w) + result.MarshalGQL(w) return nil } } @@ -102,27 +99,27 @@ type executionContext struct { var droidImplementors = []string{"Droid", "Character"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _droid(sel []query.Selection, it *Droid) jsonw.Writer { - fields := ec.collectFields(sel, droidImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _droid(sel []query.Selection, it *Droid) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, droidImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Droid") + out.Values[i] = graphql.MarshalString("Droid") case "id": res := it.ID - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalID(res) case "name": res := it.Name - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "friends": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Droid_friends(ec.ctx, it) if err != nil { @@ -130,9 +127,9 @@ func (ec *executionContext) _droid(sel []query.Selection, it *Droid) jsonw.Write return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._character(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -141,7 +138,7 @@ func (ec *executionContext) _droid(sel []query.Selection, it *Droid) jsonw.Write case "friendsConnection": var arg0 *int if tmp, ok := field.Args["first"]; ok { - tmp2, err := coerceInt(tmp) + tmp2, err := graphql.UnmarshalInt(tmp) if err != nil { ec.Error(err) continue @@ -150,7 +147,7 @@ func (ec *executionContext) _droid(sel []query.Selection, it *Droid) jsonw.Write } var arg1 *string if tmp, ok := field.Args["after"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalID(tmp) if err != nil { ec.Error(err) continue @@ -158,7 +155,7 @@ func (ec *executionContext) _droid(sel []query.Selection, it *Droid) jsonw.Write arg1 = &tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Droid_friendsConnection(ec.ctx, it, arg0, arg1) if err != nil { @@ -171,17 +168,17 @@ func (ec *executionContext) _droid(sel []query.Selection, it *Droid) jsonw.Write case "appearsIn": res := it.AppearsIn - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer - tmp1 = jsonw.String(res[idx1]) + var tmp1 graphql.Marshaler + tmp1 = graphql.MarshalString(res[idx1]) arr1 = append(arr1, tmp1) } out.Values[i] = arr1 case "primaryFunction": res := it.PrimaryFunction - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -193,23 +190,23 @@ func (ec *executionContext) _droid(sel []query.Selection, it *Droid) jsonw.Write var friendsConnectionImplementors = []string{"FriendsConnection"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _friendsConnection(sel []query.Selection, it *FriendsConnection) jsonw.Writer { - fields := ec.collectFields(sel, friendsConnectionImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _friendsConnection(sel []query.Selection, it *FriendsConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, friendsConnectionImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("FriendsConnection") + out.Values[i] = graphql.MarshalString("FriendsConnection") case "totalCount": res := it.TotalCount() - out.Values[i] = jsonw.Int(res) + out.Values[i] = graphql.MarshalInt(res) case "edges": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.FriendsConnection_edges(ec.ctx, it) if err != nil { @@ -217,9 +214,9 @@ func (ec *executionContext) _friendsConnection(sel []query.Selection, it *Friend return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._friendsEdge(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -227,7 +224,7 @@ func (ec *executionContext) _friendsConnection(sel []query.Selection, it *Friend }(i, field) case "friends": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.FriendsConnection_friends(ec.ctx, it) if err != nil { @@ -235,9 +232,9 @@ func (ec *executionContext) _friendsConnection(sel []query.Selection, it *Friend return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._character(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -258,20 +255,20 @@ func (ec *executionContext) _friendsConnection(sel []query.Selection, it *Friend var friendsEdgeImplementors = []string{"FriendsEdge"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _friendsEdge(sel []query.Selection, it *FriendsEdge) jsonw.Writer { - fields := ec.collectFields(sel, friendsEdgeImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _friendsEdge(sel []query.Selection, it *FriendsEdge) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, friendsEdgeImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("FriendsEdge") + out.Values[i] = graphql.MarshalString("FriendsEdge") case "cursor": res := it.Cursor - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalID(res) case "node": res := it.Node @@ -287,28 +284,28 @@ func (ec *executionContext) _friendsEdge(sel []query.Selection, it *FriendsEdge) var humanImplementors = []string{"Human", "Character"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Writer { - fields := ec.collectFields(sel, humanImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _human(sel []query.Selection, it *Human) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, humanImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Human") + out.Values[i] = graphql.MarshalString("Human") case "id": res := it.ID - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalID(res) case "name": res := it.Name - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "height": var arg0 string if tmp, ok := field.Args["unit"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -317,14 +314,14 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Write } res := it.Height(arg0) - out.Values[i] = jsonw.Float64(res) + out.Values[i] = graphql.MarshalFloat(res) case "mass": res := it.Mass - out.Values[i] = jsonw.Float64(res) + out.Values[i] = graphql.MarshalFloat(res) case "friends": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Human_friends(ec.ctx, it) if err != nil { @@ -332,9 +329,9 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Write return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._character(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -343,7 +340,7 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Write case "friendsConnection": var arg0 *int if tmp, ok := field.Args["first"]; ok { - tmp2, err := coerceInt(tmp) + tmp2, err := graphql.UnmarshalInt(tmp) if err != nil { ec.Error(err) continue @@ -352,7 +349,7 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Write } var arg1 *string if tmp, ok := field.Args["after"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalID(tmp) if err != nil { ec.Error(err) continue @@ -360,7 +357,7 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Write arg1 = &tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Human_friendsConnection(ec.ctx, it, arg0, arg1) if err != nil { @@ -373,16 +370,16 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Write case "appearsIn": res := it.AppearsIn - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer - tmp1 = jsonw.String(res[idx1]) + var tmp1 graphql.Marshaler + tmp1 = graphql.MarshalString(res[idx1]) arr1 = append(arr1, tmp1) } out.Values[i] = arr1 case "starships": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Human_starships(ec.ctx, it) if err != nil { @@ -390,9 +387,9 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Write return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._starship(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -409,20 +406,20 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) jsonw.Write var mutationImplementors = []string{"Mutation"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _mutation(sel []query.Selection, it *interface{}) jsonw.Writer { - fields := ec.collectFields(sel, mutationImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _mutation(sel []query.Selection, it *interface{}) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, mutationImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Mutation") + out.Values[i] = graphql.MarshalString("Mutation") case "createReview": var arg0 string if tmp, ok := field.Args["episode"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -430,10 +427,13 @@ func (ec *executionContext) _mutation(sel []query.Selection, it *interface{}) js arg0 = tmp2 } var arg1 Review - err := unpackComplexArg(&arg1, field.Args["review"]) - if err != nil { - ec.Error(err) - continue + if tmp, ok := field.Args["review"]; ok { + tmp2, err := UnmarshalReviewInput(tmp) + if err != nil { + ec.Error(err) + continue + } + arg1 = tmp2 } res, err := ec.resolvers.Mutation_createReview(ec.ctx, arg0, arg1) if err != nil { @@ -442,7 +442,7 @@ func (ec *executionContext) _mutation(sel []query.Selection, it *interface{}) js } if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec._review(field.Selections, res) } @@ -457,28 +457,28 @@ func (ec *executionContext) _mutation(sel []query.Selection, it *interface{}) js var pageInfoImplementors = []string{"PageInfo"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _pageInfo(sel []query.Selection, it *PageInfo) jsonw.Writer { - fields := ec.collectFields(sel, pageInfoImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _pageInfo(sel []query.Selection, it *PageInfo) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, pageInfoImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("PageInfo") + out.Values[i] = graphql.MarshalString("PageInfo") case "startCursor": res := it.StartCursor - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalID(res) case "endCursor": res := it.EndCursor - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalID(res) case "hasNextPage": res := it.HasNextPage - out.Values[i] = jsonw.Bool(res) + out.Values[i] = graphql.MarshalBoolean(res) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -490,20 +490,20 @@ func (ec *executionContext) _pageInfo(sel []query.Selection, it *PageInfo) jsonw var queryImplementors = []string{"Query"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw.Writer { - fields := ec.collectFields(sel, queryImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, queryImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Query") + out.Values[i] = graphql.MarshalString("Query") case "hero": var arg0 *string if tmp, ok := field.Args["episode"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -511,7 +511,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw arg0 = &tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Query_hero(ec.ctx, arg0) if err != nil { @@ -524,7 +524,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw case "reviews": var arg0 string if tmp, ok := field.Args["episode"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -533,20 +533,15 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw } var arg1 *time.Time if tmp, ok := field.Args["since"]; ok { - if tmpStr, ok := tmp.(string); ok { - tmpDate, err := time.Parse(time.RFC3339, tmpStr) - if err != nil { - ec.Error(err) - continue - } - arg1 = &tmpDate - } else { - ec.Errorf("Time 'since' should be RFC3339 formatted string") + tmp2, err := graphql.UnmarshalTime(tmp) + if err != nil { + ec.Error(err) continue } + arg1 = &tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Query_reviews(ec.ctx, arg0, arg1) if err != nil { @@ -554,9 +549,9 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._review(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -565,7 +560,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw case "search": var arg0 string if tmp, ok := field.Args["text"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -573,7 +568,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw arg0 = tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Query_search(ec.ctx, arg0) if err != nil { @@ -581,9 +576,9 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._searchResult(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -592,7 +587,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw case "character": var arg0 string if tmp, ok := field.Args["id"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalID(tmp) if err != nil { ec.Error(err) continue @@ -600,7 +595,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw arg0 = tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Query_character(ec.ctx, arg0) if err != nil { @@ -613,7 +608,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw case "droid": var arg0 string if tmp, ok := field.Args["id"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalID(tmp) if err != nil { ec.Error(err) continue @@ -621,7 +616,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw arg0 = tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Query_droid(ec.ctx, arg0) if err != nil { @@ -630,7 +625,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw } if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec._droid(field.Selections, res) } @@ -638,7 +633,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw case "human": var arg0 string if tmp, ok := field.Args["id"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalID(tmp) if err != nil { ec.Error(err) continue @@ -646,7 +641,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw arg0 = tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Query_human(ec.ctx, arg0) if err != nil { @@ -655,7 +650,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw } if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec._human(field.Selections, res) } @@ -663,7 +658,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw case "starship": var arg0 string if tmp, ok := field.Args["id"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalID(tmp) if err != nil { ec.Error(err) continue @@ -671,7 +666,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw arg0 = tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.Query_starship(ec.ctx, arg0) if err != nil { @@ -680,7 +675,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw } if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec._starship(field.Selections, res) } @@ -689,14 +684,14 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw res := ec.introspectSchema() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Schema(field.Selections, res) } case "__type": var arg0 string if tmp, ok := field.Args["name"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -706,7 +701,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw res := ec.introspectType(arg0) if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -721,32 +716,32 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) jsonw var reviewImplementors = []string{"Review"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _review(sel []query.Selection, it *Review) jsonw.Writer { - fields := ec.collectFields(sel, reviewImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _review(sel []query.Selection, it *Review) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, reviewImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Review") + out.Values[i] = graphql.MarshalString("Review") case "stars": res := it.Stars - out.Values[i] = jsonw.Int(res) + out.Values[i] = graphql.MarshalInt(res) case "commentary": res := it.Commentary if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "time": res := it.Time - out.Values[i] = jsonw.Time(res) + out.Values[i] = graphql.MarshalTime(res) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -758,28 +753,28 @@ func (ec *executionContext) _review(sel []query.Selection, it *Review) jsonw.Wri var starshipImplementors = []string{"Starship"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _starship(sel []query.Selection, it *Starship) jsonw.Writer { - fields := ec.collectFields(sel, starshipImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _starship(sel []query.Selection, it *Starship) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, starshipImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Starship") + out.Values[i] = graphql.MarshalString("Starship") case "id": res := it.ID - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalID(res) case "name": res := it.Name - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "length": var arg0 string if tmp, ok := field.Args["unit"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -788,18 +783,18 @@ func (ec *executionContext) _starship(sel []query.Selection, it *Starship) jsonw } res := it.Length(arg0) - out.Values[i] = jsonw.Float64(res) + out.Values[i] = graphql.MarshalFloat(res) case "history": res := it.History - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler - arr2 := jsonw.Array{} + arr2 := graphql.Array{} for idx2 := range res[idx1] { - var tmp2 jsonw.Writer - tmp2 = jsonw.Int(res[idx1][idx2]) + var tmp2 graphql.Marshaler + tmp2 = graphql.MarshalInt(res[idx1][idx2]) arr2 = append(arr2, tmp2) } tmp1 = arr2 @@ -817,47 +812,47 @@ func (ec *executionContext) _starship(sel []query.Selection, it *Starship) jsonw var __DirectiveImplementors = []string{"__Directive"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Directive(sel []query.Selection, it *introspection.Directive) jsonw.Writer { - fields := ec.collectFields(sel, __DirectiveImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Directive(sel []query.Selection, it *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __DirectiveImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Directive") + out.Values[i] = graphql.MarshalString("__Directive") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "locations": res := it.Locations() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer - tmp1 = jsonw.String(res[idx1]) + var tmp1 graphql.Marshaler + tmp1 = graphql.MarshalString(res[idx1]) arr1 = append(arr1, tmp1) } out.Values[i] = arr1 case "args": res := it.Args() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -875,39 +870,39 @@ func (ec *executionContext) ___Directive(sel []query.Selection, it *introspectio var __EnumValueImplementors = []string{"__EnumValue"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspection.EnumValue) jsonw.Writer { - fields := ec.collectFields(sel, __EnumValueImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __EnumValueImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__EnumValue") + out.Values[i] = graphql.MarshalString("__EnumValue") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "isDeprecated": res := it.IsDeprecated() - out.Values[i] = jsonw.Bool(res) + out.Values[i] = graphql.MarshalBoolean(res) case "deprecationReason": res := it.DeprecationReason() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -920,37 +915,37 @@ func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspectio var __FieldImplementors = []string{"__Field"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Field) jsonw.Writer { - fields := ec.collectFields(sel, __FieldImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __FieldImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Field") + out.Values[i] = graphql.MarshalString("__Field") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "args": res := it.Args() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -961,21 +956,21 @@ func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Fi res := it.Type() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } case "isDeprecated": res := it.IsDeprecated() - out.Values[i] = jsonw.Bool(res) + out.Values[i] = graphql.MarshalBoolean(res) case "deprecationReason": res := it.DeprecationReason() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -988,33 +983,33 @@ func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Fi var __InputValueImplementors = []string{"__InputValue"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspection.InputValue) jsonw.Writer { - fields := ec.collectFields(sel, __InputValueImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __InputValueImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__InputValue") + out.Values[i] = graphql.MarshalString("__InputValue") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "type": res := it.Type() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -1022,9 +1017,9 @@ func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspecti res := it.DefaultValue() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -1037,25 +1032,25 @@ func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspecti var __SchemaImplementors = []string{"__Schema"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.Schema) jsonw.Writer { - fields := ec.collectFields(sel, __SchemaImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __SchemaImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Schema") + out.Values[i] = graphql.MarshalString("__Schema") case "types": res := it.Types() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -1066,7 +1061,7 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.QueryType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -1074,7 +1069,7 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.MutationType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -1082,19 +1077,19 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.SubscriptionType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } case "directives": res := it.Directives() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Directive(field.Selections, res[idx1]) } @@ -1112,40 +1107,40 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S var __TypeImplementors = []string{"__Type"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Type) jsonw.Writer { - fields := ec.collectFields(sel, __TypeImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __TypeImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Type") + out.Values[i] = graphql.MarshalString("__Type") case "kind": res := it.Kind() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "name": res := it.Name() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "fields": var arg0 bool if tmp, ok := field.Args["includeDeprecated"]; ok { - tmp2, err := coerceBool(tmp) + tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { ec.Error(err) continue @@ -1154,12 +1149,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ } res := it.Fields(arg0) - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Field(field.Selections, res[idx1]) } @@ -1169,12 +1164,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "interfaces": res := it.Interfaces() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -1184,12 +1179,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "possibleTypes": res := it.PossibleTypes() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -1199,7 +1194,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "enumValues": var arg0 bool if tmp, ok := field.Args["includeDeprecated"]; ok { - tmp2, err := coerceBool(tmp) + tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { ec.Error(err) continue @@ -1208,12 +1203,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ } res := it.EnumValues(arg0) - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___EnumValue(field.Selections, res[idx1]) } @@ -1223,12 +1218,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "inputFields": res := it.InputFields() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -1239,7 +1234,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ res := it.OfType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -1251,10 +1246,10 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ return out } -func (ec *executionContext) _character(sel []query.Selection, it *Character) jsonw.Writer { +func (ec *executionContext) _character(sel []query.Selection, it *Character) graphql.Marshaler { switch it := (*it).(type) { case nil: - return jsonw.Null + return graphql.Null case Human: return ec._human(sel, &it) @@ -1270,10 +1265,10 @@ func (ec *executionContext) _character(sel []query.Selection, it *Character) jso } } -func (ec *executionContext) _searchResult(sel []query.Selection, it *SearchResult) jsonw.Writer { +func (ec *executionContext) _searchResult(sel []query.Selection, it *SearchResult) graphql.Marshaler { switch it := (*it).(type) { case nil: - return jsonw.Null + return graphql.Null case Human: return ec._human(sel, &it) @@ -1294,195 +1289,45 @@ func (ec *executionContext) _searchResult(sel []query.Selection, it *SearchResul } } -var parsedSchema = schema.MustParse("schema {\n query: Query\n mutation: Mutation\n}\n# The query type, represents all of the entry points into our object graph\ntype Query {\n hero(episode: Episode = NEWHOPE): Character\n reviews(episode: Episode!, since: Time): [Review]!\n search(text: String!): [SearchResult]!\n character(id: ID!): Character\n droid(id: ID!): Droid\n human(id: ID!): Human\n starship(id: ID!): Starship\n}\n# The mutation type, represents all updates we can make to our data\ntype Mutation {\n createReview(episode: Episode!, review: ReviewInput!): Review\n}\n# The episodes in the Star Wars trilogy\nenum Episode {\n # Star Wars Episode IV: A New Hope, released in 1977.\n NEWHOPE\n # Star Wars Episode V: The Empire Strikes Back, released in 1980.\n EMPIRE\n # Star Wars Episode VI: Return of the Jedi, released in 1983.\n JEDI\n}\n# A character from the Star Wars universe\ninterface Character {\n # The ID of the character\n id: ID!\n # The name of the character\n name: String!\n # The friends of the character, or an empty list if they have none\n friends: [Character]\n # The friends of the character exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this character appears in\n appearsIn: [Episode!]!\n}\n# Units of height\nenum LengthUnit {\n # The standard unit around the world\n METER\n # Primarily used in the United States\n FOOT\n}\n# A humanoid creature from the Star Wars universe\ntype Human implements Character {\n # The ID of the human\n id: ID!\n # What this human calls themselves\n name: String!\n # Height in the preferred unit, default is meters\n height(unit: LengthUnit = METER): Float!\n # Mass in kilograms, or null if unknown\n mass: Float\n # This human's friends, or an empty list if they have none\n friends: [Character]\n # The friends of the human exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this human appears in\n appearsIn: [Episode!]!\n # A list of starships this person has piloted, or an empty list if none\n starships: [Starship]\n}\n# An autonomous mechanical character in the Star Wars universe\ntype Droid implements Character {\n # The ID of the droid\n id: ID!\n # What others call this droid\n name: String!\n # This droid's friends, or an empty list if they have none\n friends: [Character]\n # The friends of the droid exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this droid appears in\n appearsIn: [Episode!]!\n # This droid's primary function\n primaryFunction: String\n}\n# A connection object for a character's friends\ntype FriendsConnection {\n # The total number of friends\n totalCount: Int!\n # The edges for each of the character's friends.\n edges: [FriendsEdge]\n # A list of the friends, as a convenience when edges are not needed.\n friends: [Character]\n # Information for paginating this connection\n pageInfo: PageInfo!\n}\n# An edge object for a character's friends\ntype FriendsEdge {\n # A cursor used for pagination\n cursor: ID!\n # The character represented by this friendship edge\n node: Character\n}\n# Information for paginating this connection\ntype PageInfo {\n startCursor: ID\n endCursor: ID\n hasNextPage: Boolean!\n}\n# Represents a review for a movie\ntype Review {\n # The number of stars this review gave, 1-5\n stars: Int!\n # Comment about the movie\n commentary: String\n # when the review was posted\n time: Time\n}\n# The input object sent when someone is creating a new review\ninput ReviewInput {\n # 0-5 stars\n stars: Int!\n # Comment about the movie, optional\n commentary: String\n # when the review was posted\n time: Time\n}\ntype Starship {\n # The ID of the starship\n id: ID!\n # The name of the starship\n name: String!\n # Length of the starship, along the longest axis\n length(unit: LengthUnit = METER): Float!\n # coordinates tracking this ship\n history: [[Int]]\n}\nunion SearchResult = Human | Droid | Starship\n") - -func (ec *executionContext) introspectSchema() *introspection.Schema { - return introspection.WrapSchema(parsedSchema) -} - -func (ec *executionContext) introspectType(name string) *introspection.Type { - t := parsedSchema.Resolve(name) - if t == nil { - return nil - } - return introspection.WrapType(t) -} - -func instanceOf(val string, satisfies []string) bool { - for _, s := range satisfies { - if val == s { - return true - } - } - return false -} - -func (ec *executionContext) collectFields(selSet []query.Selection, satisfies []string, visited map[string]bool) []collectedField { - var groupedFields []collectedField - - for _, sel := range selSet { - switch sel := sel.(type) { - case *query.Field: - f := getOrCreateField(&groupedFields, sel.Name.Name, func() collectedField { - f := collectedField{ - Alias: sel.Alias.Name, - Name: sel.Name.Name, - } - if len(sel.Arguments) > 0 { - f.Args = map[string]interface{}{} - for _, arg := range sel.Arguments { - f.Args[arg.Name.Name] = arg.Value.Value(ec.variables) - } - } - return f - }) - - f.Selections = append(f.Selections, sel.Selections...) - case *query.InlineFragment: - if !instanceOf(sel.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range ec.collectFields(sel.Selections, satisfies, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) - } - - case *query.FragmentSpread: - fragmentName := sel.Name.Name - if _, seen := visited[fragmentName]; seen { - continue - } - visited[fragmentName] = true +func UnmarshalReviewInput(v interface{}) (Review, error) { + var it Review - fragment := ec.doc.Fragments.Get(fragmentName) - if fragment == nil { - ec.Errorf("missing fragment %s", fragmentName) - continue + for k, v := range v.(map[string]interface{}) { + switch k { + case "stars": + val, err := graphql.UnmarshalInt(v) + if err != nil { + return it, err } - - if !instanceOf(fragment.On.Ident.Name, satisfies) { - continue + it.Stars = val + case "commentary": + val, err := graphql.UnmarshalString(v) + if err != nil { + return it, err } - - for _, childField := range ec.collectFields(fragment.Selections, satisfies, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) + it.Commentary = &val + case "time": + val, err := graphql.UnmarshalTime(v) + if err != nil { + return it, err } - - default: - panic(fmt.Errorf("unsupported %T", sel)) - } - } - - return groupedFields -} - -type collectedField struct { - Alias string - Name string - Args map[string]interface{} - Selections []query.Selection -} - -func decodeHook(sourceType reflect.Type, destType reflect.Type, value interface{}) (interface{}, error) { - if destType.PkgPath() == "time" && destType.Name() == "Time" { - if dateStr, ok := value.(string); ok { - return time.Parse(time.RFC3339, dateStr) + it.Time = val } - return nil, errors.Errorf("time should be an RFC3339 formatted string") - } - return value, nil -} - -// nolint: deadcode, megacheck -func unpackComplexArg(result interface{}, data interface{}) error { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - TagName: "graphql", - ErrorUnused: true, - Result: result, - DecodeHook: decodeHook, - }) - if err != nil { - panic(err) } - return decoder.Decode(data) + return it, nil } -func getOrCreateField(c *[]collectedField, name string, creator func() collectedField) *collectedField { - for i, cf := range *c { - if cf.Alias == name { - return &(*c)[i] - } - } - - f := creator() - - *c = append(*c, f) - return &(*c)[len(*c)-1] -} +var parsedSchema = schema.MustParse("schema {\n query: Query\n mutation: Mutation\n}\n# The query type, represents all of the entry points into our object graph\ntype Query {\n hero(episode: Episode = NEWHOPE): Character\n reviews(episode: Episode!, since: Time): [Review]!\n search(text: String!): [SearchResult]!\n character(id: ID!): Character\n droid(id: ID!): Droid\n human(id: ID!): Human\n starship(id: ID!): Starship\n}\n# The mutation type, represents all updates we can make to our data\ntype Mutation {\n createReview(episode: Episode!, review: ReviewInput!): Review\n}\n# The episodes in the Star Wars trilogy\nenum Episode {\n # Star Wars Episode IV: A New Hope, released in 1977.\n NEWHOPE\n # Star Wars Episode V: The Empire Strikes Back, released in 1980.\n EMPIRE\n # Star Wars Episode VI: Return of the Jedi, released in 1983.\n JEDI\n}\n# A character from the Star Wars universe\ninterface Character {\n # The ID of the character\n id: ID!\n # The name of the character\n name: String!\n # The friends of the character, or an empty list if they have none\n friends: [Character]\n # The friends of the character exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this character appears in\n appearsIn: [Episode!]!\n}\n# Units of height\nenum LengthUnit {\n # The standard unit around the world\n METER\n # Primarily used in the United States\n FOOT\n}\n# A humanoid creature from the Star Wars universe\ntype Human implements Character {\n # The ID of the human\n id: ID!\n # What this human calls themselves\n name: String!\n # Height in the preferred unit, default is meters\n height(unit: LengthUnit = METER): Float!\n # Mass in kilograms, or null if unknown\n mass: Float\n # This human's friends, or an empty list if they have none\n friends: [Character]\n # The friends of the human exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this human appears in\n appearsIn: [Episode!]!\n # A list of starships this person has piloted, or an empty list if none\n starships: [Starship]\n}\n# An autonomous mechanical character in the Star Wars universe\ntype Droid implements Character {\n # The ID of the droid\n id: ID!\n # What others call this droid\n name: String!\n # This droid's friends, or an empty list if they have none\n friends: [Character]\n # The friends of the droid exposed as a connection with edges\n friendsConnection(first: Int, after: ID): FriendsConnection!\n # The movies this droid appears in\n appearsIn: [Episode!]!\n # This droid's primary function\n primaryFunction: String\n}\n# A connection object for a character's friends\ntype FriendsConnection {\n # The total number of friends\n totalCount: Int!\n # The edges for each of the character's friends.\n edges: [FriendsEdge]\n # A list of the friends, as a convenience when edges are not needed.\n friends: [Character]\n # Information for paginating this connection\n pageInfo: PageInfo!\n}\n# An edge object for a character's friends\ntype FriendsEdge {\n # A cursor used for pagination\n cursor: ID!\n # The character represented by this friendship edge\n node: Character\n}\n# Information for paginating this connection\ntype PageInfo {\n startCursor: ID\n endCursor: ID\n hasNextPage: Boolean!\n}\n# Represents a review for a movie\ntype Review {\n # The number of stars this review gave, 1-5\n stars: Int!\n # Comment about the movie\n commentary: String\n # when the review was posted\n time: Time\n}\n# The input object sent when someone is creating a new review\ninput ReviewInput {\n # 0-5 stars\n stars: Int!\n # Comment about the movie, optional\n commentary: String\n # when the review was posted\n time: Time\n}\ntype Starship {\n # The ID of the starship\n id: ID!\n # The name of the starship\n name: String!\n # Length of the starship, along the longest axis\n length(unit: LengthUnit = METER): Float!\n # coordinates tracking this ship\n history: [[Int]]\n}\nunion SearchResult = Human | Droid | Starship\nscalar Time\n") -// nolint: deadcode, megacheck -func coerceString(v interface{}) (string, error) { - switch v := v.(type) { - case string: - return v, nil - case int: - return strconv.Itoa(v), nil - case float64: - return fmt.Sprintf("%f", v), nil - case bool: - if v { - return "true", nil - } else { - return "false", nil - } - case nil: - return "null", nil - default: - return "", fmt.Errorf("%T is not a string", v) - } -} - -// nolint: deadcode, megacheck -func coerceBool(v interface{}) (bool, error) { - switch v := v.(type) { - case string: - return "true" == strings.ToLower(v), nil - case int: - return v != 0, nil - case bool: - return v, nil - default: - return false, fmt.Errorf("%T is not a bool", v) - } -} - -// nolint: deadcode, megacheck -func coerceInt(v interface{}) (int, error) { - switch v := v.(type) { - case string: - return strconv.Atoi(v) - case int: - return v, nil - case float64: - return int(v), nil - default: - return 0, fmt.Errorf("%T is not an int", v) - } +func (ec *executionContext) introspectSchema() *introspection.Schema { + return introspection.WrapSchema(parsedSchema) } -// nolint: deadcode, megacheck -func coercefloat64(v interface{}) (float64, error) { - switch v := v.(type) { - case string: - return strconv.ParseFloat(v, 64) - case int: - return float64(v), nil - case float64: - return v, nil - default: - return 0, fmt.Errorf("%T is not an float", v) +func (ec *executionContext) introspectType(name string) *introspection.Type { + t := parsedSchema.Resolve(name) + if t == nil { + return nil } + return introspection.WrapType(t) } diff --git a/example/todo/generated.go b/example/todo/generated.go index 181bfe3926e..a416c9b8262 100644 --- a/example/todo/generated.go +++ b/example/todo/generated.go @@ -4,16 +4,11 @@ package todo import ( context "context" - fmt "fmt" io "io" - reflect "reflect" strconv "strconv" - strings "strings" sync "sync" - time "time" - mapstructure "github.com/mitchellh/mapstructure" - jsonw "github.com/vektah/gqlgen/jsonw" + graphql "github.com/vektah/gqlgen/graphql" errors "github.com/vektah/gqlgen/neelance/errors" introspection "github.com/vektah/gqlgen/neelance/introspection" query "github.com/vektah/gqlgen/neelance/query" @@ -53,7 +48,7 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ ctx: ctx, } - var data jsonw.Writer + var data graphql.Marshaler if op.Type == query.Query { data = c._myQuery(op.Selections, nil) } else if op.Type == query.Mutation { @@ -64,14 +59,14 @@ func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[ c.wg.Wait() - result := &jsonw.OrderedMap{} + result := &graphql.OrderedMap{} result.Add("data", data) if len(c.Errors) > 0 { - result.Add("errors", errors.ErrorWriter(c.Errors)) + result.Add("errors", graphql.MarshalErrors(c.Errors)) } - result.WriteJson(w) + result.MarshalGQL(w) return nil } } @@ -88,20 +83,20 @@ type executionContext struct { var myMutationImplementors = []string{"MyMutation"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _myMutation(sel []query.Selection, it *interface{}) jsonw.Writer { - fields := ec.collectFields(sel, myMutationImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _myMutation(sel []query.Selection, it *interface{}) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, myMutationImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("MyMutation") + out.Values[i] = graphql.MarshalString("MyMutation") case "createTodo": var arg0 string if tmp, ok := field.Args["text"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -118,7 +113,7 @@ func (ec *executionContext) _myMutation(sel []query.Selection, it *interface{}) case "updateTodo": var arg0 int if tmp, ok := field.Args["id"]; ok { - tmp2, err := coerceInt(tmp) + tmp2, err := graphql.UnmarshalInt(tmp) if err != nil { ec.Error(err) continue @@ -136,7 +131,7 @@ func (ec *executionContext) _myMutation(sel []query.Selection, it *interface{}) } if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec._todo(field.Selections, res) } @@ -151,20 +146,20 @@ func (ec *executionContext) _myMutation(sel []query.Selection, it *interface{}) var myQueryImplementors = []string{"MyQuery"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) jsonw.Writer { - fields := ec.collectFields(sel, myQueryImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, myQueryImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("MyQuery") + out.Values[i] = graphql.MarshalString("MyQuery") case "todo": var arg0 int if tmp, ok := field.Args["id"]; ok { - tmp2, err := coerceInt(tmp) + tmp2, err := graphql.UnmarshalInt(tmp) if err != nil { ec.Error(err) continue @@ -172,7 +167,7 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) jso arg0 = tmp2 } ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.MyQuery_todo(ec.ctx, arg0) if err != nil { @@ -181,14 +176,14 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) jso } if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec._todo(field.Selections, res) } }(i, field) case "lastTodo": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.MyQuery_lastTodo(ec.ctx) if err != nil { @@ -197,14 +192,14 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) jso } if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec._todo(field.Selections, res) } }(i, field) case "todos": ec.wg.Add(1) - go func(i int, field collectedField) { + go func(i int, field graphql.CollectedField) { defer ec.wg.Done() res, err := ec.resolvers.MyQuery_todos(ec.ctx) if err != nil { @@ -212,9 +207,9 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) jso return } - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler tmp1 = ec._todo(field.Selections, &res[idx1]) arr1 = append(arr1, tmp1) } @@ -224,14 +219,14 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) jso res := ec.introspectSchema() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Schema(field.Selections, res) } case "__type": var arg0 string if tmp, ok := field.Args["name"]; ok { - tmp2, err := coerceString(tmp) + tmp2, err := graphql.UnmarshalString(tmp) if err != nil { ec.Error(err) continue @@ -241,7 +236,7 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) jso res := ec.introspectType(arg0) if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -256,28 +251,28 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) jso var todoImplementors = []string{"Todo"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _todo(sel []query.Selection, it *Todo) jsonw.Writer { - fields := ec.collectFields(sel, todoImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) _todo(sel []query.Selection, it *Todo) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, todoImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("Todo") + out.Values[i] = graphql.MarshalString("Todo") case "id": res := it.ID() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalID(res) case "text": res := it.Text - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "done": res := it.Done - out.Values[i] = jsonw.Bool(res) + out.Values[i] = graphql.MarshalBoolean(res) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -289,47 +284,47 @@ func (ec *executionContext) _todo(sel []query.Selection, it *Todo) jsonw.Writer var __DirectiveImplementors = []string{"__Directive"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Directive(sel []query.Selection, it *introspection.Directive) jsonw.Writer { - fields := ec.collectFields(sel, __DirectiveImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Directive(sel []query.Selection, it *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __DirectiveImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Directive") + out.Values[i] = graphql.MarshalString("__Directive") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "locations": res := it.Locations() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer - tmp1 = jsonw.String(res[idx1]) + var tmp1 graphql.Marshaler + tmp1 = graphql.MarshalString(res[idx1]) arr1 = append(arr1, tmp1) } out.Values[i] = arr1 case "args": res := it.Args() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -347,39 +342,39 @@ func (ec *executionContext) ___Directive(sel []query.Selection, it *introspectio var __EnumValueImplementors = []string{"__EnumValue"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspection.EnumValue) jsonw.Writer { - fields := ec.collectFields(sel, __EnumValueImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __EnumValueImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__EnumValue") + out.Values[i] = graphql.MarshalString("__EnumValue") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "isDeprecated": res := it.IsDeprecated() - out.Values[i] = jsonw.Bool(res) + out.Values[i] = graphql.MarshalBoolean(res) case "deprecationReason": res := it.DeprecationReason() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -392,37 +387,37 @@ func (ec *executionContext) ___EnumValue(sel []query.Selection, it *introspectio var __FieldImplementors = []string{"__Field"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Field) jsonw.Writer { - fields := ec.collectFields(sel, __FieldImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __FieldImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Field") + out.Values[i] = graphql.MarshalString("__Field") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "args": res := it.Args() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -433,21 +428,21 @@ func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Fi res := it.Type() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } case "isDeprecated": res := it.IsDeprecated() - out.Values[i] = jsonw.Bool(res) + out.Values[i] = graphql.MarshalBoolean(res) case "deprecationReason": res := it.DeprecationReason() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -460,33 +455,33 @@ func (ec *executionContext) ___Field(sel []query.Selection, it *introspection.Fi var __InputValueImplementors = []string{"__InputValue"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspection.InputValue) jsonw.Writer { - fields := ec.collectFields(sel, __InputValueImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __InputValueImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__InputValue") + out.Values[i] = graphql.MarshalString("__InputValue") case "name": res := it.Name() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "type": res := it.Type() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -494,9 +489,9 @@ func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspecti res := it.DefaultValue() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -509,25 +504,25 @@ func (ec *executionContext) ___InputValue(sel []query.Selection, it *introspecti var __SchemaImplementors = []string{"__Schema"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.Schema) jsonw.Writer { - fields := ec.collectFields(sel, __SchemaImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __SchemaImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Schema") + out.Values[i] = graphql.MarshalString("__Schema") case "types": res := it.Types() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -538,7 +533,7 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.QueryType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -546,7 +541,7 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.MutationType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -554,19 +549,19 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S res := it.SubscriptionType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } case "directives": res := it.Directives() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Directive(field.Selections, res[idx1]) } @@ -584,40 +579,40 @@ func (ec *executionContext) ___Schema(sel []query.Selection, it *introspection.S var __TypeImplementors = []string{"__Type"} // nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Type) jsonw.Writer { - fields := ec.collectFields(sel, __TypeImplementors, map[string]bool{}) - out := jsonw.NewOrderedMap(len(fields)) +func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, __TypeImplementors, ec.variables) + out := graphql.NewOrderedMap(len(fields)) for i, field := range fields { out.Keys[i] = field.Alias - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null switch field.Name { case "__typename": - out.Values[i] = jsonw.String("__Type") + out.Values[i] = graphql.MarshalString("__Type") case "kind": res := it.Kind() - out.Values[i] = jsonw.String(res) + out.Values[i] = graphql.MarshalString(res) case "name": res := it.Name() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "description": res := it.Description() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { - out.Values[i] = jsonw.String(*res) + out.Values[i] = graphql.MarshalString(*res) } case "fields": var arg0 bool if tmp, ok := field.Args["includeDeprecated"]; ok { - tmp2, err := coerceBool(tmp) + tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { ec.Error(err) continue @@ -626,12 +621,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ } res := it.Fields(arg0) - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Field(field.Selections, res[idx1]) } @@ -641,12 +636,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "interfaces": res := it.Interfaces() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -656,12 +651,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "possibleTypes": res := it.PossibleTypes() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___Type(field.Selections, res[idx1]) } @@ -671,7 +666,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "enumValues": var arg0 bool if tmp, ok := field.Args["includeDeprecated"]; ok { - tmp2, err := coerceBool(tmp) + tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { ec.Error(err) continue @@ -680,12 +675,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ } res := it.EnumValues(arg0) - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___EnumValue(field.Selections, res[idx1]) } @@ -695,12 +690,12 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ case "inputFields": res := it.InputFields() - arr1 := jsonw.Array{} + arr1 := graphql.Array{} for idx1 := range res { - var tmp1 jsonw.Writer + var tmp1 graphql.Marshaler if res[idx1] == nil { - tmp1 = jsonw.Null + tmp1 = graphql.Null } else { tmp1 = ec.___InputValue(field.Selections, res[idx1]) } @@ -711,7 +706,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ res := it.OfType() if res == nil { - out.Values[i] = jsonw.Null + out.Values[i] = graphql.Null } else { out.Values[i] = ec.___Type(field.Selections, res) } @@ -736,182 +731,3 @@ func (ec *executionContext) introspectType(name string) *introspection.Type { } return introspection.WrapType(t) } - -func instanceOf(val string, satisfies []string) bool { - for _, s := range satisfies { - if val == s { - return true - } - } - return false -} - -func (ec *executionContext) collectFields(selSet []query.Selection, satisfies []string, visited map[string]bool) []collectedField { - var groupedFields []collectedField - - for _, sel := range selSet { - switch sel := sel.(type) { - case *query.Field: - f := getOrCreateField(&groupedFields, sel.Name.Name, func() collectedField { - f := collectedField{ - Alias: sel.Alias.Name, - Name: sel.Name.Name, - } - if len(sel.Arguments) > 0 { - f.Args = map[string]interface{}{} - for _, arg := range sel.Arguments { - f.Args[arg.Name.Name] = arg.Value.Value(ec.variables) - } - } - return f - }) - - f.Selections = append(f.Selections, sel.Selections...) - case *query.InlineFragment: - if !instanceOf(sel.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range ec.collectFields(sel.Selections, satisfies, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) - } - - case *query.FragmentSpread: - fragmentName := sel.Name.Name - if _, seen := visited[fragmentName]; seen { - continue - } - visited[fragmentName] = true - - fragment := ec.doc.Fragments.Get(fragmentName) - if fragment == nil { - ec.Errorf("missing fragment %s", fragmentName) - continue - } - - if !instanceOf(fragment.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range ec.collectFields(fragment.Selections, satisfies, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) - } - - default: - panic(fmt.Errorf("unsupported %T", sel)) - } - } - - return groupedFields -} - -type collectedField struct { - Alias string - Name string - Args map[string]interface{} - Selections []query.Selection -} - -func decodeHook(sourceType reflect.Type, destType reflect.Type, value interface{}) (interface{}, error) { - if destType.PkgPath() == "time" && destType.Name() == "Time" { - if dateStr, ok := value.(string); ok { - return time.Parse(time.RFC3339, dateStr) - } - return nil, errors.Errorf("time should be an RFC3339 formatted string") - } - return value, nil -} - -// nolint: deadcode, megacheck -func unpackComplexArg(result interface{}, data interface{}) error { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - TagName: "graphql", - ErrorUnused: true, - Result: result, - DecodeHook: decodeHook, - }) - if err != nil { - panic(err) - } - - return decoder.Decode(data) -} - -func getOrCreateField(c *[]collectedField, name string, creator func() collectedField) *collectedField { - for i, cf := range *c { - if cf.Alias == name { - return &(*c)[i] - } - } - - f := creator() - - *c = append(*c, f) - return &(*c)[len(*c)-1] -} - -// nolint: deadcode, megacheck -func coerceString(v interface{}) (string, error) { - switch v := v.(type) { - case string: - return v, nil - case int: - return strconv.Itoa(v), nil - case float64: - return fmt.Sprintf("%f", v), nil - case bool: - if v { - return "true", nil - } else { - return "false", nil - } - case nil: - return "null", nil - default: - return "", fmt.Errorf("%T is not a string", v) - } -} - -// nolint: deadcode, megacheck -func coerceBool(v interface{}) (bool, error) { - switch v := v.(type) { - case string: - return "true" == strings.ToLower(v), nil - case int: - return v != 0, nil - case bool: - return v, nil - default: - return false, fmt.Errorf("%T is not a bool", v) - } -} - -// nolint: deadcode, megacheck -func coerceInt(v interface{}) (int, error) { - switch v := v.(type) { - case string: - return strconv.Atoi(v) - case int: - return v, nil - case float64: - return int(v), nil - default: - return 0, fmt.Errorf("%T is not an int", v) - } -} - -// nolint: deadcode, megacheck -func coercefloat64(v interface{}) (float64, error) { - switch v := v.(type) { - case string: - return strconv.ParseFloat(v, 64) - case int: - return float64(v), nil - case float64: - return v, nil - default: - return 0, fmt.Errorf("%T is not an float", v) - } -} diff --git a/graphql/jsonw.go b/graphql/jsonw.go index 04ffaae31d6..ef9e69c7ab1 100644 --- a/graphql/jsonw.go +++ b/graphql/jsonw.go @@ -23,6 +23,10 @@ type Marshaler interface { MarshalGQL(w io.Writer) } +type Unmarshaler interface { + UnmarshalGQL(v interface{}) error +} + type OrderedMap struct { Keys []string Values []Marshaler From 5d86eeb60b9a82015e28deaf085349498b5ebb97 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Fri, 16 Feb 2018 21:18:55 +1100 Subject: [PATCH 8/8] fix jsonw test --- graphql/jsonw_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graphql/jsonw_test.go b/graphql/jsonw_test.go index 4e4110e2d37..d11973927e6 100644 --- a/graphql/jsonw_test.go +++ b/graphql/jsonw_test.go @@ -9,15 +9,15 @@ import ( func TestJsonWriter(t *testing.T) { obj := &OrderedMap{} - obj.Add("test", Int(10)) + obj.Add("test", MarshalInt(10)) obj.Add("array", &Array{ - Int(1), - String("2"), - Boolean(true), + MarshalInt(1), + MarshalString("2"), + MarshalBoolean(true), False, Null, - Float(1.3), + MarshalFloat(1.3), True, })