diff --git a/codegen/config/config.go b/codegen/config/config.go index 2e3b5a50d1f..8b80e656fe4 100644 --- a/codegen/config/config.go +++ b/codegen/config/config.go @@ -239,10 +239,6 @@ func (c *Config) injectTypesFromSchema() error { SkipRuntime: true, } - c.Directives["goTag"] = DirectiveConfig{ - SkipRuntime: true, - } - for _, schemaType := range c.Schema.Types { if schemaType == c.Schema.Query || schemaType == c.Schema.Mutation || schemaType == c.Schema.Subscription { continue @@ -265,15 +261,9 @@ func (c *Config) injectTypesFromSchema() error { if schemaType.Kind == ast.Object || schemaType.Kind == ast.InputObject { for _, field := range schemaType.Fields { - typeMapField := TypeMapField{ - ExtraTags: c.Models[schemaType.Name].Fields[field.Name].ExtraTags, - FieldName: c.Models[schemaType.Name].Fields[field.Name].FieldName, - Resolver: c.Models[schemaType.Name].Fields[field.Name].Resolver, - } - directive := false if fd := field.Directives.ForName("goField"); fd != nil { - forceResolver := typeMapField.Resolver - fieldName := typeMapField.FieldName + forceResolver := c.Models[schemaType.Name].Fields[field.Name].Resolver + fieldName := c.Models[schemaType.Name].Fields[field.Name].FieldName if ra := fd.Arguments.ForName("forceResolver"); ra != nil { if fr, err := ra.Value.Value(nil); err == nil { @@ -287,38 +277,17 @@ func (c *Config) injectTypesFromSchema() error { } } - typeMapField.FieldName = fieldName - typeMapField.Resolver = forceResolver - directive = true - } - - for _, goTag := range field.Directives.ForNames("goTag") { - key := "" - value := field.Name - if arg := goTag.Arguments.ForName("key"); arg != nil { - if k, err := arg.Value.Value(nil); err == nil { - key = k.(string) - } - } - - if arg := goTag.Arguments.ForName("value"); arg != nil { - if v, err := arg.Value.Value(nil); err == nil { - value = v.(string) - } - } - - typeMapField.ExtraTags = append(typeMapField.ExtraTags, key+":\""+value+"\"") - directive = true - } - - if directive { if c.Models[schemaType.Name].Fields == nil { c.Models[schemaType.Name] = TypeMapEntry{ Model: c.Models[schemaType.Name].Model, Fields: map[string]TypeMapField{}, } } - c.Models[schemaType.Name].Fields[field.Name] = typeMapField + + c.Models[schemaType.Name].Fields[field.Name] = TypeMapField{ + FieldName: fieldName, + Resolver: forceResolver, + } } } } @@ -333,10 +302,9 @@ type TypeMapEntry struct { } type TypeMapField struct { - Resolver bool `yaml:"resolver"` - FieldName string `yaml:"fieldName"` - ExtraTags []string `yaml:"extraTags"` - GeneratedMethod string `yaml:"-"` + Resolver bool `yaml:"resolver"` + FieldName string `yaml:"fieldName"` + GeneratedMethod string `yaml:"-"` } type StringList []string diff --git a/docs/content/recipes/modelgen-hook.md b/docs/content/recipes/modelgen-hook.md index 695ca74cb92..a97ec1c593d 100644 --- a/docs/content/recipes/modelgen-hook.md +++ b/docs/content/recipes/modelgen-hook.md @@ -154,9 +154,11 @@ type ObjectInput struct { } ``` -If a constraint being used during generation shoud not be published during introspection, the directive should be listed with `skip_runtime:true` in gqlgen.yml +If a constraint being used during generation should not be published during introspection, the directive should be listed with `skip_runtime:true` in gqlgen.yml ```yaml directives: constraint: skip_runtime: true ``` + +The built-in directive `@goTag` is implemented using the FieldMutateHook. See: `plugin/modelgen/models.go` function `GoTagFieldHook` diff --git a/plugin/modelgen/models.go b/plugin/modelgen/models.go index 64c9d9c04a7..01a22c591cd 100644 --- a/plugin/modelgen/models.go +++ b/plugin/modelgen/models.go @@ -16,10 +16,10 @@ type BuildMutateHook = func(b *ModelBuild) *ModelBuild type FieldMutateHook = func(td *ast.Definition, fd *ast.FieldDefinition, f *Field) (*Field, error) +// defaultFieldMutateHook is the default hook for the Plugin which applies the GoTagFieldHook. func defaultFieldMutateHook(td *ast.Definition, fd *ast.FieldDefinition, f *Field) (*Field, error) { - return f, nil + return GoTagFieldHook(td, fd, f) } - func defaultBuildMutateHook(b *ModelBuild) *ModelBuild { return b } @@ -171,14 +171,11 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error { typ = types.NewPointer(typ) } - tags := []string{`json:"` + field.Name + `"`} - tags = append(tags, cfg.Models[schemaType.Name].Fields[field.Name].ExtraTags...) - f := &Field{ Name: name, Type: typ, Description: field.Description, - Tag: strings.Join(tags, " "), + Tag: `json:"` + field.Name + `"`, } if m.FieldHook != nil { @@ -254,6 +251,36 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error { return nil } +// GoTagFieldHook applies the goTag directive to the generated Field f. When applying the Tag to the field, the field +// name is used when no value argument is present. +func GoTagFieldHook(td *ast.Definition, fd *ast.FieldDefinition, f *Field) (*Field, error) { + args := make([]string, 0) + for _, goTag := range fd.Directives.ForNames("goTag") { + key := "" + value := fd.Name + + if arg := goTag.Arguments.ForName("key"); arg != nil { + if k, err := arg.Value.Value(nil); err == nil { + key = k.(string) + } + } + + if arg := goTag.Arguments.ForName("value"); arg != nil { + if v, err := arg.Value.Value(nil); err == nil { + value = v.(string) + } + } + + args = append(args, key+":\""+value+"\"") + } + + if len(args) > 0 { + f.Tag = f.Tag + " " + strings.Join(args, " ") + } + + return f, nil +} + func isStruct(t types.Type) bool { _, is := t.Underlying().(*types.Struct) return is diff --git a/plugin/modelgen/models_test.go b/plugin/modelgen/models_test.go index a5f02624de8..104b0927685 100644 --- a/plugin/modelgen/models_test.go +++ b/plugin/modelgen/models_test.go @@ -7,8 +7,6 @@ import ( "strings" "testing" - "github.com/vektah/gqlparser/v2/ast" - "github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/plugin/modelgen/out" "github.com/stretchr/testify/require" @@ -20,7 +18,7 @@ func TestModelGeneration(t *testing.T) { require.NoError(t, cfg.Init()) p := Plugin{ MutateHook: mutateHook, - FieldHook: mutateFieldHook, + FieldHook: defaultFieldMutateHook, } require.NoError(t, p.MutateConfig(cfg)) @@ -77,6 +75,8 @@ func TestModelGeneration(t *testing.T) { expectedTags := []string{ `json:"name" anotherTag:"tag"`, `json:"enum" yetAnotherTag:"12"`, + `json:"noVal" yaml:"noVal"`, + `json:"repeated" someTag:"value" repeated:"true"`, } for _, tag := range expectedTags { @@ -98,19 +98,3 @@ func mutateHook(b *ModelBuild) *ModelBuild { return b } - -func mutateFieldHook(td *ast.Definition, fd *ast.FieldDefinition, f *Field) (*Field, error) { - - if fd.Directives == nil || td.Name != "FieldMutationHook" { - return f, nil - } - - directive := fd.Directives.ForName("addTag") - if directive != nil { - args := directive.ArgumentMap(map[string]interface{}{}) - if tag, ok := args["tag"]; ok { - f.Tag += " " + tag.(string) - } - } - return f, nil -} diff --git a/plugin/modelgen/out/generated.go b/plugin/modelgen/out/generated.go index d2ea061b685..299a34850db 100644 --- a/plugin/modelgen/out/generated.go +++ b/plugin/modelgen/out/generated.go @@ -31,8 +31,10 @@ type UnionWithDescription interface { } type FieldMutationHook struct { - Name *string `json:"name" anotherTag:"tag" database:"FieldMutationHookname"` - Enum *ExistingEnum `json:"enum" yetAnotherTag:"12" database:"FieldMutationHookenum"` + Name *string `json:"name" anotherTag:"tag" database:"FieldMutationHookname"` + Enum *ExistingEnum `json:"enum" yetAnotherTag:"12" database:"FieldMutationHookenum"` + NoVal *string `json:"noVal" yaml:"noVal" repeated:"true" database:"FieldMutationHooknoVal"` + Repeated *string `json:"repeated" someTag:"value" repeated:"true" database:"FieldMutationHookrepeated"` } type MissingInput struct { diff --git a/plugin/modelgen/testdata/schema.graphql b/plugin/modelgen/testdata/schema.graphql index cc36774f84e..775f14d8d18 100644 --- a/plugin/modelgen/testdata/schema.graphql +++ b/plugin/modelgen/testdata/schema.graphql @@ -1,5 +1,7 @@ -directive @addTag(tag: String!) on INPUT_FIELD_DEFINITION - | FIELD_DEFINITION +directive @goTag( + key: String! + value: String +) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION type Query { thisShoudlntGetGenerated: Boolean @@ -58,8 +60,11 @@ input ExistingInput { } type FieldMutationHook { - name: String @addTag(tag :"anotherTag:\"tag\"") - enum: ExistingEnum @addTag(tag: "yetAnotherTag:\"12\"") + name: String @goTag(key :"anotherTag", value: "tag") + enum: ExistingEnum @goTag(key: "yetAnotherTag", value: "12") + noVal: String @goTag(key: "yaml") @goTag(key : "repeated", value: "true") + repeated: String @goTag(key: "someTag", value: "value") @goTag(key : "repeated", value: "true") + } enum ExistingEnum {