diff --git a/codegen/config/resolver.go b/codegen/config/resolver.go index 9859d6e3a00..cd03f188729 100644 --- a/codegen/config/resolver.go +++ b/codegen/config/resolver.go @@ -10,11 +10,12 @@ import ( ) type ResolverConfig struct { - Filename string `yaml:"filename,omitempty"` - Package string `yaml:"package,omitempty"` - Type string `yaml:"type,omitempty"` - Layout ResolverLayout `yaml:"layout,omitempty"` - DirName string `yaml:"dir"` + Filename string `yaml:"filename,omitempty"` + FilenameTemplate string `yaml:"filename_template,omitempty"` + Package string `yaml:"package,omitempty"` + Type string `yaml:"type,omitempty"` + Layout ResolverLayout `yaml:"layout,omitempty"` + DirName string `yaml:"dir"` } type ResolverLayout string diff --git a/docs/content/config.md b/docs/content/config.md index b84013cfb9c..f4e16b3a08d 100644 --- a/docs/content/config.md +++ b/docs/content/config.md @@ -35,6 +35,7 @@ resolver: layout: follow-schema dir: graph package: graph + filename_template: "{name}.resolvers.go" # Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models # struct_tag: json diff --git a/plugin/resolvergen/resolver.go b/plugin/resolvergen/resolver.go index f4aefae20cf..6739a325dc8 100644 --- a/plugin/resolvergen/resolver.go +++ b/plugin/resolvergen/resolver.go @@ -88,7 +88,7 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error { for _, o := range data.Objects { if o.HasResolvers() { - fn := gqlToResolverName(data.Config.Resolver.Dir(), o.Position.Src.Name) + fn := gqlToResolverName(data.Config.Resolver.Dir(), o.Position.Src.Name, data.Config.Resolver.FilenameTemplate) if files[fn] == nil { files[fn] = &File{} } @@ -109,7 +109,7 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error { } resolver := Resolver{o, f, implementation} - fn := gqlToResolverName(data.Config.Resolver.Dir(), f.Position.Src.Name) + fn := gqlToResolverName(data.Config.Resolver.Dir(), f.Position.Src.Name, data.Config.Resolver.FilenameTemplate) if files[fn] == nil { files[fn] = &File{} } @@ -196,9 +196,12 @@ type Resolver struct { Implementation string } -func gqlToResolverName(base string, gqlname string) string { +func gqlToResolverName(base string, gqlname, filenameTmpl string) string { gqlname = filepath.Base(gqlname) ext := filepath.Ext(gqlname) - - return filepath.Join(base, strings.TrimSuffix(gqlname, ext)+".resolvers.go") + if filenameTmpl == "" { + filenameTmpl = "{name}.resolvers.go" + } + filename := strings.ReplaceAll(filenameTmpl, "{name}", strings.TrimSuffix(gqlname, ext)) + return filepath.Join(base, filename) } diff --git a/plugin/resolvergen/resolver_test.go b/plugin/resolvergen/resolver_test.go index cc399e53ec6..3c05a62db6c 100644 --- a/plugin/resolvergen/resolver_test.go +++ b/plugin/resolvergen/resolver_test.go @@ -31,9 +31,33 @@ func TestLayoutSingleFile(t *testing.T) { } func TestLayoutFollowSchema(t *testing.T) { - _ = syscall.Unlink("testdata/followschema/out/resolver.go") + testFollowSchemaPersistence(t, "testdata/followschema") - cfg, err := config.LoadConfig("testdata/followschema/gqlgen.yml") + b, err := ioutil.ReadFile("testdata/followschema/out/schema.resolvers.go") + require.NoError(t, err) + source := string(b) + + require.Contains(t, source, "// CustomerResolverType.Resolver implementation") + require.Contains(t, source, "// CustomerResolverType.Name implementation") + require.Contains(t, source, "// AUserHelperFunction implementation") +} + +func TestLayoutFollowSchemaWithCustomFilename(t *testing.T) { + testFollowSchemaPersistence(t, "testdata/filetemplate") + + b, err := ioutil.ReadFile("testdata/filetemplate/out/schema.custom.go") + require.NoError(t, err) + source := string(b) + + require.Contains(t, source, "// CustomerResolverType.Resolver implementation") + require.Contains(t, source, "// CustomerResolverType.Name implementation") + require.Contains(t, source, "// AUserHelperFunction implementation") +} + +func testFollowSchemaPersistence(t *testing.T, dir string) { + _ = syscall.Unlink(dir + "/out/resolver.go") + + cfg, err := config.LoadConfig(dir + "/gqlgen.yml") require.NoError(t, err) p := Plugin{} @@ -45,15 +69,7 @@ func TestLayoutFollowSchema(t *testing.T) { } require.NoError(t, p.GenerateCode(data)) - assertNoErrors(t, "github.com/99designs/gqlgen/plugin/resolvergen/testdata/followschema/out") - - b, err := ioutil.ReadFile("testdata/followschema/out/schema.resolvers.go") - require.NoError(t, err) - source := string(b) - - require.Contains(t, source, "// CustomerResolverType.Resolver implementation") - require.Contains(t, source, "// CustomerResolverType.Name implementation") - require.Contains(t, source, "// AUserHelperFunction implementation") + assertNoErrors(t, "github.com/99designs/gqlgen/plugin/resolvergen/"+dir+"/out") } func assertNoErrors(t *testing.T, pkg string) { diff --git a/plugin/resolvergen/testdata/filetemplate/gqlgen.yml b/plugin/resolvergen/testdata/filetemplate/gqlgen.yml new file mode 100644 index 00000000000..49e6ff33264 --- /dev/null +++ b/plugin/resolvergen/testdata/filetemplate/gqlgen.yml @@ -0,0 +1,16 @@ +schema: + - "testdata/schema.graphql" + +exec: + filename: testdata/singlefile/out/ignored.go +model: + filename: testdata/singlefile/out/generated.go +resolver: + type: CustomResolverType + layout: follow-schema + dir: testdata/filetemplate/out + filename_template: "{name}.custom.go" + +models: + Resolver: + model: github.com/99designs/gqlgen/plugin/resolvergen/testdata/singlefile/out.Resolver diff --git a/plugin/resolvergen/testdata/filetemplate/out/model.go b/plugin/resolvergen/testdata/filetemplate/out/model.go new file mode 100644 index 00000000000..11d6d254209 --- /dev/null +++ b/plugin/resolvergen/testdata/filetemplate/out/model.go @@ -0,0 +1,14 @@ +package customresolver + +import "context" + +type Resolver struct { +} + +type QueryResolver interface { + Resolver(ctx context.Context) (*Resolver, error) +} + +type ResolverResolver interface { + Name(ctx context.Context, obj *Resolver) (string, error) +} diff --git a/plugin/resolvergen/testdata/filetemplate/out/resolver.go b/plugin/resolvergen/testdata/filetemplate/out/resolver.go new file mode 100644 index 00000000000..fbe00ecff89 --- /dev/null +++ b/plugin/resolvergen/testdata/filetemplate/out/resolver.go @@ -0,0 +1,7 @@ +package customresolver + +// This file will not be regenerated automatically. +// +// It serves as dependency injection for your app, add any dependencies you require here. + +type CustomResolverType struct{} diff --git a/plugin/resolvergen/testdata/filetemplate/out/schema.custom.go b/plugin/resolvergen/testdata/filetemplate/out/schema.custom.go new file mode 100644 index 00000000000..5d3a464faa6 --- /dev/null +++ b/plugin/resolvergen/testdata/filetemplate/out/schema.custom.go @@ -0,0 +1,41 @@ +package customresolver + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. + +import ( + "context" + + customresolver "github.com/99designs/gqlgen/plugin/resolvergen/testdata/singlefile/out" +) + +func (r *queryCustomResolverType) Resolver(ctx context.Context) (*customresolver.Resolver, error) { + // CustomerResolverType.Resolver implementation + return nil, nil +} + +func (r *resolverCustomResolverType) Name(ctx context.Context, obj *customresolver.Resolver) (string, error) { + // CustomerResolverType.Name implementation + return "", nil +} + +// Query returns customresolver.QueryResolver implementation. +func (r *CustomResolverType) Query() customresolver.QueryResolver { return &queryCustomResolverType{r} } + +// Resolver returns customresolver.ResolverResolver implementation. +func (r *CustomResolverType) Resolver() customresolver.ResolverResolver { + return &resolverCustomResolverType{r} +} + +type queryCustomResolverType struct{ *CustomResolverType } +type resolverCustomResolverType struct{ *CustomResolverType } + +// !!! WARNING !!! +// The code below was going to be deleted when updating resolvers. It has been copied here so you have +// one last chance to move it out of harms way if you want. There are two reasons this happens: +// - When renaming or deleting a resolver the old code will be put in here. You can safely delete +// it when you're done. +// - You have helper methods in this file. Move them out to keep these resolver files clean. +func AUserHelperFunction() { + // AUserHelperFunction implementation +}