From 82fefdb51046ca80506292f25dcb2d636301f865 Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Tue, 1 Mar 2022 08:29:46 +0900 Subject: [PATCH] support to generate model for intermediate interface (#1982) * support to generate model for intermediate interface * go generate ./... in example * fixed filepath generation --- _examples/go.mod | 2 +- plugin/modelgen/models.go | 2 + plugin/modelgen/models.gotpl | 3 + plugin/modelgen/models_test.go | 100 ++++++++++++++++++++++++ plugin/modelgen/out/generated.go | 19 +++++ plugin/modelgen/testdata/schema.graphql | 20 +++++ 6 files changed, 145 insertions(+), 1 deletion(-) diff --git a/_examples/go.mod b/_examples/go.mod index 03502db3c1f..b6973d4fd61 100644 --- a/_examples/go.mod +++ b/_examples/go.mod @@ -5,7 +5,7 @@ go 1.16 replace github.com/99designs/gqlgen => ../ require ( - github.com/99designs/gqlgen v0.0.0-00010101000000-000000000000 + github.com/99designs/gqlgen v0.16.0 github.com/gorilla/websocket v1.4.2 github.com/mitchellh/mapstructure v1.4.1 github.com/opentracing/opentracing-go v1.2.0 diff --git a/plugin/modelgen/models.go b/plugin/modelgen/models.go index 0b8d90c4f10..86457476c74 100644 --- a/plugin/modelgen/models.go +++ b/plugin/modelgen/models.go @@ -36,6 +36,7 @@ type ModelBuild struct { type Interface struct { Description string Name string + Implements []string } type Object struct { @@ -97,6 +98,7 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error { it := &Interface{ Description: schemaType.Description, Name: schemaType.Name, + Implements: schemaType.Interfaces, } b.Interfaces = append(b.Interfaces, it) diff --git a/plugin/modelgen/models.gotpl b/plugin/modelgen/models.gotpl index e58d5b21a4e..8f425e58eb4 100644 --- a/plugin/modelgen/models.gotpl +++ b/plugin/modelgen/models.gotpl @@ -15,6 +15,9 @@ {{- range $model := .Interfaces }} {{ with .Description }} {{.|prefixLines "// "}} {{ end }} type {{.Name|go }} interface { + {{- range $impl := .Implements }} + {{ $impl|go }} + {{- end }} Is{{.Name|go }}() } {{- end }} diff --git a/plugin/modelgen/models_test.go b/plugin/modelgen/models_test.go index 104b0927685..42641437b8a 100644 --- a/plugin/modelgen/models_test.go +++ b/plugin/modelgen/models_test.go @@ -1,14 +1,17 @@ package modelgen import ( + "go/ast" "go/parser" "go/token" "io/ioutil" + "path/filepath" "strings" "testing" "github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/plugin/modelgen/out" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -87,6 +90,93 @@ func TestModelGeneration(t *testing.T) { t.Run("concrete types implement interface", func(t *testing.T) { var _ out.FooBarer = out.FooBarr{} }) + + t.Run("implemented interfaces", func(t *testing.T) { + pkg, err := parseAst("out") + require.NoError(t, err) + + path := filepath.Join("out", "generated.go") + generated := pkg.Files[path] + + type field struct { + typ string + name string + } + cases := []struct { + name string + wantFields []field + }{ + { + name: "A", + wantFields: []field{ + { + typ: "method", + name: "IsA", + }, + }, + }, + { + name: "B", + wantFields: []field{ + { + typ: "method", + name: "IsB", + }, + }, + }, + { + name: "C", + wantFields: []field{ + { + typ: "ident", + name: "A", + }, + { + typ: "method", + name: "IsC", + }, + }, + }, + { + name: "D", + wantFields: []field{ + { + typ: "ident", + name: "A", + }, + { + typ: "ident", + name: "B", + }, + { + typ: "method", + name: "IsD", + }, + }, + }, + } + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + typeSpec, ok := generated.Scope.Lookup(tc.name).Decl.(*ast.TypeSpec) + require.True(t, ok) + + fields := typeSpec.Type.(*ast.InterfaceType).Methods.List + for i, want := range tc.wantFields { + if want.typ == "ident" { + ident, ok := fields[i].Type.(*ast.Ident) + require.True(t, ok) + assert.Equal(t, want.name, ident.Name) + } + if want.typ == "method" { + require.GreaterOrEqual(t, 1, len(fields[i].Names)) + name := fields[i].Names[0].Name + assert.Equal(t, want.name, name) + } + } + }) + } + }) } func mutateHook(b *ModelBuild) *ModelBuild { @@ -98,3 +188,13 @@ func mutateHook(b *ModelBuild) *ModelBuild { return b } + +func parseAst(path string) (*ast.Package, error) { + // test setup to parse the types + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, path, nil, parser.AllErrors) + if err != nil { + return nil, err + } + return pkgs["out"], nil +} diff --git a/plugin/modelgen/out/generated.go b/plugin/modelgen/out/generated.go index 299a34850db..3e9db09e3d4 100644 --- a/plugin/modelgen/out/generated.go +++ b/plugin/modelgen/out/generated.go @@ -8,6 +8,25 @@ import ( "strconv" ) +type A interface { + IsA() +} + +type B interface { + IsB() +} + +type C interface { + A + IsC() +} + +type D interface { + A + B + IsD() +} + type FooBarer interface { IsFooBarer() } diff --git a/plugin/modelgen/testdata/schema.graphql b/plugin/modelgen/testdata/schema.graphql index 775f14d8d18..c183befd07e 100644 --- a/plugin/modelgen/testdata/schema.graphql +++ b/plugin/modelgen/testdata/schema.graphql @@ -105,3 +105,23 @@ interface Foo_Barer { type _Foo_Barr implements Foo_Barer { name: String! } + +# https://spec.graphql.org/October2021/#sec-Interfaces +interface A { + a: String! +} + +interface B { + b: Int! +} + +interface C implements A { + a: String! + c: Boolean! +} + +interface D implements A & B { + a: String! + b: Int! + d: String +} \ No newline at end of file