Skip to content

Commit 1629eda

Browse files
authored
[pkg/ottl] Support dynamic indexing (#36719)
1 parent 0387783 commit 1629eda

File tree

13 files changed

+441
-53
lines changed

13 files changed

+441
-53
lines changed

.chloggen/indexing-pkg-ottl.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: breaking
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: "Support dynamic indexing of maps and slices."
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: [36644]
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/contexts/internal/map.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl.
2222
return nil, err
2323
}
2424
if s == nil {
25-
return nil, fmt.Errorf("non-string indexing is not supported")
25+
resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[0])
26+
if err != nil {
27+
return nil, fmt.Errorf("unable to resolve a string index in map: %w", err)
28+
}
29+
s = resString
2630
}
2731

2832
val, ok := m.Get(*s)
@@ -43,7 +47,11 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl.
4347
return err
4448
}
4549
if s == nil {
46-
return fmt.Errorf("non-string indexing is not supported")
50+
resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[0])
51+
if err != nil {
52+
return fmt.Errorf("unable to resolve a string index in map: %w", err)
53+
}
54+
s = resString
4755
}
4856

4957
currentValue, ok := m.Get(*s)
@@ -52,3 +60,22 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl.
5260
}
5361
return setIndexableValue[K](ctx, tCtx, currentValue, val, keys[1:])
5462
}
63+
64+
func FetchValueFromExpression[K any, T int64 | string](ctx context.Context, tCtx K, key ottl.Key[K]) (*T, error) {
65+
p, err := key.ExpressionGetter(ctx, tCtx)
66+
if err != nil {
67+
return nil, err
68+
}
69+
if p == nil {
70+
return nil, fmt.Errorf("invalid key type")
71+
}
72+
res, err := p.Get(ctx, tCtx)
73+
if err != nil {
74+
return nil, err
75+
}
76+
resVal, ok := res.(T)
77+
if !ok {
78+
return nil, fmt.Errorf("could not resolve key for map/slice, expecting '%T' but got '%T'", resVal, res)
79+
}
80+
return &resVal, nil
81+
}

pkg/ottl/contexts/internal/map_test.go

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ import (
1616
)
1717

