in libbeat/generator/fields/validate/mapping.go [143:248]
func recursiveFattenFields(fields interface{}, prefix Prefix, mapping *Mapping, key string) error {
dict, ok := fields.(map[interface{}]interface{})
if !ok {
return fmt.Errorf("fields entry [%s](%s) is not a dictionary", key, prefix)
}
keyIf, hasKey := dict["key"]
nameIf, hasName := dict["name"]
fieldsIf, hasFields := dict["fields"]
typIf, hasType := dict["type"]
requiredIf, hasRequired := dict["required"]
var name, typ string
var required bool
if hasKey {
newKey, ok := keyIf.(string)
if !ok {
return fmt.Errorf("a 'key' field is not of type string, but %T (value=%v)", keyIf, keyIf)
}
if len(key) > 0 {
return fmt.Errorf("unexpected 'key' field in [%s](%s). Keys can only be defined at top level", key, prefix)
}
key = newKey
} else {
if len(key) == 0 {
return fmt.Errorf("found top-level fields entry without a 'key' field")
}
}
if hasName {
name, ok = nameIf.(string)
if !ok {
return fmt.Errorf("a field in [%s](%s) has a 'name' entry of unexpected type (type=%T value=%v)", key, prefix, nameIf, nameIf)
}
prefix = prefix.Append(name)
} else {
if !hasKey {
if _, hasRelease := dict["release"]; hasRelease {
// Ignore fields that have no name or key, but a release. Used in metricbeat to document some modules.
return nil
}
return fmt.Errorf("field [%s](%s) has a sub-field without 'name' nor 'key'", key, prefix)
}
}
if hasType {
typ, ok = typIf.(string)
if !ok {
return fmt.Errorf("field [%s](%s) has a 'type' entry of unexpected type (type=%T value=%v)", key, prefix, nameIf, nameIf)
}
if typ == "object" {
typ = "group"
}
}
if hasRequired {
required, ok = requiredIf.(bool)
if !ok {
return fmt.Errorf("field [%s](%s) has 'required' property but is not a boolean, but %T (value=%v)", key, prefix, requiredIf, requiredIf)
}
}
if !hasFields && typ != "group" {
// Parse a leaf field (not a group)
if !hasType {
typ = "keyword"
}
path := prefix.String()
if err := mapping.addField(path, Field{Type: typ}, required); err != nil {
return fmt.Errorf("adding field [%s](%s): %w", key, path, err)
}
return nil
}
// Parse a group
if hasType && typ != "group" {
return fmt.Errorf("field [%s](%s) has a 'fields' tag but type is not group (type=%s)", key, prefix, typ)
}
if !hasType {
typ = "group"
}
if hasName {
path := prefix.String()
if err := mapping.addField(path, Field{Type: typ}, required); err != nil {
return fmt.Errorf("adding field [%s](%s): %w", key, path, err)
}
}
if fieldsIf != nil {
innerFields, ok := fieldsIf.([]interface{})
if !ok {
return fmt.Errorf("field [%s](%s) has a 'fields' tag of unexpected type (type=%T value=%v)", key, prefix, nameIf, nameIf)
}
for _, field := range innerFields {
if err := recursiveFattenFields(field, prefix, mapping, key); err != nil {
return err
}
}
}
return nil
}