input/elasticapm/internal/modeldecoder/nullable/nullable.go (217 lines of code) (raw):

// 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 nullable import ( "errors" "fmt" "net/http" "time" "unsafe" jsoniter "github.com/json-iterator/go" ) // supportedTSFormats lists variations of RFC3339 for supporting // different formats for the timezone offset. var supportedTSFormats = []string{ "2006-01-02T15:04:05Z07:00", // RFC3339 "2006-01-02T15:04:05Z0700", "2006-01-02T15:04:05Z07", } func init() { jsoniter.RegisterTypeDecoderFunc("nullable.String", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NilValue: iter.ReadNil() default: (*((*String)(ptr))).Val = iter.ReadString() (*((*String)(ptr))).isSet = true } }) jsoniter.RegisterTypeDecoderFunc("nullable.Int", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NilValue: iter.ReadNil() default: (*((*Int)(ptr))).Val = iter.ReadInt() (*((*Int)(ptr))).isSet = true } }) jsoniter.RegisterTypeDecoderFunc("nullable.Int64", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NilValue: iter.ReadNil() default: (*((*Int64)(ptr))).Val = iter.ReadInt64() (*((*Int64)(ptr))).isSet = true } }) jsoniter.RegisterTypeDecoderFunc("nullable.Float64", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NilValue: iter.ReadNil() default: (*((*Float64)(ptr))).Val = iter.ReadFloat64() (*((*Float64)(ptr))).isSet = true } }) jsoniter.RegisterTypeDecoderFunc("nullable.Bool", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NilValue: iter.ReadNil() default: (*((*Bool)(ptr))).Val = iter.ReadBool() (*((*Bool)(ptr))).isSet = true } }) jsoniter.RegisterTypeDecoderFunc("nullable.Interface", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NilValue: iter.ReadNil() default: v := iter.Read() (*((*Interface)(ptr))).Val = v (*((*Interface)(ptr))).isSet = true } }) jsoniter.RegisterTypeDecoderFunc("nullable.TimeMicrosUnix", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NilValue: iter.ReadNil() case jsoniter.NumberValue: us := iter.ReadInt64() s := us / 1000000 ns := (us - (s * 1000000)) * 1000 (*((*TimeMicrosUnix)(ptr))).Val = time.Unix(s, ns).UTC() (*((*TimeMicrosUnix)(ptr))).isSet = true case jsoniter.StringValue: tstr := iter.ReadString() for _, f := range supportedTSFormats { if t, err := time.Parse(f, tstr); err == nil { (*((*TimeMicrosUnix)(ptr))).Val = t.UTC() (*((*TimeMicrosUnix)(ptr))).isSet = true return } } iter.Error = errors.New("failed to parse the provided time string") default: iter.Error = errors.New("invalid input type for timestamp") } }) jsoniter.RegisterTypeDecoderFunc("nullable.HTTPHeader", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NilValue: iter.ReadNil() default: m, ok := iter.Read().(map[string]interface{}) if !ok { iter.Error = errors.New("invalid input type for HTTPHeader") return } h := make(http.Header, len(m)) for key, val := range m { switch v := val.(type) { case string: h.Add(key, v) case []interface{}: for _, entry := range v { switch entry := entry.(type) { case string: h.Add(key, entry) default: iter.Error = fmt.Errorf("invalid input for HTTPHeader slice value type: %T", entry) return } } default: iter.Error = fmt.Errorf("invalid input for HTTPHeader value type: %T", v) return } } (*((*HTTPHeader)(ptr))).Val = h (*((*HTTPHeader)(ptr))).isSet = true } }) } // String stores a string value and the // information if the value has been set type String struct { Val string isSet bool } // Set sets the value func (v *String) Set(val string) { v.Val = val v.isSet = true } // IsSet is true when decode was called func (v *String) IsSet() bool { return v.isSet } // Int stores an int value and the // information if the value has been set type Int struct { Val int isSet bool } // Set sets the value func (v *Int) Set(val int) { v.Val = val v.isSet = true } // IsSet is true when decode was called func (v *Int) IsSet() bool { return v.isSet } // Int64 stores an int64 value and the // information if the value has been set type Int64 struct { Val int64 isSet bool } // Set sets the value func (v *Int64) Set(val int64) { v.Val = val v.isSet = true } // IsSet is true when decode was called func (v *Int64) IsSet() bool { return v.isSet } // Float64 stores a float64 value and the // information if the value has been set type Float64 struct { Val float64 isSet bool } // Set sets the value func (v *Float64) Set(val float64) { v.Val = val v.isSet = true } // IsSet is true when decode was called func (v *Float64) IsSet() bool { return v.isSet } // Bool stores a bool value and the // information if the value has been set type Bool struct { Val bool isSet bool } // Set sets the value func (v *Bool) Set(val bool) { v.Val = val v.isSet = true } // IsSet is true when decode was called func (v *Bool) IsSet() bool { return v.isSet } // Interface stores an interface{} value and the // information if the value has been set // // TODO(simitt): follow up on https://github.com/elastic/apm-server/pull/4154#discussion_r484166721 type Interface struct { Val interface{} `json:"val,omitempty"` isSet bool } // Set sets the value func (v *Interface) Set(val interface{}) { v.Val = val v.isSet = true } // IsSet is true when decode was called func (v *Interface) IsSet() bool { return v.isSet } type TimeMicrosUnix struct { Val time.Time isSet bool } // Set sets the value func (v *TimeMicrosUnix) Set(val time.Time) { v.Val = val v.isSet = true } // IsSet is true when decode was called func (v *TimeMicrosUnix) IsSet() bool { return v.isSet } type HTTPHeader struct { Val http.Header isSet bool } // Set sets the value func (v *HTTPHeader) Set(val http.Header) { v.Val = val v.isSet = true } // IsSet is true when decode was called func (v *HTTPHeader) IsSet() bool { return v.isSet }