data_to_point.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package api
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strings"
  6. "time"
  7. "github.com/influxdata/influxdb-client-go/v2/api/write"
  8. )
  9. // DataToPoint converts custom point structures into a Point.
  10. // Each visible field of the point on input must be annotated with
  11. // 'lp' prefix and values measurement,tag, field or timestamp.
  12. // Valid point must contain measurement and at least one field.
  13. //
  14. // A field with timestamp must be of a type time.Time
  15. //
  16. // type TemperatureSensor struct {
  17. // Measurement string `lp:"measurement"`
  18. // Sensor string `lp:"tag,sensor"`
  19. // ID string `lp:"tag,device_id"`
  20. // Temp float64 `lp:"field,temperature"`
  21. // Hum int `lp:"field,humidity"`
  22. // Time time.Time `lp:"timestamp,temperature"`
  23. // Description string `lp:"-"`
  24. // }
  25. func DataToPoint(x interface{}) (*write.Point, error) {
  26. t := reflect.TypeOf(x)
  27. v := reflect.ValueOf(x)
  28. if t.Kind() == reflect.Ptr {
  29. t = t.Elem()
  30. v = v.Elem()
  31. }
  32. if t.Kind() != reflect.Struct {
  33. return nil, fmt.Errorf("cannot use %v as point", t)
  34. }
  35. fields := reflect.VisibleFields(t)
  36. var measurement = ""
  37. var lpTags = make(map[string]string)
  38. var lpFields = make(map[string]interface{})
  39. var lpTime time.Time
  40. for _, f := range fields {
  41. name := f.Name
  42. if tag, ok := f.Tag.Lookup("lp"); ok {
  43. if tag == "-" {
  44. continue
  45. }
  46. parts := strings.Split(tag, ",")
  47. if len(parts) > 2 {
  48. return nil, fmt.Errorf("multiple tag attributes are not supported")
  49. }
  50. typ := parts[0]
  51. if len(parts) == 2 {
  52. name = parts[1]
  53. }
  54. t := getFieldType(v.FieldByIndex(f.Index))
  55. if !validFieldType(t) {
  56. return nil, fmt.Errorf("cannot use field '%s' of type '%v' as to create a point", f.Name, t)
  57. }
  58. switch typ {
  59. case "measurement":
  60. if measurement != "" {
  61. return nil, fmt.Errorf("multiple measurement fields")
  62. }
  63. measurement = v.FieldByIndex(f.Index).String()
  64. case "tag":
  65. if name == "" {
  66. return nil, fmt.Errorf("cannot use field '%s': invalid lp tag name \"\"", f.Name)
  67. }
  68. lpTags[name] = v.FieldByIndex(f.Index).String()
  69. case "field":
  70. if name == "" {
  71. return nil, fmt.Errorf("cannot use field '%s': invalid lp field name \"\"", f.Name)
  72. }
  73. lpFields[name] = v.FieldByIndex(f.Index).Interface()
  74. case "timestamp":
  75. if f.Type != timeType {
  76. return nil, fmt.Errorf("cannot use field '%s' as a timestamp", f.Name)
  77. }
  78. lpTime = v.FieldByIndex(f.Index).Interface().(time.Time)
  79. default:
  80. return nil, fmt.Errorf("invalid tag %s", typ)
  81. }
  82. }
  83. }
  84. if measurement == "" {
  85. return nil, fmt.Errorf("no struct field with tag 'measurement'")
  86. }
  87. if len(lpFields) == 0 {
  88. return nil, fmt.Errorf("no struct field with tag 'field'")
  89. }
  90. return write.NewPoint(measurement, lpTags, lpFields, lpTime), nil
  91. }