From 2f358e7daacfcfd861372d2b891e19ac909c47e2 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sun, 4 Feb 2018 22:41:37 +1100 Subject: [PATCH] graphiql autocomplete working --- cmd/ggraphqlc/dumper.go | 25 +++- example/todo/gen/exec.go | 70 ++++++--- example/todo/gen/generated.go | 258 ++++++++++++++++++++++++++++------ 3 files changed, 286 insertions(+), 67 deletions(-) diff --git a/cmd/ggraphqlc/dumper.go b/cmd/ggraphqlc/dumper.go index e1e5eeb19b..fc8b5865c9 100644 --- a/cmd/ggraphqlc/dumper.go +++ b/cmd/ggraphqlc/dumper.go @@ -114,6 +114,11 @@ func (w *writer) writeObjectResolver(object object) { w.line("type %sType struct {}", lcFirst(object.Type.GraphQLName)) w.lf() + w.begin("func (%sType) accepts(name string) bool {", lcFirst(object.Type.GraphQLName)) + w.line("return true") + w.end("}") + w.lf() + w.begin("func (%sType) resolve(ec *executionContext, %s interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable {", lcFirst(object.Type.GraphQLName), objectName) if object.Type.Name != "interface{}" { w.line("it := object.(*%s)", object.Type.Local()) @@ -164,13 +169,13 @@ func (w *writer) writeMethodResolver(object object, field Field) { w.line("}") } - w.writeJsonType("json", field.Type, field.Type.Modifiers, "res") + w.writeJsonType("json", field.Type, "res") w.line("return json") } func (w *writer) writeVarResolver(field Field) { - w.writeJsonType("res", field.Type, field.Type.Modifiers, field.VarName) + w.writeJsonType("res", field.Type, field.VarName) w.line("return res") } @@ -189,12 +194,20 @@ func (w *writer) writeFuncArgs(field Field) { } } -func (w *writer) writeJsonType(result string, t Type, remainingMods []string, val string) { - isPtr := false +func (w *writer) writeJsonType(result string, t Type, val string) { + w.doWriteJsonType(result, t, val, t.Modifiers, false) +} + +func (w *writer) doWriteJsonType(result string, t Type, val string, remainingMods []string, isPtr bool) { for i := 0; i < len(remainingMods); i++ { switch remainingMods[i] { case modPtr: - isPtr = true + w.line("var %s jsonw.Encodable = jsonw.Null", result) + w.begin("if %s != nil {", val) + w.doWriteJsonType(result+"1", t, val, remainingMods[i+1:], true) + w.line("%s = %s", result, result+"1") + w.end("}") + return case modList: if isPtr { val = "*" + val @@ -202,7 +215,7 @@ func (w *writer) writeJsonType(result string, t Type, remainingMods []string, va w.line("%s := jsonw.Array{}", result) w.begin("for _, val := range %s {", val) - w.writeJsonType(result+"1", t, remainingMods[i+1:], "val") + w.doWriteJsonType(result+"1", t, "val", remainingMods[i+1:], false) w.line("%s = append(%s, %s)", result, result, result+"1") w.end("}") return diff --git a/example/todo/gen/exec.go b/example/todo/gen/exec.go index 8e51ffa56e..e46376ae74 100644 --- a/example/todo/gen/exec.go +++ b/example/todo/gen/exec.go @@ -32,6 +32,7 @@ func NewResolver(resolvers Resolvers) relay.Resolver { c := executionContext{ resolvers: resolvers, variables: variables, + doc: doc, } var rootType resolvedType @@ -59,17 +60,19 @@ type executionContext struct { errors.Builder resolvers Resolvers variables map[string]interface{} + doc *query.Document } type resolvedType interface { resolve(ec *executionContext, it interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable + accepts(name string) bool } func (c *executionContext) executeSelectionSet(sel []query.Selection, resolver resolvedType, objectValue interface{}) jsonw.Encodable { if objectValue == nil { return jsonw.Null } - groupedFieldSet := c.collectFields(sel, map[string]interface{}{}) + groupedFieldSet := c.collectFields(sel, resolver, map[string]bool{}) resultMap := jsonw.Map{} for _, collectedField := range groupedFieldSet { @@ -91,15 +94,59 @@ func (c *executionContext) introspectType(name string) *introspection.Type { return introspection.WrapType(t) } -func (c *executionContext) collectFields(selSet []query.Selection, visited map[string]interface{}) []collectedField { +func (c *executionContext) collectFields(selSet []query.Selection, resolver resolvedType, visited map[string]bool) []collectedField { var groupedFields []collectedField - // TODO: Basically everything. for _, sel := range selSet { switch sel := sel.(type) { case *query.Field: - f := findField(&groupedFields, sel, c.variables) + 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(c.variables) + } + } + return f + }) + f.Selections = append(f.Selections, sel.Selections...) + case *query.InlineFragment: + if !resolver.accepts(sel.On.Ident.Name) { + continue + } + + for _, childField := range c.collectFields(sel.Selections, resolver, 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 := c.doc.Fragments.Get(fragmentName) + if fragment == nil { + c.Errorf("missing fragment %s", fragmentName) + continue + } + + if !resolver.accepts(fragment.On.Name) { + continue + } + + for _, childField := range c.collectFields(fragment.Selections, resolver, visited) { + f := getOrCreateField(&groupedFields, childField.Name, func() collectedField { return childField }) + f.Selections = append(f.Selections, childField.Selections...) + } + default: panic(fmt.Errorf("unsupported %T", sel)) } @@ -115,23 +162,14 @@ type collectedField struct { Selections []query.Selection } -func findField(c *[]collectedField, field *query.Field, vars map[string]interface{}) *collectedField { +func getOrCreateField(c *[]collectedField, name string, creator func() collectedField) *collectedField { for i, cf := range *c { - if cf.Alias == field.Alias.Name { + if cf.Alias == name { return &(*c)[i] } } - f := collectedField{ - Alias: field.Alias.Name, - Name: field.Name.Name, - } - if len(field.Arguments) > 0 { - f.Args = map[string]interface{}{} - for _, arg := range field.Arguments { - f.Args[arg.Name.Name] = arg.Value.Value(vars) - } - } + f := creator() *c = append(*c, f) return &(*c)[len(*c)-1] diff --git a/example/todo/gen/generated.go b/example/todo/gen/generated.go index 639b0573d6..88cdedaa8e 100644 --- a/example/todo/gen/generated.go +++ b/example/todo/gen/generated.go @@ -1,11 +1,11 @@ package gen import ( - "github.com/vektah/graphql-go/jsonw" - "github.com/vektah/graphql-go/query" "github.com/vektah/graphql-go/schema" "github.com/vektah/graphql-go/introspection" "github.com/vektah/graphql-go/example/todo" + "github.com/vektah/graphql-go/jsonw" + "github.com/vektah/graphql-go/query" ) type Resolvers interface { @@ -18,6 +18,10 @@ type Resolvers interface { type mutationType struct {} +func (mutationType) accepts(name string) bool { + return true +} + func (mutationType) resolve(ec *executionContext, it interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { if it == nil { return jsonw.Null @@ -52,6 +56,10 @@ func (mutationType) resolve(ec *executionContext, it interface{}, field string, type queryType struct {} +func (queryType) accepts(name string) bool { + return true +} + func (queryType) resolve(ec *executionContext, it interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { if it == nil { return jsonw.Null @@ -65,7 +73,11 @@ func (queryType) resolve(ec *executionContext, it interface{}, field string, arg ec.Error(err) return jsonw.Null } - json := ec.executeSelectionSet(sels, todoType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, todoType{}, res) + json = json1 + } return json case "lastTodo": @@ -74,7 +86,11 @@ func (queryType) resolve(ec *executionContext, it interface{}, field string, arg ec.Error(err) return jsonw.Null } - json := ec.executeSelectionSet(sels, todoType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, todoType{}, res) + json = json1 + } return json case "todos": @@ -92,14 +108,22 @@ func (queryType) resolve(ec *executionContext, it interface{}, field string, arg case "__schema": res := ec.introspectSchema() - json := ec.executeSelectionSet(sels, __SchemaType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, __SchemaType{}, res) + json = json1 + } return json case "__type": res := ec.introspectType( arguments["name"].(string), ) - json := ec.executeSelectionSet(sels, __TypeType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, __TypeType{}, res) + json = json1 + } return json } @@ -108,6 +132,10 @@ func (queryType) resolve(ec *executionContext, it interface{}, field string, arg type todoType struct {} +func (todoType) accepts(name string) bool { + return true +} + func (todoType) resolve(ec *executionContext, object interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { it := object.(*todo.Todo) if it == nil { @@ -132,6 +160,10 @@ func (todoType) resolve(ec *executionContext, object interface{}, field string, type __DirectiveType struct {} +func (__DirectiveType) accepts(name string) bool { + return true +} + func (__DirectiveType) resolve(ec *executionContext, object interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { it := object.(*introspection.Directive) if it == nil { @@ -145,7 +177,11 @@ func (__DirectiveType) resolve(ec *executionContext, object interface{}, field s case "description": res := it.Description() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json case "locations": @@ -161,7 +197,11 @@ func (__DirectiveType) resolve(ec *executionContext, object interface{}, field s res := it.Args() json := jsonw.Array{} for _, val := range res { - json1 := ec.executeSelectionSet(sels, __InputValueType{}, val) + var json1 jsonw.Encodable = jsonw.Null + if val != nil { + json11 := ec.executeSelectionSet(sels, __InputValueType{}, val) + json1 = json11 + } json = append(json, json1) } return json @@ -172,6 +212,10 @@ func (__DirectiveType) resolve(ec *executionContext, object interface{}, field s type __EnumValueType struct {} +func (__EnumValueType) accepts(name string) bool { + return true +} + func (__EnumValueType) resolve(ec *executionContext, object interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { it := object.(*introspection.EnumValue) if it == nil { @@ -185,7 +229,11 @@ func (__EnumValueType) resolve(ec *executionContext, object interface{}, field s case "description": res := it.Description() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json case "isDeprecated": @@ -195,7 +243,11 @@ func (__EnumValueType) resolve(ec *executionContext, object interface{}, field s case "deprecationReason": res := it.DeprecationReason() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json } @@ -204,6 +256,10 @@ func (__EnumValueType) resolve(ec *executionContext, object interface{}, field s type __FieldType struct {} +func (__FieldType) accepts(name string) bool { + return true +} + func (__FieldType) resolve(ec *executionContext, object interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { it := object.(*introspection.Field) if it == nil { @@ -217,21 +273,33 @@ func (__FieldType) resolve(ec *executionContext, object interface{}, field strin case "description": res := it.Description() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json case "args": res := it.Args() json := jsonw.Array{} for _, val := range res { - json1 := ec.executeSelectionSet(sels, __InputValueType{}, val) + var json1 jsonw.Encodable = jsonw.Null + if val != nil { + json11 := ec.executeSelectionSet(sels, __InputValueType{}, val) + json1 = json11 + } json = append(json, json1) } return json case "type": res := it.Type() - json := ec.executeSelectionSet(sels, __TypeType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, __TypeType{}, res) + json = json1 + } return json case "isDeprecated": @@ -241,7 +309,11 @@ func (__FieldType) resolve(ec *executionContext, object interface{}, field strin case "deprecationReason": res := it.DeprecationReason() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json } @@ -250,6 +322,10 @@ func (__FieldType) resolve(ec *executionContext, object interface{}, field strin type __InputValueType struct {} +func (__InputValueType) accepts(name string) bool { + return true +} + func (__InputValueType) resolve(ec *executionContext, object interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { it := object.(*introspection.InputValue) if it == nil { @@ -263,17 +339,29 @@ func (__InputValueType) resolve(ec *executionContext, object interface{}, field case "description": res := it.Description() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json case "type": res := it.Type() - json := ec.executeSelectionSet(sels, __TypeType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, __TypeType{}, res) + json = json1 + } return json case "defaultValue": res := it.DefaultValue() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json } @@ -282,6 +370,10 @@ func (__InputValueType) resolve(ec *executionContext, object interface{}, field type __SchemaType struct {} +func (__SchemaType) accepts(name string) bool { + return true +} + func (__SchemaType) resolve(ec *executionContext, object interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { it := object.(*introspection.Schema) if it == nil { @@ -292,31 +384,51 @@ func (__SchemaType) resolve(ec *executionContext, object interface{}, field stri res := it.Types() json := jsonw.Array{} for _, val := range res { - json1 := ec.executeSelectionSet(sels, __TypeType{}, val) + var json1 jsonw.Encodable = jsonw.Null + if val != nil { + json11 := ec.executeSelectionSet(sels, __TypeType{}, val) + json1 = json11 + } json = append(json, json1) } return json case "queryType": res := it.QueryType() - json := ec.executeSelectionSet(sels, __TypeType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, __TypeType{}, res) + json = json1 + } return json case "mutationType": res := it.MutationType() - json := ec.executeSelectionSet(sels, __TypeType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, __TypeType{}, res) + json = json1 + } return json case "subscriptionType": res := it.SubscriptionType() - json := ec.executeSelectionSet(sels, __TypeType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, __TypeType{}, res) + json = json1 + } return json case "directives": res := it.Directives() json := jsonw.Array{} for _, val := range res { - json1 := ec.executeSelectionSet(sels, __DirectiveType{}, val) + var json1 jsonw.Encodable = jsonw.Null + if val != nil { + json11 := ec.executeSelectionSet(sels, __DirectiveType{}, val) + json1 = json11 + } json = append(json, json1) } return json @@ -327,6 +439,10 @@ func (__SchemaType) resolve(ec *executionContext, object interface{}, field stri type __TypeType struct {} +func (__TypeType) accepts(name string) bool { + return true +} + func (__TypeType) resolve(ec *executionContext, object interface{}, field string, arguments map[string]interface{}, sels []query.Selection) jsonw.Encodable { it := object.(*introspection.Type) if it == nil { @@ -340,40 +456,72 @@ func (__TypeType) resolve(ec *executionContext, object interface{}, field string case "name": res := it.Name() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json case "description": res := it.Description() - json := jsonw.String(*res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.String(*res) + json = json1 + } return json case "fields": res := it.Fields( arguments["includeDeprecated"].(bool), ) - json := jsonw.Array{} - for _, val := range *res { - json1 := ec.executeSelectionSet(sels, __FieldType{}, val) - json = append(json, json1) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.Array{} + for _, val := range *res { + var json11 jsonw.Encodable = jsonw.Null + if val != nil { + json111 := ec.executeSelectionSet(sels, __FieldType{}, val) + json11 = json111 + } + json1 = append(json1, json11) + } + json = json1 } return json case "interfaces": res := it.Interfaces() - json := jsonw.Array{} - for _, val := range *res { - json1 := ec.executeSelectionSet(sels, __TypeType{}, val) - json = append(json, json1) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.Array{} + for _, val := range *res { + var json11 jsonw.Encodable = jsonw.Null + if val != nil { + json111 := ec.executeSelectionSet(sels, __TypeType{}, val) + json11 = json111 + } + json1 = append(json1, json11) + } + json = json1 } return json case "possibleTypes": res := it.PossibleTypes() - json := jsonw.Array{} - for _, val := range *res { - json1 := ec.executeSelectionSet(sels, __TypeType{}, val) - json = append(json, json1) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.Array{} + for _, val := range *res { + var json11 jsonw.Encodable = jsonw.Null + if val != nil { + json111 := ec.executeSelectionSet(sels, __TypeType{}, val) + json11 = json111 + } + json1 = append(json1, json11) + } + json = json1 } return json @@ -381,25 +529,45 @@ func (__TypeType) resolve(ec *executionContext, object interface{}, field string res := it.EnumValues( arguments["includeDeprecated"].(bool), ) - json := jsonw.Array{} - for _, val := range *res { - json1 := ec.executeSelectionSet(sels, __EnumValueType{}, val) - json = append(json, json1) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.Array{} + for _, val := range *res { + var json11 jsonw.Encodable = jsonw.Null + if val != nil { + json111 := ec.executeSelectionSet(sels, __EnumValueType{}, val) + json11 = json111 + } + json1 = append(json1, json11) + } + json = json1 } return json case "inputFields": res := it.InputFields() - json := jsonw.Array{} - for _, val := range *res { - json1 := ec.executeSelectionSet(sels, __InputValueType{}, val) - json = append(json, json1) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := jsonw.Array{} + for _, val := range *res { + var json11 jsonw.Encodable = jsonw.Null + if val != nil { + json111 := ec.executeSelectionSet(sels, __InputValueType{}, val) + json11 = json111 + } + json1 = append(json1, json11) + } + json = json1 } return json case "ofType": res := it.OfType() - json := ec.executeSelectionSet(sels, __TypeType{}, res) + var json jsonw.Encodable = jsonw.Null + if res != nil { + json1 := ec.executeSelectionSet(sels, __TypeType{}, res) + json = json1 + } return json }