Skip to content

Commit 8812fe1

Browse files
authored
fix: Custom attribute values of type float and decimal were not serialized correctly. (#2975)
1 parent 0ab2e1b commit 8812fe1

File tree

4 files changed

+94
-37
lines changed

4 files changed

+94
-37
lines changed

src/Agent/NewRelic/Agent/Core/Attributes/AttributeDefinition.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,10 @@ public static AttributeDefinitionBuilder<object, object> CreateCustomAttribute(s
121121
case TypeCode.Int32:
122122
return Convert.ToInt64(input);
123123

124+
// don't convert Decimal and Single to double.
125+
// The value ends up getting serialized as a string, so there's no need for the conversion
124126
case TypeCode.Decimal:
125127
case TypeCode.Single:
126-
return Convert.ToDouble(input);
127128

128129
case TypeCode.Double:
129130
case TypeCode.Int64:

tests/Agent/UnitTests/Core.UnitTest/Attributes/AttributeTests.cs

+25-25
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System;
77
using System.Linq;
88
using System.Collections.Generic;
9-
using NUnit.Framework.Internal;
109

1110
namespace NewRelic.Agent.Core.Attributes.Tests
1211
{
@@ -25,38 +24,17 @@ public class AttributeTests
2524
[TestCase((ulong)8, 8L)]
2625
[TestCase((float)1.0, 1D)]
2726
[TestCase(/*(double)*/2.0, 2D)]
27+
[TestCase(0.2D, 0.2D)]
28+
[TestCase(0.2F, 0.2F)]
2829
public void Attributes_with_valid_type_are_valid_attributes(object attributeValue, object expectedResult)
2930
{
3031
TestValue(attributeValue, expectedResult);
3132
}
3233

33-
34-
private void TestValue(object attributeValue, object expectedResult)
35-
{
36-
var filter = new AttributeFilter(new AttributeFilter.Settings());
37-
38-
var attribDef = AttributeDefinitionBuilder
39-
.CreateCustomAttribute("test", AttributeDestinations.All)
40-
.Build(filter);
41-
42-
var attribVals = new AttributeValueCollection(AttributeValueCollection.AllTargetModelTypes);
43-
44-
attribDef.TrySetValue(attribVals, attributeValue);
45-
46-
var actualAttribVal = attribVals.GetAttributeValues(AttributeClassification.UserAttributes)
47-
.FirstOrDefault(x => x.AttributeDefinition == attribDef);
48-
49-
50-
NrAssert.Multiple(
51-
() => Assert.That(actualAttribVal, Is.Not.Null),
52-
() => Assert.That(actualAttribVal.Value, Is.EqualTo(expectedResult))
53-
);
54-
}
55-
5634
[Test]
5735
public void Attributes_with_decimal_type_are_valid_attributes()
5836
{
59-
TestValue(1.0m, 1D);
37+
TestValue(0.2M, 0.2M);
6038
}
6139

6240
[Test]
@@ -261,5 +239,27 @@ public void Attributes_value_size_other_strings()
261239
() => Assert.That(values.ElementAt(2).Value.ToString(), Has.Length.EqualTo(255))
262240
);
263241
}
242+
243+
private void TestValue(object attributeValue, object expectedResult)
244+
{
245+
var filter = new AttributeFilter(new AttributeFilter.Settings());
246+
247+
var attribDef = AttributeDefinitionBuilder
248+
.CreateCustomAttribute("test", AttributeDestinations.All)
249+
.Build(filter);
250+
251+
var attribVals = new AttributeValueCollection(AttributeValueCollection.AllTargetModelTypes);
252+
253+
attribDef.TrySetValue(attribVals, attributeValue);
254+
255+
var actualAttribVal = attribVals.GetAttributeValues(AttributeClassification.UserAttributes)
256+
.FirstOrDefault(x => x.AttributeDefinition == attribDef);
257+
258+
259+
NrAssert.Multiple(
260+
() => Assert.That(actualAttribVal, Is.Not.Null),
261+
() => Assert.That(actualAttribVal.Value, Is.EqualTo(expectedResult))
262+
);
263+
}
264264
}
265265
}

tests/Agent/UnitTests/Core.UnitTest/CustomEvents/CustomEventWireModelTests.cs

+59-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public void CustomEvents_MultipleEvents_Serialization()
3939
var customEvents = new CustomEventWireModel[countEvents];
4040
var expectedSerializations = new List<Dictionary<string, object>[]>();
4141

42-
for(var i = 0; i < countEvents; i++)
42+
for (var i = 0; i < countEvents; i++)
4343
{
4444
var timestampVal = DateTime.UtcNow;
4545
var typeVal = $"CustomEvent{i}";
@@ -87,7 +87,64 @@ public void CustomEvents_MultipleEvents_Serialization()
8787
AttributeComparer.CompareDictionaries(expectedSerializations[0], deserialized[0]);
8888
AttributeComparer.CompareDictionaries(expectedSerializations[1], deserialized[1]);
8989
}
90-
}
9190

