From c348a3135f17467cd1ca6923d3740a06de8399a5 Mon Sep 17 00:00:00 2001 From: Dan Kortschak <90160302+efd6@users.noreply.github.com> Date: Thu, 10 Nov 2022 19:41:50 +1030 Subject: [PATCH] x-pack/filebeat/input/httpjson: fix handling of split on array of string/array (#33609) This enables splitting of arrays of arrays and arrays of string in the same way that splitting on maps currently works. Co-authored-by: Alex Resnick --- CHANGELOG.next.asciidoc | 1 + x-pack/filebeat/input/httpjson/split.go | 17 +++-- x-pack/filebeat/input/httpjson/split_test.go | 77 ++++++++++++++++++++ 3 files changed, 87 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index a26b011797dc..583fa044117b 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -57,6 +57,7 @@ https://github.com/elastic/beats/compare/v8.2.0\...main[Check the HEAD diff] - Fix gc.log always shipped even if gc fileset is disabled {issue}30995[30995] - Fix handling of empty array in httpjson input. {pull}32001[32001] - Fix reporting of `filebeat.events.active` in log events such that the current value is always reported instead of the difference from the last value. {pull}33597[33597] +- Fix splitting array of strings/arrays in httpjson input {issue}30345[30345] {pull}33609[33609] *Heartbeat* - Fix bug affecting let's encrypt and other users of cross-signed certs, where cert expiration was incorrectly calculated. {issue}33215[33215] diff --git a/x-pack/filebeat/input/httpjson/split.go b/x-pack/filebeat/input/httpjson/split.go index 3585fd9fef46..714ba026be64 100644 --- a/x-pack/filebeat/input/httpjson/split.go +++ b/x-pack/filebeat/input/httpjson/split.go @@ -144,14 +144,15 @@ func (s *split) split(ctx *transformContext, root mapstr.M, ch chan<- maybeMsg) } for _, e := range varr { - if err := s.sendMessage(ctx, root, "", e, ch); err != nil { + err := s.sendMessage(ctx, root, s.targetInfo.Name, e, ch) + if err != nil { s.log.Debug(err) } } return nil case splitTypeMap: - vmap, ok := toMapStr(v) + vmap, ok := toMapStr(v, s.targetInfo.Name) if !ok { return errExpectedSplitObj } @@ -211,19 +212,17 @@ func (s *split) split(ctx *transformContext, root mapstr.M, ch chan<- maybeMsg) // sendMessage sends an array or map split result value, v, on ch after performing // any necessary transformations. If key is "", the value is an element of an array. func (s *split) sendMessage(ctx *transformContext, root mapstr.M, key string, v interface{}, ch chan<- maybeMsg) error { - obj, ok := toMapStr(v) + obj, ok := toMapStr(v, s.targetInfo.Name) if !ok { return errExpectedSplitObj } - - clone := root.Clone() - if s.keyField != "" && key != "" { _, _ = obj.Put(s.keyField, key) } + clone := root.Clone() if s.keepParent { - _, _ = clone.Put(s.targetInfo.Name, obj) + _, _ = clone.Put(s.targetInfo.Name, v) } else { clone = obj } @@ -248,7 +247,7 @@ func (s *split) sendMessage(ctx *transformContext, root mapstr.M, key string, v return nil } -func toMapStr(v interface{}) (mapstr.M, bool) { +func toMapStr(v interface{}, key string) (mapstr.M, bool) { if v == nil { return mapstr.M{}, false } @@ -257,6 +256,8 @@ func toMapStr(v interface{}) (mapstr.M, bool) { return t, true case map[string]interface{}: return mapstr.M(t), true + case string, []bool, []int, []string, []interface{}: + return mapstr.M{key: t}, true } return mapstr.M{}, false } diff --git a/x-pack/filebeat/input/httpjson/split_test.go b/x-pack/filebeat/input/httpjson/split_test.go index b99220ff92f1..c8c800769701 100644 --- a/x-pack/filebeat/input/httpjson/split_test.go +++ b/x-pack/filebeat/input/httpjson/split_test.go @@ -624,6 +624,83 @@ func TestSplit(t *testing.T) { {"@timestamp": "1234567890", "other_items": "Line 3"}, }, }, + { + name: "Array of Strings with keep_parent", + config: &splitConfig{ + Target: "body.alerts", + Type: "array", + KeepParent: true, + }, + ctx: emptyTransformContext(), + resp: transformable{ + "body": mapstr.M{ + "this": "is kept", + "alerts": []interface{}{ + "test1", + "test2", + "test3", + }, + }, + }, + expectedMessages: []mapstr.M{ + { + "this": "is kept", + "alerts": "test1", + }, + { + "this": "is kept", + "alerts": "test2", + }, + { + "this": "is kept", + "alerts": "test3", + }, + }, + expectedErr: nil, + }, + { + name: "Array of Arrays with keep_parent", + config: &splitConfig{ + Target: "body.alerts", + Type: "array", + KeepParent: true, + }, + ctx: emptyTransformContext(), + resp: transformable{ + "body": mapstr.M{ + "this": "is kept", + "alerts": []interface{}{ + []interface{}{"test1-1", "test1-2"}, + []string{"test2-1", "test2-2"}, + []int{1, 2}, + }, + }, + }, + expectedMessages: []mapstr.M{ + { + "this": "is kept", + "alerts": []interface{}{ + "test1-1", + "test1-2", + }, + }, + { + "this": "is kept", + "alerts": []string{ + "test2-1", + "test2-2", + }, + }, + { + "this": "is kept", + "alerts": []int{ + 1, + 2, + }, + }, + }, + expectedErr: nil, + }, } for _, tc := range cases {