grafana/pkg/services/live/pipeline/json_to_frame.go

137 lines
3.0 KiB
Go

package pipeline
import (
"fmt"
"strings"
"time"
"github.com/grafana/grafana-plugin-sdk-go/data"
jsoniter "github.com/json-iterator/go"
)
type doc struct {
path []string
iterator *jsoniter.Iterator
fields []*data.Field
fieldNames map[string]struct{}
fieldTips map[string]Field
}
func (d *doc) next() error {
switch d.iterator.WhatIsNext() {
case jsoniter.StringValue:
d.addString(d.iterator.ReadString())
case jsoniter.NumberValue:
d.addNumber(d.iterator.ReadFloat64())
case jsoniter.BoolValue:
d.addBool(d.iterator.ReadBool())
case jsoniter.NilValue:
d.addNil()
d.iterator.ReadNil()
case jsoniter.ArrayValue:
index := 0
size := len(d.path)
for d.iterator.ReadArray() {
d.path = append(d.path, fmt.Sprintf("[%d]", index))
err := d.next()
if err != nil {
return err
}
d.path = d.path[:size]
index++
}
case jsoniter.ObjectValue:
size := len(d.path)
for fname := d.iterator.ReadObject(); fname != ""; fname = d.iterator.ReadObject() {
if size > 0 {
d.path = append(d.path, ".")
}
d.path = append(d.path, fname)
err := d.next()
if err != nil {
return err
}
d.path = d.path[:size]
}
case jsoniter.InvalidValue:
return fmt.Errorf("invalid value")
}
return nil
}
func (d *doc) key() string {
return strings.Join(d.path, "")
}
func (d *doc) addString(v string) {
f := data.NewFieldFromFieldType(data.FieldTypeNullableString, 1)
f.Name = d.key()
f.SetConcrete(0, v)
d.fields = append(d.fields, f)
d.fieldNames[d.key()] = struct{}{}
}
func (d *doc) addNumber(v float64) {
f := data.NewFieldFromFieldType(data.FieldTypeNullableFloat64, 1)
f.Name = d.key()
f.SetConcrete(0, v)
d.fields = append(d.fields, f)
d.fieldNames[d.key()] = struct{}{}
}
func (d *doc) addBool(v bool) {
f := data.NewFieldFromFieldType(data.FieldTypeNullableBool, 1)
f.Name = d.key()
f.SetConcrete(0, v)
d.fields = append(d.fields, f)
d.fieldNames[d.key()] = struct{}{}
}
func (d *doc) addNil() {
if tip, ok := d.fieldTips[d.key()]; ok {
f := data.NewFieldFromFieldType(tip.Type, 1)
f.Name = d.key()
f.Set(0, nil)
d.fields = append(d.fields, f)
d.fieldNames[d.key()] = struct{}{}
} else {
logger.Warn("Skip nil field", "key", d.key())
}
}
func jsonDocToFrame(name string, body []byte, fields map[string]Field, nowTimeFunc func() time.Time) (*data.Frame, error) {
d := doc{
iterator: jsoniter.ParseBytes(jsoniter.ConfigDefault, body),
path: make([]string, 0),
fieldTips: fields,
fieldNames: map[string]struct{}{},
}
f := data.NewFieldFromFieldType(data.FieldTypeTime, 1)
f.Name = "Time"
f.Set(0, nowTimeFunc())
d.fields = append(d.fields, f)
err := d.next()
if err != nil {
return nil, err
}
if len(d.fields) < 2 {
return nil, fmt.Errorf("no fields found")
}
for name, tip := range fields {
if _, ok := d.fieldNames[name]; ok {
continue
}
f := data.NewFieldFromFieldType(tip.Type, 1)
f.Name = name
f.Set(0, nil)
f.Config = tip.Config
d.fields = append(d.fields, f)
}
return data.NewFrame(name, d.fields...), nil
}