github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/elasticsearch/mappings.go (about)

     1  // Copyright (c) 2016 Arista Networks, Inc.
     2  // Use of this source code is governed by the Apache License 2.0
     3  // that can be found in the COPYING file.
     4  
     5  package elasticsearch
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"math"
    11  	"strings"
    12  
    13  	"github.com/openconfig/gnmi/proto/gnmi"
    14  )
    15  
    16  // EscapeFieldName escapes field names for Elasticsearch
    17  func EscapeFieldName(name string) string {
    18  	return strings.Replace(name, ".", "_", -1)
    19  }
    20  
    21  // SetKey fills a Data map's relevant key if the key is a simple type.
    22  func SetKey(m map[string]interface{}, key interface{}) error {
    23  	// In the case of gnmi, these will always be strings
    24  	if str, ok := key.(string); ok {
    25  		m["KeyString"] = &str
    26  		return nil
    27  	}
    28  	return fmt.Errorf("unknown type %v", key)
    29  }
    30  
    31  // SetValue fills a Data map's relevant Value fields
    32  func SetValue(m map[string]interface{}, val interface{}) error {
    33  	if val == nil {
    34  		return nil
    35  	}
    36  	if str := toStringPtr(val); str != nil {
    37  		m["ValueString"] = str
    38  	} else if long := toLongPtr(val); long != nil {
    39  		m["ValueLong"] = long
    40  	} else if bl := toBoolPtr(val); bl != nil {
    41  		m["ValueBool"] = bl
    42  	} else if dub := toDoublePtr(val); dub != nil {
    43  		m["ValueDouble"] = dub
    44  	} else if arr := toValueArray(val); arr != nil {
    45  		m["Value"] = arr
    46  	} else if json, err := toJSONValue(val); err == nil {
    47  		switch tv := json.(type) {
    48  		case string:
    49  			m["ValueString"] = &tv
    50  		case int, uint:
    51  			m["ValueLong"] = &tv
    52  		case bool:
    53  			m["ValueBool"] = &tv
    54  		case float32:
    55  			m["ValueDouble"] = &tv
    56  		case float64:
    57  			m["ValueDouble"] = &tv
    58  		}
    59  	} else if bytesVal, ok := val.(*gnmi.TypedValue_BytesVal); ok {
    60  		// TODO: handle byte arrays properly (BUG589248)
    61  		fmt.Printf("ignoring byte array with string value %s\n", bytesVal.BytesVal)
    62  	} else {
    63  		// this type may not be supported yet, or could not convert
    64  		return fmt.Errorf("unknown type %T for value %v", val, val)
    65  	}
    66  	return nil
    67  }
    68  
    69  // *TypedValue_StringVal
    70  func toStringPtr(val interface{}) *string {
    71  	if tv, ok := val.(*gnmi.TypedValue_StringVal); ok {
    72  		return &tv.StringVal
    73  	}
    74  	return nil
    75  }
    76  
    77  // *TypedValue_IntVal, *TypedValue_UintVal
    78  func toLongPtr(val interface{}) *int64 {
    79  	switch tv := val.(type) {
    80  	case *gnmi.TypedValue_IntVal:
    81  		val := int64(tv.IntVal)
    82  		return &val
    83  	case *gnmi.TypedValue_UintVal:
    84  		val := int64(tv.UintVal)
    85  		return &val
    86  	}
    87  	return nil
    88  }
    89  
    90  // *TypedValue_BoolVal
    91  func toBoolPtr(val interface{}) *bool {
    92  	if tv, ok := val.(*gnmi.TypedValue_BoolVal); ok {
    93  		return &tv.BoolVal
    94  	}
    95  	return nil
    96  }
    97  
    98  // *TypedValue_FloatVal, *TypedValue_DecimalVal
    99  func toDoublePtr(val interface{}) *float64 {
   100  	switch tv := val.(type) {
   101  	case *gnmi.TypedValue_FloatVal:
   102  		val := float64(tv.FloatVal)
   103  		if !math.IsInf(val, 0) && !math.IsNaN(val) {
   104  			return &val
   105  		}
   106  	case *gnmi.TypedValue_DoubleVal:
   107  		val := float64(tv.DoubleVal)
   108  		if !math.IsInf(val, 0) && !math.IsNaN(val) {
   109  			return &val
   110  		}
   111  	case *gnmi.TypedValue_DecimalVal:
   112  		// convert to float64 for now
   113  		val := float64(tv.DecimalVal.Digits)
   114  		for i := 0; i < int(tv.DecimalVal.Precision); i++ {
   115  			val /= 10
   116  		}
   117  		if !math.IsInf(val, 0) && !math.IsNaN(val) {
   118  			return &val
   119  		}
   120  	}
   121  	return nil
   122  }
   123  
   124  // Flatten a non-simple type into a []*field
   125  func toValueArray(val interface{}) []*map[string]interface{} {
   126  	if tv, ok := val.(*gnmi.TypedValue_LeaflistVal); ok {
   127  		elements := tv.LeaflistVal.Element
   128  		fields := make([]*map[string]interface{}, len(elements))
   129  		// LeaflistVal should only have simple types
   130  		for i, el := range elements {
   131  			m := make(map[string]interface{})
   132  			if str := toStringPtr(el.Value); str != nil {
   133  				m["String"] = str
   134  			} else if long := toLongPtr(el.Value); long != nil {
   135  				m["Long"] = long
   136  			} else if bl := toBoolPtr(el.Value); bl != nil {
   137  				m["Bool"] = bl
   138  			} else if dub := toDoublePtr(el.Value); dub != nil {
   139  				m["Double"] = dub
   140  			} else {
   141  				// this type is not supported yet
   142  				return nil
   143  			}
   144  			fields[i] = &m
   145  		}
   146  		return fields
   147  	}
   148  	return nil
   149  }
   150  
   151  // *TypedValue_JsonVal, *TypedValue_JsonIetfVal
   152  func toJSONValue(val interface{}) (interface{}, error) {
   153  	var out interface{}
   154  	if tv, ok := val.(*gnmi.TypedValue_JsonVal); ok {
   155  		err := json.Unmarshal(tv.JsonVal, &out)
   156  		return out, err
   157  	}
   158  	if tv, ok := val.(*gnmi.TypedValue_JsonIetfVal); ok {
   159  		err := json.Unmarshal(tv.JsonIetfVal, &out)
   160  		return out, err
   161  	}
   162  	return nil, nil
   163  }