diff --git a/.chloggen/26304-tailsampling-processor-checkapi.yaml b/.chloggen/26304-tailsampling-processor-checkapi.yaml new file mode 100755 index 0000000000000..1985120418e8e --- /dev/null +++ b/.chloggen/26304-tailsampling-processor-checkapi.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: tailsamplingprocessor + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Unexport `SamplingProcessorMetricViews` to comply with checkapi + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [26304] + +# (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: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [api] diff --git a/.chloggen/filelogreceiver_log_globing_io_errors.yaml b/.chloggen/filelogreceiver_log_globing_io_errors.yaml new file mode 100644 index 0000000000000..9bd6fefd7a81f --- /dev/null +++ b/.chloggen/filelogreceiver_log_globing_io_errors.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# 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: filelogreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Log the globbing IO errors + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [23768] + +# (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: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/.chloggen/mongodbreceiver-checkapi-26304.yaml b/.chloggen/mongodbreceiver-checkapi-26304.yaml new file mode 100755 index 0000000000000..ea2b291db8716 --- /dev/null +++ b/.chloggen/mongodbreceiver-checkapi-26304.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: mongodbreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Do not export the function `NewClient` and pass checkapi. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [26304] + +# (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: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [api] \ No newline at end of file diff --git a/.chloggen/ottl-named-arguments.yaml b/.chloggen/ottl-named-arguments.yaml new file mode 100644 index 0000000000000..b2e51f6c1c589 --- /dev/null +++ b/.chloggen/ottl-named-arguments.yaml @@ -0,0 +1,30 @@ +# Use this changelog template to create an entry for release notes. + +# 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: Allow named arguments in function invocations + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [20879] + +# (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: | + Arguments can now be specified by a snake-cased version of their name in the function's + `Arguments` struct. Named arguments can be specified in any order, but must be specified + after arguments without a name. + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/.chloggen/ottl-optional-parameters.yaml b/.chloggen/ottl-optional-parameters.yaml new file mode 100755 index 0000000000000..f4c9e7656b6b6 --- /dev/null +++ b/.chloggen/ottl-optional-parameters.yaml @@ -0,0 +1,29 @@ +# Use this changelog template to create an entry for release notes. + +# 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 support for optional parameters + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [20879] + +# (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: | + The new `ottl.Optional` type can now be used in a function's `Arguments` struct + to indicate that a parameter is optional. + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [api] diff --git a/cmd/checkapi/allowlist.txt b/cmd/checkapi/allowlist.txt index 8b72c5272f87d..5e977a91b43d4 100644 --- a/cmd/checkapi/allowlist.txt +++ b/cmd/checkapi/allowlist.txt @@ -15,7 +15,6 @@ processor/groupbyattrsprocessor processor/groupbytraceprocessor processor/probabilisticsamplerprocessor processor/servicegraphprocessor -processor/tailsamplingprocessor receiver/carbonreceiver receiver/collectdreceiver receiver/dockerstatsreceiver @@ -23,7 +22,6 @@ receiver/jaegerreceiver receiver/journaldreceiver receiver/kafkareceiver receiver/mongodbatlasreceiver -receiver/mongodbreceiver receiver/podmanreceiver receiver/pulsarreceiver receiver/windowseventlogreceiver diff --git a/pkg/ottl/README.md b/pkg/ottl/README.md index d8f953db154b7..82ea6c5998f2f 100644 --- a/pkg/ottl/README.md +++ b/pkg/ottl/README.md @@ -90,6 +90,18 @@ For slice parameters, the following types are supported: - `uint8`. Byte slice literals are parsed as byte slices by the OTTL. - `Getter` +To make a parameter optional, use the `Optional` type, which takes a type argument for the underlying +parameter type. For example, an optional string parameter would be specified as `Optional[string]`. +All optional parameters must be specified after all required parameters. + +#### Arguments in invocations + +Function arguments must be passed in the order defined in the `Arguments` struct for the function unless they are named, in which case the arguments can come in any order. All named arguments must come after all arguments without +names. Argument names are snake-cased versions of the argument's field name in the function's `Arguments` struct. + +When passing optional arguments, all optional arguments preceding a given optional argument must be specified if +the arguments are not named. Passing a named argument allows skipping the preceding optional arguments. + ### Values Values are passed as function parameters or are used in a Boolean Expression. Values can take the form of: diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index fea6c99ba81d4..fbe2120b6ee02 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -8,6 +8,8 @@ import ( "fmt" "reflect" "strings" + + "github.com/iancoleman/strcase" ) type PathExpressionParser[K any] func(*Path) (GetSetter[K], error) @@ -21,17 +23,20 @@ func (p *Parser[K]) newFunctionCall(ed editor) (Expr[K], error) { if !ok { return Expr[K]{}, fmt.Errorf("undefined function %q", ed.Function) } - args := f.CreateDefaultArguments() + defaultArgs := f.CreateDefaultArguments() + var args Arguments // A nil value indicates the function takes no arguments. - if args != nil { + if defaultArgs != nil { // Pointer values are necessary to fulfill the Go reflection // settability requirements. Non-pointer values are not // modifiable through reflection. - if reflect.TypeOf(args).Kind() != reflect.Pointer { + if reflect.TypeOf(defaultArgs).Kind() != reflect.Pointer { return Expr[K]{}, fmt.Errorf("factory for %q must return a pointer to an Arguments value in its CreateDefaultArguments method", ed.Function) } + args = reflect.New(reflect.ValueOf(defaultArgs).Elem().Type()).Interface() + err := p.buildArgs(ed, reflect.ValueOf(args).Elem()) if err != nil { return Expr[K]{}, fmt.Errorf("error while parsing arguments for call to %q: %w", ed.Function, err) @@ -47,24 +52,70 @@ func (p *Parser[K]) newFunctionCall(ed editor) (Expr[K], error) { } func (p *Parser[K]) buildArgs(ed editor, argsVal reflect.Value) error { - if len(ed.Arguments) != argsVal.NumField() { - return fmt.Errorf("incorrect number of arguments. Expected: %d Received: %d", argsVal.NumField(), len(ed.Arguments)) + requiredArgs := 0 + seenNamed := false + + for i := 0; i < len(ed.Arguments); i++ { + if !seenNamed && ed.Arguments[i].Name != "" { + seenNamed = true + } else if seenNamed && ed.Arguments[i].Name == "" { + return errors.New("unnamed argument used after named argument") + } } for i := 0; i < argsVal.NumField(); i++ { - field := argsVal.Field(i) - fieldType := field.Type() - argVal := ed.Arguments[i] + if !strings.HasPrefix(argsVal.Field(i).Type().Name(), "Optional") { + requiredArgs++ + } + } + + if len(ed.Arguments) < requiredArgs || len(ed.Arguments) > argsVal.NumField() { + return fmt.Errorf("incorrect number of arguments. Expected: %d Received: %d", argsVal.NumField(), len(ed.Arguments)) + } + + for i, edArg := range ed.Arguments { + var field reflect.Value + var fieldType reflect.Type + var isOptional bool + var arg argument + + if edArg.Name == "" { + field = argsVal.Field(i) + fieldType = field.Type() + isOptional = strings.HasPrefix(fieldType.Name(), "Optional") + arg = ed.Arguments[i] + } else { + field = argsVal.FieldByName(strcase.ToCamel(edArg.Name)) + if !field.IsValid() { + return fmt.Errorf("no such parameter: %s", edArg.Name) + } + fieldType = field.Type() + isOptional = strings.HasPrefix(fieldType.Name(), "Optional") + arg = edArg + } + var val any + var manager optionalManager var err error + var ok bool + if isOptional { + manager, ok = field.Interface().(optionalManager) + + if !ok { + return errors.New("optional type is not manageable by the OTTL parser. This is an error in the OTTL") + } + + fieldType = manager.get().Type() + } + switch { case strings.HasPrefix(fieldType.Name(), "FunctionGetter"): var name string switch { - case argVal.Enum != nil: - name = string(*argVal.Enum) - case argVal.FunctionName != nil: - name = *argVal.FunctionName + case arg.Value.Enum != nil: + name = string(*arg.Value.Enum) + case arg.Value.FunctionName != nil: + name = *arg.Value.FunctionName default: return fmt.Errorf("invalid function name given") } @@ -74,14 +125,18 @@ func (p *Parser[K]) buildArgs(ed editor, argsVal reflect.Value) error { } val = StandardFunctionGetter[K]{fCtx: FunctionContext{Set: p.telemetrySettings}, fact: f} case fieldType.Kind() == reflect.Slice: - val, err = p.buildSliceArg(argVal, fieldType) + val, err = p.buildSliceArg(arg.Value, fieldType) default: - val, err = p.buildArg(argVal, fieldType) + val, err = p.buildArg(arg.Value, fieldType) } if err != nil { return fmt.Errorf("invalid argument at position %v: %w", i, err) } - field.Set(reflect.ValueOf(val)) + if isOptional { + field.Set(manager.set(val)) + } else { + field.Set(reflect.ValueOf(val)) + } } return nil @@ -280,7 +335,7 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) { } return bool(*argVal.Bool), nil default: - return nil, errors.New("unsupported argument type") + return nil, fmt.Errorf("unsupported argument type: %s", name) } } @@ -310,3 +365,56 @@ func buildSlice[T any](argVal value, argType reflect.Type, buildArg buildArgFunc return vals, nil } + +// optionalManager provides a way for the parser to handle Optional[T] structs +// without needing to know the concrete type of T, which is inaccessible through +// the reflect package. +// Would likely be resolved by https://github.com/golang/go/issues/54393. +type optionalManager interface { + // set takes a non-reflection value and returns a reflect.Value of + // an Optional[T] struct with this value set. + set(val any) reflect.Value + + // get returns a reflect.Value value of the value contained within + // an Optional[T]. This allows obtaining a reflect.Type for T. + get() reflect.Value +} + +type Optional[T any] struct { + val T + hasValue bool +} + +// This is called only by reflection. +// nolint:unused +func (o Optional[T]) set(val any) reflect.Value { + return reflect.ValueOf(Optional[T]{ + val: val.(T), + hasValue: true, + }) +} + +func (o Optional[T]) IsEmpty() bool { + return !o.hasValue +} + +func (o Optional[T]) Get() T { + return o.val +} + +func (o Optional[T]) get() reflect.Value { + // `(reflect.Value).Call` will create a reflect.Value containing a zero-valued T. + // Trying to create a reflect.Value for T by calling reflect.TypeOf or + // reflect.ValueOf on an empty T value creates an invalid reflect.Value object, + // the `Call` method appears to do extra processing to capture the type. + return reflect.ValueOf(o).MethodByName("Get").Call(nil)[0] +} + +// Allows creating an Optional with a value already populated for use in testing +// OTTL functions. +func NewTestingOptional[T any](val T) Optional[T] { + return Optional[T]{ + val: val, + hasValue: true, + } +} diff --git a/pkg/ottl/functions_test.go b/pkg/ottl/functions_test.go index 011c9c7e58f4d..07f18d545c5e7 100644 --- a/pkg/ottl/functions_test.go +++ b/pkg/ottl/functions_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component/componenttest" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" @@ -73,6 +74,11 @@ func Test_NewFunctionCall_invalid(t *testing.T) { &functionGetterArguments{}, functionWithFunctionGetter, ), + createFactory[any]( + "testing_optional_args", + &optionalArgsArguments{}, + functionWithOptionalArgs, + ), ) p, _ := NewParser( @@ -90,16 +96,18 @@ func Test_NewFunctionCall_invalid(t *testing.T) { name: "unknown function", inv: editor{ Function: "unknownfunc", - Arguments: []value{}, + Arguments: []argument{}, }, }, { name: "Invalid Function Name", inv: editor{ Function: "testing_functiongetter", - Arguments: []value{ + Arguments: []argument{ { - String: (ottltest.Strp("SHA256")), + Value: value{ + String: (ottltest.Strp("SHA256")), + }, }, }, }, @@ -108,9 +116,11 @@ func Test_NewFunctionCall_invalid(t *testing.T) { name: "not accessor", inv: editor{ Function: "testing_getsetter", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("not path"), + Value: value{ + String: ottltest.Strp("not path"), + }, }, }, }, @@ -119,11 +129,13 @@ func Test_NewFunctionCall_invalid(t *testing.T) { name: "not reader (invalid function)", inv: editor{ Function: "testing_getter", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Converter: &converter{ - Function: "Unknownfunc", + Value: value{ + Literal: &mathExprLiteral{ + Converter: &converter{ + Function: "Unknownfunc", + }, }, }, }, @@ -134,20 +146,24 @@ func Test_NewFunctionCall_invalid(t *testing.T) { name: "not enough args", inv: editor{ Function: "testing_multiple_args", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, }, }, { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, }, }, @@ -156,41 +172,42 @@ func Test_NewFunctionCall_invalid(t *testing.T) { name: "too many args", inv: editor{ Function: "testing_multiple_args", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, }, }, { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, }, }, }, { - name: "not enough args with telemetrySettings", + name: "not matching arg type", inv: editor{ - Function: "testing_telemetry_settings_first", - Arguments: []value{ - { - String: ottltest.Strp("test"), - }, + Function: "testing_string", + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, + Value: value{ + Literal: &mathExprLiteral{ + Int: ottltest.Intp(10), }, }, }, @@ -198,94 +215,142 @@ func Test_NewFunctionCall_invalid(t *testing.T) { }, }, { - name: "too many args with telemetrySettings", + name: "not matching arg type when byte slice", inv: editor{ - Function: "testing_telemetry_settings_first", - Arguments: []value{ + Function: "testing_byte_slice", + Arguments: []argument{ { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - }, + Value: value{ + String: ottltest.Strp("test"), }, }, { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(10), + Value: value{ + String: ottltest.Strp("test"), }, }, + }, + }, + }, + { + name: "mismatching slice element type", + inv: editor{ + Function: "testing_string_slice", + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(10), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(10), + }, + }, + }, + }, }, }, }, }, }, { - name: "not matching arg type", + name: "mismatching slice argument type", inv: editor{ - Function: "testing_string", - Arguments: []value{ + Function: "testing_string_slice", + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(10), + Value: value{ + String: ottltest.Strp("test"), }, }, }, }, }, { - name: "not matching arg type when byte slice", + name: "named parameters used before unnamed parameters", inv: editor{ - Function: "testing_byte_slice", - Arguments: []value{ + Function: "testing_optional_args", + Arguments: []argument{ { - String: ottltest.Strp("test"), + Name: "get_setter_arg", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, + }, + }, + }, + }, + }, + { + Value: value{ + String: ottltest.Strp("test"), + }, }, { - String: ottltest.Strp("test"), + Name: "optional_arg", + Value: value{ + String: ottltest.Strp("test_optional"), + }, }, { - String: ottltest.Strp("test"), + Name: "optional_float_arg", + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, + }, }, }, }, }, { - name: "mismatching slice element type", + name: "nonexistent named parameter used", inv: editor{ - Function: "testing_string_slice", - Arguments: []value{ + Function: "testing_optional_args", + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(10), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, }, }, - }, - }, - }, - { - name: "mismatching slice argument type", - inv: editor{ - Function: "testing_string_slice", - Arguments: []value{ { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, + }, + { + Name: "no_such_name", + Value: value{ + String: ottltest.Strp("test_optional"), + }, + }, + { + Name: "optional_float_arg", + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, + }, }, }, }, @@ -300,9 +365,11 @@ func Test_NewFunctionCall_invalid(t *testing.T) { name: "Enum not found", inv: editor{ Function: "testing_enum", - Arguments: []value{ + Arguments: []argument{ { - Enum: (*EnumSymbol)(ottltest.Strp("SYMBOL_NOT_FOUND")), + Value: value{ + Enum: (*EnumSymbol)(ottltest.Strp("SYMBOL_NOT_FOUND")), + }, }, }, }, @@ -311,9 +378,11 @@ func Test_NewFunctionCall_invalid(t *testing.T) { name: "Unknown Function", inv: editor{ Function: "testing_functiongetter", - Arguments: []value{ + Arguments: []argument{ { - FunctionName: (ottltest.Strp("SHA256")), + Value: value{ + FunctionName: (ottltest.Strp("SHA256")), + }, }, }, }, @@ -351,14 +420,8 @@ func Test_NewFunctionCall(t *testing.T) { { name: "no arguments", inv: editor{ - Function: "testing_noop", - Arguments: []value{ - { - List: &list{ - Values: []value{}, - }, - }, - }, + Function: "testing_noop", + Arguments: []argument{}, }, want: nil, }, @@ -366,10 +429,12 @@ func Test_NewFunctionCall(t *testing.T) { name: "empty slice arg", inv: editor{ Function: "testing_string_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{}, + Value: value{ + List: &list{ + Values: []value{}, + }, }, }, }, @@ -380,18 +445,20 @@ func Test_NewFunctionCall(t *testing.T) { name: "string slice arg", inv: editor{ Function: "testing_string_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - { - String: ottltest.Strp("test"), - }, - { - String: ottltest.Strp("test"), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + { + String: ottltest.Strp("test"), + }, + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -404,23 +471,25 @@ func Test_NewFunctionCall(t *testing.T) { name: "float slice arg", inv: editor{ Function: "testing_float_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.1), + Value: value{ + List: &list{ + Values: []value{ + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, }, - }, - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.2), + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.2), + }, }, - }, - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.3), + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.3), + }, }, }, }, @@ -434,23 +503,25 @@ func Test_NewFunctionCall(t *testing.T) { name: "int slice arg", inv: editor{ Function: "testing_int_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + Value: value{ + List: &list{ + Values: []value{ + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, - }, - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, - }, - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, }, }, @@ -464,72 +535,74 @@ func Test_NewFunctionCall(t *testing.T) { name: "getter slice arg", inv: editor{ Function: "testing_getter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + List: &list{ + Values: []value{ + { + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, }, - }, - { - String: ottltest.Strp("test"), - }, - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + { + String: ottltest.Strp("test"), }, - }, - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.1), + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, - }, - { - Bool: (*boolean)(ottltest.Boolp(true)), - }, - { - Enum: (*EnumSymbol)(ottltest.Strp("TEST_ENUM")), - }, - { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - { - String: ottltest.Strp("test"), - }, + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), }, }, - }, - { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), + { + Bool: (*boolean)(ottltest.Boolp(true)), + }, + { + Enum: (*EnumSymbol)(ottltest.Strp("TEST_ENUM")), + }, + { + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + { + String: ottltest.Strp("test"), + }, }, - { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - { - String: ottltest.Strp("test"), + }, + }, + { + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + { + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + { + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -539,18 +612,20 @@ func Test_NewFunctionCall(t *testing.T) { }, }, }, - }, - { - Literal: &mathExprLiteral{ - Converter: &converter{ - Function: "testing_getter", - Arguments: []value{ - { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + { + Literal: &mathExprLiteral{ + Converter: &converter{ + Function: "testing_getter", + Arguments: []argument{ + { + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, + }, }, }, }, @@ -571,15 +646,17 @@ func Test_NewFunctionCall(t *testing.T) { name: "stringgetter slice arg", inv: editor{ Function: "testing_stringgetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - { - String: ottltest.Strp("also test"), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + { + String: ottltest.Strp("also test"), + }, }, }, }, @@ -592,12 +669,14 @@ func Test_NewFunctionCall(t *testing.T) { name: "durationgetter slice arg", inv: editor{ Function: "testing_durationgetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -609,12 +688,14 @@ func Test_NewFunctionCall(t *testing.T) { name: "timegetter slice arg", inv: editor{ Function: "testing_timegetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -626,16 +707,18 @@ func Test_NewFunctionCall(t *testing.T) { name: "floatgetter slice arg", inv: editor{ Function: "testing_floatgetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("1.1"), - }, - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("1.1"), + }, + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1), + }, }, }, }, @@ -649,18 +732,20 @@ func Test_NewFunctionCall(t *testing.T) { name: "intgetter slice arg", inv: editor{ Function: "testing_intgetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + Value: value{ + List: &list{ + Values: []value{ + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, - }, - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(2), + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(2), + }, }, }, }, @@ -674,27 +759,29 @@ func Test_NewFunctionCall(t *testing.T) { name: "pmapgetter slice arg", inv: editor{ Function: "testing_pmapgetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + List: &list{ + Values: []value{ + { + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, }, - }, - { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + { + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, @@ -711,16 +798,18 @@ func Test_NewFunctionCall(t *testing.T) { name: "stringlikegetter slice arg", inv: editor{ Function: "testing_stringlikegetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), + }, + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, }, }, @@ -734,16 +823,18 @@ func Test_NewFunctionCall(t *testing.T) { name: "floatlikegetter slice arg", inv: editor{ Function: "testing_floatlikegetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("1.1"), - }, - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.1), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("1.1"), + }, + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, }, }, }, @@ -757,16 +848,18 @@ func Test_NewFunctionCall(t *testing.T) { name: "intlikegetter slice arg", inv: editor{ Function: "testing_intlikegetter_slice", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("1"), - }, - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.1), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("1"), + }, + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, }, }, }, @@ -780,13 +873,15 @@ func Test_NewFunctionCall(t *testing.T) { name: "setter arg", inv: editor{ Function: "testing_setter", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, @@ -800,13 +895,15 @@ func Test_NewFunctionCall(t *testing.T) { name: "getsetter arg", inv: editor{ Function: "testing_getsetter", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, @@ -820,13 +917,15 @@ func Test_NewFunctionCall(t *testing.T) { name: "getter arg", inv: editor{ Function: "testing_getter", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, @@ -840,9 +939,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "getter arg with nil literal", inv: editor{ Function: "testing_getter", - Arguments: []value{ + Arguments: []argument{ { - IsNil: (*isNil)(ottltest.Boolp(true)), + Value: value{ + IsNil: (*isNil)(ottltest.Boolp(true)), + }, }, }, }, @@ -852,51 +953,55 @@ func Test_NewFunctionCall(t *testing.T) { name: "getter arg with list", inv: editor{ Function: "testing_getter", - Arguments: []value{ + Arguments: []argument{ { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("test"), - }, - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("test"), }, - }, - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.1), + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, - }, - { - Bool: (*boolean)(ottltest.Boolp(true)), - }, - { - Bytes: (*byteSlice)(&[]byte{1, 2, 3, 4, 5, 6, 7, 8}), - }, - { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, + }, + { + Bool: (*boolean)(ottltest.Boolp(true)), + }, + { + Bytes: (*byteSlice)(&[]byte{1, 2, 3, 4, 5, 6, 7, 8}), + }, + { + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, }, - }, - { - Literal: &mathExprLiteral{ - Converter: &converter{ - Function: "testing_getter", - Arguments: []value{ - { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + { + Literal: &mathExprLiteral{ + Converter: &converter{ + Function: "testing_getter", + Arguments: []argument{ + { + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, + }, }, }, }, @@ -917,9 +1022,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "stringgetter arg", inv: editor{ Function: "testing_stringgetter", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, }, }, @@ -929,9 +1036,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "durationgetter arg", inv: editor{ Function: "testing_durationgetter", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, }, }, @@ -940,9 +1049,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "timegetter arg", inv: editor{ Function: "testing_timegetter", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, }, }, @@ -951,9 +1062,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "functiongetter arg (Uppercase)", inv: editor{ Function: "testing_functiongetter", - Arguments: []value{ + Arguments: []argument{ { - FunctionName: (ottltest.Strp("SHA256")), + Value: value{ + FunctionName: (ottltest.Strp("SHA256")), + }, }, }, }, @@ -963,9 +1076,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "functiongetter arg", inv: editor{ Function: "testing_functiongetter", - Arguments: []value{ + Arguments: []argument{ { - FunctionName: (ottltest.Strp("Sha256")), + Value: value{ + FunctionName: (ottltest.Strp("Sha256")), + }, }, }, }, @@ -975,9 +1090,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "stringlikegetter arg", inv: editor{ Function: "testing_stringlikegetter", - Arguments: []value{ + Arguments: []argument{ { - Bool: (*boolean)(ottltest.Boolp(false)), + Value: value{ + Bool: (*boolean)(ottltest.Boolp(false)), + }, }, }, }, @@ -987,9 +1104,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "floatgetter arg", inv: editor{ Function: "testing_floatgetter", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("1.1"), + Value: value{ + String: ottltest.Strp("1.1"), + }, }, }, }, @@ -999,9 +1118,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "floatlikegetter arg", inv: editor{ Function: "testing_floatlikegetter", - Arguments: []value{ + Arguments: []argument{ { - Bool: (*boolean)(ottltest.Boolp(false)), + Value: value{ + Bool: (*boolean)(ottltest.Boolp(false)), + }, }, }, }, @@ -1011,10 +1132,12 @@ func Test_NewFunctionCall(t *testing.T) { name: "intgetter arg", inv: editor{ Function: "testing_intgetter", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + Value: value{ + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, }, }, @@ -1025,10 +1148,12 @@ func Test_NewFunctionCall(t *testing.T) { name: "intlikegetter arg", inv: editor{ Function: "testing_intgetter", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.1), + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, }, }, }, @@ -1039,13 +1164,15 @@ func Test_NewFunctionCall(t *testing.T) { name: "pmapgetter arg", inv: editor{ Function: "testing_pmapgetter", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, @@ -1059,9 +1186,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "string arg", inv: editor{ Function: "testing_string", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, }, }, @@ -1071,10 +1200,12 @@ func Test_NewFunctionCall(t *testing.T) { name: "float arg", inv: editor{ Function: "testing_float", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.1), + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, }, }, }, @@ -1085,10 +1216,12 @@ func Test_NewFunctionCall(t *testing.T) { name: "int arg", inv: editor{ Function: "testing_int", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + Value: value{ + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, }, }, }, @@ -1099,9 +1232,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "bool arg", inv: editor{ Function: "testing_bool", - Arguments: []value{ + Arguments: []argument{ { - Bool: (*boolean)(ottltest.Boolp(true)), + Value: value{ + Bool: (*boolean)(ottltest.Boolp(true)), + }, }, }, }, @@ -1111,9 +1246,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "byteSlice arg", inv: editor{ Function: "testing_byte_slice", - Arguments: []value{ + Arguments: []argument{ { - Bytes: (*byteSlice)(&[]byte{1, 2, 3, 4, 5, 6, 7, 8}), + Value: value{ + Bytes: (*byteSlice)(&[]byte{1, 2, 3, 4, 5, 6, 7, 8}), + }, }, }, }, @@ -1123,29 +1260,117 @@ func Test_NewFunctionCall(t *testing.T) { name: "multiple args", inv: editor{ Function: "testing_multiple_args", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, + }, + }, + }, + }, + }, + { + Value: value{ + String: ottltest.Strp("test"), + }, + }, + { + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, + }, + }, + { + Value: value{ + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1), + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "optional args", + inv: editor{ + Function: "testing_optional_args", + Arguments: []argument{ + { + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, }, }, { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.1), + Value: value{ + String: ottltest.Strp("test_optional"), }, }, { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1), + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "optional named args", + inv: editor{ + Function: "testing_optional_args", + Arguments: []argument{ + { + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, + }, + }, + }, + }, + }, + { + Value: value{ + String: ottltest.Strp("test"), + }, + }, + { + Name: "optional_arg", + Value: value{ + String: ottltest.Strp("test_optional"), + }, + }, + { + Name: "optional_float_arg", + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, }, }, }, @@ -1156,9 +1381,11 @@ func Test_NewFunctionCall(t *testing.T) { name: "Enum arg", inv: editor{ Function: "testing_enum", - Arguments: []value{ + Arguments: []argument{ { - Enum: (*EnumSymbol)(ottltest.Strp("TEST_ENUM")), + Value: value{ + Enum: (*EnumSymbol)(ottltest.Strp("TEST_ENUM")), + }, }, }, }, @@ -1178,6 +1405,94 @@ func Test_NewFunctionCall(t *testing.T) { } } +func Test_ArgumentsNotMutated(t *testing.T) { + args := optionalArgsArguments{} + fact := createFactory[any]( + "testing_optional_args", + &args, + functionWithOptionalArgs, + ) + p, _ := NewParser( + CreateFactoryMap[any](fact), + testParsePath, + componenttest.NewNopTelemetrySettings(), + WithEnumParser[any](testParseEnum), + ) + + invWithOptArg := editor{ + Function: "testing_optional_args", + Arguments: []argument{ + { + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, + }, + }, + }, + }, + }, + { + Value: value{ + String: ottltest.Strp("test"), + }, + }, + { + Value: value{ + String: ottltest.Strp("test_optional"), + }, + }, + { + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.1), + }, + }, + }, + }, + } + + invWithoutOptArg := editor{ + Function: "testing_optional_args", + Arguments: []argument{ + { + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, + }, + }, + }, + }, + }, + { + Value: value{ + String: ottltest.Strp("test"), + }, + }, + }, + } + + fn, err := p.newFunctionCall(invWithOptArg) + require.NoError(t, err) + res, _ := fn.Eval(context.Background(), nil) + require.Equal(t, 4, res) + + fn, err = p.newFunctionCall(invWithoutOptArg) + require.NoError(t, err) + res, _ = fn.Eval(context.Background(), nil) + require.Equal(t, 2, res) + + assert.Zero(t, args.OptionalArg) + assert.Zero(t, args.OptionalFloatArg) +} + func functionWithNoArguments() (ExprFunc[any], error) { return func(context.Context, any) (any, error) { return nil, nil @@ -1513,6 +1828,28 @@ func functionWithMultipleArgs(GetSetter[interface{}], string, float64, int64) (E }, nil } +type optionalArgsArguments struct { + GetSetterArg GetSetter[any] `ottlarg:"0"` + StringArg string `ottlarg:"1"` + OptionalArg Optional[StringGetter[any]] `ottlarg:"2"` + OptionalFloatArg Optional[float64] `ottlarg:"3"` +} + +func functionWithOptionalArgs(_ GetSetter[interface{}], _ string, stringOpt Optional[StringGetter[any]], floatOpt Optional[float64]) (ExprFunc[interface{}], error) { + return func(context.Context, interface{}) (interface{}, error) { + argCount := 2 + + if !stringOpt.IsEmpty() { + argCount++ + } + if !floatOpt.IsEmpty() { + argCount++ + } + + return argCount, nil + }, nil +} + type errorFunctionArguments struct{} func functionThatHasAnError() (ExprFunc[interface{}], error) { @@ -1744,6 +2081,11 @@ func defaultFunctionsForTests() map[string]Factory[any] { &multipleArgsArguments{}, functionWithMultipleArgs, ), + createFactory[any]( + "testing_optional_args", + &optionalArgsArguments{}, + functionWithOptionalArgs, + ), createFactory[any]( "testing_enum", &enumArguments{}, diff --git a/pkg/ottl/grammar.go b/pkg/ottl/grammar.go index 73934c9b7e5fa..a5b4577ac05ce 100644 --- a/pkg/ottl/grammar.go +++ b/pkg/ottl/grammar.go @@ -189,14 +189,15 @@ func (c *comparison) checkForCustomError() error { // editor represents the function call of a statement. type editor struct { - Function string `parser:"@(Lowercase(Uppercase | Lowercase)*)"` - Arguments []value `parser:"'(' ( @@ ( ',' @@ )* )? ')'"` + Function string `parser:"@(Lowercase(Uppercase | Lowercase)*)"` + Arguments []argument `parser:"'(' ( @@ ( ',' @@ )* )? ')'"` // If keys are matched return an error Keys []Key `parser:"( @@ )*"` } func (i *editor) checkForCustomError() error { var err error + for _, arg := range i.Arguments { err = arg.checkForCustomError() if err != nil { @@ -211,9 +212,18 @@ func (i *editor) checkForCustomError() error { // converter represents a converter function call. type converter struct { - Function string `parser:"@(Uppercase(Uppercase | Lowercase)*)"` - Arguments []value `parser:"'(' ( @@ ( ',' @@ )* )? ')'"` - Keys []Key `parser:"( @@ )*"` + Function string `parser:"@(Uppercase(Uppercase | Lowercase)*)"` + Arguments []argument `parser:"'(' ( @@ ( ',' @@ )* )? ')'"` + Keys []Key `parser:"( @@ )*"` +} + +type argument struct { + Name string `parser:"(@(Lowercase(Uppercase | Lowercase)*) Equal)?"` + Value value `parser:"@@"` +} + +func (a *argument) checkForCustomError() error { + return a.Value.checkForCustomError() } // value represents a part of a parsed statement which is resolved to a value of some sort. This can be a telemetry path @@ -424,6 +434,7 @@ func buildLexer() *lexer.StatefulDefinition { {Name: `Float`, Pattern: `[-+]?\d*\.\d+([eE][-+]?\d+)?`}, {Name: `Int`, Pattern: `[-+]?\d+`}, {Name: `String`, Pattern: `"(\\"|[^"])*"`}, + {Name: `Equal`, Pattern: `=[^=]`}, {Name: `OpNot`, Pattern: `\b(not)\b`}, {Name: `OpOr`, Pattern: `\b(or)\b`}, {Name: `OpAnd`, Pattern: `\b(and)\b`}, diff --git a/pkg/ottl/math_test.go b/pkg/ottl/math_test.go index 30e58c273e8d1..183a62a8141fa 100644 --- a/pkg/ottl/math_test.go +++ b/pkg/ottl/math_test.go @@ -276,12 +276,16 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2023-04-12"), + Value: value{ + String: ottltest.Strp("2023-04-12"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -296,12 +300,16 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2023-04-12"), + Value: value{ + String: ottltest.Strp("2023-04-12"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -321,9 +329,11 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("100h100m100s100ns"), + Value: value{ + String: ottltest.Strp("100h100m100s100ns"), + }, }, }, }, @@ -338,9 +348,11 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("1h1m1s1ns"), + Value: value{ + String: ottltest.Strp("1h1m1s1ns"), + }, }, }, }, @@ -360,12 +372,16 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2023-04-12"), + Value: value{ + String: ottltest.Strp("2023-04-12"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -395,9 +411,11 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("1h1m1s1ns"), + Value: value{ + String: ottltest.Strp("1h1m1s1ns"), + }, }, }, }, @@ -427,12 +445,16 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2023-04-12"), + Value: value{ + String: ottltest.Strp("2023-04-12"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -447,12 +469,16 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2022-05-11"), + Value: value{ + String: ottltest.Strp("2022-05-11"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -472,9 +498,11 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2h"), + Value: value{ + String: ottltest.Strp("2h"), + }, }, }, }, @@ -489,12 +517,16 @@ func Test_evaluateMathExpression_error(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2000-10-30"), + Value: value{ + String: ottltest.Strp("2000-10-30"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -604,12 +636,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2023-04-12"), + Value: value{ + String: ottltest.Strp("2023-04-12"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -624,12 +660,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("2023-04-12"), + Value: value{ + String: ottltest.Strp("2023-04-12"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -649,12 +689,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("1986-10-30T00:17:33"), + Value: value{ + String: ottltest.Strp("1986-10-30T00:17:33"), + }, }, { - String: ottltest.Strp("%Y-%m-%dT%H:%M:%S"), + Value: value{ + String: ottltest.Strp("%Y-%m-%dT%H:%M:%S"), + }, }, }, }, @@ -669,12 +713,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("1986-11-01"), + Value: value{ + String: ottltest.Strp("1986-11-01"), + }, }, { - String: ottltest.Strp("%Y-%m-%d"), + Value: value{ + String: ottltest.Strp("%Y-%m-%d"), + }, }, }, }, @@ -694,9 +742,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("10h"), + Value: value{ + String: ottltest.Strp("10h"), + }, }, }, }, @@ -711,12 +761,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("01-01-2000"), + Value: value{ + String: ottltest.Strp("01-01-2000"), + }, }, { - String: ottltest.Strp("%m-%d-%Y"), + Value: value{ + String: ottltest.Strp("%m-%d-%Y"), + }, }, }, }, @@ -736,12 +790,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("Feb 15, 2023"), + Value: value{ + String: ottltest.Strp("Feb 15, 2023"), + }, }, { - String: ottltest.Strp("%b %d, %Y"), + Value: value{ + String: ottltest.Strp("%b %d, %Y"), + }, }, }, }, @@ -756,9 +814,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("10h"), + Value: value{ + String: ottltest.Strp("10h"), + }, }, }, }, @@ -778,12 +838,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("02/04/2023"), + Value: value{ + String: ottltest.Strp("02/04/2023"), + }, }, { - String: ottltest.Strp("%m/%d/%Y"), + Value: value{ + String: ottltest.Strp("%m/%d/%Y"), + }, }, }, }, @@ -798,9 +862,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("1h2m3s"), + Value: value{ + String: ottltest.Strp("1h2m3s"), + }, }, }, }, @@ -820,12 +886,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("Mar 14 2023 17:02:59"), + Value: value{ + String: ottltest.Strp("Mar 14 2023 17:02:59"), + }, }, { - String: ottltest.Strp("%b %d %Y %H:%M:%S"), + Value: value{ + String: ottltest.Strp("%b %d %Y %H:%M:%S"), + }, }, }, }, @@ -840,9 +910,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("11h2m58s"), + Value: value{ + String: ottltest.Strp("11h2m58s"), + }, }, }, }, @@ -862,12 +934,16 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Time", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("Monday, May 01, 2023"), + Value: value{ + String: ottltest.Strp("Monday, May 01, 2023"), + }, }, { - String: ottltest.Strp("%A, %B %d, %Y"), + Value: value{ + String: ottltest.Strp("%A, %B %d, %Y"), + }, }, }, }, @@ -882,9 +958,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("100ns"), + Value: value{ + String: ottltest.Strp("100ns"), + }, }, }, }, @@ -904,9 +982,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("100h100m100s100ns"), + Value: value{ + String: ottltest.Strp("100h100m100s100ns"), + }, }, }, }, @@ -921,9 +1001,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("1h1m1s1ns"), + Value: value{ + String: ottltest.Strp("1h1m1s1ns"), + }, }, }, }, @@ -943,9 +1025,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("0h"), + Value: value{ + String: ottltest.Strp("0h"), + }, }, }, }, @@ -960,9 +1044,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("1000h"), + Value: value{ + String: ottltest.Strp("1000h"), + }, }, }, }, @@ -982,9 +1068,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("0h"), + Value: value{ + String: ottltest.Strp("0h"), + }, }, }, }, @@ -999,9 +1087,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("328m"), + Value: value{ + String: ottltest.Strp("328m"), + }, }, }, }, @@ -1021,9 +1111,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("11h11ns"), + Value: value{ + String: ottltest.Strp("11h11ns"), + }, }, }, }, @@ -1038,9 +1130,11 @@ func Test_evaluateMathExpressionTimeDuration(t *testing.T) { Literal: &mathExprLiteral{ Converter: &converter{ Function: "Duration", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("12m12s"), + Value: value{ + String: ottltest.Strp("12m12s"), + }, }, }, }, diff --git a/pkg/ottl/parser.go b/pkg/ottl/parser.go index 1b6335cbd43f4..fe67e31cefb70 100644 --- a/pkg/ottl/parser.go +++ b/pkg/ottl/parser.go @@ -168,7 +168,7 @@ func newParser[G any]() *participle.Parser[G] { participle.UseLookahead(participle.MaxLookahead), // Allows negative lookahead to work properly in 'value' for 'mathExprLiteral'. ) if err != nil { - panic("Unable to initialize parser; this is a programming error in the transformprocessor:" + err.Error()) + panic("Unable to initialize parser; this is a programming error in OTTL:" + err.Error()) } return parser } diff --git a/pkg/ottl/parser_test.go b/pkg/ottl/parser_test.go index 674623d4e7e93..fd8038b6131ef 100644 --- a/pkg/ottl/parser_test.go +++ b/pkg/ottl/parser_test.go @@ -35,9 +35,11 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("foo"), + Value: value{ + String: ottltest.Strp("foo"), + }, }, }, }, @@ -50,10 +52,12 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "met", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(1.2), + Value: value{ + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(1.2), + }, }, }, }, @@ -67,10 +71,12 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "fff", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(12), + Value: value{ + Literal: &mathExprLiteral{ + Int: ottltest.Intp(12), + }, }, }, }, @@ -84,24 +90,30 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("foo"), + Value: value{ + String: ottltest.Strp("foo"), + }, }, { - Literal: &mathExprLiteral{ - Converter: &converter{ - Function: "GetSomething", - Arguments: []value{ - { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "bear", - }, - { - Name: "honey", + Value: value{ + Literal: &mathExprLiteral{ + Converter: &converter{ + Function: "GetSomething", + Arguments: []argument{ + { + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "bear", + }, + { + Name: "honey", + }, + }, }, }, }, @@ -122,31 +134,35 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "foo", - }, - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("bar"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "foo", + }, + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("bar"), + }, }, }, - }, - { - Name: "cat", + { + Name: "cat", + }, }, }, }, }, }, { - String: ottltest.Strp("dog"), + Value: value{ + String: ottltest.Strp("dog"), + }, }, }, }, @@ -159,16 +175,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "replace_pattern", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("message"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("message"), + }, }, }, }, @@ -177,17 +195,21 @@ func Test_parse(t *testing.T) { }, }, { - String: ottltest.Strp("device=*"), + Value: value{ + String: ottltest.Strp("device=*"), + }, }, { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("device_name"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("device_name"), + }, }, }, }, @@ -196,7 +218,9 @@ func Test_parse(t *testing.T) { }, }, { - Enum: (*EnumSymbol)(ottltest.Strp("SHA256")), + Value: value{ + Enum: (*EnumSymbol)(ottltest.Strp("SHA256")), + }, }, }, }, @@ -209,16 +233,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "replace_pattern", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("message"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("message"), + }, }, }, }, @@ -227,7 +253,9 @@ func Test_parse(t *testing.T) { }, }, { - FunctionName: (ottltest.Strp("Sha256")), + Value: value{ + FunctionName: (ottltest.Strp("Sha256")), + }, }, }, }, @@ -240,16 +268,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "replace_pattern", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("message"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("message"), + }, }, }, }, @@ -258,7 +288,9 @@ func Test_parse(t *testing.T) { }, }, { - Enum: (*EnumSymbol)(ottltest.Strp("S")), + Value: value{ + Enum: (*EnumSymbol)(ottltest.Strp("S")), + }, }, }, }, @@ -271,42 +303,46 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "foo", - }, - { - Name: "bar", - Keys: []Key{ - { - String: ottltest.Strp("x"), - }, - { - String: ottltest.Strp("y"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "foo", + }, + { + Name: "bar", + Keys: []Key{ + { + String: ottltest.Strp("x"), + }, + { + String: ottltest.Strp("y"), + }, }, }, - }, - { - Name: "z", + { + Name: "z", + }, }, }, }, }, }, { - Literal: &mathExprLiteral{ - Converter: &converter{ - Function: "Test", - Keys: []Key{ - { - Int: ottltest.Intp(0), - }, - { - String: ottltest.Strp("pass"), + Value: value{ + Literal: &mathExprLiteral{ + Converter: &converter{ + Function: "Test", + Keys: []Key{ + { + Int: ottltest.Intp(0), + }, + { + String: ottltest.Strp("pass"), + }, }, }, }, @@ -323,31 +359,35 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "foo", - }, - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("bar"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "foo", + }, + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("bar"), + }, }, }, - }, - { - Name: "cat", + { + Name: "cat", + }, }, }, }, }, }, { - String: ottltest.Strp("dog"), + Value: value{ + String: ottltest.Strp("dog"), + }, }, }, }, @@ -382,31 +422,35 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "foo", - }, - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("bar"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "foo", + }, + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("bar"), + }, }, }, - }, - { - Name: "cat", + { + Name: "cat", + }, }, }, }, }, }, { - String: ottltest.Strp("dog"), + Value: value{ + String: ottltest.Strp("dog"), + }, }, }, }, @@ -441,31 +485,35 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "foo", - }, - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("bar"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "foo", + }, + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("bar"), + }, }, }, - }, - { - Name: "cat", + { + Name: "cat", + }, }, }, }, }, }, { - String: ottltest.Strp("dog"), + Value: value{ + String: ottltest.Strp("dog"), + }, }, }, }, @@ -500,9 +548,11 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("fo\"o"), + Value: value{ + String: ottltest.Strp("fo\"o"), + }, }, }, }, @@ -515,12 +565,16 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "convert_gauge_to_sum", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("cumulative"), + Value: value{ + String: ottltest.Strp("cumulative"), + }, }, { - Bool: (*boolean)(ottltest.Boolp(false)), + Value: value{ + Bool: (*boolean)(ottltest.Boolp(false)), + }, }, }, }, @@ -533,12 +587,16 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "convert_gauge_to_sum", - Arguments: []value{ + Arguments: []argument{ { - String: ottltest.Strp("cumulative"), + Value: value{ + String: ottltest.Strp("cumulative"), + }, }, { - Bool: (*boolean)(ottltest.Boolp(true)), + Value: value{ + Bool: (*boolean)(ottltest.Boolp(true)), + }, }, }, }, @@ -551,16 +609,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("bytes"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("bytes"), + }, }, }, }, @@ -569,7 +629,9 @@ func Test_parse(t *testing.T) { }, }, { - Bytes: (*byteSlice)(&[]byte{1, 2, 3, 4, 5, 6, 7, 8}), + Value: value{ + Bytes: (*byteSlice)(&[]byte{1, 2, 3, 4, 5, 6, 7, 8}), + }, }, }, }, @@ -582,16 +644,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("test"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -600,7 +664,9 @@ func Test_parse(t *testing.T) { }, }, { - IsNil: (*isNil)(ottltest.Boolp(true)), + Value: value{ + IsNil: (*isNil)(ottltest.Boolp(true)), + }, }, }, }, @@ -613,16 +679,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("test"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -631,7 +699,9 @@ func Test_parse(t *testing.T) { }, }, { - Enum: (*EnumSymbol)(ottltest.Strp("TEST_ENUM")), + Value: value{ + Enum: (*EnumSymbol)(ottltest.Strp("TEST_ENUM")), + }, }, }, }, @@ -644,16 +714,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("test"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -662,8 +734,10 @@ func Test_parse(t *testing.T) { }, }, { - List: &list{ - Values: nil, + Value: value{ + List: &list{ + Values: nil, + }, }, }, }, @@ -677,16 +751,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("test"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -695,10 +771,12 @@ func Test_parse(t *testing.T) { }, }, { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("value0"), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("value0"), + }, }, }, }, @@ -714,16 +792,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("test"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -732,13 +812,15 @@ func Test_parse(t *testing.T) { }, }, { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("value1"), - }, - { - String: ottltest.Strp("value2"), + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("value1"), + }, + { + String: ottltest.Strp("value2"), + }, }, }, }, @@ -754,16 +836,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("test"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -772,63 +856,69 @@ func Test_parse(t *testing.T) { }, }, { - List: &list{ - Values: []value{ - { - Literal: &mathExprLiteral{ - Converter: &converter{ - Function: "Concat", - Arguments: []value{ - { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("a"), - }, - { - String: ottltest.Strp("b"), + Value: value{ + List: &list{ + Values: []value{ + { + Literal: &mathExprLiteral{ + Converter: &converter{ + Function: "Concat", + Arguments: []argument{ + { + Value: value{ + List: &list{ + Values: []value{ + { + String: ottltest.Strp("a"), + }, + { + String: ottltest.Strp("b"), + }, + }, }, }, }, - }, - { - String: ottltest.Strp("+"), + { + Value: value{ + String: ottltest.Strp("+"), + }, + }, }, }, }, }, - }, - { - List: &list{ - Values: []value{ - { - String: ottltest.Strp("1"), - }, - { - Literal: &mathExprLiteral{ - Int: ottltest.Intp(2), + { + List: &list{ + Values: []value{ + { + String: ottltest.Strp("1"), }, - }, - { - Literal: &mathExprLiteral{ - Float: ottltest.Floatp(3.0), + { + Literal: &mathExprLiteral{ + Int: ottltest.Intp(2), + }, + }, + { + Literal: &mathExprLiteral{ + Float: ottltest.Floatp(3.0), + }, }, }, }, }, - }, - { - IsNil: (*isNil)(ottltest.Boolp(true)), - }, - { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("test"), + { + IsNil: (*isNil)(ottltest.Boolp(true)), + }, + { + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -850,16 +940,18 @@ func Test_parse(t *testing.T) { expected: &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "attributes", - Keys: []Key{ - { - String: ottltest.Strp("test"), + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + Keys: []Key{ + { + String: ottltest.Strp("test"), + }, }, }, }, @@ -868,21 +960,23 @@ func Test_parse(t *testing.T) { }, }, { - MathExpression: &mathExpression{ - Left: &addSubTerm{ - Left: &mathValue{ - Literal: &mathExprLiteral{ - Int: ottltest.Intp(1000), + Value: value{ + MathExpression: &mathExpression{ + Left: &addSubTerm{ + Left: &mathValue{ + Literal: &mathExprLiteral{ + Int: ottltest.Intp(1000), + }, }, }, - }, - Right: []*opAddSubTerm{ - { - Operator: SUB, - Term: &addSubTerm{ - Left: &mathValue{ - Literal: &mathExprLiteral{ - Int: ottltest.Intp(600), + Right: []*opAddSubTerm{ + { + Operator: SUB, + Term: &addSubTerm{ + Left: &mathValue{ + Literal: &mathExprLiteral{ + Int: ottltest.Intp(600), + }, }, }, }, @@ -1027,20 +1121,24 @@ func setNameTest(b *booleanExpression) *parsedStatement { return &parsedStatement{ Editor: editor{ Function: "set", - Arguments: []value{ + Arguments: []argument{ { - Literal: &mathExprLiteral{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Value: value{ + Literal: &mathExprLiteral{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, }, }, { - String: ottltest.Strp("test"), + Value: value{ + String: ottltest.Strp("test"), + }, }, }, }, @@ -1574,7 +1672,7 @@ func Test_parseStatement(t *testing.T) { {`set() where true and 1 == int() `, true}, {`set() where false or 1 == int() `, true}, {`set(foo.attributes["bar"].cat, "dog")`, false}, - {`set(foo.attributes["animal"], "dog") where animal == "cat"`, false}, + {`set(set = foo.attributes["animal"], val = "dog") where animal == "cat"`, false}, {`test() where service == "pinger" or foo.attributes["endpoint"] == "/x/alive"`, false}, {`test() where service == "pinger" or foo.attributes["verb"] == "GET" and foo.attributes["endpoint"] == "/x/alive"`, false}, {`test() where animal > "cat"`, false}, @@ -1598,9 +1696,10 @@ func Test_parseStatement(t *testing.T) { for _, tt := range tests { name := pat.ReplaceAllString(tt.statement, "_") t.Run(name, func(t *testing.T) { - _, err := parseStatement(tt.statement) + ast, err := parseStatement(tt.statement) if (err != nil) != tt.wantErr { t.Errorf("parseStatement(%s) error = %v, wantErr %v", tt.statement, err, tt.wantErr) + t.Errorf("AST: %+v", ast) return } }) diff --git a/pkg/stanza/fileconsumer/matcher/internal/finder/finder.go b/pkg/stanza/fileconsumer/matcher/internal/finder/finder.go index 972846c7bef6a..f534411f0ee09 100644 --- a/pkg/stanza/fileconsumer/matcher/internal/finder/finder.go +++ b/pkg/stanza/fileconsumer/matcher/internal/finder/finder.go @@ -4,6 +4,7 @@ package finder // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/fileconsumer/matcher/internal/finder" import ( + "errors" "fmt" "github.com/bmatcuk/doublestar/v4" @@ -20,10 +21,17 @@ func Validate(globs []string) error { } // FindFiles gets a list of paths given an array of glob patterns to include and exclude -func FindFiles(includes []string, excludes []string) []string { +func FindFiles(includes []string, excludes []string) ([]string, error) { + var errs error all := make([]string, 0, len(includes)) for _, include := range includes { - matches, _ := doublestar.FilepathGlob(include, doublestar.WithFilesOnly()) // compile error checked in build + matches, err := doublestar.FilepathGlob(include, doublestar.WithFilesOnly(), doublestar.WithFailOnIOErrors()) + if err != nil { + errs = errors.Join(errs, fmt.Errorf("find files with '%s' pattern: %w", include, err)) + // the same pattern could cause an IO error due to one file or directory, + // but also could still find files without `doublestar.WithFailOnIOErrors()`. + matches, _ = doublestar.FilepathGlob(include, doublestar.WithFilesOnly()) + } INCLUDE: for _, match := range matches { for _, exclude := range excludes { @@ -42,5 +50,5 @@ func FindFiles(includes []string, excludes []string) []string { } } - return all + return all, errs } diff --git a/pkg/stanza/fileconsumer/matcher/internal/finder/finder_test.go b/pkg/stanza/fileconsumer/matcher/internal/finder/finder_test.go index f69dd3e16dcc1..96ca6112a0419 100644 --- a/pkg/stanza/fileconsumer/matcher/internal/finder/finder_test.go +++ b/pkg/stanza/fileconsumer/matcher/internal/finder/finder_test.go @@ -177,14 +177,77 @@ func TestFindFiles(t *testing.T) { for _, f := range tc.files { require.NoError(t, os.MkdirAll(filepath.Dir(f), 0700)) - file, err := os.OpenFile(f, os.O_CREATE|os.O_RDWR, 0600) + var file *os.File + file, err = os.OpenFile(f, os.O_CREATE|os.O_RDWR, 0600) require.NoError(t, err) _, err = file.WriteString(filepath.Base(f)) require.NoError(t, err) require.NoError(t, file.Close()) } - assert.Equal(t, tc.expected, FindFiles(tc.include, tc.exclude)) + files, err := FindFiles(tc.include, tc.exclude) + assert.NoError(t, err) + assert.Equal(t, tc.expected, files) + }) + } +} + +func TestFindFilesWithIOErrors(t *testing.T) { + cwd, err := os.Getwd() + require.NoError(t, err) + require.NoError(t, os.Chdir(t.TempDir())) + defer func() { + require.NoError(t, os.Chdir(cwd)) + }() + + for _, f := range []string{ + "1.log", + "2.log", + filepath.Join("no_permission", "1.log"), + filepath.Join("no_permission", "2.log"), + filepath.Join("dir1", "1.log"), + filepath.Join("dir1", "2.log"), + } { + require.NoError(t, os.MkdirAll(filepath.Dir(f), 0700)) + + _, err = os.OpenFile(f, os.O_CREATE|os.O_RDWR, 0600) + require.NoError(t, err) + } + + require.NoError(t, os.Chmod("no_permission", 0000)) + defer func() { + require.NoError(t, os.Chmod("no_permission", 0700)) + }() + + cases := []struct { + name string + include []string + expected []string + failedMsg string + }{ + { + name: "failed pattern should not affect others", + include: []string{ + "*.log", + filepath.Join("no_permission", "*.log"), + filepath.Join("dir1", "*.log"), + }, + expected: []string{"1.log", "2.log", "dir1/1.log", "dir1/2.log"}, + failedMsg: "no_permission/*.log", + }, + { + name: "partial failure of FindFiles", + include: []string{"**/*.log"}, + expected: []string{"1.log", "2.log", "dir1/1.log", "dir1/2.log"}, + failedMsg: "**/*.log", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + files, err := FindFiles(tc.include, []string{}) + assert.ErrorContains(t, err, tc.failedMsg) + assert.Equal(t, tc.expected, files) }) } } diff --git a/pkg/stanza/fileconsumer/matcher/matcher.go b/pkg/stanza/fileconsumer/matcher/matcher.go index dd71c7039e35f..0a7a0628edacd 100644 --- a/pkg/stanza/fileconsumer/matcher/matcher.go +++ b/pkg/stanza/fileconsumer/matcher/matcher.go @@ -4,6 +4,7 @@ package matcher // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/fileconsumer/matcher" import ( + "errors" "fmt" "regexp" @@ -109,20 +110,24 @@ type Matcher struct { // MatchFiles gets a list of paths given an array of glob patterns to include and exclude func (m Matcher) MatchFiles() ([]string, error) { - files := finder.FindFiles(m.include, m.exclude) + var errs error + files, err := finder.FindFiles(m.include, m.exclude) + if err != nil { + errs = errors.Join(errs, err) + } if len(files) == 0 { - return files, fmt.Errorf("no files match the configured criteria") + return files, errors.Join(fmt.Errorf("no files match the configured criteria"), errs) } if len(m.filterOpts) == 0 { - return files, nil + return files, errs } result, err := filter.Filter(files, m.regex, m.filterOpts...) if len(result) == 0 { - return result, err + return result, errors.Join(err, errs) } // Return only the first item. // See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/23788 - return result[:1], err + return result[:1], errors.Join(err, errs) } diff --git a/processor/tailsamplingprocessor/factory.go b/processor/tailsamplingprocessor/factory.go index 4ef95244acc30..ba608001bc88e 100644 --- a/processor/tailsamplingprocessor/factory.go +++ b/processor/tailsamplingprocessor/factory.go @@ -25,7 +25,7 @@ var onceMetrics sync.Once func NewFactory() processor.Factory { onceMetrics.Do(func() { // TODO: this is hardcoding the metrics level and skips error handling - _ = view.Register(SamplingProcessorMetricViews(configtelemetry.LevelNormal)...) + _ = view.Register(samplingProcessorMetricViews(configtelemetry.LevelNormal)...) }) return processor.NewFactory( diff --git a/processor/tailsamplingprocessor/metrics.go b/processor/tailsamplingprocessor/metrics.go index 11b4b39e217a8..b54fabb750c2c 100644 --- a/processor/tailsamplingprocessor/metrics.go +++ b/processor/tailsamplingprocessor/metrics.go @@ -34,8 +34,8 @@ var ( statTracesOnMemoryGauge = stats.Int64("sampling_traces_on_memory", "Tracks the number of traces current on memory", stats.UnitDimensionless) ) -// SamplingProcessorMetricViews return the metrics views according to given telemetry level. -func SamplingProcessorMetricViews(level configtelemetry.Level) []*view.View { +// samplingProcessorMetricViews return the metrics views according to given telemetry level. +func samplingProcessorMetricViews(level configtelemetry.Level) []*view.View { if level == configtelemetry.LevelNone { return nil } diff --git a/receiver/mongodbreceiver/client.go b/receiver/mongodbreceiver/client.go index 9b5b3dd2de9dd..c9da17fb49d0f 100644 --- a/receiver/mongodbreceiver/client.go +++ b/receiver/mongodbreceiver/client.go @@ -35,9 +35,9 @@ type mongodbClient struct { *mongo.Client } -// NewClient creates a new client to connect and query mongo for the +// newClient creates a new client to connect and query mongo for the // mongodbreceiver -func NewClient(ctx context.Context, config *Config, logger *zap.Logger) (client, error) { +func newClient(ctx context.Context, config *Config, logger *zap.Logger) (client, error) { driver, err := mongo.Connect(ctx, config.ClientOptions()) if err != nil { return nil, err diff --git a/receiver/mongodbreceiver/scraper.go b/receiver/mongodbreceiver/scraper.go index 7f37f4868949b..7557e66136a54 100644 --- a/receiver/mongodbreceiver/scraper.go +++ b/receiver/mongodbreceiver/scraper.go @@ -40,7 +40,7 @@ func newMongodbScraper(settings receiver.CreateSettings, config *Config) *mongod } func (s *mongodbScraper) start(ctx context.Context, _ component.Host) error { - c, err := NewClient(ctx, s.config, s.logger) + c, err := newClient(ctx, s.config, s.logger) if err != nil { return fmt.Errorf("create mongo client: %w", err) }