Skip to content

Commit 0613fb6

Browse files
(pkg/ottl) Fix issue with the hash value of a match subgroup in the replace_pattern editors (#29408)
**Description:** Fix issue with the hash value of a match group in replace_pattern* **Link to tracking Issue:** #29409 --------- Co-authored-by: Tyler Helmuth <[email protected]>
1 parent 5e5a6de commit 0613fb6

7 files changed

+346
-66
lines changed

.chloggen/fix-hash-value.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: 'bug_fix'
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/ottl
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Fix issue with the hash value of a match subgroup in replace_pattern functions.
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [29409]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

pkg/ottl/ottlfuncs/func_replace_all_matches_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func Test_replaceAllMatches(t *testing.T) {
2525
FCtx: ottl.FunctionContext{
2626
Set: componenttest.NewNopTelemetrySettings(),
2727
},
28-
Fact: StandardConverters[pcommon.Map]()["SHA256"],
28+
Fact: optionalFnTestFactory[pcommon.Map](),
2929
}
3030
optionalArg := ottl.NewTestingOptional[ottl.FunctionGetter[pcommon.Map]](ottlValue)
3131

@@ -54,8 +54,8 @@ func Test_replaceAllMatches(t *testing.T) {
5454
},
5555
function: optionalArg,
5656
want: func(expectedMap pcommon.Map) {
57-
expectedMap.PutStr("test", "4804d6b7f03268e33f78c484977f3d81771220df07cc6aac4ad4868102141fad")
58-
expectedMap.PutStr("test2", "4804d6b7f03268e33f78c484977f3d81771220df07cc6aac4ad4868102141fad")
57+
expectedMap.PutStr("test", "hash(hello {universe})")
58+
expectedMap.PutStr("test2", "hash(hello {universe})")
5959
expectedMap.PutStr("test3", "goodbye")
6060
},
6161
},

pkg/ottl/ottlfuncs/func_replace_all_patterns.go

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ type ReplaceAllPatternsArguments[K any] struct {
2626
Function ottl.Optional[ottl.FunctionGetter[K]]
2727
}
2828

