Skip to content

Commit

Permalink
[pkg/ottl] Add new IsMap and IsString functions (open-telemetry#2…
Browse files Browse the repository at this point in the history
…2750)

* 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
  • Loading branch information
TylerHelmuth authored and Caleb-Hurshman committed Jul 6, 2023
1 parent f507cef commit 5ec3c92
Show file tree
Hide file tree
Showing 9 changed files with 447 additions and 25 deletions.
20 changes: 20 additions & 0 deletions .chloggen/ottl-check-type.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use this changelog template to create an entry for release notes.
# If your change doesn't affect end users, such as a test fix or a tooling change,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# 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: Add new `IsString` and `IsMap` functions to facilitate type checking.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [22750]

# (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: Especially useful for checking log body type before parsing.
79 changes: 54 additions & 25 deletions pkg/ottl/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,23 +133,34 @@ func (l *listGetter[K]) Get(ctx context.Context, tCtx K) (interface{}, error) {
return evaluated, nil
}

// TypeError represents that a value was not an expected type.
type TypeError string

func (t TypeError) Error() string {
return string(t)
}

// StringGetter is a Getter that must return a string.
type StringGetter[K any] interface {
// Get retrieves a string value. If the value is not a string, an error is returned.
// Get retrieves a string value.
Get(ctx context.Context, tCtx K) (string, error)
}

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

