Skip to content

Commit

Permalink
[pkg/ottl] Add new FloatLikeGetter and FloatGetter (#21896)
Browse files Browse the repository at this point in the history
* add new getters

* changelog

* Add support for converting bool

* Add to function parser

* Add to floatgetter tests

* Add to floatgetter slice support
  • Loading branch information
TylerHelmuth committed May 12, 2023
1 parent 67bdff0 commit 0e43390
Show file tree
Hide file tree
Showing 5 changed files with 402 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .chloggen/ottl-floatlikegetter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: pkg/ottl

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add `FloatLikeGetter` and `FloatGetter` to facilitate float retrival for functions.

# One or more tracking issues related to the change
issues: [21896]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
69 changes: 69 additions & 0 deletions pkg/ottl/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"
"encoding/hex"
"fmt"
"strconv"

jsoniter "github.com/json-iterator/go"
"go.opentelemetry.io/collector/pdata/pcommon"
Expand Down Expand Up @@ -153,6 +154,10 @@ type IntGetter[K any] interface {
Get(ctx context.Context, tCtx K) (int64, error)
}

type FloatGetter[K any] interface {
Get(ctx context.Context, tCtx K) (float64, error)
}

type PMapGetter[K any] interface {
Get(ctx context.Context, tCtx K) (pcommon.Map, error)
}
Expand Down Expand Up @@ -225,6 +230,70 @@ func (g StandardStringLikeGetter[K]) Get(ctx context.Context, tCtx K) (*string,
return &result, nil
}

// FloatLikeGetter is a Getter that returns a float64 by converting the underlying value to a float64 if necessary.
type FloatLikeGetter[K any] interface {
// Get retrieves a float64 value.
// Unlike `FloatGetter`, the expectation is that the underlying value is converted to a float64 if possible.
// If the value cannot be converted to a float64, nil and an error are returned.
// If the value is nil, nil is returned without an error.
Get(ctx context.Context, tCtx K) (*float64, error)
}

type StandardFloatLikeGetter[K any] struct {
Getter func(ctx context.Context, tCtx K) (interface{}, error)
}

func (g StandardFloatLikeGetter[K]) Get(ctx context.Context, tCtx K) (*float64, error) {
val, err := g.Getter(ctx, tCtx)
if err != nil {
return nil, err
}
if val == nil {
return nil, nil
}
var result float64
switch v := val.(type) {
case float64:
result = v
case int64:
result = float64(v)
case string:
result, err = strconv.ParseFloat(v, 64)
if err != nil {
return nil, err
}
case bool:
if v {
result = float64(1)
} else {
result = float64(0)
}
case pcommon.Value:
switch v.Type() {
case pcommon.ValueTypeDouble:
result = v.Double()
case pcommon.ValueTypeInt:
result = float64(v.Int())
case pcommon.ValueTypeStr:
result, err = strconv.ParseFloat(v.Str(), 64)
if err != nil {
return nil, err
}
case pcommon.ValueTypeBool:
if v.Bool() {
result = float64(1)
} else {
result = float64(0)
}
default:
return nil, fmt.Errorf("unsupported value type: %v", v.Type())
}
default:
return nil, fmt.Errorf("unsupported type: %T", v)
}
return &result, nil
}

func (p *Parser[K]) newGetter(val value) (Getter[K], error) {
if val.IsNil != nil && *val.IsNil {
return &literal[K]{value: nil}, nil
Expand Down
163 changes: 163 additions & 0 deletions pkg/ottl/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -808,3 +808,166 @@ func Test_StandardStringLikeGetter(t *testing.T) {
})
}
}

func Test_StandardFloatLikeGetter(t *testing.T) {
tests := []struct {
name string
getter FloatLikeGetter[interface{}]
want interface{}
valid bool
expectedErrorMsg string
}{
{
name: "string type",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return "1.0", nil
},
},
want: 1.0,
valid: true,
},
{
name: "int64 type",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return int64(1), nil
},
},
want: float64(1),
valid: true,
},
{
name: "float64 type",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return 1.1, nil
},
},
want: 1.1,
valid: true,
},
{
name: "float64 bool true",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return true, nil
},
},
want: float64(1),
valid: true,
},
{
name: "float64 bool false",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return false, nil
},
},
want: float64(0),
valid: true,
},
{
name: "pcommon.value type int",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
v := pcommon.NewValueInt(int64(100))
return v, nil
},
},
want: float64(100),
valid: true,
},
{
name: "pcommon.value type float",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
v := pcommon.NewValueDouble(float64(1.1))
return v, nil
},
},
want: 1.1,
valid: true,
},
{
name: "pcommon.value type string",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
v := pcommon.NewValueStr("1.1")
return v, nil
},
},
want: 1.1,
valid: true,
},
{
name: "pcommon.value type bool true",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
v := pcommon.NewValueBool(true)
return v, nil
},
},
want: float64(1),
valid: true,
},
{
name: "pcommon.value type bool false",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
v := pcommon.NewValueBool(false)
return v, nil
},
},
want: float64(0),
valid: true,
},
{
name: "nil",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return nil, nil
},
},
want: nil,
valid: true,
},
{
name: "invalid type",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
return []byte{}, nil
},
},
valid: false,
expectedErrorMsg: "unsupported type: []uint8",
},
{
name: "invalid pcommon.Value type",
getter: StandardFloatLikeGetter[interface{}]{
Getter: func(ctx context.Context, tCtx interface{}) (interface{}, error) {
v := pcommon.NewValueMap()
return v, nil
},
},
valid: false,
expectedErrorMsg: "unsupported value type: Map",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
val, err := tt.getter.Get(context.Background(), nil)
if tt.valid {
assert.NoError(t, err)
if tt.want == nil {
assert.Nil(t, val)
} else {
assert.Equal(t, tt.want, *val)
}
} else {
assert.EqualError(t, err, tt.expectedErrorMsg)
}
})
}
}
24 changes: 24 additions & 0 deletions pkg/ottl/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,18 @@ func (p *Parser[K]) buildSliceArg(argVal value, argType reflect.Type) (any, erro
return nil, err
}
return arg, nil
case strings.HasPrefix(name, "FloatGetter"):
arg, err := buildSlice[FloatGetter[K]](argVal, argType, p.buildArg, name)
if err != nil {
return nil, err
}
return arg, nil
case strings.HasPrefix(name, "FloatLikeGetter"):
arg, err := buildSlice[FloatLikeGetter[K]](argVal, argType, p.buildArg, name)
if err != nil {
return nil, err
}
return arg, nil
default:
return nil, fmt.Errorf("unsupported slice type '%s' for function", argType.Elem().Name())
}
Expand Down Expand Up @@ -193,6 +205,18 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) {
return nil, err
}
return StandardStringLikeGetter[K]{Getter: arg.Get}, nil
case strings.HasPrefix(name, "FloatGetter"):
arg, err := p.newGetter(argVal)
if err != nil {
return nil, err
}
return StandardTypeGetter[K, float64]{Getter: arg.Get}, nil
case strings.HasPrefix(name, "FloatLikeGetter"):
arg, err := p.newGetter(argVal)
if err != nil {
return nil, err
}
return StandardFloatLikeGetter[K]{Getter: arg.Get}, nil
case strings.HasPrefix(name, "IntGetter"):
arg, err := p.newGetter(argVal)
if err != nil {
Expand Down
Loading

0 comments on commit 0e43390

Please sign in to comment.