Skip to content

Commit

Permalink
Embed extra fields config (#3088)
Browse files Browse the repository at this point in the history
* bugfix for @gofield + @goExtraField combination

* embedExtraFields configuration added, @goExtraFields 'name' may be empty now

---------

Co-authored-by: Roman A. Grigorovich <ragrigorov@mts.ru>
  • Loading branch information
atzedus and Roman A. Grigorovich committed May 21, 2024
1 parent 0b9e6f9 commit d5c9f89
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 48 deletions.
50 changes: 26 additions & 24 deletions codegen/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 == "" {
Expand All @@ -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
}
}
}
}
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 7 additions & 2 deletions docs/content/recipes/extra_fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -86,5 +90,6 @@ type User struct {
// A Session used by this user.
Session mypkg.Session `xml:"session"`
Activated bool
time.Time
}
```
75 changes: 53 additions & 22 deletions plugin/modelgen/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit d5c9f89

Please sign in to comment.