Skip to content

Commit 8a2ac13

Browse files
authored
[pkg/ottl] Add new IsMap and IsString functions (open-telemetry#22750)
* Add new functions * Add docs * changelog * fix lint * Switch to typed getters using typed errors * Use errors.As * Revert "Use errors.As" This reverts commit 386d53540e5d1bb1cc8d0e30cf1db47309460d57. * Ensure wrapped TypeErrors are returned * Add type check to tests * Added more tests * Use TypeError in tests to ensure the error is wrapped * Check that false is returned with error * Fix lint * Wrap errors in TypeLike getters
1 parent fbf6bbe commit 8a2ac13

9 files changed

+447
-25
lines changed

.chloggen/ottl-check-type.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Use this changelog template to create an entry for release notes.
2+
# If your change doesn't affect end users, such as a test fix or a tooling change,
3+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
4+
5+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
6+
change_type: enhancement
7+
8+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
9+
component: pkg/ottl
10+
11+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
12+
note: Add new `IsString` and `IsMap` functions to facilitate type checking.
13+
14+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
15+
issues: [22750]
16+
17+
# (Optional) One or more lines of additional information to render under the primary note.
18+
# These lines will be padded with 2 spaces and then inserted directly into the document.
19+
# Use pipe (|) for multiline entries.
20+
subtext: Especially useful for checking log body type before parsing.

pkg/ottl/expression.go

+54-25
Original file line numberDiff line numberDiff line change
@@ -133,23 +133,34 @@ func (l *listGetter[K]) Get(ctx context.Context, tCtx K) (interface{}, error) {
133133
return evaluated, nil
134134
}
135135

136+
// TypeError represents that a value was not an expected type.
137+
type TypeError string
138+
139+
func (t TypeError) Error() string {
140+
return string(t)
141+
}
142+
136143
// StringGetter is a Getter that must return a string.
137144
type StringGetter[K any] interface {
138-
// Get retrieves a string value. If the value is not a string, an error is returned.
145+
// Get retrieves a string value.
139146
Get(ctx context.Context, tCtx K) (string, error)
140147
}
141148

149+
// StandardStringGetter is a basic implementation of StringGetter
142150
type StandardStringGetter[K any] struct {
143151
Getter func(ctx context.Context, tCtx K) (interface{}, error)
144152
}
145153

154+
// Get retrieves a string value.
155+
// If the value is not a string a new TypeError is returned.
156+
// If there is an error getting the value it will be returned.
146157
func (g StandardStringGetter[K]) Get(ctx context.Context, tCtx K) (string, error) {
147158
val, err := g.Getter(ctx, tCtx)
148159
if err != nil {
149-
return "", err
160+
return "", fmt.Errorf("error getting value in %T: %w", g, err)
150161
}
151162
if val == nil {
152-
return "", fmt.Errorf("expected string but got nil")
163+
return "", TypeError("expected string but got nil")
153164
}
154165
switch v := val.(type) {
155166
case string:
@@ -158,27 +169,33 @@ func (g StandardStringGetter[K]) Get(ctx context.Context, tCtx K) (string, error
158169
if v.Type() == pcommon.ValueTypeStr {
159170
return v.Str(), nil
160171
}
161-
return "", fmt.Errorf("expected string but got %v", v.Type())
172+
return "", TypeError(fmt.Sprintf("expected string but got %v", v.Type()))
162173
default:
163-
return "", fmt.Errorf("expected string but got %T", val)
174+
return "", TypeError(fmt.Sprintf("expected string but got %T", val))
164175
}
165176
}
166177

178+
// IntGetter is a Getter that must return an int64.
167179
type IntGetter[K any] interface {
180+
// Get retrieves an int64 value.
168181
Get(ctx context.Context, tCtx K) (int64, error)
169182
}
170183

184+
// StandardIntGetter is a basic implementation of IntGetter
171185
type StandardIntGetter[K any] struct {
172186
Getter func(ctx context.Context, tCtx K) (interface{}, error)
173187
}
174188

189+
// Get retrieves an int64 value.
190+
// If the value is not an int64 a new TypeError is returned.
191+
// If there is an error getting the value it will be returned.
175192
func (g StandardIntGetter[K]) Get(ctx context.Context, tCtx K) (int64, error) {
176193
val, err := g.Getter(ctx, tCtx)
177194
if err != nil {
178-
return 0, err
195+
return 0, fmt.Errorf("error getting value in %T: %w", g, err)
179196
}
180197
if val == nil {
181-
return 0, fmt.Errorf("expected int64 but got nil")
198+
return 0, TypeError("expected int64 but got nil")
182199
}
183200
switch v := val.(type) {
184201
case int64:
@@ -187,27 +204,33 @@ func (g StandardIntGetter[K]) Get(ctx context.Context, tCtx K) (int64, error) {
187204
if v.Type() == pcommon.ValueTypeInt {
188205
return v.Int(), nil
189206
}
190-
return 0, fmt.Errorf("expected int64 but got %v", v.Type())
207+
return 0, TypeError(fmt.Sprintf("expected int64 but got %v", v.Type()))
191208
default:
192-
return 0, fmt.Errorf("expected int64 but got %T", val)
209+
return 0, TypeError(fmt.Sprintf("expected int64 but got %T", val))
193210
}
194211
}
195212

213+
// FloatGetter is a Getter that must return a float64.
196214
type FloatGetter[K any] interface {
215+
// Get retrieves a float64 value.
197216
Get(ctx context.Context, tCtx K) (float64, error)
198217
}
199218

219+
// StandardFloatGetter is a basic implementation of FloatGetter
200220
type StandardFloatGetter[K any] struct {
201221
Getter func(ctx context.Context, tCtx K) (interface{}, error)
202222
}
203223

224+
// Get retrieves a float64 value.
225+
// If the value is not a float64 a new TypeError is returned.
226+
// If there is an error getting the value it will be returned.
204227
func (g StandardFloatGetter[K]) Get(ctx context.Context, tCtx K) (float64, error) {
205228
val, err := g.Getter(ctx, tCtx)
206229
if err != nil {
207-
return 0, err
230+
return 0, fmt.Errorf("error getting value in %T: %w", g, err)
208231
}
209232
if val == nil {
210-
return 0, fmt.Errorf("expected float64 but got nil")
233+
return 0, TypeError("expected float64 but got nil")
211234
}
212235
switch v := val.(type) {
213236
case float64:
@@ -216,27 +239,33 @@ func (g StandardFloatGetter[K]) Get(ctx context.Context, tCtx K) (float64, error
216239
if v.Type() == pcommon.ValueTypeDouble {
217240
return v.Double(), nil
218241
}
219-
return 0, fmt.Errorf("expected float64 but got %v", v.Type())
242+
return 0, TypeError(fmt.Sprintf("expected float64 but got %v", v.Type()))
220243
default:
221-
return 0, fmt.Errorf("expected float64 but got %T", val)
244+
return 0, TypeError(fmt.Sprintf("expected float64 but got %T", val))
222245
}
223246
}
224247

248+
// PMapGetter is a Getter that must return a pcommon.Map.
225249
type PMapGetter[K any] interface {
250+
// Get retrieves a pcommon.Map value.
226251
Get(ctx context.Context, tCtx K) (pcommon.Map, error)
227252
}
228253

254+
// StandardPMapGetter is a basic implementation of PMapGetter
229255
type StandardPMapGetter[K any] struct {
230256
Getter func(ctx context.Context, tCtx K) (interface{}, error)
231257
}
232258

259+
// Get retrieves a pcommon.Map value.
260+
// If the value is not a pcommon.Map a new TypeError is returned.
261+
// If there is an error getting the value it will be returned.
233262
func (g StandardPMapGetter[K]) Get(ctx context.Context, tCtx K) (pcommon.Map, error) {
234263
val, err := g.Getter(ctx, tCtx)
235264
if err != nil {
236-
return pcommon.Map{}, err
265+
return pcommon.Map{}, fmt.Errorf("error getting value in %T: %w", g, err)
237266
}
238267
if val == nil {
239-
return pcommon.Map{}, fmt.Errorf("expected pcommon.Map but got nil")
268+
return pcommon.Map{}, TypeError("expected pcommon.Map but got nil")
240269
}
241270
switch v := val.(type) {
242271
case pcommon.Map:
@@ -245,7 +274,7 @@ func (g StandardPMapGetter[K]) Get(ctx context.Context, tCtx K) (pcommon.Map, er
245274
if v.Type() == pcommon.ValueTypeMap {
246275
return v.Map(), nil
247276
}
248-
return pcommon.Map{}, fmt.Errorf("expected pcommon.Map but got %v", v.Type())
277+
return pcommon.Map{}, TypeError(fmt.Sprintf("expected pcommon.Map but got %v", v.Type()))
249278
case map[string]any:
250279
m := pcommon.NewMap()
251280
err = m.FromRaw(v)
@@ -254,7 +283,7 @@ func (g StandardPMapGetter[K]) Get(ctx context.Context, tCtx K) (pcommon.Map, er
254283
}
255284
return m, nil
256285
default:
257-
return pcommon.Map{}, fmt.Errorf("expected pcommon.Map but got %T", val)
286+
return pcommon.Map{}, TypeError(fmt.Sprintf("expected pcommon.Map but got %T", val))
258287
}
259288
}
260289

@@ -274,7 +303,7 @@ type StandardStringLikeGetter[K any] struct {
274303
func (g StandardStringLikeGetter[K]) Get(ctx context.Context, tCtx K) (*string, error) {
275304
val, err := g.Getter(ctx, tCtx)
276305
if err != nil {
277-
return nil, err
306+
return nil, fmt.Errorf("error getting value in %T: %w", g, err)
278307
}
279308
if val == nil {
280309
return nil, nil
@@ -300,7 +329,7 @@ func (g StandardStringLikeGetter[K]) Get(ctx context.Context, tCtx K) (*string,
300329
default:
301330
result, err = jsoniter.MarshalToString(v)
302331
if err != nil {
303-
return nil, fmt.Errorf("unsupported type: %T", v)
332+
return nil, TypeError(fmt.Sprintf("unsupported type: %T", v))
304333
}
305334
}
306335
return &result, nil
@@ -322,7 +351,7 @@ type StandardFloatLikeGetter[K any] struct {
322351
func (g StandardFloatLikeGetter[K]) Get(ctx context.Context, tCtx K) (*float64, error) {
323352
val, err := g.Getter(ctx, tCtx)
324353
if err != nil {
325-
return nil, err
354+
return nil, fmt.Errorf("error getting value in %T: %w", g, err)
326355
}
327356
if val == nil {
328357
return nil, nil
@@ -362,10 +391,10 @@ func (g StandardFloatLikeGetter[K]) Get(ctx context.Context, tCtx K) (*float64,
362391
result = float64(0)
363392
}
364393
default:
365-
return nil, fmt.Errorf("unsupported value type: %v", v.Type())
394+
return nil, TypeError(fmt.Sprintf("unsupported value type: %v", v.Type()))
366395
}
367396
default:
368-
return nil, fmt.Errorf("unsupported type: %T", v)
397+
return nil, TypeError(fmt.Sprintf("unsupported type: %T", v))
369398
}
370399
return &result, nil
371400
}
@@ -386,7 +415,7 @@ type StandardIntLikeGetter[K any] struct {
386415
func (g StandardIntLikeGetter[K]) Get(ctx context.Context, tCtx K) (*int64, error) {
387416
val, err := g.Getter(ctx, tCtx)
388417
if err != nil {
389-
return nil, err
418+
return nil, fmt.Errorf("error getting value in %T: %w", g, err)
390419
}
391420
if val == nil {
392421
return nil, nil
@@ -426,10 +455,10 @@ func (g StandardIntLikeGetter[K]) Get(ctx context.Context, tCtx K) (*int64, erro
426455
result = int64(0)
427456
}
428457
default:
429-
return nil, fmt.Errorf("unsupported value type: %v", v.Type())
458+
return nil, TypeError(fmt.Sprintf("unsupported value type: %v", v.Type()))
430459
}
431460
default:
432-
return nil, fmt.Errorf("unsupported type: %T", v)
461+
return nil, TypeError(fmt.Sprintf("unsupported type: %T", v))
433462
}
434463
return &result, nil
435464
}

0 commit comments

Comments
 (0)