// Get retrieves a string value.
// If the value is not a string a new TypeError is returned.
// If there is an error getting the value it will be returned.
func (g StandardStringGetter[K]) Get(ctx context.Context, tCtx K) (string, error) {
val, err := g.Getter(ctx, tCtx)
if err != nil {
return "", err
return "", fmt.Errorf("error getting value in %T: %w", g, err)
}
if val == nil {
return "", fmt.Errorf("expected string but got nil")
return "", TypeError("expected string but got nil")
}
switch v := val.(type) {
case string:
Expand All @@ -158,27 +169,33 @@ func (g StandardStringGetter[K]) Get(ctx context.Context, tCtx K) (string, error
if v.Type() == pcommon.ValueTypeStr {
return v.Str(), nil
}
return "", fmt.Errorf("expected string but got %v", v.Type())
return "", TypeError(fmt.Sprintf("expected string but got %v", v.Type()))
default:
return "", fmt.Errorf("expected string but got %T", val)
return "", TypeError(fmt.Sprintf("expected string but got %T", val))
}
}

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

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

// Get retrieves an int64 value.
// If the value is not an int64 a new TypeError is returned.
// If there is an error getting the value it will be returned.
func (g StandardIntGetter[K]) Get(ctx context.Context, tCtx K) (int64, error) {
val, err := g.Getter(ctx, tCtx)
if err != nil {
return 0, err
return 0, fmt.Errorf("error getting value in %T: %w", g, err)
}
if val == nil {
return 0, fmt.Errorf("expected int64 but got nil")
return 0, TypeError("expected int64 but got nil")
}
switch v := val.(type) {
case int64:
Expand All @@ -187,27 +204,33 @@ func (g StandardIntGetter[K]) Get(ctx context.Context, tCtx K) (int64, error) {
if v.Type() == pcommon.ValueTypeInt {
return v.Int(), nil
}
return 0, fmt.Errorf("expected int64 but got %v", v.Type())
return 0, TypeError(fmt.Sprintf("expected int64 but got %v", v.Type()))
default:
return 0, fmt.Errorf("expected int64 but got %T", val)
return 0, TypeError(fmt.Sprintf("expected int64 but got %T", val))
}
}

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

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

// Get retrieves a float64 value.
// If the value is not a float64 a new TypeError is returned.
// If there is an error getting the value it will be returned.
func (g StandardFloatGetter[K]) Get(ctx context.Context, tCtx K) (float64, error) {
val, err := g.Getter(ctx, tCtx)
if err != nil {
return 0, err
return 0, fmt.Errorf("error getting value in %T: %w", g, err)
}
if val == nil {
return 0, fmt.Errorf("expected float64 but got nil")
return 0, TypeError("expected float64 but got nil")
}
switch v := val.(type) {
case float64:
Expand All @@ -216,27 +239,33 @@ func (g StandardFloatGetter[K]) Get(ctx context.Context, tCtx K) (float64, error
if v.Type() == pcommon.ValueTypeDouble {
return v.Double(), nil
}
return 0, fmt.Errorf("expected float64 but got %v", v.Type())
return 0, TypeError(fmt.Sprintf("expected float64 but got %v", v.Type()))
default:
return 0, fmt.Errorf("expected float64 but got %T", val)
return 0, TypeError(fmt.Sprintf("expected float64 but got %T", val))
}
}

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

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

// Get retrieves a pcommon.Map value.
// If the value is not a pcommon.Map a new TypeError is returned.
// If there is an error getting the value it will be returned.
func (g StandardPMapGetter[K]) Get(ctx context.Context, tCtx K) (pcommon.Map, error) {
val, err := g.Getter(ctx, tCtx)
if err != nil {
return pcommon.Map{}, err
return pcommon.Map{}, fmt.Errorf("error getting value in %T: %w", g, err)
}
if val == nil {
return pcommon.Map{}, fmt.Errorf("expected pcommon.Map but got nil")
return pcommon.Map{}, TypeError("expected pcommon.Map but got nil")
}
switch v := val.(type) {
case pcommon.Map:
Expand All @@ -245,7 +274,7 @@ func (g StandardPMapGetter[K]) Get(ctx context.Context, tCtx K) (pcommon.Map, er
if v.Type() == pcommon.ValueTypeMap {
return v.Map(), nil
}
return pcommon.Map{}, fmt.Errorf("expected pcommon.Map but got %v", v.Type())
return pcommon.Map{}, TypeError(fmt.Sprintf("expected pcommon.Map but got %v", v.Type()))
case map[string]any:
m := pcommon.NewMap()
err = m.FromRaw(v)
Expand All @@ -254,7 +283,7 @@ func (g StandardPMapGetter[K]) Get(ctx context.Context, tCtx K) (pcommon.Map, er
}
return m, nil
default:
return pcommon.Map{}, fmt.Errorf("expected pcommon.Map but got %T", val)
return pcommon.Map{}, TypeError(fmt.Sprintf("expected pcommon.Map but got %T", val))
}
}

Expand All @@ -274,7 +303,7 @@ type StandardStringLikeGetter[K any] struct {
func (g StandardStringLikeGetter[K]) Get(ctx context.Context, tCtx K) (*string, error) {
val, err := g.Getter(ctx, tCtx)
if err != nil {
return nil, err
return nil, fmt.Errorf("error getting value in %T: %w", g, err)
}
if val == nil {
return nil, nil
Expand All @@ -300,7 +329,7 @@ func (g StandardStringLikeGetter[K]) Get(ctx context.Context, tCtx K) (*string,
default:
result, err = jsoniter.MarshalToString(v)
if err != nil {
return nil, fmt.Errorf("unsupported type: %T", v)
return nil, TypeError(fmt.Sprintf("unsupported type: %T", v))
}
}
return &result, nil
Expand All @@ -322,7 +351,7 @@ type StandardFloatLikeGetter[K any] struct {
func (g StandardFloatLikeGetter[K]) Get(ctx context.Context, tCtx K) (*float64, error) {
val, err := g.Getter(ctx, tCtx)
if err != nil {
return nil, err
return nil, fmt.Errorf("error getting value in %T: %w", g, err)
}
if val == nil {
return nil, nil
Expand Down Expand Up @@ -362,10 +391,10 @@ func (g StandardFloatLikeGetter[K]) Get(ctx context.Context, tCtx K) (*float64,
result = float64(0)
}
default:
return nil, fmt.Errorf("unsupported value type: %v", v.Type())
return nil, TypeError(fmt.Sprintf("unsupported value type: %v", v.Type()))
}
default:
return nil, fmt.Errorf("unsupported type: %T", v)
return nil, TypeError(fmt.Sprintf("unsupported type: %T", v))
}
return &result, nil
}
Expand All @@ -386,7 +415,7 @@ type StandardIntLikeGetter[K any] struct {
func (g StandardIntLikeGetter[K]) Get(ctx context.Context, tCtx K) (*int64, error) {
val, err := g.Getter(ctx, tCtx)
if err != nil {
return nil, err
return nil, fmt.Errorf("error getting value in %T: %w", g, err)
}
if val == nil {
return nil, nil
Expand Down Expand Up @@ -426,10 +455,10 @@ func (g StandardIntLikeGetter[K]) Get(ctx context.Context, tCtx K) (*int64, erro
result = int64(0)
}
default:
return nil, fmt.Errorf("unsupported value type: %v", v.Type())
return nil, TypeError(fmt.Sprintf("unsupported value type: %v", v.Type()))
}
default:
return nil, fmt.Errorf("unsupported type: %T", v)
return nil, TypeError(fmt.Sprintf("unsupported type: %T", v))
}
return &result, nil
}
Expand Down
Loading

0 comments on commit 5ec3c92

Please sign in to comment.