From d3ee8f54ece49accaa24c573a68c9fc58843a5c9 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 16 Apr 2024 11:33:15 +0200 Subject: [PATCH] Fix validation of empty numeric keywords arrays with normalization --- internal/fields/testdata/fields/fields.yml | 8 ++++++++ internal/fields/testdata/numeric.json | 7 +++++-- internal/fields/validate.go | 22 +++++++++++++++++++--- internal/fields/validate_test.go | 8 +++++++- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/internal/fields/testdata/fields/fields.yml b/internal/fields/testdata/fields/fields.yml index 542498d137..77d39fd730 100644 --- a/internal/fields/testdata/fields/fields.yml +++ b/internal/fields/testdata/fields/fields.yml @@ -3,6 +3,10 @@ fields: - name: code type: keyword + - name: pid + type: keyword + - name: ppid + type: keyword - name: flattened type: group fields: @@ -78,3 +82,7 @@ multi_fields: - name: text type: text +- name: tags + type: keyword + normalize: + - array diff --git a/internal/fields/testdata/numeric.json b/internal/fields/testdata/numeric.json index 6ee0acc218..10d6995af1 100644 --- a/internal/fields/testdata/numeric.json +++ b/internal/fields/testdata/numeric.json @@ -1,11 +1,14 @@ { "foo": { "code": 42, + "pid": [345, 678, 901], + "ppid": [], "flattened": { "request_parameters": { "userName": "Bob", "groupName": "admin" } } - } -} \ No newline at end of file + }, + "tags": [] +} diff --git a/internal/fields/validate.go b/internal/fields/validate.go index 0fb168c041..9f5b5930ee 100644 --- a/internal/fields/validate.go +++ b/internal/fields/validate.go @@ -593,7 +593,7 @@ func (v *Validator) validateScalarElement(key string, val interface{}, doc commo // Convert numeric keyword fields to string for validation. _, found := v.numericKeywordFields[key] if (found || v.defaultNumericConversion) && isNumericKeyword(*definition, val) { - val = fmt.Sprintf("%q", val) + val = convertNumericKeyword(val) } if !v.disabledNormalization { @@ -692,12 +692,15 @@ func createDocExpandingObjects(doc common.MapStr) (common.MapStr, error) { } return newDoc, nil } -func isNumericKeyword(definition FieldDefinition, val interface{}) bool { + +// isNumericKeyword is used to identify values that can be numbers in the documents, but are ingested +// as keywords. +func isNumericKeyword(definition FieldDefinition, val any) bool { var isNumber bool switch val := val.(type) { case bool, []bool, float64, []float64: isNumber = true - case []interface{}: + case []any: isNumber = true loop: for _, v := range val { @@ -712,6 +715,19 @@ func isNumericKeyword(definition FieldDefinition, val interface{}) bool { return isNumber && (definition.Type == "keyword" || definition.Type == "constant_keyword") } +func convertNumericKeyword(val any) any { + switch val := val.(type) { + case []any: + converted := make([]any, len(val)) + for i, e := range val { + converted[i] = convertNumericKeyword(e) + } + return converted + default: + return fmt.Sprintf("%q", val) + } +} + // skipValidationForField skips field validation (field presence) of special fields. The special fields are present // in every (most?) documents collected by Elastic Agent, but aren't defined in any integration in `fields.yml` files. // FIXME https://github.com/elastic/elastic-package/issues/147 diff --git a/internal/fields/validate_test.go b/internal/fields/validate_test.go index ecc38f02c2..5ed37050fe 100644 --- a/internal/fields/validate_test.go +++ b/internal/fields/validate_test.go @@ -66,7 +66,13 @@ func TestValidate_WithFlattenedFields(t *testing.T) { func TestValidate_WithNumericKeywordFields(t *testing.T) { validator, err := CreateValidatorForDirectory("testdata", - WithNumericKeywordFields([]string{"foo.code"}), + WithNumericKeywordFields([]string{ + "foo.code", // Contains a number. + "foo.pid", // Contains an array of numbers. + "foo.ppid", // Contains an empty array. + "tags", // Contains an empty array, and expects normalization as array. + }), + WithSpecVersion("2.3.0"), // Needed to validate normalization. WithDisabledDependencyManagement()) require.NoError(t, err) require.NotNil(t, validator)