From b1a05b658fc29ab5741c415bb625c7023b493c18 Mon Sep 17 00:00:00 2001 From: Bill Rose Date: Fri, 19 Aug 2022 10:32:33 -0400 Subject: [PATCH 1/3] Make modelgen test fail if generated doesn't build Added returning list of interface to modelgen test schema --- plugin/modelgen/models_test.go | 14 ++++++++++++++ plugin/modelgen/testdata/schema.graphql | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/plugin/modelgen/models_test.go b/plugin/modelgen/models_test.go index c8fda7f08a..71dfbe5a8d 100644 --- a/plugin/modelgen/models_test.go +++ b/plugin/modelgen/models_test.go @@ -1,10 +1,12 @@ package modelgen import ( + "errors" "go/ast" "go/parser" "go/token" "os" + "os/exec" "path/filepath" "sort" "strings" @@ -27,6 +29,7 @@ func TestModelGeneration(t *testing.T) { FieldHook: defaultFieldMutateHook, } require.NoError(t, p.MutateConfig(cfg)) + require.NoError(t, goBuild(t, "./out/")) require.True(t, cfg.Models.UserDefined("MissingTypeNotNull")) require.True(t, cfg.Models.UserDefined("MissingTypeNullable")) @@ -330,3 +333,14 @@ func parseAst(path string) (*ast.Package, error) { } return pkgs["out"], nil } + +func goBuild(t *testing.T, path string) error { + t.Helper() + cmd := exec.Command("go", "build", path) + out, err := cmd.CombinedOutput() + if err != nil { + return errors.New(string(out)) + } + + return nil +} diff --git a/plugin/modelgen/testdata/schema.graphql b/plugin/modelgen/testdata/schema.graphql index 3a99ee168e..2bd4069800 100644 --- a/plugin/modelgen/testdata/schema.graphql +++ b/plugin/modelgen/testdata/schema.graphql @@ -169,3 +169,11 @@ type RenameFieldTest { badName: String! otherField: String! } + +interface ArrayOfA { + trickyField: [A!]! +} + +type ImplArrayOfA implements ArrayOfA { + trickyField: [CDImplemented!]! +} \ No newline at end of file From 0cac483a1542102be4bcffff84c40df77d83efc6 Mon Sep 17 00:00:00 2001 From: Bill Rose Date: Fri, 19 Aug 2022 11:00:48 -0400 Subject: [PATCH 2/3] Implement slice copying when returning interface slices --- plugin/modelgen/models.go | 33 +++++++++++++++++++------ plugin/modelgen/out/generated.go | 33 +++++++++++++++++++++++++ plugin/modelgen/testdata/schema.graphql | 2 ++ 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/plugin/modelgen/models.go b/plugin/modelgen/models.go index 0fc1f157b8..3cf62046b5 100644 --- a/plugin/modelgen/models.go +++ b/plugin/modelgen/models.go @@ -229,7 +229,6 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error { return "" } - getter := fmt.Sprintf("func (this %s) Get%s() %s { return ", templates.ToGo(model.Name), field.GoName, templates.CurrentImports.LookupType(field.Type)) _, interfaceFieldTypeIsPointer := field.Type.(*types.Pointer) var structFieldTypeIsPointer bool for _, f := range model.Fields { @@ -238,15 +237,33 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error { break } } + goType := templates.CurrentImports.LookupType(field.Type) + if strings.HasPrefix(goType, "[]") { + getter := fmt.Sprintf("func (this %s) Get%s() %s {\n", templates.ToGo(model.Name), field.GoName, goType) + getter += fmt.Sprintf("\tif this.%s == nil { return nil }\n", field.GoName) + getter += fmt.Sprintf("\tinterfaceSlice := make(%s, 0, len(this.%s))\n", goType, field.GoName) + getter += fmt.Sprintf("\tfor _, concrete := range this.%s { interfaceSlice = append(interfaceSlice, ", field.GoName) + if interfaceFieldTypeIsPointer && !structFieldTypeIsPointer { + getter += "&" + } else if !interfaceFieldTypeIsPointer && structFieldTypeIsPointer { + getter += "*" + } + getter += "concrete) }\n" + getter += "\treturn interfaceSlice\n" + getter += "}" + return getter + } else { + getter := fmt.Sprintf("func (this %s) Get%s() %s { return ", templates.ToGo(model.Name), field.GoName, goType) - if interfaceFieldTypeIsPointer && !structFieldTypeIsPointer { - getter += "&" - } else if !interfaceFieldTypeIsPointer && structFieldTypeIsPointer { - getter += "*" - } + if interfaceFieldTypeIsPointer && !structFieldTypeIsPointer { + getter += "&" + } else if !interfaceFieldTypeIsPointer && structFieldTypeIsPointer { + getter += "*" + } - getter += fmt.Sprintf("this.%s }", field.GoName) - return getter + getter += fmt.Sprintf("this.%s }", field.GoName) + return getter + } } funcMap := template.FuncMap{ "getInterfaceByName": getInterfaceByName, diff --git a/plugin/modelgen/out/generated.go b/plugin/modelgen/out/generated.go index 7979b8c30b..7381a95a5f 100644 --- a/plugin/modelgen/out/generated.go +++ b/plugin/modelgen/out/generated.go @@ -13,6 +13,12 @@ type A interface { GetA() string } +type ArrayOfA interface { + IsArrayOfA() + GetTrickyField() []A + GetTrickyFieldPointer() []A +} + type B interface { IsB() GetB() int @@ -101,6 +107,33 @@ type FieldMutationHook struct { Repeated *string `json:"repeated" someTag:"value" repeated:"true" database:"FieldMutationHookrepeated"` } +type ImplArrayOfA struct { + TrickyField []*CDImplemented `json:"trickyField" database:"ImplArrayOfAtrickyField"` + TrickyFieldPointer []*CDImplemented `json:"trickyFieldPointer" database:"ImplArrayOfAtrickyFieldPointer"` +} + +func (ImplArrayOfA) IsArrayOfA() {} +func (this ImplArrayOfA) GetTrickyField() []A { + if this.TrickyField == nil { + return nil + } + interfaceSlice := make([]A, 0, len(this.TrickyField)) + for _, concrete := range this.TrickyField { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} +func (this ImplArrayOfA) GetTrickyFieldPointer() []A { + if this.TrickyFieldPointer == nil { + return nil + } + interfaceSlice := make([]A, 0, len(this.TrickyFieldPointer)) + for _, concrete := range this.TrickyFieldPointer { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} + type MissingInput struct { Name *string `json:"name" database:"MissingInputname"` Enum *MissingEnum `json:"enum" database:"MissingInputenum"` diff --git a/plugin/modelgen/testdata/schema.graphql b/plugin/modelgen/testdata/schema.graphql index 2bd4069800..f800ce97f0 100644 --- a/plugin/modelgen/testdata/schema.graphql +++ b/plugin/modelgen/testdata/schema.graphql @@ -172,8 +172,10 @@ type RenameFieldTest { interface ArrayOfA { trickyField: [A!]! + trickyFieldPointer: [A] } type ImplArrayOfA implements ArrayOfA { trickyField: [CDImplemented!]! + trickyFieldPointer: [CDImplemented] } \ No newline at end of file From 8278a3e80a6a52973fe34f17a380863ae955ac5d Mon Sep 17 00:00:00 2001 From: Bill Rose Date: Fri, 19 Aug 2022 11:10:34 -0400 Subject: [PATCH 3/3] Re-generate to satisfy the linter --- _examples/selection/models_gen.go | 48 ++++++++++++++++--- .../modelgen/out_struct_pointers/generated.go | 33 +++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/_examples/selection/models_gen.go b/_examples/selection/models_gen.go index f898a183d1..3a66264849 100644 --- a/_examples/selection/models_gen.go +++ b/_examples/selection/models_gen.go @@ -19,9 +19,27 @@ type Like struct { Collected []string `json:"collected"` } -func (Like) IsEvent() {} -func (this Like) GetSelection() []string { return this.Selection } -func (this Like) GetCollected() []string { return this.Collected } +func (Like) IsEvent() {} +func (this Like) GetSelection() []string { + if this.Selection == nil { + return nil + } + interfaceSlice := make([]string, 0, len(this.Selection)) + for _, concrete := range this.Selection { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} +func (this Like) GetCollected() []string { + if this.Collected == nil { + return nil + } + interfaceSlice := make([]string, 0, len(this.Collected)) + for _, concrete := range this.Collected { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} type Post struct { Message string `json:"message"` @@ -30,6 +48,24 @@ type Post struct { Collected []string `json:"collected"` } -func (Post) IsEvent() {} -func (this Post) GetSelection() []string { return this.Selection } -func (this Post) GetCollected() []string { return this.Collected } +func (Post) IsEvent() {} +func (this Post) GetSelection() []string { + if this.Selection == nil { + return nil + } + interfaceSlice := make([]string, 0, len(this.Selection)) + for _, concrete := range this.Selection { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} +func (this Post) GetCollected() []string { + if this.Collected == nil { + return nil + } + interfaceSlice := make([]string, 0, len(this.Collected)) + for _, concrete := range this.Collected { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} diff --git a/plugin/modelgen/out_struct_pointers/generated.go b/plugin/modelgen/out_struct_pointers/generated.go index 4b7b650f8b..3b311c64d0 100644 --- a/plugin/modelgen/out_struct_pointers/generated.go +++ b/plugin/modelgen/out_struct_pointers/generated.go @@ -13,6 +13,12 @@ type A interface { GetA() string } +type ArrayOfA interface { + IsArrayOfA() + GetTrickyField() []A + GetTrickyFieldPointer() []A +} + type B interface { IsB() GetB() int @@ -101,6 +107,33 @@ type FieldMutationHook struct { Repeated *string `json:"repeated" someTag:"value" repeated:"true" database:"FieldMutationHookrepeated"` } +type ImplArrayOfA struct { + TrickyField []*CDImplemented `json:"trickyField" database:"ImplArrayOfAtrickyField"` + TrickyFieldPointer []*CDImplemented `json:"trickyFieldPointer" database:"ImplArrayOfAtrickyFieldPointer"` +} + +func (ImplArrayOfA) IsArrayOfA() {} +func (this ImplArrayOfA) GetTrickyField() []A { + if this.TrickyField == nil { + return nil + } + interfaceSlice := make([]A, 0, len(this.TrickyField)) + for _, concrete := range this.TrickyField { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} +func (this ImplArrayOfA) GetTrickyFieldPointer() []A { + if this.TrickyFieldPointer == nil { + return nil + } + interfaceSlice := make([]A, 0, len(this.TrickyFieldPointer)) + for _, concrete := range this.TrickyFieldPointer { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} + type MissingInput struct { Name *string `json:"name" database:"MissingInputname"` Enum *MissingEnum `json:"enum" database:"MissingInputenum"`