Skip to content

Commit 9fe87a2

Browse files
odubajDTedmocostaevan-bradleyTylerHelmuth
authored
[pkg/ottl] Introduce ToLowerCase() converter function (#37428)
<!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes #32942 --------- Signed-off-by: odubajDT <[email protected]> Co-authored-by: Edmo Vamerlatti Costa <[email protected]> Co-authored-by: Evan Bradley <[email protected]> Co-authored-by: Tyler Helmuth <[email protected]>
1 parent 516a7a7 commit 9fe87a2

File tree

6 files changed

+192
-0
lines changed

6 files changed

+192
-0
lines changed

.chloggen/convert-case-lower.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: enhancement
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: "Introduce ToLowerCase converter function"
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: [32942]
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/e2e/e2e_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,12 @@ func Test_e2e_converters(t *testing.T) {
459459
tCtx.GetLogRecord().Attributes().PutStr("test", http.MethodGet)
460460
},
461461
},
462+
{
463+
statement: `set(attributes["test"], ToLowerCase("PASS"))`,
464+
want: func(tCtx ottllog.TransformContext) {
465+
tCtx.GetLogRecord().Attributes().PutStr("test", "pass")
466+
},
467+
},
462468
{
463469
statement: `set(attributes["test"], ConvertAttributesToElementsXML("<Log id=\"1\"><Message>This is a log message!</Message></Log>"))`,
464470
want: func(tCtx ottllog.TransformContext) {

pkg/ottl/ottlfuncs/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ Available Converters:
467467
- [Substring](#substring)
468468
- [Time](#time)
469469
- [ToKeyValueString](#tokeyvaluestring)
470+
- [ToLowerCase](#tolowercase)
470471
- [ToSnakeCase](#tosnakecase)
471472
- [ToUpperCase](#touppercase)
472473
- [TraceID](#traceid)
@@ -2058,6 +2059,18 @@ Examples:
20582059
- `ToKeyValueString(body)`
20592060
- `ToKeyValueString(body, ":", ",", true)`
20602061

2062+
### ToLowerCase
2063+
2064+
`ToLowerCase(target)`
2065+
2066+
The `ToLowerCase` Converter converts the `target` string into lower case (e.g. `MyMetricName` to `mymetricmame`).
2067+
2068+
`target` is a string.
2069+
2070+
Examples:
2071+
2072+
- `ToLowerCase(metric.name)`
2073+
20612074
### ToSnakeCase
20622075

20632076
`ToSnakeCase(target)`
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package ottlfuncs // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs"
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"strings"
10+
11+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
12+
)
13+
14+
type ToLowerCaseArguments[K any] struct {
15+
Target ottl.StringGetter[K]
16+
}
17+
18+
func NewToLowerCaseFactory[K any]() ottl.Factory[K] {
19+
return ottl.NewFactory("ToLowerCase", &ToLowerCaseArguments[K]{}, createToLowerCaseFunction[K])
20+
}
21+
22+
func createToLowerCaseFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) (ottl.ExprFunc[K], error) {
23+
args, ok := oArgs.(*ToLowerCaseArguments[K])
24+
25+
if !ok {
26+
return nil, fmt.Errorf("ToLowerCaseFactory args must be of type *ToLowerCaseArguments[K]")
27+
}
28+
29+
return toLowerCase(args.Target), nil
30+
}
31+
32+
func toLowerCase[K any](target ottl.StringGetter[K]) ottl.ExprFunc[K] {
33+
return func(ctx context.Context, tCtx K) (any, error) {
34+
val, err := target.Get(ctx, tCtx)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
if val == "" {
40+
return val, nil
41+
}
42+
43+
return strings.ToLower(val), nil
44+
}
45+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package ottlfuncs
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
12+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
13+
)
14+
15+
func Test_toLowerCase(t *testing.T) {
16+
tests := []struct {
17+
name string
18+
target ottl.StringGetter[any]
19+
expected any
20+
}{
21+
{
22+
name: "simple",
23+
target: &ottl.StandardStringGetter[any]{
24+
Getter: func(_ context.Context, _ any) (any, error) {
25+
return "SIMPLE", nil
26+
},
27+
},
28+
expected: "simple",
29+
},
30+
{
31+
name: "already lower",
32+
target: &ottl.StandardStringGetter[any]{
33+
Getter: func(_ context.Context, _ any) (any, error) {
34+
return "simple", nil
35+
},
36+
},
37+
expected: "simple",
38+
},
39+
{
40+
name: "complex",
41+
target: &ottl.StandardStringGetter[any]{
42+
Getter: func(_ context.Context, _ any) (any, error) {
43+
return "complex_SET-of.WORDS1234", nil
44+
},
45+
},
46+
expected: "complex_set-of.words1234",
47+
},
48+
{
49+
name: "empty string",
50+
target: &ottl.StandardStringGetter[any]{
51+
Getter: func(_ context.Context, _ any) (any, error) {
52+
return "", nil
53+
},
54+
},
55+
expected: "",
56+
},
57+
}
58+
for _, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
exprFunc := toLowerCase(tt.target)
61+
result, err := exprFunc(nil, nil)
62+
assert.NoError(t, err)
63+
assert.Equal(t, tt.expected, result)
64+
})
65+
}
66+
}
67+
68+
func Test_toLowerCaseRuntimeError(t *testing.T) {
69+
tests := []struct {
70+
name string
71+
target ottl.StringGetter[any]
72+
expectedError string
73+
}{
74+
{
75+
name: "non-string",
76+
target: &ottl.StandardStringGetter[any]{
77+
Getter: func(_ context.Context, _ any) (any, error) {
78+
return 10, nil
79+
},
80+
},
81+
expectedError: "expected string but got int",
82+
},
83+
{
84+
name: "nil",
85+
target: &ottl.StandardStringGetter[any]{
86+
Getter: func(_ context.Context, _ any) (any, error) {
87+
return nil, nil
88+
},
89+
},
90+
expectedError: "expected string but got nil",
91+
},
92+
}
93+
for _, tt := range tests {
94+
t.Run(tt.name, func(t *testing.T) {
95+
exprFunc := toLowerCase[any](tt.target)
96+
_, err := exprFunc(context.Background(), nil)
97+
assert.ErrorContains(t, err, tt.expectedError)
98+
})
99+
}
100+
}

pkg/ottl/ottlfuncs/functions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ func converters[K any]() []ottl.Factory[K] {
9292
NewFormatTimeFactory[K](),
9393
NewTrimFactory[K](),
9494
NewToKeyValueStringFactory[K](),
95+
NewToLowerCaseFactory[K](),
9596
NewToSnakeCaseFactory[K](),
9697
NewToUpperCaseFactory[K](),
9798
NewTruncateTimeFactory[K](),

0 commit comments

Comments
 (0)