1818
func Test_GetMapValue_Invalid(t *testing.T) {
19+
getSetter := &ottl.StandardGetSetter[any]{
20+
Getter: func(_ context.Context, _ any) (any, error) {
21+
return nil, nil
22+
},
23+
Setter: func(_ context.Context, _ any, _ any) error {
24+
return nil
25+
},
26+
}
1927
tests := []struct {
2028
name string
2129
keys []ottl.Key[any]
@@ -26,42 +34,49 @@ func Test_GetMapValue_Invalid(t *testing.T) {
2634
keys: []ottl.Key[any]{
2735
&TestKey[any]{
2836
I: ottltest.Intp(0),
37+
G: getSetter,
2938
},
3039
},
31-
err: fmt.Errorf("non-string indexing is not supported"),
40+
err: fmt.Errorf("unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
3241
},
3342
{
3443
name: "index map with int",
3544
keys: []ottl.Key[any]{
3645
&TestKey[any]{
3746
S: ottltest.Strp("map"),
47+
G: getSetter,
3848
},
3949
&TestKey[any]{
4050
I: ottltest.Intp(0),
51+
G: getSetter,
4152
},
4253
},
43-
err: fmt.Errorf("map must be indexed by a string"),
54+
err: fmt.Errorf("unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
4455
},
4556
{
4657
name: "index slice with string",
4758
keys: []ottl.Key[any]{
4859
&TestKey[any]{
4960
S: ottltest.Strp("slice"),
61+
G: getSetter,
5062
},
5163
&TestKey[any]{
5264
S: ottltest.Strp("invalid"),
65+
G: getSetter,
5366
},
5467
},
55-
err: fmt.Errorf("slice must be indexed by an int"),
68+
err: fmt.Errorf("unable to resolve an integer index in slice: could not resolve key for map/slice, expecting 'int64' but got '<nil>'"),
5669
},
5770
{
5871
name: "index too large",
5972
keys: []ottl.Key[any]{
6073
&TestKey[any]{
6174
S: ottltest.Strp("slice"),
75+
G: getSetter,
6276
},
6377
&TestKey[any]{
6478
I: ottltest.Intp(1),
79+
G: getSetter,
6580
},
6681
},
6782
err: fmt.Errorf("index 1 out of bounds"),
@@ -71,9 +86,11 @@ func Test_GetMapValue_Invalid(t *testing.T) {
7186
keys: []ottl.Key[any]{
7287
&TestKey[any]{
7388
S: ottltest.Strp("slice"),
89+
G: getSetter,
7490
},
7591
&TestKey[any]{
7692
I: ottltest.Intp(-1),
93+
G: getSetter,
7794
},
7895
},
7996
err: fmt.Errorf("index -1 out of bounds"),
@@ -83,9 +100,11 @@ func Test_GetMapValue_Invalid(t *testing.T) {
83100
keys: []ottl.Key[any]{
84101
&TestKey[any]{
85102
S: ottltest.Strp("string"),
103+
G: getSetter,
86104
},
87105
&TestKey[any]{
88106
S: ottltest.Strp("string"),
107+
G: getSetter,
89108
},
90109
},
91110
err: fmt.Errorf("type Str does not support string indexing"),
@@ -102,7 +121,7 @@ func Test_GetMapValue_Invalid(t *testing.T) {
102121
s.AppendEmpty()
103122

104123
_, err := GetMapValue[any](context.Background(), nil, m, tt.keys)
105-
assert.Equal(t, tt.err, err)
124+
assert.Equal(t, tt.err.Error(), err.Error())
106125
})
107126
}
108127
}
@@ -129,6 +148,14 @@ func Test_GetMapValue_NilKey(t *testing.T) {
129148
}
130149

131150
func Test_SetMapValue_Invalid(t *testing.T) {
151+
getSetter := &ottl.StandardGetSetter[any]{
152+
Getter: func(_ context.Context, _ any) (any, error) {
153+
return nil, nil
154+
},
155+
Setter: func(_ context.Context, _ any, _ any) error {
156+
return nil
157+
},
158+
}
132159
tests := []struct {
133160
name string
134161
keys []ottl.Key[any]
@@ -139,42 +166,49 @@ func Test_SetMapValue_Invalid(t *testing.T) {
139166
keys: []ottl.Key[any]{
140167
&TestKey[any]{
141168
I: ottltest.Intp(0),
169+
G: getSetter,
142170
},
143171
},
144-
err: fmt.Errorf("non-string indexing is not supported"),
172+
err: fmt.Errorf("unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
145173
},
146174
{
147175
name: "index map with int",
148176
keys: []ottl.Key[any]{
149177
&TestKey[any]{
150178
S: ottltest.Strp("map"),
179+
G: getSetter,
151180
},
152181
&TestKey[any]{
153182
I: ottltest.Intp(0),
183+
G: getSetter,
154184
},
155185
},
156-
err: fmt.Errorf("map must be indexed by a string"),
186+
err: fmt.Errorf("unable to resolve a string index in map: could not resolve key for map/slice, expecting 'string' but got '<nil>'"),
157187
},
158188
{
159189
name: "index slice with string",
160190
keys: []ottl.Key[any]{
161191
&TestKey[any]{
162192
S: ottltest.Strp("slice"),
193+
G: getSetter,
163194
},
164195
&TestKey[any]{
165196
S: ottltest.Strp("map"),
197+
G: getSetter,
166198
},
167199
},
168-
err: fmt.Errorf("slice must be indexed by an int"),
200+
err: fmt.Errorf("unable to resolve an integer index in slice: could not resolve key for map/slice, expecting 'int64' but got '<nil>'"),
169201
},
170202
{
171203
name: "slice index too large",
172204
keys: []ottl.Key[any]{
173205
&TestKey[any]{
174206
S: ottltest.Strp("slice"),
207+
G: getSetter,
175208
},
176209
&TestKey[any]{
177210
I: ottltest.Intp(1),
211+
G: getSetter,
178212
},
179213
},
180214
err: fmt.Errorf("index 1 out of bounds"),
@@ -184,9 +218,11 @@ func Test_SetMapValue_Invalid(t *testing.T) {
184218
keys: []ottl.Key[any]{
185219
&TestKey[any]{
186220
S: ottltest.Strp("slice"),
221+
G: getSetter,
187222
},
188223
&TestKey[any]{
189224
I: ottltest.Intp(-1),
225+
G: getSetter,
190226
},
191227
},
192228
err: fmt.Errorf("index -1 out of bounds"),
@@ -196,9 +232,11 @@ func Test_SetMapValue_Invalid(t *testing.T) {
196232
keys: []ottl.Key[any]{
197233
&TestKey[any]{
198234
S: ottltest.Strp("string"),
235+
G: getSetter,
199236
},
200237
&TestKey[any]{
201238
S: ottltest.Strp("string"),
239+
G: getSetter,
202240
},
203241
},
204242
err: fmt.Errorf("type Str does not support string indexing"),
@@ -215,7 +253,7 @@ func Test_SetMapValue_Invalid(t *testing.T) {
215253
s.AppendEmpty()
216254

217255
err := SetMapValue[any](context.Background(), nil, m, tt.keys, "value")
218-
assert.Equal(t, tt.err, err)
256+
assert.Equal(t, tt.err.Error(), err.Error())
219257
})
220258
}
221259
}

pkg/ottl/contexts/internal/path.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ var _ ottl.Key[any] = &TestKey[any]{}
4646
type TestKey[K any] struct {
4747
S *string
4848
I *int64
49+
G ottl.Getter[K]
4950
}
5051

5152
func (k *TestKey[K]) String(_ context.Context, _ K) (*string, error) {
@@ -55,3 +56,7 @@ func (k *TestKey[K]) String(_ context.Context, _ K) (*string, error) {
5556
func (k *TestKey[K]) Int(_ context.Context, _ K) (*int64, error) {
5657
return k.I, nil
5758
}
59+
60+
func (k *TestKey[K]) ExpressionGetter(_ context.Context, _ K) (ottl.Getter[K], error) {
61+
return k.G, nil
62+
}

pkg/ottl/contexts/internal/slice.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ func GetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o
2222
return nil, err
2323
}
2424
if i == nil {
25-
return nil, fmt.Errorf("non-integer indexing is not supported")
25+
resInt, err := FetchValueFromExpression[K, int64](ctx, tCtx, keys[0])
26+
if err != nil {
27+
return nil, fmt.Errorf("unable to resolve an integer index in slice: %w", err)
28+
}
29+
i = resInt
2630
}
2731

2832
idx := int(*i)
@@ -44,7 +48,11 @@ func SetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o
4448
return err
4549
}
4650
if i == nil {
47-
return fmt.Errorf("non-integer indexing is not supported")
51+
resInt, err := FetchValueFromExpression[K, int64](ctx, tCtx, keys[0])
52+
if err != nil {
53+
return fmt.Errorf("unable to resolve an integer index in slice: %w", err)
54+
}
55+
i = resInt
4856
}
4957

5058
idx := int(*i)

0 commit comments

Comments
 (0)