diff --git a/pkg/ottl/expression.go b/pkg/ottl/expression.go index 9fbfa9c7756b..c83b5f90b560 100644 --- a/pkg/ottl/expression.go +++ b/pkg/ottl/expression.go @@ -583,13 +583,13 @@ func (p *Parser[K]) newGetterFromConverter(c converter) (Getter[K], error) { }, nil } -// TimeGetter is a Getter that must return an time.Time. +// TimeGetter is a Getter that must return a time.Time. type TimeGetter[K any] interface { - // Get retrieves an time.Time value. + // Get retrieves a time.Time value. Get(ctx context.Context, tCtx K) (time.Time, error) } -// StandardTimeGetter is a basic implementation of IntGetter +// StandardTimeGetter is a basic implementation of TimeGetter type StandardTimeGetter[K any] struct { Getter func(ctx context.Context, tCtx K) (interface{}, error) } @@ -613,9 +613,9 @@ func (g StandardTimeGetter[K]) Get(ctx context.Context, tCtx K) (time.Time, erro } } -// DurationGetter is a Getter that must return an time.Duration. +// DurationGetter is a Getter that must return a time.Duration. type DurationGetter[K any] interface { - // Get retrieves an int64 value. + // Get retrieves a time.Duration value. Get(ctx context.Context, tCtx K) (time.Duration, error) } diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index 967f80453363..46845ac85f3c 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -181,6 +181,18 @@ func (p *Parser[K]) buildSliceArg(argVal value, argType reflect.Type) (any, erro return nil, err } return arg, nil + case strings.HasPrefix(name, "DurationGetter"): + arg, err := buildSlice[DurationGetter[K]](argVal, argType, p.buildArg, name) + if err != nil { + return nil, err + } + return arg, nil + case strings.HasPrefix(name, "TimeGetter"): + arg, err := buildSlice[TimeGetter[K]](argVal, argType, p.buildArg, name) + if err != nil { + return nil, err + } + return arg, nil default: return nil, fmt.Errorf("unsupported slice type %q for function", argType.Elem().Name()) } @@ -249,6 +261,18 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) { return nil, err } return StandardPMapGetter[K]{Getter: arg.Get}, nil + case strings.HasPrefix(name, "DurationGetter"): + arg, err := p.newGetter(argVal) + if err != nil { + return nil, err + } + return StandardDurationGetter[K]{Getter: arg.Get}, nil + case strings.HasPrefix(name, "TimeGetter"): + arg, err := p.newGetter(argVal) + if err != nil { + return nil, err + } + return StandardTimeGetter[K]{Getter: arg.Get}, nil case name == "Enum": arg, err := p.enumParser(argVal.Enum) if err != nil { diff --git a/pkg/ottl/functions_test.go b/pkg/ottl/functions_test.go index 55d63fe1a42c..55ca83e432cb 100644 --- a/pkg/ottl/functions_test.go +++ b/pkg/ottl/functions_test.go @@ -668,6 +668,40 @@ func Test_NewFunctionCall(t *testing.T) { }, want: 2, }, + { + name: "durationgetter slice arg", + inv: editor{ + Function: "testing_durationgetter_slice", + Arguments: []value{ + { + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + }, + }, + }, + }, + }, + }, + { + name: "timegetter slice arg", + inv: editor{ + Function: "testing_timegetter_slice", + Arguments: []value{ + { + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + }, + }, + }, + }, + }, + }, { name: "floatgetter slice arg", inv: editor{ @@ -971,6 +1005,28 @@ func Test_NewFunctionCall(t *testing.T) { }, want: nil, }, + { + name: "durationgetter arg", + inv: editor{ + Function: "testing_durationgetter", + Arguments: []value{ + { + String: ottltest.Strp("test"), + }, + }, + }, + }, + { + name: "timegetter arg", + inv: editor{ + Function: "testing_timegetter", + Arguments: []value{ + { + String: ottltest.Strp("test"), + }, + }, + }, + }, { name: "functiongetter arg (Uppercase)", inv: editor{ @@ -1274,6 +1330,26 @@ func functionWithStringGetterSlice(getters []StringGetter[interface{}]) (ExprFun }, nil } +type durationGetterSliceArguments struct { + DurationGetters []DurationGetter[any] `ottlarg:"0"` +} + +func functionWithDurationGetterSlice(_ []DurationGetter[interface{}]) (ExprFunc[interface{}], error) { + return func(context.Context, interface{}) (interface{}, error) { + return nil, nil + }, nil +} + +type timeGetterSliceArguments struct { + TimeGetters []TimeGetter[any] `ottlarg:"0"` +} + +func functionWithTimeGetterSlice(_ []TimeGetter[interface{}]) (ExprFunc[interface{}], error) { + return func(context.Context, interface{}) (interface{}, error) { + return nil, nil + }, nil +} + type floatGetterSliceArguments struct { FloatGetters []FloatGetter[any] `ottlarg:"0"` } @@ -1374,6 +1450,26 @@ func functionWithStringGetter(StringGetter[interface{}]) (ExprFunc[interface{}], }, nil } +type durationGetterArguments struct { + DurationGetterArg DurationGetter[any] `ottlarg:"0"` +} + +func functionWithDurationGetter(DurationGetter[interface{}]) (ExprFunc[interface{}], error) { + return func(context.Context, interface{}) (interface{}, error) { + return "anything", nil + }, nil +} + +type timeGetterArguments struct { + TimeGetterArg TimeGetter[any] `ottlarg:"0"` +} + +func functionWithTimeGetter(TimeGetter[interface{}]) (ExprFunc[interface{}], error) { + return func(context.Context, interface{}) (interface{}, error) { + return "anything", nil + }, nil +} + type functionGetterArguments struct { FunctionGetterArg FunctionGetter[any] `ottlarg:"0"` } @@ -1608,6 +1704,16 @@ func defaultFunctionsForTests() map[string]Factory[any] { &stringGetterSliceArguments{}, functionWithStringGetterSlice, ), + createFactory[any]( + "testing_durationgetter_slice", + &durationGetterSliceArguments{}, + functionWithDurationGetterSlice, + ), + createFactory[any]( + "testing_timegetter_slice", + &timeGetterSliceArguments{}, + functionWithTimeGetterSlice, + ), createFactory[any]( "testing_stringlikegetter_slice", &stringLikeGetterSliceArguments{}, @@ -1653,6 +1759,16 @@ func defaultFunctionsForTests() map[string]Factory[any] { &getterArguments{}, functionWithGetter, ), + createFactory[any]( + "testing_durationgetter", + &durationGetterArguments{}, + functionWithDurationGetter, + ), + createFactory[any]( + "testing_timegetter", + &timeGetterArguments{}, + functionWithTimeGetter, + ), createFactory[any]( "testing_stringgetter", &stringGetterArguments{}, diff --git a/pkg/ottl/ottlfuncs/README.md b/pkg/ottl/ottlfuncs/README.md index fe82c55b76d6..6a93e829a59f 100644 --- a/pkg/ottl/ottlfuncs/README.md +++ b/pkg/ottl/ottlfuncs/README.md @@ -279,20 +279,29 @@ Available Converters: - [ConvertCase](#convertcase) - [ExtractPatterns](#extractpatterns) - [FNV](#fnv) +- [Hours](#hours) - [Duration](#duration) - [Int](#int) - [IsMap](#ismap) - [IsMatch](#ismatch) - [IsString](#isstring) - [Log](#log) +- [Microseconds](#microseconds) +- [Milliseconds](#milliseconds) +- [Minutes](#minutes) +- [Nanoseconds](#nanoseconds) - [ParseJSON](#parsejson) +- [Seconds](#seconds) - [SHA1](#sha1) - [SHA256](#sha256) - [SpanID](#spanid) - [Split](#split) -- [Time](#time) -- [TraceID](#traceid) - [Substring](#substring) +- [Time](#time) +- [UnixMicro](#unixmicro) +- [UnixMilli](#unixmilli) +- [UnixNano](#unixnano) +- [UnixSeconds](#unixseconds) - [UUID](#UUID) ### Concat @@ -342,9 +351,9 @@ Examples: `Duration(duration)` -The `Duration` Converter takes a string representation of a duration and converts it to a Golang `time.duration`. +The `Duration` Converter takes a string representation of a duration and converts it to a [Golang `time.duration`](https://pkg.go.dev/time#ParseDuration). -`duration` is a string. +`duration` is a string. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". If either `duration` is nil or is in a format that cannot be converted to Golang `time.duration`, an error is returned. @@ -389,6 +398,20 @@ Examples: - `FNV("name")` +### Hours + +`Hours(value)` + +The `Hours` Converter returns the duration as a floating point number of hours. + +`value` is a `time.Duration`. If `value` is another type an error is returned. + +The returned type is `float64`. + +Examples: + +- `Hours(Duration("1h"))` + ### Int `Int(value)` @@ -516,6 +539,62 @@ Examples: - `Int(Log(attributes["duration_ms"])` +### Microseconds + +`Microseconds(value)` + +The `Microseconds` Converter returns the duration as an integer millisecond count. + +`value` is a `time.Duration`. If `value` is another type an error is returned. + +The returned type is `int64`. + +Examples: + +- `Microseconds(Duration("1h"))` + +### Milliseconds + +`Milliseconds(value)` + +The `Milliseconds` Converter returns the duration as an integer millisecond count. + +`value` is a `time.Duration`. If `value` is another type an error is returned. + +The returned type is `int64`. + +Examples: + +- `Milliseconds(Duration("1h"))` + +### Minutes + +`Minutes(value)` + +The `Minutes` Converter returns the duration as a floating point number of minutes. + +`value` is a `time.Duration`. If `value` is another type an error is returned. + +The returned type is `float64`. + +Examples: + +- `Minutes(Duration("1h"))` + +### Nanoseconds + +`Nanoseconds(value)` + +The `Nanoseconds` Converter returns the duration as an integer nanosecond count. + +`value` is a `time.Duration`. If `value` is another type an error is returned. + +The returned type is `int64`. + +Examples: + +- `Nanoseconds(Duration("1h"))` + ### ParseJSON `ParseJSON(target)` @@ -547,6 +626,20 @@ Examples: - `ParseJSON(body)` +### Seconds + +`Seconds(value)` + +The `Seconds` Converter returns the duration as a floating point number of seconds. + +`value` is a `time.Duration`. If `value` is another type an error is returned. + +The returned type is `float64`. + +Examples: + +- `Seconds(Duration("1h"))` + ### SHA1 `SHA1(value)` @@ -618,6 +711,21 @@ Examples: - ```Split("A|B|C", "|")``` +### Substring + +`Substring(target, start, length)` + +The `Substring` Converter returns a substring from the given start index to the specified length. + +`target` is a string. `start` and `length` are `int64`. + +If `target` is not a string or is nil, an error is returned. +If the start/length exceed the length of the `target` string, an error is returned. + +Examples: + +- `Substring("123456789", 0, 3)` + ### Time The `Time` Converter takes a string representation of a time and converts it to a Golang `time.Time`. @@ -642,20 +750,61 @@ Examples: - `TraceID(0x00000000000000000000000000000000)` -### Substring +### UnixMicro -`Substring(target, start, length)` +`UnixMicro(value)` -The `Substring` Converter returns a substring from the given start index to the specified length. +The `UnixMicro` Converter returns the time as a Unix time, the number of microseconds elapsed since January 1, 1970 UTC. -`target` is a string. `start` and `length` are `int64`. +`value` is a `time.Time`. If `value` is another type an error is returned. -If `target` is not a string or is nil, an error is returned. -If the start/length exceed the length of the `target` string, an error is returned. +The returned type is `int64`. Examples: -- `Substring("123456789", 0, 3)` +- `UnixMicro(Time("02/04/2023", "%m/%d/%Y"))` + +### UnixMilli + +`UnixMilli(value)` + +The `UnixMilli` Converter returns the time as a Unix time, the number of milliseconds elapsed since January 1, 1970 UTC. + +`value` is a `time.Time`. If `value` is another type an error is returned. + +The returned type is `int64`. + +Examples: + +- `UnixMilli(Time("02/04/2023", "%m/%d/%Y"))` + +### UnixNano + +`UnixNano(value)` + +The `UnixNano` Converter returns the time as a Unix time, the number of nanoseconds elapsed since January 1, 1970 UTC. + +`value` is a `time.Time`. If `value` is another type an error is returned. + +The returned type is `int64`. + +Examples: + +- `UnixNano(Time("02/04/2023", "%m/%d/%Y"))` + +### UnixSeconds + +`UnixSeconds(value)` + +The `UnixSeconds` Converter returns the time as a Unix time, the number of seconds elapsed since January 1, 1970 UTC. + +`value` is a `time.Time`. If `value` is another type an error is returned. + +The returned type is `int64`. + +Examples: + +- `UnixSeconds(Time("02/04/2023", "%m/%d/%Y"))` ### UUID diff --git a/pkg/ottl/ottlfuncs/func_duration_hours.go b/pkg/ottl/ottlfuncs/func_hours.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_hours.go rename to pkg/ottl/ottlfuncs/func_hours.go diff --git a/pkg/ottl/ottlfuncs/func_duration_hours_test.go b/pkg/ottl/ottlfuncs/func_hours_test.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_hours_test.go rename to pkg/ottl/ottlfuncs/func_hours_test.go diff --git a/pkg/ottl/ottlfuncs/func_duration_micro.go b/pkg/ottl/ottlfuncs/func_microseconds.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_micro.go rename to pkg/ottl/ottlfuncs/func_microseconds.go diff --git a/pkg/ottl/ottlfuncs/func_duration_micro_test.go b/pkg/ottl/ottlfuncs/func_microseconds_test.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_micro_test.go rename to pkg/ottl/ottlfuncs/func_microseconds_test.go diff --git a/pkg/ottl/ottlfuncs/func_duration_milli.go b/pkg/ottl/ottlfuncs/func_milliseconds.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_milli.go rename to pkg/ottl/ottlfuncs/func_milliseconds.go diff --git a/pkg/ottl/ottlfuncs/func_duration_milli_test.go b/pkg/ottl/ottlfuncs/func_milliseconds_test.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_milli_test.go rename to pkg/ottl/ottlfuncs/func_milliseconds_test.go diff --git a/pkg/ottl/ottlfuncs/func_duration_minutes.go b/pkg/ottl/ottlfuncs/func_minutes.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_minutes.go rename to pkg/ottl/ottlfuncs/func_minutes.go diff --git a/pkg/ottl/ottlfuncs/func_duration_minutes_test.go b/pkg/ottl/ottlfuncs/func_minutes_test.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_minutes_test.go rename to pkg/ottl/ottlfuncs/func_minutes_test.go diff --git a/pkg/ottl/ottlfuncs/func_duration_nano.go b/pkg/ottl/ottlfuncs/func_nanoseconds.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_nano.go rename to pkg/ottl/ottlfuncs/func_nanoseconds.go diff --git a/pkg/ottl/ottlfuncs/func_duration_nano_test.go b/pkg/ottl/ottlfuncs/func_nanoseconds_test.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_nano_test.go rename to pkg/ottl/ottlfuncs/func_nanoseconds_test.go diff --git a/pkg/ottl/ottlfuncs/func_duration_seconds.go b/pkg/ottl/ottlfuncs/func_seconds.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_seconds.go rename to pkg/ottl/ottlfuncs/func_seconds.go diff --git a/pkg/ottl/ottlfuncs/func_duration_seconds_test.go b/pkg/ottl/ottlfuncs/func_seconds_test.go similarity index 100% rename from pkg/ottl/ottlfuncs/func_duration_seconds_test.go rename to pkg/ottl/ottlfuncs/func_seconds_test.go