91+
[Test]
92+
public void CustomEvents_UserAttributes_AllAttributeTypesSerializeCorrectly()
93+
{
94+
var dateTime = DateTime.UtcNow;
95+
var timestampVal = dateTime;
96+
var typeVal = $"CustomEvent";
97+
98+
var guid = Guid.NewGuid();
99+
var expectedSerialization = new Dictionary<string, object>[]
100+
{
101+
new Dictionary<string, object>()
102+
{
103+
{_attribDefs.Timestamp.Name, timestampVal.ToUnixTimeMilliseconds() },
104+
{_attribDefs.CustomEventType.Name, typeVal }
105+
},
106+
107+
new Dictionary<string, object>()
108+
{
109+
{ "boolVal", true},
110+
{ "dateTimeVal", dateTime},
111+
{ "decimalVal", 0.2M},
112+
{ "doubleVal", 0.2D},
113+
{ "enumVal", AttributeDestinations.CustomEvent},
114+
{ "floatVal", 0.2f},
115+
{ "guidVal", guid},
116+
{ "intVal", 2},
117+
{ "longVal", 2L},
118+
{ "stringVal", "string"},
119+
},
120+
121+
new Dictionary<string, object>()
122+
};
123+
124+
var attribVals = new AttributeValueCollection(AttributeDestinations.CustomEvent);
125+
126+
_attribDefs.Timestamp.TrySetValue(attribVals, timestampVal);
127+
_attribDefs.CustomEventType.TrySetValue(attribVals, typeVal);
128+
129+
_attribDefs.GetCustomAttributeForCustomEvent("boolVal").TrySetValue(attribVals, true);
130+
_attribDefs.GetCustomAttributeForCustomEvent("dateTimeVal").TrySetValue(attribVals, dateTime);
131+
_attribDefs.GetCustomAttributeForCustomEvent("decimalVal").TrySetValue(attribVals, 0.2M);
132+
_attribDefs.GetCustomAttributeForCustomEvent("doubleVal").TrySetValue(attribVals, 0.2D);
133+
_attribDefs.GetCustomAttributeForCustomEvent("enumVal").TrySetValue(attribVals, AttributeDestinations.CustomEvent);
134+
_attribDefs.GetCustomAttributeForCustomEvent("floatVal").TrySetValue(attribVals, 0.2f);
135+
_attribDefs.GetCustomAttributeForCustomEvent("guidVal").TrySetValue(attribVals, guid);
136+
_attribDefs.GetCustomAttributeForCustomEvent("intVal").TrySetValue(attribVals, 2);
137+
_attribDefs.GetCustomAttributeForCustomEvent("longVal").TrySetValue(attribVals, 2L);
138+
_attribDefs.GetCustomAttributeForCustomEvent("stringVal").TrySetValue(attribVals, "string");
139+
140+
var customEvent = new CustomEventWireModel(.5f, attribVals);
141+
142+
var serialized = JsonConvert.SerializeObject(customEvent);
143+
var expectedSerialized = JsonConvert.SerializeObject(expectedSerialization);
144+
145+
Assert.That(serialized, Is.Not.Null);
146+
Assert.That(serialized, Is.EqualTo(expectedSerialized));
147+
}
148+
}
92149
}
93150

tests/Agent/UnitTests/NewRelic.Agent.TestUtilities/AttributeComparer.cs

+8-9
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static bool IsEqualTo(this object val1, object val2)
7878
case TypeCode.Single:
7979
case TypeCode.Double:
8080
case TypeCode.Decimal:
81-
return Math.Abs((double)val1Comp - (double)val2Comp) < .0001;
81+
return Math.Abs(Convert.ToDouble(val1Comp) - Convert.ToDouble(val2Comp)) < .0001; // close enough, I guess?
8282

8383
default:
8484
return val1Comp.Equals(val2Comp);
@@ -111,25 +111,24 @@ public static object ConvertForCompare(object val)
111111
{
112112
case TypeCode.SByte:
113113
case TypeCode.Byte:
114-
case TypeCode.Int16:
115114
case TypeCode.UInt16:
116-
case TypeCode.Int32:
117115
case TypeCode.UInt32:
118-
case TypeCode.Int64:
119116
case TypeCode.UInt64:
117+
case TypeCode.Int16:
118+
case TypeCode.Int32:
120119
return Convert.ToInt64(val);
121120

121+
case TypeCode.Decimal:
122122
case TypeCode.Single:
123123
case TypeCode.Double:
124-
case TypeCode.Decimal:
125-
return Convert.ToDouble(val);
124+
case TypeCode.Int64:
125+
case TypeCode.Boolean:
126+
case TypeCode.String:
127+
return val;
126128

127129
case TypeCode.DateTime:
128130
return ((DateTime)val).ToString("o");
129131

130-
case TypeCode.String:
131-
return val;
132-
133132
default:
134133
return val.ToString();
135134
}

0 commit comments

Comments
 (0)