youtubebeat/vendor/github.com/elastic/beats/libbeat/common/field.go

219 lines
5.8 KiB
Go
Raw Normal View History

2018-11-18 11:08:38 +01:00
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package common
import (
"fmt"
"strings"
"github.com/elastic/go-ucfg/yaml"
)
//This reflects allowed attributes for field definitions in the fields.yml.
//No logic is put into this data structure.
//The purpose is to enable using different kinds of transformation, on top of the same data structure.
//Current transformation:
// -ElasticSearch Template
// -Kibana Index Pattern
type Fields []Field
type Field struct {
Name string `config:"name"`
Type string `config:"type"`
Description string `config:"description"`
Format string `config:"format"`
ScalingFactor int `config:"scaling_factor"`
Fields Fields `config:"fields"`
MultiFields Fields `config:"multi_fields"`
ObjectType string `config:"object_type"`
ObjectTypeMappingType string `config:"object_type_mapping_type"`
Enabled *bool `config:"enabled"`
Analyzer string `config:"analyzer"`
SearchAnalyzer string `config:"search_analyzer"`
Norms bool `config:"norms"`
Dynamic DynamicType `config:"dynamic"`
Index *bool `config:"index"`
DocValues *bool `config:"doc_values"`
CopyTo string `config:"copy_to"`
IgnoreAbove int `config:"ignore_above"`
AliasPath string `config:"path"`
// Kibana specific
Analyzed *bool `config:"analyzed"`
Count int `config:"count"`
Searchable *bool `config:"searchable"`
Aggregatable *bool `config:"aggregatable"`
Script string `config:"script"`
// Kibana params
Pattern string `config:"pattern"`
InputFormat string `config:"input_format"`
OutputFormat string `config:"output_format"`
OutputPrecision *int `config:"output_precision"`
LabelTemplate string `config:"label_template"`
UrlTemplate []VersionizedString `config:"url_template"`
OpenLinkInCurrentTab *bool `config:"open_link_in_current_tab"`
Path string
}
type VersionizedString struct {
MinVersion string `config:"min_version"`
Value string `config:"value"`
}
type DynamicType struct{ Value interface{} }
func (d *DynamicType) Unpack(s string) error {
switch s {
case "true":
d.Value = true
case "false":
d.Value = false
case "strict":
d.Value = s
default:
return fmt.Errorf("'%v' is invalid dynamic setting", s)
}
return nil
}
func LoadFieldsYaml(path string) (Fields, error) {
keys := []Field{}
cfg, err := yaml.NewConfigWithFile(path)
if err != nil {
return nil, err
}
cfg.Unpack(&keys)
fields := Fields{}
for _, key := range keys {
fields = append(fields, key.Fields...)
}
return fields, nil
}
// HasKey checks if inside fields the given key exists
// The key can be in the form of a.b.c and it will check if the nested field exist
// In case the key is `a` and there is a value `a.b` false is return as it only
// returns true if it's a leave node
func (f Fields) HasKey(key string) bool {
keys := strings.Split(key, ".")
return f.hasKey(keys)
}
// HasNode checks if inside fields the given node exists
// In contrast to HasKey it not only compares the leaf nodes but
// every single key it traverses.
func (f Fields) HasNode(key string) bool {
keys := strings.Split(key, ".")
return f.hasNode(keys)
}
func (f Fields) hasNode(keys []string) bool {
// Nothing to compare, so does not contain it
if len(keys) == 0 {
return false
}
key := keys[0]
keys = keys[1:]
for _, field := range f {
if field.Name == key {
//// It's the last key to compare
if len(keys) == 0 {
return true
}
// It's the last field to compare
if len(field.Fields) == 0 {
return true
}
return field.Fields.hasNode(keys)
}
}
return false
}
// Recursively generates the correct key based on the dots
// The mapping requires "properties" between each layer. This is added here.
func GenerateKey(key string) string {
if strings.Contains(key, ".") {
keys := strings.SplitN(key, ".", 2)
key = keys[0] + ".properties." + GenerateKey(keys[1])
}
return key
}
func (f Fields) hasKey(keys []string) bool {
// Nothing to compare anymore
if len(keys) == 0 {
return false
}
key := keys[0]
keys = keys[1:]
for _, field := range f {
if field.Name == key {
if len(field.Fields) > 0 {
return field.Fields.hasKey(keys)
}
// Last entry in the tree but still more keys
if len(keys) > 0 {
return false
}
return true
}
}
return false
}
// GetKeys returns a flat list of keys this Fields contains
func (f Fields) GetKeys() []string {
return f.getKeys("")
}
func (f Fields) getKeys(namespace string) []string {
var keys []string
for _, field := range f {
fieldName := namespace + "." + field.Name
if namespace == "" {
fieldName = field.Name
}
if len(field.Fields) == 0 {
keys = append(keys, fieldName)
} else {
keys = append(keys, field.Fields.getKeys(fieldName)...)
}
}
return keys
}