func recursiveFattenFields()

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
}