Skip to content
Merged
54 changes: 34 additions & 20 deletions pkg/ottl/contexts/internal/ctxutil/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,9 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl.
return nil, errors.New("cannot get map value without keys")
}

s, err := keys[0].String(ctx, tCtx)
s, err := GetMapKeyName(ctx, tCtx, keys[0])
if err != nil {
return nil, err
}
if s == nil {
resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[0])
if err != nil {
return nil, fmt.Errorf("unable to resolve a string index in map: %w", err)
}
s = resString
return nil, fmt.Errorf("cannot get map value: %w", err)
}

val, ok := m.Get(*s)
Expand All @@ -40,19 +33,12 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl.

func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl.Key[K], val any) error {
if len(keys) == 0 {
return errors.New("cannot set map value without key")
return errors.New("cannot set map value without keys")
}

s, err := keys[0].String(ctx, tCtx)
s, err := GetMapKeyName(ctx, tCtx, keys[0])
if err != nil {
return err
}
if s == nil {
resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[0])
if err != nil {
return fmt.Errorf("unable to resolve a string index in map: %w", err)
}
s = resString
return fmt.Errorf("cannot set map value: %w", err)
}

currentValue, ok := m.Get(*s)
Expand All @@ -62,6 +48,20 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl.
return SetIndexableValue[K](ctx, tCtx, currentValue, val, keys[1:])
}

func GetMapKeyName[K any](ctx context.Context, tCtx K, key ottl.Key[K]) (*string, error) {
resolvedKey, err := key.String(ctx, tCtx)
if err != nil {
return nil, err
}
if resolvedKey == nil {
resolvedKey, err = FetchValueFromExpression[K, string](ctx, tCtx, key)
if err != nil {
return nil, fmt.Errorf("unable to resolve a string index in map: %w", err)
}
}
return resolvedKey, nil
}

func FetchValueFromExpression[K any, T int64 | string](ctx context.Context, tCtx K, key ottl.Key[K]) (*T, error) {
p, err := key.ExpressionGetter(ctx, tCtx)
if err != nil {
Expand Down Expand Up @@ -89,5 +89,19 @@ func SetMap(target pcommon.Map, val any) error {
if rm, ok := val.(map[string]any); ok {
return target.FromRaw(rm)
}
return nil
return fmt.Errorf("failed to convert type %T into pcommon.Map", val)
}

func GetMap(val any) (pcommon.Map, error) {
if m, ok := val.(pcommon.Map); ok {
return m, nil
}
if rm, ok := val.(map[string]any); ok {
m := pcommon.NewMap()
if err := m.FromRaw(rm); err != nil {
return pcommon.Map{}, err
}
return m, nil
}
return pcommon.Map{}, fmt.Errorf("failed to convert type %T into pcommon.Map", val)
}
134 changes: 132 additions & 2 deletions pkg/ottl/contexts/internal/ctxutil/map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/pdata/pcommon"

"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
Expand Down Expand Up @@ -39,7 +40,7 @@ func Test_GetMapValue_Invalid(t *testing.T) {
G: getSetter,
},
},
err: errors.New("unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
err: errors.New("cannot get map value: unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
},
{
name: "index map with int",
Expand Down Expand Up @@ -171,7 +172,7 @@ func Test_SetMapValue_Invalid(t *testing.T) {
G: getSetter,
},
},
err: errors.New("unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
err: errors.New("cannot set map value: unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
},
{
name: "index map with int",
Expand Down Expand Up @@ -311,3 +312,132 @@ func Test_SetMapValue_NilKey(t *testing.T) {
err := ctxutil.SetMapValue[any](context.Background(), nil, pcommon.NewMap(), nil, "bar")
assert.Error(t, err)
}

func Test_SetMap(t *testing.T) {
createMap := func() pcommon.Map {
m := pcommon.NewMap()
require.NoError(t, m.FromRaw(map[string]any{"foo": "bar"}))
return m
}
tests := []struct {
name string
val any
err error
}{
{
name: "invalid type",
val: "invalid",
err: errors.New("failed to convert type string into pcommon.Map"),
},
{
name: "raw map",
val: map[string]any{"foo": "bar"},
},
{
name: "pcommon.Map",
val: createMap(),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := pcommon.NewMap()
err := ctxutil.SetMap(m, tt.val)
if tt.err != nil {
require.Equal(t, tt.err, err)
return
}
assert.Equal(t, m, createMap())
})
}
}

func Test_GetMap(t *testing.T) {
createMap := func() pcommon.Map {
m := pcommon.NewMap()
require.NoError(t, m.FromRaw(map[string]any{"foo": "bar"}))
return m
}
tests := []struct {
name string
val any
err error
}{
{
name: "invalid type",
val: "invalid",
err: errors.New("failed to convert type string into pcommon.Map"),
},
{
name: "raw map",
val: map[string]any{"foo": "bar"},
},
{
name: "pcommon.Map",
val: createMap(),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m, err := ctxutil.GetMap(tt.val)
if tt.err != nil {
require.Equal(t, tt.err, err)
return
}
assert.Equal(t, m, createMap())
})
}
}

func Test_GetMapKeyName(t *testing.T) {
getSetter := &ottl.StandardGetSetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return nil, nil
},
}
tests := []struct {
name string
keys []ottl.Key[any]
err error
key string
}{
{
name: "first key not a string",
keys: []ottl.Key[any]{
&pathtest.Key[any]{
I: ottltest.Intp(0),
G: getSetter,
},
},
err: errors.New("unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
},
{
name: "first key not initialized",
keys: []ottl.Key[any]{
&pathtest.Key[any]{},
},
err: errors.New("unable to resolve a string index in map: invalid key type"),
},
{
name: "valid",
keys: []ottl.Key[any]{
&pathtest.Key[any]{
S: ottltest.Strp("string"),
},
},
key: "string",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resolvedKey, err := ctxutil.GetMapKeyName[any](context.Background(), nil, tt.keys[0])
if tt.err != nil {
assert.Equal(t, tt.err.Error(), err.Error())
return
}
assert.Equal(t, tt.key, *resolvedKey)
})
}
}
18 changes: 2 additions & 16 deletions pkg/ottl/contexts/internal/ctxutil/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,10 @@ func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value,
for index := 0; index < len(keys); index++ {
switch val.Type() {
case pcommon.ValueTypeMap:
s, err := keys[index].String(ctx, tCtx)
s, err := GetMapKeyName(ctx, tCtx, keys[index])
if err != nil {
return nil, err
}
if s == nil {
resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[index])
if err != nil {
return nil, fmt.Errorf("unable to resolve a string index in map: %w", err)
}
s = resString
}
val, ok = val.Map().Get(*s)
if !ok {
return nil, nil
Expand Down Expand Up @@ -128,17 +121,10 @@ func SetIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon.
for index := 0; index < len(keys); index++ {
switch currentValue.Type() {
case pcommon.ValueTypeMap:
s, err := keys[index].String(ctx, tCtx)
s, err := GetMapKeyName(ctx, tCtx, keys[index])
if err != nil {
return err
}
if s == nil {
resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[index])
if err != nil {
return fmt.Errorf("unable to resolve a string index in map: %w", err)
}
s = resString
}
potentialValue, ok := currentValue.Map().Get(*s)
if !ok {
currentValue = currentValue.Map().PutEmpty(*s)
Expand Down
Loading