diff --git a/codegen/config.go b/codegen/config.go index 75ab9655d7..e1e5ceb348 100644 --- a/codegen/config.go +++ b/codegen/config.go @@ -29,6 +29,7 @@ type TypeMapEntry struct { } type TypeMapField struct { + Resolver bool `yaml:"resolver"` } func (c *PackageConfig) normalize() error { diff --git a/codegen/object.go b/codegen/object.go index 266f347536..1c03c0bae3 100644 --- a/codegen/object.go +++ b/codegen/object.go @@ -22,13 +22,14 @@ type Object struct { type Field struct { *Type - GQLName string // The name of the field in graphql - GoMethodName string // The name of the method in go, if any - GoVarName string // The name of the var in go, if any - Args []FieldArgument // A list of arguments to be passed to this field - NoErr bool // If this is bound to a go method, does that method have an error as the second argument - Object *Object // A link back to the parent object - Default interface{} // The default value + GQLName string // The name of the field in graphql + GoMethodName string // The name of the method in go, if any + GoVarName string // The name of the var in go, if any + Args []FieldArgument // A list of arguments to be passed to this field + ForceResolver bool // Should be emit Resolver method + NoErr bool // If this is bound to a go method, does that method have an error as the second argument + Object *Object // A link back to the parent object + Default interface{} // The default value } type FieldArgument struct { @@ -60,7 +61,7 @@ func (o *Object) HasResolvers() bool { } func (f *Field) IsResolver() bool { - return f.GoMethodName == "" && f.GoVarName == "" + return f.ForceResolver || f.GoMethodName == "" && f.GoVarName == "" } func (f *Field) IsConcurrent() bool { diff --git a/codegen/object_build.go b/codegen/object_build.go index 0809f7d26c..0a4005872b 100644 --- a/codegen/object_build.go +++ b/codegen/object_build.go @@ -81,12 +81,21 @@ func sanitizeGoName(name string) string { func (cfg *Config) buildObject(types NamedTypes, typ *schema.Object) (*Object, error) { obj := &Object{NamedType: types[typ.TypeName()]} + typeEntry, entryExists := cfg.Models[typ.TypeName()] for _, i := range typ.Interfaces { obj.Satisfies = append(obj.Satisfies, i.Name) } for _, field := range typ.Fields { + + var forceResolver bool + if entryExists { + if typeField, ok := typeEntry.Fields[field.Name]; ok { + forceResolver = typeField.Resolver + } + } + var args []FieldArgument for _, arg := range field.Args { newArg := FieldArgument{ @@ -108,10 +117,11 @@ func (cfg *Config) buildObject(types NamedTypes, typ *schema.Object) (*Object, e } obj.Fields = append(obj.Fields, Field{ - GQLName: field.Name, - Type: types.getType(field.Type), - Args: args, - Object: obj, + GQLName: field.Name, + Type: types.getType(field.Type), + Args: args, + Object: obj, + ForceResolver: forceResolver, }) } diff --git a/test/.gqlgen.yml b/test/.gqlgen.yml index 421c38713a..45481ebd41 100644 --- a/test/.gqlgen.yml +++ b/test/.gqlgen.yml @@ -14,3 +14,6 @@ models: model: github.com/vektah/gqlgen/test.Viewer User: model: remote_api.User + fields: + likes: + resolver: true diff --git a/test/generated.go b/test/generated.go index 57cbba167f..2dde771bb5 100644 --- a/test/generated.go +++ b/test/generated.go @@ -32,11 +32,14 @@ type Resolvers interface { Query_date(ctx context.Context, filter models.DateFilter) (bool, error) Query_viewer(ctx context.Context) (*Viewer, error) Query_jsonEncoding(ctx context.Context) (string, error) + + User_likes(ctx context.Context, obj *remote_api.User) ([]string, error) } type ResolverRoot interface { Element() ElementResolver Query() QueryResolver + User() UserResolver } type ElementResolver interface { Child(ctx context.Context, obj *Element) (Element, error) @@ -48,6 +51,9 @@ type QueryResolver interface { Viewer(ctx context.Context) (*Viewer, error) JsonEncoding(ctx context.Context) (string, error) } +type UserResolver interface { + Likes(ctx context.Context, obj *remote_api.User) ([]string, error) +} type shortMapper struct { r ResolverRoot @@ -77,6 +83,10 @@ func (s shortMapper) Query_jsonEncoding(ctx context.Context) (string, error) { return s.r.Query().JsonEncoding(ctx) } +func (s shortMapper) User_likes(ctx context.Context, obj *remote_api.User) ([]string, error) { + return s.r.User().Likes(ctx, obj) +} + type executableSchema struct { resolvers Resolvers } @@ -434,6 +444,8 @@ func (ec *executionContext) _User(ctx context.Context, sel []query.Selection, ob out.Values[i] = graphql.MarshalString("User") case "name": out.Values[i] = ec._User_name(ctx, field, obj) + case "likes": + out.Values[i] = ec._User_likes(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -453,6 +465,35 @@ func (ec *executionContext) _User_name(ctx context.Context, field graphql.Collec return graphql.MarshalString(res) } +func (ec *executionContext) _User_likes(ctx context.Context, field graphql.CollectedField, obj *remote_api.User) graphql.Marshaler { + ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ + Object: "User", + Args: nil, + Field: field, + }) + return graphql.Defer(func() (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + userErr := ec.Recover(ctx, r) + ec.Error(ctx, userErr) + ret = graphql.Null + } + }() + + res := obj.Likes + arr1 := graphql.Array{} + for idx1 := range res { + arr1 = append(arr1, func() graphql.Marshaler { + rctx := graphql.GetResolverContext(ctx) + rctx.PushIndex(idx1) + defer rctx.Pop() + return graphql.MarshalString(res[idx1]) + }()) + } + return arr1 + }) +} + var viewerImplementors = []string{"Viewer"} // nolint: gocyclo, errcheck, gas, goconst @@ -1297,7 +1338,9 @@ input DateFilter { type User { name: String + likes: [String] } + type Viewer { user: User } diff --git a/test/resolvers_test.go b/test/resolvers_test.go index 805e68e76b..3a468fb617 100644 --- a/test/resolvers_test.go +++ b/test/resolvers_test.go @@ -106,7 +106,7 @@ func (r *testResolvers) Query_jsonEncoding(ctx context.Context) (string, error) func (r *testResolvers) Query_viewer(ctx context.Context) (*Viewer, error) { return &Viewer{ - User: &remote_api.User{"Bob"}, + User: &remote_api.User{Name: "Bob"}, }, nil } @@ -129,6 +129,10 @@ func (r *testResolvers) Element_error(ctx context.Context, obj *Element) (bool, return false, r.err } +func (r *testResolvers) User_likes(ctx context.Context, obj *remote_api.User) ([]string, error) { + return obj.Likes, nil +} + type specialErr struct{} func (*specialErr) Error() string { diff --git a/test/schema.graphql b/test/schema.graphql index 6a1918d8d5..1b8245236d 100644 --- a/test/schema.graphql +++ b/test/schema.graphql @@ -20,7 +20,9 @@ input DateFilter { type User { name: String + likes: [String] } + type Viewer { user: User } diff --git a/test/vendor/remote_api/user.go b/test/vendor/remote_api/user.go index d90cbde895..e6262a29ba 100644 --- a/test/vendor/remote_api/user.go +++ b/test/vendor/remote_api/user.go @@ -1,5 +1,6 @@ package remote_api type User struct { - Name string + Name string + Likes []string }