Skip to content

Commit 22045d6

Browse files
authored
Feat: Added OptimizelyJson class to support GetFeatureVariableJson (#214)
1 parent 39f7409 commit 22045d6

File tree

9 files changed

+421
-1
lines changed

9 files changed

+421
-1
lines changed

OptimizelySDK.Net35/OptimizelySDK.Net35.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@
9696
</Compile>
9797
<Compile Include="..\OptimizelySDK\Entity\IdKeyEntity.cs">
9898
<Link>Entity\IdKeyEntity.cs</Link>
99+
</Compile>
100+
<Compile Include="..\OptimizelySDK\OptimizelyJson.cs">
101+
<Link>OptimizelyJson.cs</Link>
99102
</Compile>
100103
<Compile Include="..\OptimizelySDK\Entity\TrafficAllocation.cs">
101104
<Link>Entity\TrafficAllocation.cs</Link>

OptimizelySDK.Net40/OptimizelySDK.Net40.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@
9898
</Compile>
9999
<Compile Include="..\OptimizelySDK\Entity\IdKeyEntity.cs">
100100
<Link>Entity\IdKeyEntity.cs</Link>
101+
</Compile>
102+
<Compile Include="..\OptimizelySDK\OptimizelyJson.cs">
103+
<Link>OptimizelyJson.cs</Link>
101104
</Compile>
102105
<Compile Include="..\OptimizelySDK\Entity\TrafficAllocation.cs">
103106
<Link>Entity\TrafficAllocation.cs</Link>

OptimizelySDK.NetStandard16/OptimizelySDK.NetStandard16.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<Compile Include="..\OptimizelySDK\Entity\ForcedVariation.cs" />
3232
<Compile Include="..\OptimizelySDK\Entity\Group.cs" />
3333
<Compile Include="..\OptimizelySDK\Entity\IdKeyEntity.cs" />
34+
<Compile Include="..\OptimizelySDK\OptimizelyJson.cs" />
3435
<Compile Include="..\OptimizelySDK\Entity\TrafficAllocation.cs" />
3536
<Compile Include="..\OptimizelySDK\Entity\UserAttributes.cs" />
3637
<Compile Include="..\OptimizelySDK\Entity\Variation.cs" />

OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@
114114
</Compile>
115115
<Compile Include="..\OptimizelySDK\Entity\IdKeyEntity.cs">
116116
<Link>Entity\IdKeyEntity.cs</Link>
117+
</Compile>
118+
<Compile Include="..\OptimizelySDK\OptimizelyJson.cs">
119+
<Link>Entity\OptimizelyJson.cs</Link>
117120
</Compile>
118121
<Compile Include="..\OptimizelySDK\Entity\Rollout.cs">
119122
<Link>Entity\Rollout.cs</Link>
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/**
2+
*
3+
* Copyright 2020, Optimizely and contributors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
using Moq;
19+
using NUnit.Framework;
20+
using OptimizelySDK.ErrorHandler;
21+
using OptimizelySDK.Exceptions;
22+
using OptimizelySDK.Logger;
23+
using System;
24+
using System.Collections.Generic;
25+
26+
namespace OptimizelySDK.Tests
27+
{
28+
class ParentJson
29+
{
30+
public string strField { get; set; }
31+
public int intField { get; set; }
32+
public double doubleField { get; set; }
33+
public bool boolField { get; set; }
34+
public ObjectJson objectField { get; set; }
35+
36+
}
37+
class ObjectJson
38+
{
39+
public int inner_field_int { get; set; }
40+
public double inner_field_double { get; set; }
41+
public string inner_field_string {get;set;}
42+
public bool inner_field_boolean { get; set; }
43+
}
44+
45+
class Field4
46+
{
47+
public long inner_field1 { get; set; }
48+
public InnerField2 inner_field2 { get; set; }
49+
}
50+
class InnerField2 : List<object> { }
51+
52+
53+
[TestFixture]
54+
public class OptimizelyJsonTest
55+
{
56+
private string Payload;
57+
private Dictionary<string, object> Map;
58+
private Mock<ILogger> LoggerMock;
59+
private Mock<IErrorHandler> ErrorHandlerMock;
60+
61+
[SetUp]
62+
public void Initialize()
63+
{
64+
ErrorHandlerMock = new Mock<IErrorHandler>();
65+
ErrorHandlerMock.Setup(e => e.HandleError(It.IsAny<Exception>()));
66+
67+
LoggerMock = new Mock<ILogger>();
68+
LoggerMock.Setup(i => i.Log(It.IsAny<LogLevel>(), It.IsAny<string>()));
69+
70+
Payload = "{ \"field1\": 1, \"field2\": 2.5, \"field3\": \"three\", \"field4\": {\"inner_field1\":3,\"inner_field2\":[\"1\",\"2\", 3, 4.23, true]}, \"field5\": true, }";
71+
Map = new Dictionary<string, object>() {
72+
{ "strField", "john doe" },
73+
{ "intField", 12 },
74+
{ "doubleField", 2.23 },
75+
{ "boolField", true},
76+
{ "objectField", new Dictionary<string, object> () {
77+
{ "inner_field_int", 3 },
78+
{ "inner_field_double", 13.21 },
79+
{ "inner_field_string", "john" },
80+
{ "inner_field_boolean", true }
81+
}
82+
}
83+
};
84+
}
85+
86+
[Test]
87+
public void TestOptimizelyJsonObjectIsValid()
88+
{
89+
var optimizelyJSONUsingMap = new OptimizelyJson(Map, ErrorHandlerMock.Object, LoggerMock.Object);
90+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
91+
92+
Assert.IsNotNull(optimizelyJSONUsingMap);
93+
Assert.IsNotNull(optimizelyJSONUsingString);
94+
}
95+
[Test]
96+
public void TestToStringReturnValidString()
97+
{
98+
var map = new Dictionary<string, object>() {
99+
{ "strField", "john doe" },
100+
{ "intField", 12 },
101+
{ "objectField", new Dictionary<string, object> () {
102+
{ "inner_field_int", 3 }
103+
}
104+
}
105+
};
106+
var optimizelyJSONUsingMap = new OptimizelyJson(map, ErrorHandlerMock.Object, LoggerMock.Object);
107+
string str = optimizelyJSONUsingMap.ToString();
108+
string expectedStringObj = "{\"strField\":\"john doe\",\"intField\":12,\"objectField\":{\"inner_field_int\":3}}";
109+
Assert.AreEqual(expectedStringObj, str);
110+
}
111+
112+
[Test]
113+
public void TestGettingErrorUponInvalidJsonString()
114+
{
115+
var optimizelyJSONUsingString = new OptimizelyJson("{\"invalid\":}", ErrorHandlerMock.Object, LoggerMock.Object);
116+
LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Provided string could not be converted to map."), Times.Once);
117+
ErrorHandlerMock.Verify(er => er.HandleError(It.IsAny<InvalidJsonException>()), Times.Once);
118+
}
119+
120+
[Test]
121+
public void TestOptimizelyJsonGetVariablesWhenSetUsingMap()
122+
{
123+
var optimizelyJSONUsingMap = new OptimizelyJson(Map, ErrorHandlerMock.Object, LoggerMock.Object);
124+
125+
Assert.AreEqual(optimizelyJSONUsingMap.GetValue<string>("strField"), "john doe");
126+
Assert.AreEqual(optimizelyJSONUsingMap.GetValue<int>("intField"), 12);
127+
Assert.AreEqual(optimizelyJSONUsingMap.GetValue<double>("doubleField"), 2.23);
128+
Assert.AreEqual(optimizelyJSONUsingMap.GetValue<bool>("boolField"), true);
129+
Assert.AreEqual(optimizelyJSONUsingMap.GetValue<int>("objectField.inner_field_int"), 3);
130+
Assert.AreEqual(optimizelyJSONUsingMap.GetValue<double>("objectField.inner_field_double"), 13.21);
131+
Assert.AreEqual(optimizelyJSONUsingMap.GetValue<string>("objectField.inner_field_string"), "john");
132+
Assert.AreEqual(optimizelyJSONUsingMap.GetValue<bool>("objectField.inner_field_boolean"), true);
133+
Assert.IsTrue(optimizelyJSONUsingMap.GetValue<Dictionary<string, object>>("objectField") is Dictionary<string, object>);
134+
}
135+
136+
[Test]
137+
public void TestOptimizelyJsonGetVariablesWhenSetUsingString()
138+
{
139+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
140+
141+
Assert.AreEqual(optimizelyJSONUsingString.GetValue<long>("field1"), 1);
142+
Assert.AreEqual(optimizelyJSONUsingString.GetValue<double>("field2"), 2.5);
143+
Assert.AreEqual(optimizelyJSONUsingString.GetValue<string>("field3"), "three");
144+
Assert.AreEqual(optimizelyJSONUsingString.GetValue<long>("field4.inner_field1"), 3);
145+
Assert.True(TestData.CompareObjects(optimizelyJSONUsingString.GetValue<List<object>>("field4.inner_field2"), new List<object>() { "1", "2", 3, 4.23, true }));
146+
}
147+
148+
[Test]
149+
public void TestGetValueReturnsEntireDictWhenJsonPathIsEmptyAndTypeIsValid()
150+
{
151+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
152+
var actualDict = optimizelyJSONUsingString.ToDictionary();
153+
var expectedValue = optimizelyJSONUsingString.GetValue<Dictionary<string, object>>("");
154+
Assert.NotNull(expectedValue);
155+
Assert.True(TestData.CompareObjects(expectedValue, actualDict));
156+
}
157+
158+
[Test]
159+
public void TestGetValueReturnsDefaultValueWhenJsonIsInvalid()
160+
{
161+
var payload = "{ \"field1\" : {1:\"Csharp\", 2:\"Java\"} }";
162+
var optimizelyJSONUsingString = new OptimizelyJson(payload, ErrorHandlerMock.Object, LoggerMock.Object);
163+
var expectedValue = optimizelyJSONUsingString.GetValue<Dictionary<float, string>>("field1");
164+
// Even though above given JSON is not valid, newtonsoft is parsing it so
165+
Assert.IsNotNull(expectedValue);
166+
}
167+
168+
[Test]
169+
public void TestGetValueReturnsDefaultValueWhenTypeIsInvalid()
170+
{
171+
var payload = "{ \"field1\" : {\"1\":\"Csharp\",\"2\":\"Java\"} }";
172+
var optimizelyJSONUsingString = new OptimizelyJson(payload, ErrorHandlerMock.Object, LoggerMock.Object);
173+
var expectedValue = optimizelyJSONUsingString.GetValue<Dictionary<float, string>>("field1");
174+
Assert.IsNotNull(expectedValue);
175+
}
176+
177+
[Test]
178+
public void TestGetValueReturnsNullWhenJsonPathIsEmptyAndTypeIsOfObject()
179+
{
180+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
181+
var expectedValue = optimizelyJSONUsingString.GetValue<object>("");
182+
Assert.NotNull(expectedValue);
183+
}
184+
185+
[Test]
186+
public void TestGetValueReturnsDefaultValueWhenJsonPathIsEmptyAndTypeIsNotValid()
187+
{
188+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
189+
var expectedValue = optimizelyJSONUsingString.GetValue<string>("");
190+
Assert.IsNull(expectedValue);
191+
LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Value for path could not be assigned to provided type."), Times.Once);
192+
ErrorHandlerMock.Verify(er => er.HandleError(It.IsAny<OptimizelyRuntimeException>()), Times.Once);
193+
}
194+
195+
[Test]
196+
public void TestGetValueReturnsDefaultValueWhenJsonPathIsInvalid()
197+
{
198+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
199+
var expectedValue = optimizelyJSONUsingString.GetValue<string>("field11");
200+
Assert.IsNull(expectedValue);
201+
LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Value for JSON key not found."), Times.Once);
202+
ErrorHandlerMock.Verify(er => er.HandleError(It.IsAny<OptimizelyRuntimeException>()), Times.Once);
203+
}
204+
205+
[Test]
206+
public void TestGetValueReturnsDefaultValueWhenJsonPath1IsInvalid()
207+
{
208+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
209+
var expectedValue = optimizelyJSONUsingString.GetValue<string>("field4.");
210+
Assert.IsNull(expectedValue);
211+
LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Value for JSON key not found."), Times.Once);
212+
ErrorHandlerMock.Verify(er => er.HandleError(It.IsAny<OptimizelyRuntimeException>()), Times.Once);
213+
}
214+
215+
[Test]
216+
public void TestGetValueReturnsDefaultValueWhenJsonPath2IsInvalid()
217+
{
218+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
219+
var expectedValue = optimizelyJSONUsingString.GetValue<string>("field4..inner_field1");
220+
Assert.IsNull(expectedValue);
221+
LoggerMock.Verify(log => log.Log(LogLevel.ERROR, "Value for JSON key not found."), Times.Once);
222+
ErrorHandlerMock.Verify(er => er.HandleError(It.IsAny<OptimizelyRuntimeException>()), Times.Once);
223+
}
224+
225+
[Test]
226+
public void TestGetValueObjectNotModifiedIfCalledTwice()
227+
{
228+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
229+
var expectedValue = optimizelyJSONUsingString.GetValue<string>("field4.inner_field1");
230+
var expectedValue2 = optimizelyJSONUsingString.GetValue<string>("field4.inner_field1");
231+
232+
Assert.AreEqual(expectedValue, expectedValue2);
233+
}
234+
235+
[Test]
236+
public void TestGetValueReturnsUsingGivenClassType()
237+
{
238+
var optimizelyJSONUsingString = new OptimizelyJson(Payload, ErrorHandlerMock.Object, LoggerMock.Object);
239+
var expectedValue = optimizelyJSONUsingString.GetValue<Field4>("field4");
240+
241+
Assert.AreEqual(expectedValue.inner_field1, 3);
242+
Assert.AreEqual(expectedValue.inner_field2, new List<object>() { "1", "2", 3, 4.23, true });
243+
}
244+
245+
[Test]
246+
public void TestGetValueReturnsCastedObject()
247+
{
248+
var optimizelyJson = new OptimizelyJson(Map, ErrorHandlerMock.Object, LoggerMock.Object);
249+
var expectedValue = optimizelyJson.ToDictionary();
250+
var actualValue = optimizelyJson.GetValue<ParentJson>(null);
251+
252+
Assert.IsTrue(TestData.CompareObjects(actualValue, expectedValue));
253+
}
254+
}
255+
}

OptimizelySDK.Tests/OptimizelySDK.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<Compile Include="ConfigTest\FallbackProjectConfigManagerTest.cs" />
7878
<Compile Include="DecisionServiceTest.cs" />
7979
<Compile Include="DefaultErrorHandlerTest.cs" />
80+
<Compile Include="OptimizelyJsonTest.cs" />
8081
<Compile Include="EventTests\BatchEventProcessorTest.cs" />
8182
<Compile Include="EventTests\DefaultEventDispatcherTest.cs" />
8283
<Compile Include="EventTests\EventBuilderTest.cs" />

OptimizelySDK/Exceptions/OptimizelyException.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017, Optimizely
2+
* Copyright 2017, 2020, Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,6 +33,14 @@ public OptimizelyRuntimeException(string message)
3333
{
3434
}
3535
}
36+
37+
public class InvalidJsonException : OptimizelyException
38+
{
39+
public InvalidJsonException(string message)
40+
: base(message)
41+
{
42+
}
43+
}
3644

3745
public class InvalidAttributeException : OptimizelyException
3846
{

0 commit comments

Comments
 (0)