diff --git a/codegen/config/config.go b/codegen/config/config.go index 39120e562a9..da8b3c86a08 100644 --- a/codegen/config/config.go +++ b/codegen/config/config.go @@ -353,25 +353,11 @@ func (c *Config) injectTypesFromSchema() error { if efds := schemaType.Directives.ForNames("goExtraField"); len(efds) != 0 { for _, efd := range efds { - if fn := efd.Arguments.ForName("name"); fn != nil { - extraFieldName := "" - if fnv, err := fn.Value.Value(nil); err == nil { - extraFieldName = fnv.(string) - } - - if extraFieldName == "" { - return fmt.Errorf( - "argument 'name' for directive @goExtraField (src: %s, line: %d) cannot by empty", - efd.Position.Src.Name, - efd.Position.Line, - ) - } - + if t := efd.Arguments.ForName("type"); t != nil { extraField := ModelExtraField{} - if t := efd.Arguments.ForName("type"); t != nil { - if tv, err := t.Value.Value(nil); err == nil { - extraField.Type = tv.(string) - } + + if tv, err := t.Value.Value(nil); err == nil { + extraField.Type = tv.(string) } if extraField.Type == "" { @@ -394,13 +380,28 @@ func (c *Config) injectTypesFromSchema() error { } } - typeMapEntry := c.Models[schemaType.Name] - if typeMapEntry.ExtraFields == nil { - typeMapEntry.ExtraFields = make(map[string]ModelExtraField) + extraFieldName := "" + if fn := efd.Arguments.ForName("name"); fn != nil { + if fnv, err := fn.Value.Value(nil); err == nil { + extraFieldName = fnv.(string) + } } - c.Models[schemaType.Name] = typeMapEntry - c.Models[schemaType.Name].ExtraFields[extraFieldName] = extraField + if extraFieldName == "" { + // Embeddable fields + typeMapEntry := c.Models[schemaType.Name] + typeMapEntry.EmbedExtraFields = append(typeMapEntry.EmbedExtraFields, extraField) + c.Models[schemaType.Name] = typeMapEntry + } else { + // Regular fields + typeMapEntry := c.Models[schemaType.Name] + if typeMapEntry.ExtraFields == nil { + typeMapEntry.ExtraFields = make(map[string]ModelExtraField) + } + + c.Models[schemaType.Name] = typeMapEntry + c.Models[schemaType.Name].ExtraFields[extraFieldName] = extraField + } } } } @@ -439,7 +440,8 @@ type TypeMapEntry struct { EnumValues map[string]EnumValue `yaml:"enum_values,omitempty"` // Key is the Go name of the field. - ExtraFields map[string]ModelExtraField `yaml:"extraFields,omitempty"` + ExtraFields map[string]ModelExtraField `yaml:"extraFields,omitempty"` + EmbedExtraFields []ModelExtraField `yaml:"embedExtraFields,omitempty"` } type TypeMapField struct { diff --git a/docs/content/recipes/extra_fields.md b/docs/content/recipes/extra_fields.md index f5a631e21d2..63770b401d1 100644 --- a/docs/content/recipes/extra_fields.md +++ b/docs/content/recipes/extra_fields.md @@ -54,7 +54,7 @@ To start using it you first need to define it: ```graphql directive @goExtraField( - name: String! + name: String type: String! overrideTags: String description: String @@ -71,7 +71,11 @@ type User description: "A Session used by this user" overrideTags: "xml:\"session\"" ) - @goExtraField(name: "Activated", type: "bool") { + @goExtraField(name: "Activated", type: "bool") + @goExtraField( + type: "time.Time" + description: "type without name will be embedded" + ) { id: ID name: String } @@ -86,5 +90,6 @@ type User struct { // A Session used by this user. Session mypkg.Session `xml:"session"` Activated bool + time.Time } ``` diff --git a/plugin/modelgen/models.go b/plugin/modelgen/models.go index 9b60923fc16..379e245bcc8 100644 --- a/plugin/modelgen/models.go +++ b/plugin/modelgen/models.go @@ -446,36 +446,67 @@ func (m *Plugin) generateFields(cfg *config.Config, schemaType *ast.Definition) fields = append(fields, f) } - // appending extra fields at the end of the fields list. - modelcfg := cfg.Models[schemaType.Name] + fields = append(fields, getExtraFields(cfg, schemaType.Name)...) + + return fields, nil +} + +func getExtraFields(cfg *config.Config, modelName string) []*Field { + modelcfg := cfg.Models[modelName] + + extraFieldsCount := len(modelcfg.ExtraFields) + len(modelcfg.EmbedExtraFields) + if extraFieldsCount == 0 { + return nil + } + + extraFields := make([]*Field, 0, extraFieldsCount) + + makeExtraField := func(fname string, fspec config.ModelExtraField) *Field { + ftype := buildType(fspec.Type) + + tag := `json:"-"` + if fspec.OverrideTags != "" { + tag = fspec.OverrideTags + } + + return &Field{ + Name: fname, + GoName: fname, + Type: ftype, + Description: fspec.Description, + Tag: tag, + } + } + if len(modelcfg.ExtraFields) > 0 { - ff := make([]*Field, 0, len(modelcfg.ExtraFields)) for fname, fspec := range modelcfg.ExtraFields { - ftype := buildType(fspec.Type) + extraFields = append(extraFields, makeExtraField(fname, fspec)) + } + } - tag := `json:"-"` - if fspec.OverrideTags != "" { - tag = fspec.OverrideTags - } + if len(modelcfg.EmbedExtraFields) > 0 { + for _, fspec := range modelcfg.EmbedExtraFields { + extraFields = append(extraFields, makeExtraField("", fspec)) + } + } - ff = append(ff, - &Field{ - Name: fname, - GoName: fname, - Type: ftype, - Description: fspec.Description, - Tag: tag, - }) + sort.Slice(extraFields, func(i, j int) bool { + if extraFields[i].Name == "" && extraFields[j].Name == "" { + return extraFields[i].Type.String() < extraFields[j].Type.String() } - sort.Slice(ff, func(i, j int) bool { - return ff[i].Name < ff[j].Name - }) + if extraFields[i].Name == "" { + return false + } - fields = append(fields, ff...) - } + if extraFields[j].Name == "" { + return true + } - return fields, nil + return extraFields[i].Name < extraFields[j].Name + }) + + return extraFields } func getStructTagFromField(cfg *config.Config, field *ast.FieldDefinition) string {