diff --git a/exporter/loggingexporter/internal/otlptext/databuffer_test.go b/exporter/loggingexporter/internal/otlptext/databuffer_test.go index 2e1c92dec55..d6d08d2f77a 100644 --- a/exporter/loggingexporter/internal/otlptext/databuffer_test.go +++ b/exporter/loggingexporter/internal/otlptext/databuffer_test.go @@ -41,12 +41,9 @@ func TestNestedArraySerializesCorrectly(t *testing.T) { func TestNestedMapSerializesCorrectly(t *testing.T) { ava := pcommon.NewValueMap() av := ava.MapVal() - av.Insert("foo", pcommon.NewValueString("test")) + av.UpsertString("foo", "test") - ava2 := pcommon.NewValueMap() - av2 := ava2.MapVal() - av2.InsertInt("bar", 13) - av.Insert("zoo", ava2) + av.UpsertEmptyMap("zoo").UpsertInt("bar", 13) expected := `{ -> foo: STRING(test) diff --git a/pdata/pcommon/common.go b/pdata/pcommon/common.go index 943604cf573..8c031a23ae8 100644 --- a/pdata/pcommon/common.go +++ b/pdata/pcommon/common.go @@ -298,6 +298,22 @@ func (v Value) SetBytesVal(bv ImmutableByteSlice) { v.getOrig().Value = &otlpcommon.AnyValue_BytesValue{BytesValue: bv.getOrig()} } +// SetEmptyMap sets value to an empty map and returns it. +// Calling this function on zero-initialized Value will cause a panic. +func (v Value) SetEmptyMap() Map { + kv := &otlpcommon.AnyValue_KvlistValue{KvlistValue: &otlpcommon.KeyValueList{}} + v.getOrig().Value = kv + return newMap(&kv.KvlistValue.Values) +} + +// SetEmptySlice sets value to an empty slice and returns it. +// Calling this function on zero-initialized Value will cause a panic. +func (v Value) SetEmptySlice() Slice { + av := &otlpcommon.AnyValue_ArrayValue{ArrayValue: &otlpcommon.ArrayValue{}} + v.getOrig().Value = av + return newSlice(&av.ArrayValue.Values) +} + // copyTo copies the value to Value. Will panic if dest is nil. func (v Value) copyTo(dest *otlpcommon.AnyValue) { switch ov := v.getOrig().Value.(type) { @@ -648,6 +664,7 @@ func (m Map) RemoveIf(f func(string, Value) bool) { // // Important: this function should not be used if the caller has access to // the raw value to avoid an extra allocation. +// Deprecated: [0.59.0] Use Get and CopyTo instead. func (m Map) Insert(k string, v Value) { if _, existing := m.Get(k); !existing { *m.getOrig() = append(*m.getOrig(), newAttributeKeyValue(k, v)) @@ -710,6 +727,7 @@ func (m Map) InsertBytes(k string, v ImmutableByteSlice) { // // Important: this function should not be used if the caller has access to // the raw value to avoid an extra allocation. +// Deprecated: [0.59.0] Use Get and CopyTo instead. func (m Map) Update(k string, v Value) { if av, existing := m.Get(k); existing { v.copyTo(av.getOrig()) @@ -764,6 +782,7 @@ func (m Map) UpdateBytes(k string, v ImmutableByteSlice) { // // Important: this function should not be used if the caller has access to // the raw value to avoid an extra allocation. +// Deprecated: [0.59.0] Use UpsertEmpty instead. func (m Map) Upsert(k string, v Value) { if av, existing := m.Get(k); existing { v.copyTo(av.getOrig()) @@ -772,6 +791,18 @@ func (m Map) Upsert(k string, v Value) { } } +// UpsertEmpty inserts or updates an empty value to the map under given key +// and return the updated/inserted value. +func (m Map) UpsertEmpty(k string) Value { + v := NewValueEmpty() + if av, existing := m.Get(k); existing { + *av.getOrig() = *v.getOrig() + } else { + *m.getOrig() = append(*m.getOrig(), newAttributeKeyValue(k, v)) + } + return v +} + // UpsertString performs the Insert or Update action. The Value is // inserted to the map that did not originally have the key. The key/value is // updated to the map where the key already existed. @@ -827,6 +858,28 @@ func (m Map) UpsertBytes(k string, v ImmutableByteSlice) { } } +// UpsertEmptyMap inserts or updates an empty map under given key and returns it. +func (m Map) UpsertEmptyMap(k string) Map { + kvl := otlpcommon.AnyValue_KvlistValue{KvlistValue: &otlpcommon.KeyValueList{Values: []otlpcommon.KeyValue(nil)}} + if av, existing := m.Get(k); existing { + av.getOrig().Value = &kvl + } else { + *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k, Value: otlpcommon.AnyValue{Value: &kvl}}) + } + return Map(internal.NewMap(&kvl.KvlistValue.Values)) +} + +// UpsertEmptySlice inserts or updates an empty clice under given key and returns it. +func (m Map) UpsertEmptySlice(k string) Slice { + vl := otlpcommon.AnyValue_ArrayValue{ArrayValue: &otlpcommon.ArrayValue{Values: []otlpcommon.AnyValue(nil)}} + if av, existing := m.Get(k); existing { + av.getOrig().Value = &vl + } else { + *m.getOrig() = append(*m.getOrig(), otlpcommon.KeyValue{Key: k, Value: otlpcommon.AnyValue{Value: &vl}}) + } + return Slice(internal.NewSlice(&vl.ArrayValue.Values)) +} + // Sort sorts the entries in the Map so two instances can be compared. // Returns the same instance to allow nicer code like: // diff --git a/pdata/pcommon/common_test.go b/pdata/pcommon/common_test.go index 6c56eb79418..9ac6296778b 100644 --- a/pdata/pcommon/common_test.go +++ b/pdata/pcommon/common_test.go @@ -166,6 +166,14 @@ func TestNilOrigSetValue(t *testing.T) { av = NewValueEmpty() av.SetBytesVal(NewImmutableByteSlice([]byte{1, 2, 3})) assert.Equal(t, NewImmutableByteSlice([]byte{1, 2, 3}), av.BytesVal()) + + av = NewValueEmpty() + av.SetEmptyMap().UpsertString("k", "v") + assert.Equal(t, NewMapFromRaw(map[string]interface{}{"k": "v"}), av.MapVal()) + + av = NewValueEmpty() + av.SetEmptySlice().AppendEmpty().SetIntVal(1) + assert.Equal(t, NewSliceFromRaw([]interface{}{1}), av.SliceVal()) } func TestValueEqual(t *testing.T) { @@ -345,6 +353,42 @@ func TestMap(t *testing.T) { assert.EqualValues(t, NewMap(), NewMap().Sort()) } +func TestChildMap(t *testing.T) { + parentMap := NewMap() + childMap := parentMap.UpsertEmptyMap("k1") + childMap.UpsertEmptySlice("k2").AppendEmpty().SetStringVal("val") + assert.EqualValues(t, NewMapFromRaw(map[string]interface{}{ + "k1": map[string]interface{}{ + "k2": []interface{}{"val"}, + }, + }), parentMap) + + sl, ok := childMap.Get("k2") + assert.True(t, ok) + sl.SliceVal().AppendEmpty().SetIntVal(1) + assert.EqualValues(t, NewMapFromRaw(map[string]interface{}{ + "k1": map[string]interface{}{ + "k2": []interface{}{"val", 1}, + }, + }), parentMap) + + mv, ok := parentMap.Get("k1") + assert.True(t, ok) + assert.EqualValues(t, childMap, mv.MapVal()) + mv.MapVal().UpsertBool("k3", true) + bv, ok := childMap.Get("k3") + assert.True(t, ok) + assert.True(t, bv.BoolVal()) + + ok = childMap.Remove("k2") + assert.True(t, ok) + assert.EqualValues(t, NewMapFromRaw(map[string]interface{}{ + "k1": map[string]interface{}{ + "k3": true, + }, + }), parentMap) +} + func TestMapWithEmpty(t *testing.T) { origWithNil := []otlpcommon.KeyValue{ {}, diff --git a/pdata/ptrace/json_test.go b/pdata/ptrace/json_test.go index f4a44b4aa0e..4c95d11715d 100644 --- a/pdata/ptrace/json_test.go +++ b/pdata/ptrace/json_test.go @@ -95,14 +95,12 @@ var tracesOTLPFull = func() Traces { sp.Attributes().UpsertInt("int", 1) sp.Attributes().UpsertDouble("double", 1.1) sp.Attributes().UpsertBytes("bytes", pcommon.NewImmutableByteSlice([]byte("foo"))) - arr := pcommon.NewValueSlice() - arr.SliceVal().AppendEmpty().SetIntVal(1) - arr.SliceVal().AppendEmpty().SetStringVal("str") - sp.Attributes().Upsert("array", arr) - kvList := pcommon.NewValueMap() - kvList.MapVal().Upsert("int", pcommon.NewValueInt(1)) - kvList.MapVal().Upsert("string", pcommon.NewValueString("string")) - sp.Attributes().Upsert("kvList", kvList) + arr := sp.Attributes().UpsertEmptySlice("array") + arr.AppendEmpty().SetIntVal(1) + arr.AppendEmpty().SetStringVal("str") + kvList := sp.Attributes().UpsertEmptyMap("kvList") + kvList.UpsertInt("int", 1) + kvList.UpsertString("string", "string") // Add events. event := sp.Events().AppendEmpty() event.SetName("eventName")