29-
type replaceAllPatternFuncArgs[K any] struct {
30-
Input ottl.StringGetter[K]
31-
}
32-
3329
func NewReplaceAllPatternsFactory[K any]() ottl.Factory[K] {
3430
return ottl.NewFactory("replace_all_patterns", &ReplaceAllPatternsArguments[K]{}, createReplaceAllPatternsFunction[K])
3531
}
@@ -59,42 +55,41 @@ func replaceAllPatterns[K any](target ottl.PMapGetter[K], mode string, regexPatt
5955
if err != nil {
6056
return nil, err
6157
}
62-
if fn.IsEmpty() {
63-
replacementVal, err = replacement.Get(ctx, tCtx)
64-
if err != nil {
65-
return nil, err
66-
}
67-
} else {
68-
fnVal := fn.Get()
69-
replacementExpr, errNew := fnVal.Get(&replaceAllPatternFuncArgs[K]{Input: replacement})
70-
if errNew != nil {
71-
return nil, errNew
72-
}
73-
replacementValRaw, errNew := replacementExpr.Eval(ctx, tCtx)
74-
if errNew != nil {
75-
return nil, errNew
76-
}
77-
replacementValStr, ok := replacementValRaw.(string)
78-
if !ok {
79-
return nil, fmt.Errorf("replacement value is not a string")
80-
}
81-
replacementVal = replacementValStr
58+
replacementVal, err = replacement.Get(ctx, tCtx)
59+
if err != nil {
60+
return nil, err
8261
}
8362
updated := pcommon.NewMap()
8463
updated.EnsureCapacity(val.Len())
8564
val.Range(func(key string, originalValue pcommon.Value) bool {
8665
switch mode {
8766
case modeValue:
8867
if compiledPattern.MatchString(originalValue.Str()) {
89-
updatedString := compiledPattern.ReplaceAllString(originalValue.Str(), replacementVal)
90-
updated.PutStr(key, updatedString)
68+
if !fn.IsEmpty() {
69+
updatedString, err := applyOptReplaceFunction(ctx, tCtx, compiledPattern, fn, originalValue.Str(), replacementVal)
70+
if err != nil {
71+
return false
72+
}
73+
updated.PutStr(key, updatedString)
74+
} else {
75+
updatedString := compiledPattern.ReplaceAllString(originalValue.Str(), replacementVal)
76+
updated.PutStr(key, updatedString)
77+
}
9178
} else {
9279
originalValue.CopyTo(updated.PutEmpty(key))
9380
}
9481
case modeKey:
9582
if compiledPattern.MatchString(key) {
96-
updatedKey := compiledPattern.ReplaceAllString(key, replacementVal)
97-
originalValue.CopyTo(updated.PutEmpty(updatedKey))
83+
if !fn.IsEmpty() {
84+
updatedString, err := applyOptReplaceFunction(ctx, tCtx, compiledPattern, fn, key, replacementVal)
85+
if err != nil {
86+
return false
87+
}
88+
updated.PutStr(key, updatedString)
89+
} else {
90+
updatedKey := compiledPattern.ReplaceAllString(key, replacementVal)
91+
originalValue.CopyTo(updated.PutEmpty(updatedKey))
92+
}
9893
} else {
9994
originalValue.CopyTo(updated.PutEmpty(key))
10095
}

pkg/ottl/ottlfuncs/func_replace_all_patterns_test.go

Lines changed: 163 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func Test_replaceAllPatterns(t *testing.T) {
2828
FCtx: ottl.FunctionContext{
2929
Set: componenttest.NewNopTelemetrySettings(),
3030
},
31-
Fact: StandardConverters[pcommon.Map]()["SHA256"],
31+
Fact: optionalFnTestFactory[pcommon.Map](),
3232
}
3333
optionalArg := ottl.NewTestingOptional[ottl.FunctionGetter[pcommon.Map]](ottlValue)
3434

@@ -59,8 +59,68 @@ func Test_replaceAllPatterns(t *testing.T) {
5959
},
6060
function: optionalArg,
6161
want: func(expectedMap pcommon.Map) {
62-
expectedMap.PutStr("test", "4804d6b7f03268e33f78c484977f3d81771220df07cc6aac4ad4868102141fad world")
63-
expectedMap.PutStr("test2", "4804d6b7f03268e33f78c484977f3d81771220df07cc6aac4ad4868102141fad")
62+
expectedMap.PutStr("test", "hash(hello {universe}) world")
63+
expectedMap.PutStr("test2", "hash(hello {universe})")
64+
expectedMap.PutStr("test3", "goodbye world1 and world2")
65+
expectedMap.PutInt("test4", 1234)
66+
expectedMap.PutDouble("test5", 1234)
67+
expectedMap.PutBool("test6", true)
68+
},
69+
},
70+
{
71+
name: "replace only matches (with capture group and hash function)",
72+
target: target,
73+
mode: modeValue,
74+
pattern: "(hello)",
75+
replacement: ottl.StandardStringGetter[pcommon.Map]{
76+
Getter: func(context.Context, pcommon.Map) (any, error) {
77+
return "$1", nil
78+
},
79+
},
80+
function: optionalArg,
81+
want: func(expectedMap pcommon.Map) {
82+
expectedMap.PutStr("test", "hash(hello) world")
83+
expectedMap.PutStr("test2", "hash(hello)")
84+
expectedMap.PutStr("test3", "goodbye world1 and world2")
85+
expectedMap.PutInt("test4", 1234)
86+
expectedMap.PutDouble("test5", 1234)
87+
expectedMap.PutBool("test6", true)
88+
},
89+
},
90+
{
91+
name: "replace only matches (no capture group and with hash function)",
92+
target: target,
93+
mode: modeValue,
94+
pattern: "hello",
95+
replacement: ottl.StandardStringGetter[pcommon.Map]{
96+
Getter: func(context.Context, pcommon.Map) (any, error) {
97+
return "$1", nil
98+
},
99+
},
100+
function: optionalArg,
101+
want: func(expectedMap pcommon.Map) {
102+
expectedMap.PutStr("test", "hash() world")
103+
expectedMap.PutStr("test2", "hash()")
104+
expectedMap.PutStr("test3", "goodbye world1 and world2")
105+
expectedMap.PutInt("test4", 1234)
106+
expectedMap.PutDouble("test5", 1234)
107+
expectedMap.PutBool("test6", true)
108+
},
109+
},
110+
{
111+
name: "replace only matches (no capture group or hash function)",
112+
target: target,
113+
mode: modeValue,
114+
pattern: "hello",
115+
replacement: ottl.StandardStringGetter[pcommon.Map]{
116+
Getter: func(context.Context, pcommon.Map) (any, error) {
117+
return "$1", nil
118+
},
119+
},
120+
function: ottl.Optional[ottl.FunctionGetter[pcommon.Map]]{},
121+
want: func(expectedMap pcommon.Map) {
122+
expectedMap.PutStr("test", " world")
123+
expectedMap.PutStr("test2", "")
64124
expectedMap.PutStr("test3", "goodbye world1 and world2")
65125
expectedMap.PutInt("test4", 1234)
66126
expectedMap.PutDouble("test5", 1234)
@@ -127,6 +187,106 @@ func Test_replaceAllPatterns(t *testing.T) {
127187
expectedMap.PutBool("test6", true)
128188
},
129189
},
190+
{
191+
name: "regex match (with multiple capture groups)",
192+
target: target,
193+
mode: modeValue,
194+
pattern: `(world1) and (world2)`,
195+
replacement: ottl.StandardStringGetter[pcommon.Map]{
196+
Getter: func(context.Context, pcommon.Map) (any, error) {
197+
return "blue-$1 and blue-$2", nil
198+
},
199+
},
200+
function: ottl.Optional[ottl.FunctionGetter[pcommon.Map]]{},
201+
want: func(expectedMap pcommon.Map) {
202+
expectedMap.PutStr("test", "hello world")
203+
expectedMap.PutStr("test2", "hello")
204+
expectedMap.PutStr("test3", "goodbye blue-world1 and blue-world2")
205+
expectedMap.PutInt("test4", 1234)
206+
expectedMap.PutDouble("test5", 1234)
207+
expectedMap.PutBool("test6", true)
208+
},
209+
},
210+
{
211+
name: "regex match (with multiple matches from one capture group)",
212+
target: target,
213+
mode: modeValue,
214+
pattern: `(world\d)`,
215+
replacement: ottl.StandardStringGetter[pcommon.Map]{
216+
Getter: func(context.Context, pcommon.Map) (any, error) {
217+
return "blue-$1", nil
218+
},
219+
},
220+
function: ottl.Optional[ottl.FunctionGetter[pcommon.Map]]{},
221+
want: func(expectedMap pcommon.Map) {
222+
expectedMap.PutStr("test", "hello world")
223+
expectedMap.PutStr("test2", "hello")
224+
expectedMap.PutStr("test3", "goodbye blue-world1 and blue-world2")
225+
expectedMap.PutInt("test4", 1234)
226+
expectedMap.PutDouble("test5", 1234)
227+
expectedMap.PutBool("test6", true)
228+
},
229+
},
230+
{
231+
name: "regex match (with multiple capture groups and hash function)",
232+
target: target,
233+
mode: modeValue,
234+
pattern: `(world1) and (world2)`,
235+
replacement: ottl.StandardStringGetter[pcommon.Map]{
236+
Getter: func(context.Context, pcommon.Map) (any, error) {
237+
return "$1", nil
238+
},
239+
},
240+
function: optionalArg,
241+
want: func(expectedMap pcommon.Map) {
242+
expectedMap.PutStr("test", "hello world")
243+
expectedMap.PutStr("test2", "hello")
244+
expectedMap.PutStr("test3", "goodbye hash(world1)")
245+
expectedMap.PutInt("test4", 1234)
246+
expectedMap.PutDouble("test5", 1234)
247+
expectedMap.PutBool("test6", true)
248+
},
249+
},
250+
{
251+
name: "regex match (with multiple capture groups and hash function)",
252+
target: target,
253+
mode: modeValue,
254+
pattern: `(world1) and (world2)`,
255+
replacement: ottl.StandardStringGetter[pcommon.Map]{
256+
Getter: func(context.Context, pcommon.Map) (any, error) {
257+
return "$2", nil
258+
},
259+
},
260+
function: optionalArg,
261+
want: func(expectedMap pcommon.Map) {
262+
expectedMap.PutStr("test", "hello world")
263+
expectedMap.PutStr("test2", "hello")
264+
expectedMap.PutStr("test3", "goodbye hash(world2)")
265+
expectedMap.PutInt("test4", 1234)
266+
expectedMap.PutDouble("test5", 1234)
267+
expectedMap.PutBool("test6", true)
268+
},
269+
},
270+
{
271+
name: "regex match (with multiple matches from one capture group and hash function)",
272+
target: target,
273+
mode: modeValue,
274+
pattern: `(world\d)`,
275+
replacement: ottl.StandardStringGetter[pcommon.Map]{
276+
Getter: func(context.Context, pcommon.Map) (any, error) {
277+
return "$1", nil
278+
},
279+
},
280+
function: optionalArg,
281+
want: func(expectedMap pcommon.Map) {
282+
expectedMap.PutStr("test", "hello world")
283+
expectedMap.PutStr("test2", "hello")
284+
expectedMap.PutStr("test3", "goodbye hash(world1) and hash(world2)")
285+
expectedMap.PutInt("test4", 1234)
286+
expectedMap.PutDouble("test5", 1234)
287+
expectedMap.PutBool("test6", true)
288+
},
289+
},
130290
{
131291
name: "replace only matches",
132292
target: target,

pkg/ottl/ottlfuncs/func_replace_match_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func Test_replaceMatch(t *testing.T) {
2121
FCtx: ottl.FunctionContext{
2222
Set: componenttest.NewNopTelemetrySettings(),
2323
},
24-
Fact: StandardConverters[pcommon.Value]()["SHA256"],
24+
Fact: optionalFnTestFactory[pcommon.Value](),
2525
}
2626
optionalArg := ottl.NewTestingOptional[ottl.FunctionGetter[pcommon.Value]](ottlValue)
2727
target := &ottl.StandardGetSetter[pcommon.Value]{
@@ -53,7 +53,7 @@ func Test_replaceMatch(t *testing.T) {
5353
},
5454
function: optionalArg,
5555
want: func(expectedValue pcommon.Value) {
56-
expectedValue.SetStr("4804d6b7f03268e33f78c484977f3d81771220df07cc6aac4ad4868102141fad")
56+
expectedValue.SetStr("hash(hello {universe})")
5757
},
5858
},
5959
{

0 commit comments

Comments
 (0)