diff --git a/.chloggen/fix-ottl-functions.yaml b/.chloggen/fix-ottl-functions.yaml new file mode 100644 index 0000000000000..e01399109041c --- /dev/null +++ b/.chloggen/fix-ottl-functions.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# 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: Fix OTTL functions by using setters. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [39100] + +# (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: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/pkg/ottl/ottlfuncs/func_delete_key.go b/pkg/ottl/ottlfuncs/func_delete_key.go index 9f0921c74fb13..c29d2c5f395ce 100644 --- a/pkg/ottl/ottlfuncs/func_delete_key.go +++ b/pkg/ottl/ottlfuncs/func_delete_key.go @@ -11,7 +11,7 @@ import ( ) type DeleteKeyArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Key string } @@ -29,13 +29,13 @@ func createDeleteKeyFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments return deleteKey(args.Target, args.Key), nil } -func deleteKey[K any](target ottl.PMapGetter[K], key string) ottl.ExprFunc[K] { +func deleteKey[K any](target ottl.PMapGetSetter[K], key string) ottl.ExprFunc[K] { return func(ctx context.Context, tCtx K) (any, error) { val, err := target.Get(ctx, tCtx) if err != nil { return nil, err } val.Remove(key) - return nil, nil + return nil, target.Set(ctx, tCtx, val) } } diff --git a/pkg/ottl/ottlfuncs/func_delete_key_test.go b/pkg/ottl/ottlfuncs/func_delete_key_test.go index 502aee5077016..fbb2fdef23176 100644 --- a/pkg/ottl/ottlfuncs/func_delete_key_test.go +++ b/pkg/ottl/ottlfuncs/func_delete_key_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -19,40 +20,30 @@ func Test_deleteKey(t *testing.T) { input.PutInt("test2", 3) input.PutBool("test3", true) - target := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { - name string - target ottl.PMapGetter[pcommon.Map] - key string - want func(pcommon.Map) + name string + key string + want func(pcommon.Map) }{ { - name: "delete test", - target: target, - key: "test", + name: "delete test", + key: "test", want: func(expectedMap pcommon.Map) { expectedMap.PutBool("test3", true) expectedMap.PutInt("test2", 3) }, }, { - name: "delete test2", - target: target, - key: "test2", + name: "delete test2", + key: "test2", want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutBool("test3", true) }, }, { - name: "delete nothing", - target: target, - key: "not a valid key", + name: "delete nothing", + key: "not a valid key", want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutInt("test2", 3) @@ -65,10 +56,26 @@ func Test_deleteKey(t *testing.T) { scenarioMap := pcommon.NewMap() input.CopyTo(scenarioMap) - exprFunc := deleteKey(tt.target, tt.key) + setterWasCalled := false + target := &ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, val any) error { + setterWasCalled = true + if v, ok := val.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc := deleteKey(target, tt.key) _, err := exprFunc(nil, scenarioMap) assert.NoError(t, err) + assert.True(t, setterWasCalled) expected := pcommon.NewMap() tt.want(expected) @@ -80,9 +87,12 @@ func Test_deleteKey(t *testing.T) { func Test_deleteKey_bad_input(t *testing.T) { input := pcommon.NewValueStr("not a map") - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } @@ -94,9 +104,12 @@ func Test_deleteKey_bad_input(t *testing.T) { } func Test_deleteKey_get_nil(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } diff --git a/pkg/ottl/ottlfuncs/func_delete_matching_keys.go b/pkg/ottl/ottlfuncs/func_delete_matching_keys.go index d4ee87c83ac15..55d204be72e19 100644 --- a/pkg/ottl/ottlfuncs/func_delete_matching_keys.go +++ b/pkg/ottl/ottlfuncs/func_delete_matching_keys.go @@ -15,7 +15,7 @@ import ( ) type DeleteMatchingKeysArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Pattern string } @@ -33,7 +33,7 @@ func createDeleteMatchingKeysFunction[K any](_ ottl.FunctionContext, oArgs ottl. return deleteMatchingKeys(args.Target, args.Pattern) } -func deleteMatchingKeys[K any](target ottl.PMapGetter[K], pattern string) (ottl.ExprFunc[K], error) { +func deleteMatchingKeys[K any](target ottl.PMapGetSetter[K], pattern string) (ottl.ExprFunc[K], error) { compiledPattern, err := regexp.Compile(pattern) if err != nil { return nil, fmt.Errorf("the regex pattern supplied to delete_matching_keys is not a valid pattern: %w", err) @@ -46,6 +46,6 @@ func deleteMatchingKeys[K any](target ottl.PMapGetter[K], pattern string) (ottl. val.RemoveIf(func(key string, _ pcommon.Value) bool { return compiledPattern.MatchString(key) }) - return nil, nil + return nil, target.Set(ctx, tCtx, val) }, nil } diff --git a/pkg/ottl/ottlfuncs/func_delete_matching_keys_test.go b/pkg/ottl/ottlfuncs/func_delete_matching_keys_test.go index 14627f9ea5ee5..554cfc90ef560 100644 --- a/pkg/ottl/ottlfuncs/func_delete_matching_keys_test.go +++ b/pkg/ottl/ottlfuncs/func_delete_matching_keys_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -20,21 +21,13 @@ func Test_deleteMatchingKeys(t *testing.T) { input.PutInt("test2", 3) input.PutBool("test3", true) - target := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { name string - target ottl.PMapGetter[pcommon.Map] pattern string want func(pcommon.Map) }{ { name: "delete everything", - target: target, pattern: "test.*", want: func(expectedMap pcommon.Map) { expectedMap.EnsureCapacity(3) @@ -42,7 +35,6 @@ func Test_deleteMatchingKeys(t *testing.T) { }, { name: "delete attributes that end in a number", - target: target, pattern: "\\d$", want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") @@ -50,7 +42,6 @@ func Test_deleteMatchingKeys(t *testing.T) { }, { name: "delete nothing", - target: target, pattern: "not a matching pattern", want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") @@ -64,11 +55,27 @@ func Test_deleteMatchingKeys(t *testing.T) { scenarioMap := pcommon.NewMap() input.CopyTo(scenarioMap) - exprFunc, err := deleteMatchingKeys(tt.target, tt.pattern) + setterWasCalled := false + target := &ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, m any) error { + setterWasCalled = true + if v, ok := m.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc, err := deleteMatchingKeys(target, tt.pattern) assert.NoError(t, err) _, err = exprFunc(nil, scenarioMap) assert.NoError(t, err) + assert.True(t, setterWasCalled) expected := pcommon.NewMap() tt.want(expected) @@ -80,9 +87,12 @@ func Test_deleteMatchingKeys(t *testing.T) { func Test_deleteMatchingKeys_bad_input(t *testing.T) { input := pcommon.NewValueInt(1) - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } @@ -94,9 +104,12 @@ func Test_deleteMatchingKeys_bad_input(t *testing.T) { } func Test_deleteMatchingKeys_get_nil(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } @@ -107,10 +120,10 @@ func Test_deleteMatchingKeys_get_nil(t *testing.T) { } func Test_deleteMatchingKeys_invalid_pattern(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, _ any) (any, error) { + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, _ any) (pcommon.Map, error) { t.Errorf("nothing should be received in this scenario") - return nil, nil + return pcommon.Map{}, nil }, } diff --git a/pkg/ottl/ottlfuncs/func_flatten.go b/pkg/ottl/ottlfuncs/func_flatten.go index d6e0f5ea56d37..282191f999ee8 100644 --- a/pkg/ottl/ottlfuncs/func_flatten.go +++ b/pkg/ottl/ottlfuncs/func_flatten.go @@ -16,7 +16,7 @@ import ( ) type FlattenArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Prefix ottl.Optional[string] Depth ottl.Optional[int64] ResolveConflicts ottl.Optional[bool] @@ -43,7 +43,7 @@ func createFlattenFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) return flatten(args.Target, args.Prefix, args.Depth, args.ResolveConflicts) } -func flatten[K any](target ottl.PMapGetter[K], p ottl.Optional[string], d ottl.Optional[int64], c ottl.Optional[bool]) (ottl.ExprFunc[K], error) { +func flatten[K any](target ottl.PMapGetSetter[K], p ottl.Optional[string], d ottl.Optional[int64], c ottl.Optional[bool]) (ottl.ExprFunc[K], error) { depth := int64(math.MaxInt64) if !d.IsEmpty() { depth = d.Get() @@ -70,9 +70,7 @@ func flatten[K any](target ottl.PMapGetter[K], p ottl.Optional[string], d ottl.O flattenData := initFlattenData(resolveConflict, depth) flattenData.flattenMap(m, prefix, 0) - flattenData.result.MoveTo(m) - - return nil, nil + return nil, target.Set(ctx, tCtx, flattenData.result) }, nil } diff --git a/pkg/ottl/ottlfuncs/func_flatten_test.go b/pkg/ottl/ottlfuncs/func_flatten_test.go index 8dc8eb3e9d7ea..e727d1be0b03d 100644 --- a/pkg/ottl/ottlfuncs/func_flatten_test.go +++ b/pkg/ottl/ottlfuncs/func_flatten_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "reflect" "testing" @@ -340,16 +341,28 @@ func Test_flatten(t *testing.T) { m := pcommon.NewMap() err := m.FromRaw(tt.target) assert.NoError(t, err) - target := ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, _ any) (any, error) { + + setterWasCalled := false + target := ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, _ any) (pcommon.Map, error) { return m, nil }, + Setter: func(_ context.Context, _ any, val any) error { + setterWasCalled = true + if v, ok := val.(pcommon.Map); ok { + v.CopyTo(m) + return nil + } + return errors.New("expected pcommon.Map") + }, } exprFunc, err := flatten[any](target, tt.prefix, tt.depth, ottl.NewTestingOptional[bool](tt.conflict)) assert.NoError(t, err) + _, err = exprFunc(nil, nil) assert.NoError(t, err) + assert.True(t, setterWasCalled) assert.Equal(t, tt.expected, m.AsRaw()) }) @@ -490,16 +503,28 @@ func Test_flatten_undeterministic(t *testing.T) { m := pcommon.NewMap() err := m.FromRaw(tt.target) assert.NoError(t, err) - target := ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, _ any) (any, error) { + + setterWasCalled := false + target := ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, _ any) (pcommon.Map, error) { return m, nil }, + Setter: func(_ context.Context, _ any, val any) error { + setterWasCalled = true + if v, ok := val.(pcommon.Map); ok { + v.CopyTo(m) + return nil + } + return errors.New("expected pcommon.Map") + }, } exprFunc, err := flatten[any](target, tt.prefix, tt.depth, ottl.NewTestingOptional[bool](tt.conflict)) assert.NoError(t, err) + _, err = exprFunc(nil, nil) assert.NoError(t, err) + assert.True(t, setterWasCalled) keys, val := extractKeysAndValues(m.AsRaw()) @@ -509,18 +534,6 @@ func Test_flatten_undeterministic(t *testing.T) { } } -func Test_flatten_bad_target(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, _ any) (any, error) { - return 1, nil - }, - } - exprFunc, err := flatten[any](target, ottl.Optional[string]{}, ottl.Optional[int64]{}, ottl.NewTestingOptional[bool](false)) - assert.NoError(t, err) - _, err = exprFunc(nil, nil) - assert.Error(t, err) -} - func Test_flatten_bad_depth(t *testing.T) { tests := []struct { name string @@ -538,8 +551,8 @@ func Test_flatten_bad_depth(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, _ any) (any, error) { + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, _ any) (pcommon.Map, error) { return pcommon.NewMap(), nil }, } diff --git a/pkg/ottl/ottlfuncs/func_keep_keys.go b/pkg/ottl/ottlfuncs/func_keep_keys.go index 3ac8dd0ae9e88..baf1d921932b1 100644 --- a/pkg/ottl/ottlfuncs/func_keep_keys.go +++ b/pkg/ottl/ottlfuncs/func_keep_keys.go @@ -13,7 +13,7 @@ import ( ) type KeepKeysArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Keys []string } @@ -31,7 +31,7 @@ func createKeepKeysFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) return keepKeys(args.Target, args.Keys), nil } -func keepKeys[K any](target ottl.PMapGetter[K], keys []string) ottl.ExprFunc[K] { +func keepKeys[K any](target ottl.PMapGetSetter[K], keys []string) ottl.ExprFunc[K] { keySet := make(map[string]struct{}, len(keys)) for _, key := range keys { keySet[key] = struct{}{} @@ -49,6 +49,6 @@ func keepKeys[K any](target ottl.PMapGetter[K], keys []string) ottl.ExprFunc[K] if val.Len() == 0 { val.Clear() } - return nil, nil + return nil, target.Set(ctx, tCtx, val) } } diff --git a/pkg/ottl/ottlfuncs/func_keep_keys_test.go b/pkg/ottl/ottlfuncs/func_keep_keys_test.go index e3f8f3009b8c6..0926b7385aef2 100644 --- a/pkg/ottl/ottlfuncs/func_keep_keys_test.go +++ b/pkg/ottl/ottlfuncs/func_keep_keys_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -19,46 +20,35 @@ func Test_keepKeys(t *testing.T) { input.PutInt("test2", 3) input.PutBool("test3", true) - target := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { - name string - target ottl.PMapGetter[pcommon.Map] - keys []string - want func(pcommon.Map) + name string + keys []string + want func(pcommon.Map) }{ { - name: "keep one", - target: target, - keys: []string{"test"}, + name: "keep one", + keys: []string{"test"}, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") }, }, { - name: "keep two", - target: target, - keys: []string{"test", "test2"}, + name: "keep two", + keys: []string{"test", "test2"}, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutInt("test2", 3) }, }, { - name: "keep none", - target: target, - keys: []string{}, - want: func(_ pcommon.Map) {}, + name: "keep none", + keys: []string{}, + want: func(_ pcommon.Map) {}, }, { - name: "no match", - target: target, - keys: []string{"no match"}, - want: func(_ pcommon.Map) {}, + name: "no match", + keys: []string{"no match"}, + want: func(_ pcommon.Map) {}, }, } for _, tt := range tests { @@ -66,10 +56,26 @@ func Test_keepKeys(t *testing.T) { scenarioMap := pcommon.NewMap() input.CopyTo(scenarioMap) - exprFunc := keepKeys(tt.target, tt.keys) + setterWasCalled := false + target := &ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, m any) error { + setterWasCalled = true + if v, ok := m.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc := keepKeys(target, tt.keys) _, err := exprFunc(nil, scenarioMap) assert.NoError(t, err) + assert.True(t, setterWasCalled) expected := pcommon.NewMap() tt.want(expected) @@ -81,9 +87,12 @@ func Test_keepKeys(t *testing.T) { func Test_keepKeys_bad_input(t *testing.T) { input := pcommon.NewValueStr("not a map") - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } @@ -96,9 +105,12 @@ func Test_keepKeys_bad_input(t *testing.T) { } func Test_keepKeys_get_nil(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } diff --git a/pkg/ottl/ottlfuncs/func_keep_matching_keys.go b/pkg/ottl/ottlfuncs/func_keep_matching_keys.go index 8b743b795de3a..258caeb791af9 100644 --- a/pkg/ottl/ottlfuncs/func_keep_matching_keys.go +++ b/pkg/ottl/ottlfuncs/func_keep_matching_keys.go @@ -14,7 +14,7 @@ import ( ) type KeepMatchingKeysArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Pattern string } @@ -32,7 +32,7 @@ func createKeepMatchingKeysFunction[K any](_ ottl.FunctionContext, oArgs ottl.Ar return keepMatchingKeys(args.Target, args.Pattern) } -func keepMatchingKeys[K any](target ottl.PMapGetter[K], pattern string) (ottl.ExprFunc[K], error) { +func keepMatchingKeys[K any](target ottl.PMapGetSetter[K], pattern string) (ottl.ExprFunc[K], error) { compiledPattern, err := regexp.Compile(pattern) if err != nil { return nil, fmt.Errorf("the regex pattern provided to keep_matching_keys is not a valid pattern: %w", err) @@ -47,6 +47,6 @@ func keepMatchingKeys[K any](target ottl.PMapGetter[K], pattern string) (ottl.Ex val.RemoveIf(func(key string, _ pcommon.Value) bool { return !compiledPattern.MatchString(key) }) - return nil, nil + return nil, target.Set(ctx, tCtx, val) }, nil } diff --git a/pkg/ottl/ottlfuncs/func_keep_matching_keys_test.go b/pkg/ottl/ottlfuncs/func_keep_matching_keys_test.go index c77e70c2fb224..a44594d8ae6e7 100644 --- a/pkg/ottl/ottlfuncs/func_keep_matching_keys_test.go +++ b/pkg/ottl/ottlfuncs/func_keep_matching_keys_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -19,22 +20,14 @@ func Test_keepMatchingKeys(t *testing.T) { in.PutStr("foo1", "bar") in.PutInt("foo2", 3) - target := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { name string - target ottl.PMapGetter[pcommon.Map] pattern string want func() *pcommon.Map wantError bool }{ { name: "keep everything that ends with a number", - target: target, pattern: "\\d$", want: func() *pcommon.Map { m := pcommon.NewMap() @@ -45,7 +38,6 @@ func Test_keepMatchingKeys(t *testing.T) { }, { name: "keep nothing", - target: target, pattern: "bar.*", want: func() *pcommon.Map { m := pcommon.NewMap() @@ -57,7 +49,6 @@ func Test_keepMatchingKeys(t *testing.T) { }, { name: "keep everything", - target: target, pattern: "foo.*", want: func() *pcommon.Map { m := pcommon.NewMap() @@ -69,7 +60,6 @@ func Test_keepMatchingKeys(t *testing.T) { }, { name: "invalid pattern", - target: target, pattern: "*", want: func() *pcommon.Map { return nil @@ -82,7 +72,22 @@ func Test_keepMatchingKeys(t *testing.T) { scenarioMap := pcommon.NewMap() in.CopyTo(scenarioMap) - exprFunc, err := keepMatchingKeys(tt.target, tt.pattern) + setterWasCalled := false + target := &ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, m any) error { + setterWasCalled = true + if v, ok := m.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc, err := keepMatchingKeys(target, tt.pattern) if tt.wantError { assert.Error(t, err) @@ -92,6 +97,7 @@ func Test_keepMatchingKeys(t *testing.T) { _, err = exprFunc(nil, scenarioMap) assert.NoError(t, err) + assert.True(t, setterWasCalled) assert.Equal(t, *tt.want(), scenarioMap) }) @@ -100,9 +106,12 @@ func Test_keepMatchingKeys(t *testing.T) { func Test_keepMatchingKeys_bad_input(t *testing.T) { input := pcommon.NewValueInt(1) - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } @@ -114,9 +123,12 @@ func Test_keepMatchingKeys_bad_input(t *testing.T) { } func Test_keepMatchingKeys_get_nil(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } diff --git a/pkg/ottl/ottlfuncs/func_limit.go b/pkg/ottl/ottlfuncs/func_limit.go index 0749a59f50c28..0f8e21f17bcfc 100644 --- a/pkg/ottl/ottlfuncs/func_limit.go +++ b/pkg/ottl/ottlfuncs/func_limit.go @@ -14,7 +14,7 @@ import ( ) type LimitArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Limit int64 PriorityKeys []string } @@ -33,7 +33,7 @@ func createLimitFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) (o return limit(args.Target, args.Limit, args.PriorityKeys) } -func limit[K any](target ottl.PMapGetter[K], limit int64, priorityKeys []string) (ottl.ExprFunc[K], error) { +func limit[K any](target ottl.PMapGetSetter[K], limit int64, priorityKeys []string) (ottl.ExprFunc[K], error) { if limit < 0 { return nil, fmt.Errorf("invalid limit for limit function, %d cannot be negative", limit) } @@ -77,6 +77,6 @@ func limit[K any](target ottl.PMapGetter[K], limit int64, priorityKeys []string) }) // TODO: Write log when limiting is performed // https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9730 - return nil, nil + return nil, target.Set(ctx, tCtx, val) }, nil } diff --git a/pkg/ottl/ottlfuncs/func_limit_test.go b/pkg/ottl/ottlfuncs/func_limit_test.go index d048ac3ca0963..a12e2b10e5991 100644 --- a/pkg/ottl/ottlfuncs/func_limit_test.go +++ b/pkg/ottl/ottlfuncs/func_limit_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -19,39 +20,29 @@ func Test_limit(t *testing.T) { input.PutInt("test2", 3) input.PutBool("test3", true) - target := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { - name string - target ottl.PMapGetter[pcommon.Map] - limit int64 - keep []string - want func(pcommon.Map) + name string + limit int64 + keep []string + want func(pcommon.Map) }{ { - name: "limit to 1", - target: target, - limit: int64(1), + name: "limit to 1", + limit: int64(1), want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") }, }, { - name: "limit to zero", - target: target, - limit: int64(0), + name: "limit to zero", + limit: int64(0), want: func(expectedMap pcommon.Map) { expectedMap.EnsureCapacity(input.Len()) }, }, { - name: "limit nothing", - target: target, - limit: int64(100), + name: "limit nothing", + limit: int64(100), want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutInt("test2", 3) @@ -59,9 +50,8 @@ func Test_limit(t *testing.T) { }, }, { - name: "limit exact", - target: target, - limit: int64(3), + name: "limit exact", + limit: int64(3), want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutInt("test2", 3) @@ -69,39 +59,35 @@ func Test_limit(t *testing.T) { }, }, { - name: "keep one key", - target: target, - limit: int64(2), - keep: []string{"test3"}, + name: "keep one key", + limit: int64(2), + keep: []string{"test3"}, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutBool("test3", true) }, }, { - name: "keep same # of keys as limit", - target: target, - limit: int64(2), - keep: []string{"test", "test3"}, + name: "keep same # of keys as limit", + limit: int64(2), + keep: []string{"test", "test3"}, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutBool("test3", true) }, }, { - name: "keep not existing key", - target: target, - limit: int64(1), - keep: []string{"te"}, + name: "keep not existing key", + limit: int64(1), + keep: []string{"te"}, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") }, }, { - name: "keep not-/existing keys", - target: target, - limit: int64(2), - keep: []string{"te", "test3"}, + name: "keep not-/existing keys", + limit: int64(2), + keep: []string{"te", "test3"}, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutBool("test3", true) @@ -113,12 +99,31 @@ func Test_limit(t *testing.T) { scenarioMap := pcommon.NewMap() input.CopyTo(scenarioMap) - exprFunc, err := limit(tt.target, tt.limit, tt.keep) + setterWasCalled := false + target := &ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, m any) error { + setterWasCalled = true + if v, ok := m.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc, err := limit(target, tt.limit, tt.keep) assert.NoError(t, err) result, err := exprFunc(nil, scenarioMap) assert.NoError(t, err) assert.Nil(t, result) + // There is a shortcut in limit() that does not call the setter. + if int(tt.limit) < scenarioMap.Len() { + assert.True(t, setterWasCalled) + } expected := pcommon.NewMap() tt.want(expected) @@ -131,18 +136,18 @@ func Test_limit(t *testing.T) { func Test_limit_validation(t *testing.T) { tests := []struct { name string - target ottl.PMapGetter[any] + target ottl.PMapGetSetter[any] keep []string limit int64 }{ { name: "limit less than zero", - target: &ottl.StandardPMapGetter[any]{}, + target: &ottl.StandardPMapGetSetter[any]{}, limit: int64(-1), }, { name: "limit less than # of keep attrs", - target: &ottl.StandardPMapGetter[any]{}, + target: &ottl.StandardPMapGetSetter[any]{}, keep: []string{"test", "test"}, limit: int64(1), }, @@ -157,9 +162,12 @@ func Test_limit_validation(t *testing.T) { func Test_limit_bad_input(t *testing.T) { input := pcommon.NewValueStr("not a map") - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } @@ -170,9 +178,12 @@ func Test_limit_bad_input(t *testing.T) { } func Test_limit_get_nil(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } diff --git a/pkg/ottl/ottlfuncs/func_merge_maps.go b/pkg/ottl/ottlfuncs/func_merge_maps.go index 3b1150208b193..c4e334d57933a 100644 --- a/pkg/ottl/ottlfuncs/func_merge_maps.go +++ b/pkg/ottl/ottlfuncs/func_merge_maps.go @@ -18,7 +18,7 @@ const ( ) type MergeMapsArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Source ottl.PMapGetter[K] Strategy string } @@ -43,7 +43,7 @@ func createMergeMapsFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments // insert: Insert the value from `source` into `target` where the key does not already exist. // update: Update the entry in `target` with the value from `source` where the key does exist // upsert: Performs insert or update. Insert the value from `source` into `target` where the key does not already exist and update the entry in `target` with the value from `source` where the key does exist. -func mergeMaps[K any](target ottl.PMapGetter[K], source ottl.PMapGetter[K], strategy string) (ottl.ExprFunc[K], error) { +func mergeMaps[K any](target ottl.PMapGetSetter[K], source ottl.PMapGetter[K], strategy string) (ottl.ExprFunc[K], error) { if strategy != INSERT && strategy != UPDATE && strategy != UPSERT { return nil, fmt.Errorf("invalid value for strategy, %v, must be 'insert', 'update' or 'upsert'", strategy) } @@ -79,6 +79,6 @@ func mergeMaps[K any](target ottl.PMapGetter[K], source ottl.PMapGetter[K], stra default: return nil, fmt.Errorf("unknown strategy, %v", strategy) } - return nil, nil + return nil, target.Set(ctx, tCtx, targetMap) }, nil } diff --git a/pkg/ottl/ottlfuncs/func_merge_maps_test.go b/pkg/ottl/ottlfuncs/func_merge_maps_test.go index 7a00c43bb8910..62b0ca0278b23 100644 --- a/pkg/ottl/ottlfuncs/func_merge_maps_test.go +++ b/pkg/ottl/ottlfuncs/func_merge_maps_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -17,12 +18,6 @@ func Test_MergeMaps(t *testing.T) { input := pcommon.NewMap() input.PutStr("attr1", "value1") - targetGetter := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { name string source ottl.PMapGetter[pcommon.Map] @@ -125,12 +120,28 @@ func Test_MergeMaps(t *testing.T) { scenarioMap := pcommon.NewMap() input.CopyTo(scenarioMap) - exprFunc, err := mergeMaps[pcommon.Map](targetGetter, tt.source, tt.strategy) + setterWasCalled := false + target := &ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, m any) error { + setterWasCalled = true + if v, ok := m.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc, err := mergeMaps[pcommon.Map](target, tt.source, tt.strategy) assert.NoError(t, err) result, err := exprFunc(context.Background(), scenarioMap) assert.NoError(t, err) assert.Nil(t, result) + assert.True(t, setterWasCalled) expected := pcommon.NewMap() tt.want(expected) @@ -146,9 +157,12 @@ func Test_MergeMaps_bad_target(t *testing.T) { return tCtx, nil }, } - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, _ any) (any, error) { - return 1, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } @@ -164,9 +178,12 @@ func Test_MergeMaps_bad_input(t *testing.T) { return 1, nil }, } - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } diff --git a/pkg/ottl/ottlfuncs/func_replace_all_matches.go b/pkg/ottl/ottlfuncs/func_replace_all_matches.go index e027d3d15f172..8639e62015ba6 100644 --- a/pkg/ottl/ottlfuncs/func_replace_all_matches.go +++ b/pkg/ottl/ottlfuncs/func_replace_all_matches.go @@ -15,7 +15,7 @@ import ( ) type ReplaceAllMatchesArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Pattern string Replacement ottl.StringGetter[K] Function ottl.Optional[ottl.FunctionGetter[K]] @@ -40,17 +40,18 @@ func createReplaceAllMatchesFunction[K any](_ ottl.FunctionContext, oArgs ottl.A return replaceAllMatches(args.Target, args.Pattern, args.Replacement, args.Function, args.ReplacementFormat) } -func replaceAllMatches[K any](target ottl.PMapGetter[K], pattern string, replacement ottl.StringGetter[K], fn ottl.Optional[ottl.FunctionGetter[K]], replacementFormat ottl.Optional[ottl.StringGetter[K]]) (ottl.ExprFunc[K], error) { +func replaceAllMatches[K any](target ottl.PMapGetSetter[K], pattern string, replacement ottl.StringGetter[K], fn ottl.Optional[ottl.FunctionGetter[K]], replacementFormat ottl.Optional[ottl.StringGetter[K]]) (ottl.ExprFunc[K], error) { glob, err := glob.Compile(pattern) if err != nil { return nil, fmt.Errorf("the pattern supplied to replace_match is not a valid pattern: %w", err) } return func(ctx context.Context, tCtx K) (any, error) { val, err := target.Get(ctx, tCtx) - var replacementVal string if err != nil { return nil, err } + + var replacementVal string if fn.IsEmpty() { replacementVal, err = replacement.Get(ctx, tCtx) if err != nil { @@ -75,11 +76,12 @@ func replaceAllMatches[K any](target ottl.PMapGetter[K], pattern string, replace return nil, err } } + for _, value := range val.All() { if value.Type() == pcommon.ValueTypeStr && glob.Match(value.Str()) { value.SetStr(replacementVal) } } - return nil, nil + return nil, target.Set(ctx, tCtx, val) }, nil } diff --git a/pkg/ottl/ottlfuncs/func_replace_all_matches_test.go b/pkg/ottl/ottlfuncs/func_replace_all_matches_test.go index f70591d5315c7..173c4336554b3 100644 --- a/pkg/ottl/ottlfuncs/func_replace_all_matches_test.go +++ b/pkg/ottl/ottlfuncs/func_replace_all_matches_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -36,15 +37,8 @@ func Test_replaceAllMatches(t *testing.T) { } optionalArg := ottl.NewTestingOptional[ottl.FunctionGetter[pcommon.Map]](ottlValue) - target := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { name string - target ottl.PMapGetter[pcommon.Map] pattern string replacement ottl.StringGetter[pcommon.Map] function ottl.Optional[ottl.FunctionGetter[pcommon.Map]] @@ -53,7 +47,6 @@ func Test_replaceAllMatches(t *testing.T) { }{ { name: "replace only matches (with hash function)", - target: target, pattern: "hello*", replacement: ottl.StandardStringGetter[pcommon.Map]{ Getter: func(context.Context, pcommon.Map) (any, error) { @@ -72,7 +65,6 @@ func Test_replaceAllMatches(t *testing.T) { }, { name: "replace only matches", - target: target, pattern: "hello*", replacement: ottl.StandardStringGetter[pcommon.Map]{ Getter: func(context.Context, pcommon.Map) (any, error) { @@ -91,7 +83,6 @@ func Test_replaceAllMatches(t *testing.T) { }, { name: "no matches", - target: target, pattern: "nothing*", replacement: ottl.StandardStringGetter[pcommon.Map]{ Getter: func(context.Context, pcommon.Map) (any, error) { @@ -110,7 +101,6 @@ func Test_replaceAllMatches(t *testing.T) { }, { name: "replace empty string", - target: target, pattern: "", replacement: ottl.StandardStringGetter[pcommon.Map]{ Getter: func(context.Context, pcommon.Map) (any, error) { @@ -133,12 +123,27 @@ func Test_replaceAllMatches(t *testing.T) { scenarioMap := pcommon.NewMap() input.CopyTo(scenarioMap) - exprFunc, err := replaceAllMatches(tt.target, tt.pattern, tt.replacement, tt.function, tt.replacementFormat) + setterWasCalled := false + target := &ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, m any) error { + setterWasCalled = true + if v, ok := m.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc, err := replaceAllMatches(target, tt.pattern, tt.replacement, tt.function, tt.replacementFormat) assert.NoError(t, err) - result, err := exprFunc(nil, scenarioMap) + _, err = exprFunc(nil, scenarioMap) assert.NoError(t, err) - assert.Nil(t, result) + assert.True(t, setterWasCalled) expected := pcommon.NewMap() tt.want(expected) @@ -150,9 +155,12 @@ func Test_replaceAllMatches(t *testing.T) { func Test_replaceAllMatches_bad_input(t *testing.T) { input := pcommon.NewValueStr("not a map") - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } replacement := &ottl.StandardStringGetter[any]{ @@ -171,9 +179,12 @@ func Test_replaceAllMatches_bad_input(t *testing.T) { func Test_replaceAllMatches_bad_function_input(t *testing.T) { input := pcommon.NewValueInt(1) - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } replacement := &ottl.StandardStringGetter[any]{ @@ -195,9 +206,12 @@ func Test_replaceAllMatches_bad_function_input(t *testing.T) { func Test_replaceAllMatches_bad_function_result(t *testing.T) { input := pcommon.NewValueInt(1) - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } replacement := &ottl.StandardStringGetter[any]{ @@ -223,9 +237,12 @@ func Test_replaceAllMatches_bad_function_result(t *testing.T) { } func Test_replaceAllMatches_get_nil(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } replacement := &ottl.StandardStringGetter[any]{ diff --git a/pkg/ottl/ottlfuncs/func_replace_all_patterns.go b/pkg/ottl/ottlfuncs/func_replace_all_patterns.go index 28073cda04505..879a39847b42a 100644 --- a/pkg/ottl/ottlfuncs/func_replace_all_patterns.go +++ b/pkg/ottl/ottlfuncs/func_replace_all_patterns.go @@ -20,7 +20,7 @@ const ( ) type ReplaceAllPatternsArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Mode string RegexPattern string Replacement ottl.StringGetter[K] @@ -42,7 +42,7 @@ func createReplaceAllPatternsFunction[K any](_ ottl.FunctionContext, oArgs ottl. return replaceAllPatterns(args.Target, args.Mode, args.RegexPattern, args.Replacement, args.Function, args.ReplacementFormat) } -func replaceAllPatterns[K any](target ottl.PMapGetter[K], mode string, regexPattern string, replacement ottl.StringGetter[K], fn ottl.Optional[ottl.FunctionGetter[K]], replacementFormat ottl.Optional[ottl.StringGetter[K]]) (ottl.ExprFunc[K], error) { +func replaceAllPatterns[K any](target ottl.PMapGetSetter[K], mode string, regexPattern string, replacement ottl.StringGetter[K], fn ottl.Optional[ottl.FunctionGetter[K]], replacementFormat ottl.Optional[ottl.StringGetter[K]]) (ottl.ExprFunc[K], error) { compiledPattern, err := regexp.Compile(regexPattern) if err != nil { return nil, fmt.Errorf("the regex pattern supplied to replace_all_patterns is not a valid pattern: %w", err) @@ -53,14 +53,16 @@ func replaceAllPatterns[K any](target ottl.PMapGetter[K], mode string, regexPatt return func(ctx context.Context, tCtx K) (any, error) { val, err := target.Get(ctx, tCtx) - var replacementVal string if err != nil { return nil, err } + + var replacementVal string replacementVal, err = replacement.Get(ctx, tCtx) if err != nil { return nil, err } + updated := pcommon.NewMap() updated.EnsureCapacity(val.Len()) AttributeLoop: @@ -98,7 +100,6 @@ func replaceAllPatterns[K any](target ottl.PMapGetter[K], mode string, regexPatt } } } - updated.MoveTo(val) - return nil, nil + return nil, target.Set(ctx, tCtx, updated) }, nil } diff --git a/pkg/ottl/ottlfuncs/func_replace_all_patterns_test.go b/pkg/ottl/ottlfuncs/func_replace_all_patterns_test.go index 29b80ea3416e6..9ba302bcde6b5 100644 --- a/pkg/ottl/ottlfuncs/func_replace_all_patterns_test.go +++ b/pkg/ottl/ottlfuncs/func_replace_all_patterns_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -43,15 +44,8 @@ func Test_replaceAllPatterns(t *testing.T) { } optionalArg := ottl.NewTestingOptional[ottl.FunctionGetter[pcommon.Map]](ottlValue) - target := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { name string - target ottl.PMapGetter[pcommon.Map] mode string pattern string replacement ottl.StringGetter[pcommon.Map] @@ -61,7 +55,6 @@ func Test_replaceAllPatterns(t *testing.T) { }{ { name: "replace only matches (with hash function)", - target: target, mode: modeValue, pattern: "hello", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -83,7 +76,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replace only matches (with capture group and hash function)", - target: target, mode: modeValue, pattern: "(hello)", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -104,7 +96,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replace only matches (no capture group and with hash function)", - target: target, mode: modeValue, pattern: "hello", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -125,7 +116,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replace only matches (no capture group or hash function)", - target: target, mode: modeValue, pattern: "hello", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -146,7 +136,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replace only matches (with replacement format)", - target: target, mode: modeValue, pattern: "hello", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -168,7 +157,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replace only matches (with invalid replacement format)", - target: target, mode: modeValue, pattern: "hello", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -185,7 +173,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replace only matches", - target: target, mode: modeValue, pattern: "hello", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -207,7 +194,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "no matches", - target: target, mode: modeValue, pattern: "nothing", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -229,7 +215,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "multiple regex match", - target: target, mode: modeValue, pattern: `world[^\s]*(\s?)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -251,7 +236,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "regex match (with multiple capture groups)", - target: target, mode: modeValue, pattern: `(world1) and (world2)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -272,7 +256,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "regex match (with multiple matches from one capture group)", - target: target, mode: modeValue, pattern: `(world\d)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -293,7 +276,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "regex match (with multiple capture groups and hash function)", - target: target, mode: modeValue, pattern: `(world1) and (world2)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -314,7 +296,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "regex match (with multiple capture groups and hash function)", - target: target, mode: modeValue, pattern: `(world1) and (world2)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -335,7 +316,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "regex match (with multiple matches from one capture group and hash function)", - target: target, mode: modeValue, pattern: `(world\d)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -356,7 +336,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replace only matches", - target: target, mode: modeKey, pattern: "test2", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -379,7 +358,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "no matches", - target: target, mode: modeKey, pattern: "nothing", replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -402,7 +380,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "multiple regex match", - target: target, mode: modeKey, pattern: `test`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -425,7 +402,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "expand capturing groups in values", - target: target, mode: modeValue, pattern: `world(\d)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -448,7 +424,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "expand capturing groups in keys", - target: target, mode: modeKey, pattern: `test(\d)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -470,7 +445,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replacement with literal $", - target: target, mode: modeValue, pattern: `world(\d)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -493,7 +467,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replacement for empty string", - target: target, mode: modeValue, pattern: `^$`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -516,7 +489,6 @@ func Test_replaceAllPatterns(t *testing.T) { }, { name: "replacement matches with function", - target: target, mode: modeKey, pattern: `test(\d)`, replacement: ottl.StandardStringGetter[pcommon.Map]{ @@ -542,11 +514,27 @@ func Test_replaceAllPatterns(t *testing.T) { scenarioMap := pcommon.NewMap() input.CopyTo(scenarioMap) - exprFunc, err := replaceAllPatterns[pcommon.Map](tt.target, tt.mode, tt.pattern, tt.replacement, tt.function, tt.replacementFormat) + setterWasCalled := false + target := ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, m any) error { + setterWasCalled = true + if v, ok := m.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc, err := replaceAllPatterns[pcommon.Map](target, tt.mode, tt.pattern, tt.replacement, tt.function, tt.replacementFormat) assert.NoError(t, err) _, err = exprFunc(nil, scenarioMap) assert.NoError(t, err) + assert.True(t, setterWasCalled) expected := pcommon.NewMap() tt.want(expected) @@ -558,9 +546,12 @@ func Test_replaceAllPatterns(t *testing.T) { func Test_replaceAllPatterns_bad_input(t *testing.T) { input := pcommon.NewValueStr("not a map") - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } replacement := &ottl.StandardStringGetter[any]{ @@ -580,9 +571,12 @@ func Test_replaceAllPatterns_bad_input(t *testing.T) { func Test_replaceAllPatterns_bad_function_input(t *testing.T) { input := pcommon.NewValueInt(1) - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } replacement := &ottl.StandardStringGetter[any]{ @@ -604,9 +598,21 @@ func Test_replaceAllPatterns_bad_function_input(t *testing.T) { func Test_replaceAllPatterns_bad_function_result(t *testing.T) { input := pcommon.NewValueInt(1) - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") + }, + Setter: func(_ context.Context, tCtx any, m any) error { + if v, ok := tCtx.(pcommon.Map); ok { + if v2, ok2 := m.(pcommon.Map); ok2 { + v.CopyTo(v2) + return nil + } + } + return errors.New("expected pcommon.Map") }, } replacement := &ottl.StandardStringGetter[any]{ @@ -632,9 +638,19 @@ func Test_replaceAllPatterns_bad_function_result(t *testing.T) { } func Test_replaceAllPatterns_get_nil(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + assert.Nil(t, tCtx) + return pcommon.NewMap(), nil + }, + Setter: func(_ context.Context, tCtx any, m any) error { + if v, ok := tCtx.(pcommon.Map); ok { + if v2, ok2 := m.(pcommon.Map); ok2 { + v.CopyTo(v2) + return nil + } + } + return errors.New("expected pcommon.Map") }, } replacement := &ottl.StandardStringGetter[any]{ @@ -653,10 +669,9 @@ func Test_replaceAllPatterns_get_nil(t *testing.T) { } func Test_replaceAllPatterns_invalid_pattern(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, _ any) (any, error) { - t.Errorf("nothing should be received in this scenario") - return nil, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(context.Context, any) (pcommon.Map, error) { + return pcommon.Map{}, errors.New("nothing should be received in this scenario") }, } replacement := &ottl.StandardStringGetter[any]{ @@ -675,10 +690,9 @@ func Test_replaceAllPatterns_invalid_pattern(t *testing.T) { } func Test_replaceAllPatterns_invalid_model(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, _ any) (any, error) { - t.Errorf("nothing should be received in this scenario") - return nil, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(context.Context, any) (pcommon.Map, error) { + return pcommon.Map{}, errors.New("nothing should be received in this scenario") }, } replacement := &ottl.StandardStringGetter[any]{ diff --git a/pkg/ottl/ottlfuncs/func_truncate_all.go b/pkg/ottl/ottlfuncs/func_truncate_all.go index 9c5f0caff3a30..b65b604451270 100644 --- a/pkg/ottl/ottlfuncs/func_truncate_all.go +++ b/pkg/ottl/ottlfuncs/func_truncate_all.go @@ -12,7 +12,7 @@ import ( ) type TruncateAllArguments[K any] struct { - Target ottl.PMapGetter[K] + Target ottl.PMapGetSetter[K] Limit int64 } @@ -30,7 +30,7 @@ func createTruncateAllFunction[K any](_ ottl.FunctionContext, oArgs ottl.Argumen return TruncateAll(args.Target, args.Limit) } -func TruncateAll[K any](target ottl.PMapGetter[K], limit int64) (ottl.ExprFunc[K], error) { +func TruncateAll[K any](target ottl.PMapGetSetter[K], limit int64) (ottl.ExprFunc[K], error) { if limit < 0 { return nil, fmt.Errorf("invalid limit for truncate_all function, %d cannot be negative", limit) } @@ -51,6 +51,6 @@ func TruncateAll[K any](target ottl.PMapGetter[K], limit int64) (ottl.ExprFunc[K } // TODO: Write log when truncation is performed // https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9730 - return nil, nil + return nil, target.Set(ctx, tCtx, val) }, nil } diff --git a/pkg/ottl/ottlfuncs/func_truncate_all_test.go b/pkg/ottl/ottlfuncs/func_truncate_all_test.go index f2c43a349ab11..c8b4168f066e6 100644 --- a/pkg/ottl/ottlfuncs/func_truncate_all_test.go +++ b/pkg/ottl/ottlfuncs/func_truncate_all_test.go @@ -5,6 +5,7 @@ package ottlfuncs import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -20,22 +21,14 @@ func Test_truncateAll(t *testing.T) { input.PutInt("test2", 3) input.PutBool("test3", true) - target := &ottl.StandardPMapGetter[pcommon.Map]{ - Getter: func(_ context.Context, tCtx pcommon.Map) (any, error) { - return tCtx, nil - }, - } - tests := []struct { - name string - target ottl.PMapGetter[pcommon.Map] - limit int64 - want func(pcommon.Map) + name string + limit int64 + want func(pcommon.Map) }{ { - name: "truncate map", - target: target, - limit: 1, + name: "truncate map", + limit: 1, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "h") expectedMap.PutInt("test2", 3) @@ -43,9 +36,8 @@ func Test_truncateAll(t *testing.T) { }, }, { - name: "truncate map to zero", - target: target, - limit: 0, + name: "truncate map to zero", + limit: 0, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "") expectedMap.PutInt("test2", 3) @@ -53,9 +45,8 @@ func Test_truncateAll(t *testing.T) { }, }, { - name: "truncate nothing", - target: target, - limit: 100, + name: "truncate nothing", + limit: 100, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutInt("test2", 3) @@ -63,9 +54,8 @@ func Test_truncateAll(t *testing.T) { }, }, { - name: "truncate exact", - target: target, - limit: 11, + name: "truncate exact", + limit: 11, want: func(expectedMap pcommon.Map) { expectedMap.PutStr("test", "hello world") expectedMap.PutInt("test2", 3) @@ -78,12 +68,27 @@ func Test_truncateAll(t *testing.T) { scenarioMap := pcommon.NewMap() input.CopyTo(scenarioMap) - exprFunc, err := TruncateAll(tt.target, tt.limit) + setterWasCalled := false + target := &ottl.StandardPMapGetSetter[pcommon.Map]{ + Getter: func(_ context.Context, tCtx pcommon.Map) (pcommon.Map, error) { + return tCtx, nil + }, + Setter: func(_ context.Context, tCtx pcommon.Map, m any) error { + setterWasCalled = true + if v, ok := m.(pcommon.Map); ok { + v.CopyTo(tCtx) + return nil + } + return errors.New("expected pcommon.Map") + }, + } + + exprFunc, err := TruncateAll(target, tt.limit) assert.NoError(t, err) - result, err := exprFunc(nil, scenarioMap) + _, err = exprFunc(nil, scenarioMap) assert.NoError(t, err) - assert.Nil(t, result) + assert.True(t, setterWasCalled) expected := pcommon.NewMap() tt.want(expected) @@ -94,16 +99,19 @@ func Test_truncateAll(t *testing.T) { } func Test_truncateAll_validation(t *testing.T) { - _, err := TruncateAll[any](&ottl.StandardPMapGetter[any]{}, -1) + _, err := TruncateAll[any](&ottl.StandardPMapGetSetter[any]{}, -1) require.Error(t, err) assert.ErrorContains(t, err, "invalid limit for truncate_all function, -1 cannot be negative") } func Test_truncateAll_bad_input(t *testing.T) { input := pcommon.NewValueStr("not a map") - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, } @@ -115,9 +123,12 @@ func Test_truncateAll_bad_input(t *testing.T) { } func Test_truncateAll_get_nil(t *testing.T) { - target := &ottl.StandardPMapGetter[any]{ - Getter: func(_ context.Context, tCtx any) (any, error) { - return tCtx, nil + target := &ottl.StandardPMapGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (pcommon.Map, error) { + if v, ok := tCtx.(pcommon.Map); ok { + return v, nil + } + return pcommon.Map{}, errors.New("expected pcommon.Map") }, }