From ec407714a36c0f69c1d2d5de7b5526559bc05b9f Mon Sep 17 00:00:00 2001 From: odubajDT Date: Fri, 3 Jan 2025 09:07:05 +0100 Subject: [PATCH] e2e tests, readme, chlog Signed-off-by: odubajDT --- .chloggen/flatten-conflict.yaml | 27 +++++++++++++++++++ pkg/ottl/e2e/e2e_test.go | 47 +++++++++++++++++++++++++++++++++ pkg/ottl/ottlfuncs/README.md | 44 ++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 .chloggen/flatten-conflict.yaml diff --git a/.chloggen/flatten-conflict.yaml b/.chloggen/flatten-conflict.yaml new file mode 100644 index 000000000000..8706963cc738 --- /dev/null +++ b/.chloggen/flatten-conflict.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: pkg/ottl + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Enhance flatten() editor to resolve attribute key conflicts by adding a number suffix to the conflicting keys." + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35793] + +# (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/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index bb1dd43b5282..07e9abb99468 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -59,16 +59,21 @@ func Test_e2e_editors(t *testing.T) { tCtx.GetLogRecord().Attributes().Remove("total.string") tCtx.GetLogRecord().Attributes().Remove("foo") tCtx.GetLogRecord().Attributes().Remove("things") + tCtx.GetLogRecord().Attributes().Remove("conflict.conflict1") + tCtx.GetLogRecord().Attributes().Remove("conflict") }, }, { statement: `flatten(attributes)`, want: func(tCtx ottllog.TransformContext) { tCtx.GetLogRecord().Attributes().Remove("foo") + tCtx.GetLogRecord().Attributes().Remove("conflict.conflict1") + tCtx.GetLogRecord().Attributes().Remove("conflict") tCtx.GetLogRecord().Attributes().PutStr("foo.bar", "pass") tCtx.GetLogRecord().Attributes().PutStr("foo.flags", "pass") tCtx.GetLogRecord().Attributes().PutStr("foo.slice.0", "val") tCtx.GetLogRecord().Attributes().PutStr("foo.nested.test", "pass") + tCtx.GetLogRecord().Attributes().PutStr("conflict.conflict1.conflict2", "nopass") tCtx.GetLogRecord().Attributes().Remove("things") m1 := tCtx.GetLogRecord().Attributes().PutEmptyMap("things.0") @@ -95,6 +100,7 @@ func Test_e2e_editors(t *testing.T) { m.PutStr("test.foo.flags", "pass") m.PutStr("test.foo.slice.0", "val") m.PutStr("test.foo.nested.test", "pass") + m.PutStr("test.conflict.conflict1.conflict2", "nopass") m1 := m.PutEmptyMap("test.things.0") m1.PutStr("name", "foo") @@ -106,6 +112,35 @@ func Test_e2e_editors(t *testing.T) { m.CopyTo(tCtx.GetLogRecord().Attributes()) }, }, + { + statement: `flatten(attributes, "test", resolveConflicts=true)`, + want: func(tCtx ottllog.TransformContext) { + m := pcommon.NewMap() + m.PutStr("test.http.method", "get") + m.PutStr("test.http.path", "/health") + m.PutStr("test.http.url", "http://localhost/health") + m.PutStr("test.flags", "A|B|C") + m.PutStr("test.total.string", "123456789") + m.PutStr("test.foo.bar", "pass") + m.PutStr("test.foo.flags", "pass") + m.PutStr("test.foo.bar", "pass") + m.PutStr("test.foo.flags", "pass") + m.PutStr("test.foo.slice", "val") + m.PutStr("test.foo.nested.test", "pass") + + m.PutStr("test.conflict.conflict1.conflict2", "pass") + m.PutStr("test.conflict.conflict1.conflict2.0", "nopass") + + m1 := m.PutEmptyMap("test.things") + m1.PutStr("name", "foo") + m1.PutInt("value", 2) + + m2 := m.PutEmptyMap("test.things.0") + m2.PutStr("name", "bar") + m2.PutInt("value", 5) + m.CopyTo(tCtx.GetLogRecord().Attributes()) + }, + }, { statement: `flatten(attributes, depth=1)`, want: func(tCtx ottllog.TransformContext) { @@ -120,6 +155,9 @@ func Test_e2e_editors(t *testing.T) { m.PutStr("foo.bar", "pass") m.PutStr("foo.flags", "pass") m.PutEmptySlice("foo.slice").AppendEmpty().SetStr("val") + m.PutStr("conflict.conflict1.conflict2", "nopass") + mm := m.PutEmptyMap("conflict.conflict1") + mm.PutStr("conflict2", "pass") m1 := m.PutEmptyMap("things.0") m1.PutStr("name", "foo") @@ -142,6 +180,8 @@ func Test_e2e_editors(t *testing.T) { tCtx.GetLogRecord().Attributes().Remove("http.url") tCtx.GetLogRecord().Attributes().Remove("foo") tCtx.GetLogRecord().Attributes().Remove("things") + tCtx.GetLogRecord().Attributes().Remove("conflict.conflict1") + tCtx.GetLogRecord().Attributes().Remove("conflict") }, }, { @@ -157,6 +197,8 @@ func Test_e2e_editors(t *testing.T) { tCtx.GetLogRecord().Attributes().Remove("flags") tCtx.GetLogRecord().Attributes().Remove("foo") tCtx.GetLogRecord().Attributes().Remove("things") + tCtx.GetLogRecord().Attributes().Remove("conflict.conflict1") + tCtx.GetLogRecord().Attributes().Remove("conflict") }, }, { @@ -1148,6 +1190,11 @@ func constructLogTransformContext() ottllog.TransformContext { logRecord.Attributes().PutStr("http.url", "http://localhost/health") logRecord.Attributes().PutStr("flags", "A|B|C") logRecord.Attributes().PutStr("total.string", "123456789") + mm := logRecord.Attributes().PutEmptyMap("conflict") + mm1 := mm.PutEmptyMap("conflict1") + mm1.PutStr("conflict2", "pass") + mmm := logRecord.Attributes().PutEmptyMap("conflict.conflict1") + mmm.PutStr("conflict2", "nopass") m := logRecord.Attributes().PutEmptyMap("foo") m.PutStr("bar", "pass") m.PutStr("flags", "pass") diff --git a/pkg/ottl/ottlfuncs/README.md b/pkg/ottl/ottlfuncs/README.md index 8e48eb3083b2..c5d842ccbea1 100644 --- a/pkg/ottl/ottlfuncs/README.md +++ b/pkg/ottl/ottlfuncs/README.md @@ -126,11 +126,12 @@ Examples: ### flatten -`flatten(target, Optional[prefix], Optional[depth])` +`flatten(target, Optional[prefix], Optional[depth], Optional[resolveConflicts])` The `flatten` function flattens a `pcommon.Map` by moving items from nested maps to the root. -`target` is a path expression to a `pcommon.Map` type field. `prefix` is an optional string. `depth` is an optional non-negative int. +`target` is a path expression to a `pcommon.Map` type field. `prefix` is an optional string. `depth` is an optional non-negative int, `resolveConflicts` resolves the potential conflicts in the map keys by adding a number suffix starting with `0` from the first duplicated key. + For example, the following map @@ -199,6 +200,42 @@ the result would be A `depth` of `0` means that no flattening will occur. +If `resolveConflicts` is set to `true`, conflicts within the map will be resolved + +```json +{ + "address": { + "street": { + "number": "first", + }, + "house": "1234", + }, + "address.street": { + "number": ["second", "third"], + }, + "address.street.number": "fourth", + "occupants": [ + "user 1", + "user 2", + ], +} +``` + +the result would be + +```json +{ + "address.street.number": "first", + "address.house": "1234", + "address.street.number.0": "second", + "address.street.number.1": "third", + "occupants": "user 1", + "occupants.0": "user 2", + "address.street.number.2": "fourth", +} + +``` + Examples: - `flatten(attributes)` @@ -210,6 +247,9 @@ Examples: - `flatten(body, depth=2)` +- `flatten(body, resolveConflicts=true)` + + ### keep_keys `keep_keys(target, keys[])`