diff --git a/pkg/ottl/contexts/internal/ctxutil/map.go b/pkg/ottl/contexts/internal/ctxutil/map.go index 232f89247d8a2..e935a39ce0145 100644 --- a/pkg/ottl/contexts/internal/ctxutil/map.go +++ b/pkg/ottl/contexts/internal/ctxutil/map.go @@ -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) @@ -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) @@ -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 { @@ -91,3 +91,17 @@ func SetMap(target pcommon.Map, val any) error { } return nil } + +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) +} diff --git a/pkg/ottl/contexts/internal/ctxutil/map_test.go b/pkg/ottl/contexts/internal/ctxutil/map_test.go index 90192b49fe180..b6508fd6243b3 100644 --- a/pkg/ottl/contexts/internal/ctxutil/map_test.go +++ b/pkg/ottl/contexts/internal/ctxutil/map_test.go @@ -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" @@ -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 ''"), + 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 ''"), }, { name: "index map with int", @@ -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 ''"), + 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 ''"), }, { name: "index map with int", @@ -311,3 +312,136 @@ 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 + expected any + }{ + { + name: "invalid type", + val: "invalid", + err: nil, // This is an issue in SetMap(), not returning an error here. + expected: pcommon.NewMap(), + }, + { + name: "raw map", + val: map[string]any{"foo": "bar"}, + expected: createMap(), + }, + { + name: "pcommon.Map", + val: createMap(), + expected: 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, tt.expected, m) + }) + } +} + +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 ''"), + }, + { + 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) + }) + } +} diff --git a/pkg/ottl/contexts/internal/ctxutil/value.go b/pkg/ottl/contexts/internal/ctxutil/value.go index bc1da05c62dc9..09a0c1a71ae3f 100644 --- a/pkg/ottl/contexts/internal/ctxutil/value.go +++ b/pkg/ottl/contexts/internal/ctxutil/value.go @@ -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 @@ -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)