grafana/pkg/services/store/entity/json.go

302 lines
7.6 KiB
Go

package entity
import (
"encoding/base64"
"encoding/json"
"fmt"
"unsafe"
jsoniter "github.com/json-iterator/go"
)
func init() { //nolint:gochecknoinits
jsoniter.RegisterTypeEncoder("entity.EntitySearchResult", &searchResultCodec{})
jsoniter.RegisterTypeEncoder("entity.WriteEntityResponse", &writeResponseCodec{})
jsoniter.RegisterTypeEncoder("entity.Entity", &rawEntityCodec{})
jsoniter.RegisterTypeDecoder("entity.Entity", &rawEntityCodec{})
}
func writeRawJson(stream *jsoniter.Stream, val []byte) {
if json.Valid(val) {
_, _ = stream.Write(val)
} else {
stream.WriteString(string(val))
}
}
// Unlike the standard JSON marshal, this will write bytes as JSON when it can
type rawEntityCodec struct{}
func (obj *Entity) MarshalJSON() ([]byte, error) {
var json = jsoniter.ConfigCompatibleWithStandardLibrary
return json.Marshal(obj)
}
// UnmarshalJSON will read JSON into a Entity
func (obj *Entity) UnmarshalJSON(b []byte) error {
if obj == nil {
return fmt.Errorf("unexpected nil for raw objcet")
}
iter := jsoniter.ParseBytes(jsoniter.ConfigDefault, b)
readEntity(iter, obj)
return iter.Error
}
func (codec *rawEntityCodec) IsEmpty(ptr unsafe.Pointer) bool {
f := (*Entity)(ptr)
return f.GRN == nil && f.Body == nil
}
func (codec *rawEntityCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
obj := (*Entity)(ptr)
stream.WriteObjectStart()
stream.WriteObjectField("GRN")
stream.WriteVal(obj.GRN)
if obj.Version != "" {
stream.WriteMore()
stream.WriteObjectField("version")
stream.WriteString(obj.Version)
}
if obj.CreatedAt > 0 {
stream.WriteMore()
stream.WriteObjectField("createdAt")
stream.WriteInt64(obj.CreatedAt)
}
if obj.UpdatedAt > 0 {
stream.WriteMore()
stream.WriteObjectField("updatedAt")
stream.WriteInt64(obj.UpdatedAt)
}
if obj.CreatedBy != "" {
stream.WriteMore()
stream.WriteObjectField("createdBy")
stream.WriteString(obj.CreatedBy)
}
if obj.UpdatedBy != "" {
stream.WriteMore()
stream.WriteObjectField("updatedBy")
stream.WriteString(obj.UpdatedBy)
}
if obj.Folder != "" {
stream.WriteMore()
stream.WriteObjectField("folder")
stream.WriteString(obj.Folder)
}
if obj.Body != nil {
stream.WriteMore()
if json.Valid(obj.Body) {
stream.WriteObjectField("body")
stream.WriteRaw(string(obj.Body)) // works for strings
} else {
sEnc := base64.StdEncoding.EncodeToString(obj.Body)
stream.WriteObjectField("body_base64")
stream.WriteString(sEnc) // works for strings
}
}
if len(obj.SummaryJson) > 0 {
stream.WriteMore()
stream.WriteObjectField("summary")
writeRawJson(stream, obj.SummaryJson)
}
if obj.ETag != "" {
stream.WriteMore()
stream.WriteObjectField("etag")
stream.WriteString(obj.ETag)
}
if obj.Size > 0 {
stream.WriteMore()
stream.WriteObjectField("size")
stream.WriteInt64(obj.Size)
}
if obj.Origin != nil {
stream.WriteMore()
stream.WriteObjectField("origin")
stream.WriteVal(obj.Origin)
}
stream.WriteObjectEnd()
}
func (codec *rawEntityCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
*(*Entity)(ptr) = Entity{}
raw := (*Entity)(ptr)
readEntity(iter, raw)
}
func readEntity(iter *jsoniter.Iterator, raw *Entity) {
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field {
case "GRN":
raw.GRN = &GRN{}
iter.ReadVal(raw.GRN)
case "updatedAt":
raw.UpdatedAt = iter.ReadInt64()
case "updatedBy":
raw.UpdatedBy = iter.ReadString()
case "createdAt":
raw.CreatedAt = iter.ReadInt64()
case "createdBy":
raw.CreatedBy = iter.ReadString()
case "size":
raw.Size = iter.ReadInt64()
case "etag":
raw.ETag = iter.ReadString()
case "version":
raw.Version = iter.ReadString()
case "folder":
raw.Folder = iter.ReadString()
case "origin":
raw.Origin = &EntityOriginInfo{}
iter.ReadVal(raw.Origin)
case "summary":
var val interface{}
iter.ReadVal(&val) // ??? is there a smarter way to just keep the underlying bytes without read+marshal
body, err := json.Marshal(val)
if err != nil {
iter.ReportError("raw entity", "error reading summary body")
return
}
raw.SummaryJson = body
case "body":
var val interface{}
iter.ReadVal(&val) // ??? is there a smarter way to just keep the underlying bytes without read+marshal
body, err := json.Marshal(val)
if err != nil {
iter.ReportError("raw entity", "error creating json from body")
return
}
raw.Body = body
case "body_base64":
val := iter.ReadString()
body, err := base64.StdEncoding.DecodeString(val)
if err != nil {
iter.ReportError("raw entity", "error decoding base64 body")
return
}
raw.Body = body
default:
iter.ReportError("raw object", "unexpected field: "+l1Field)
return
}
}
}
// Unlike the standard JSON marshal, this will write bytes as JSON when it can
type searchResultCodec struct{}
func (obj *EntitySearchResult) MarshalJSON() ([]byte, error) {
var json = jsoniter.ConfigCompatibleWithStandardLibrary
return json.Marshal(obj)
}
func (codec *searchResultCodec) IsEmpty(ptr unsafe.Pointer) bool {
f := (*EntitySearchResult)(ptr)
return f.GRN == nil && f.Body == nil
}
func (codec *searchResultCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
obj := (*EntitySearchResult)(ptr)
stream.WriteObjectStart()
stream.WriteObjectField("GRN")
stream.WriteVal(obj.GRN)
if obj.Name != "" {
stream.WriteMore()
stream.WriteObjectField("name")
stream.WriteString(obj.Name)
}
if obj.Description != "" {
stream.WriteMore()
stream.WriteObjectField("description")
stream.WriteString(obj.Description)
}
if obj.Size > 0 {
stream.WriteMore()
stream.WriteObjectField("size")
stream.WriteInt64(obj.Size)
}
if obj.UpdatedAt > 0 {
stream.WriteMore()
stream.WriteObjectField("updatedAt")
stream.WriteInt64(obj.UpdatedAt)
}
if obj.UpdatedBy != "" {
stream.WriteMore()
stream.WriteObjectField("updatedBy")
stream.WriteVal(obj.UpdatedBy)
}
if obj.Body != nil {
stream.WriteMore()
if json.Valid(obj.Body) {
stream.WriteObjectField("body")
_, _ = stream.Write(obj.Body) // works for strings
} else {
stream.WriteObjectField("body_base64")
stream.WriteVal(obj.Body) // works for strings
}
}
if obj.Labels != nil {
stream.WriteMore()
stream.WriteObjectField("labels")
stream.WriteVal(obj.Labels)
}
if obj.ErrorJson != nil {
stream.WriteMore()
stream.WriteObjectField("error")
writeRawJson(stream, obj.ErrorJson)
}
if obj.FieldsJson != nil {
stream.WriteMore()
stream.WriteObjectField("fields")
writeRawJson(stream, obj.FieldsJson)
}
stream.WriteObjectEnd()
}
// Unlike the standard JSON marshal, this will write bytes as JSON when it can
type writeResponseCodec struct{}
func (obj *WriteEntityResponse) MarshalJSON() ([]byte, error) {
var json = jsoniter.ConfigCompatibleWithStandardLibrary
return json.Marshal(obj)
}
func (codec *writeResponseCodec) IsEmpty(ptr unsafe.Pointer) bool {
f := (*WriteEntityResponse)(ptr)
return f == nil
}
func (codec *writeResponseCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
obj := (*WriteEntityResponse)(ptr)
stream.WriteObjectStart()
stream.WriteObjectField("status")
stream.WriteString(obj.Status.String())
if obj.Error != nil {
stream.WriteMore()
stream.WriteObjectField("error")
stream.WriteVal(obj.Error)
}
if obj.GRN != nil {
stream.WriteMore()
stream.WriteObjectField("GRN")
stream.WriteVal(obj.GRN)
}
if obj.Entity != nil {
stream.WriteMore()
stream.WriteObjectField("entity")
stream.WriteVal(obj.Entity)
}
if len(obj.SummaryJson) > 0 {
stream.WriteMore()
stream.WriteObjectField("summary")
writeRawJson(stream, obj.SummaryJson)
}
stream.WriteObjectEnd()
}