Skip to content

Commit 108067d

Browse files
msohailhussainmikeproeng37
authored andcommitted
fix(datafile-parsing): Prevent newer versions datafile (#101)
1 parent 03131c9 commit 108067d

File tree

8 files changed

+157
-5
lines changed

8 files changed

+157
-5
lines changed

OptimizelySDK.Tests/OptimizelySDK.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
</ItemGroup>
105105
<ItemGroup>
106106
<None Include="packages.config" />
107+
<EmbeddedResource Include="unsupported_version_datafile.json" />
107108
</ItemGroup>
108109
<ItemGroup>
109110
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />

OptimizelySDK.Tests/OptimizelyTest.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
using OptimizelySDK.Notifications;
3030
using OptimizelySDK.Tests.NotificationTests;
3131
using OptimizelySDK.Utils;
32+
using Newtonsoft.Json;
3233

3334
namespace OptimizelySDK.Tests
3435
{
@@ -185,7 +186,31 @@ public void TestValidateInputsInvalidFileJsonValidationSkipped()
185186
{
186187
string datafile = "{\"name\":\"optimizely\"}";
187188
Optimizely optimizely = new Optimizely(datafile, null, null, null, skipJsonValidation: true);
188-
Assert.IsTrue(optimizely.IsValid);
189+
Assert.IsFalse(optimizely.IsValid);
190+
}
191+
192+
[Test]
193+
public void TestErrorHandlingWithNullDatafile()
194+
{
195+
var optimizelyNullDatafile = new Optimizely(null, null, LoggerMock.Object, ErrorHandlerMock.Object, null, true);
196+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Unable to parse null datafile."), Times.Once);
197+
ErrorHandlerMock.Verify(e => e.HandleError(It.Is<ConfigParseException>(ex => ex.Message == "Unable to parse null datafile.")), Times.Once);
198+
}
199+
200+
[Test]
201+
public void TestErrorHandlingWithEmptyDatafile()
202+
{
203+
var optimizelyEmptyDatafile = new Optimizely("", null, LoggerMock.Object, ErrorHandlerMock.Object, null, true);
204+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Unable to parse empty datafile."), Times.Once);
205+
ErrorHandlerMock.Verify(e => e.HandleError(It.Is<ConfigParseException>(ex => ex.Message == "Unable to parse empty datafile.")), Times.Once);
206+
}
207+
208+
[Test]
209+
public void TestErrorHandlingWithUnsupportedConfigVersion()
210+
{
211+
var optimizelyUnsupportedVersion = new Optimizely(TestData.UnsupportedVersionDatafile, null, LoggerMock.Object, ErrorHandlerMock.Object, null, true);
212+
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, $"This version of the C# SDK does not support the given datafile version: 5"), Times.Once);
213+
ErrorHandlerMock.Verify(e => e.HandleError(It.Is<ConfigParseException>(ex => ex.Message == $"This version of the C# SDK does not support the given datafile version: 5")), Times.Once);
189214
}
190215

191216
[Test]

OptimizelySDK.Tests/ProjectConfigTest.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,5 +863,32 @@ public void TestGetAttributeIdWithInvalidAttributeKey()
863863
Assert.Null(Config.GetAttributeId("invalid_attribute"));
864864
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, @"Attribute key ""invalid_attribute"" is not in datafile."));
865865
}
866+
867+
[Test]
868+
public void TestCreateThrowsWithNullDatafile()
869+
{
870+
var exception = Assert.Throws<ConfigParseException>(() => ProjectConfig.Create(null, null, null));
871+
Assert.AreEqual("Unable to parse null datafile.", exception.Message);
872+
}
873+
874+
[Test]
875+
public void TestCreateThrowsWithEmptyDatafile()
876+
{
877+
var exception = Assert.Throws<ConfigParseException>(() => ProjectConfig.Create("", null, null));
878+
Assert.AreEqual("Unable to parse empty datafile.", exception.Message);
879+
}
880+
881+
[Test]
882+
public void TestCreateThrowsWithUnsupportedDatafileVersion()
883+
{
884+
var exception = Assert.Throws<ConfigParseException>(() => ProjectConfig.Create(TestData.UnsupportedVersionDatafile, null, null));
885+
Assert.AreEqual($"This version of the C# SDK does not support the given datafile version: 5", exception.Message);
886+
}
887+
888+
[Test]
889+
public void TestCreateDoesNotThrowWithValidDatafile()
890+
{
891+
Assert.DoesNotThrow(() => ProjectConfig.Create(TestData.Datafile, null, null));
892+
}
866893
}
867894
}

OptimizelySDK.Tests/TestData.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017, Optimizely
2+
* Copyright 2017-2018, 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.
@@ -25,6 +25,7 @@ public class TestData
2525
{
2626
private static string cachedDataFile = null;
2727
private static string simpleABExperimentsDatafile = null;
28+
private static string unsupportedVersionDatafile = null;
2829

2930
public static string Datafile
3031
{
@@ -41,7 +42,15 @@ public static string SimpleABExperimentsDatafile
4142
return simpleABExperimentsDatafile ?? (simpleABExperimentsDatafile = LoadJsonData("simple_ab_experiments.json"));
4243
}
4344
}
44-
45+
46+
public static string UnsupportedVersionDatafile
47+
{
48+
get
49+
{
50+
return unsupportedVersionDatafile ?? (unsupportedVersionDatafile = LoadJsonData("unsupported_version_datafile.json"));
51+
}
52+
}
53+
4554
private static string LoadJsonData(string fileName = "TestData.json")
4655
{
4756
var assembly = Assembly.GetExecutingAssembly();
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"version": "5",
3+
"rollouts": [],
4+
"projectId": "10431130345",
5+
"variables": [],
6+
"featureFlags": [],
7+
"experiments": [{
8+
"status": "Running",
9+
"key": "ab_running_exp_untargeted",
10+
"layerId": "10417730432",
11+
"trafficAllocation": [{
12+
"entityId": "10418551353",
13+
"endOfRange": 10000
14+
}],
15+
"audienceIds": [],
16+
"variations": [{
17+
"variables": [],
18+
"id": "10418551353",
19+
"key": "all_traffic_variation"
20+
},
21+
{
22+
"variables": [],
23+
"id": "10418510624",
24+
"key": "no_traffic_variation"
25+
}
26+
],
27+
"forcedVariations": {},
28+
"id": "10420810910"
29+
}],
30+
"audiences": [],
31+
"groups": [],
32+
"attributes": [],
33+
"accountId": "10367498574",
34+
"events": [{
35+
"experimentIds": [
36+
"10420810910"
37+
],
38+
"id": "10404198134",
39+
"key": "winning"
40+
}],
41+
"revision": "1337"
42+
}

OptimizelySDK/Exceptions/OptimizelyException.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,13 @@ public InvalidRolloutException(string message)
105105
{
106106
}
107107
}
108+
109+
public class ConfigParseException : OptimizelyException
110+
{
111+
public ConfigParseException(string message)
112+
: base(message)
113+
{
114+
115+
}
116+
}
108117
}

OptimizelySDK/Optimizely.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using OptimizelySDK.ErrorHandler;
1919
using OptimizelySDK.Event.Builder;
2020
using OptimizelySDK.Event.Dispatcher;
21+
using OptimizelySDK.Exceptions;
2122
using OptimizelySDK.Logger;
2223
using OptimizelySDK.Utils;
2324
using OptimizelySDK.Notifications;
@@ -113,7 +114,14 @@ public Optimizely(string datafile,
113114
}
114115
catch (Exception ex)
115116
{
116-
Logger.Log(LogLevel.ERROR, "Provided 'datafile' is in an invalid format. " + ex.Message);
117+
string error = String.Empty;
118+
if (ex.GetType() == typeof(ConfigParseException))
119+
error = ex.Message;
120+
else
121+
error = "Provided 'datafile' is in an invalid format. " + ex.Message;
122+
123+
Logger.Log(LogLevel.ERROR, error);
124+
ErrorHandler.HandleError(ex);
117125
}
118126
}
119127

OptimizelySDK/ProjectConfig.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Newtonsoft.Json;
1717
using OptimizelySDK.Entity;
1818
using OptimizelySDK.ErrorHandler;
19+
using OptimizelySDK.Exceptions;
1920
using OptimizelySDK.Logger;
2021
using OptimizelySDK.Utils;
2122
using System.Collections.Generic;
@@ -25,6 +26,13 @@ namespace OptimizelySDK
2526
{
2627
public class ProjectConfig
2728
{
29+
public enum OPTLYSDKVersion
30+
{
31+
V2 = 2,
32+
V3 = 3,
33+
V4 = 4
34+
}
35+
2836
public const string RESERVED_ATTRIBUTE_PREFIX = "$opt_";
2937

3038
/// <summary>
@@ -60,6 +68,14 @@ public class ProjectConfig
6068
/// </summary>
6169
public bool? BotFiltering { get; set; }
6270

71+
72+
private static List<OPTLYSDKVersion> SupportedVersions = new List<OPTLYSDKVersion> {
73+
OPTLYSDKVersion.V2,
74+
OPTLYSDKVersion.V3,
75+
OPTLYSDKVersion.V4
76+
};
77+
78+
6379
//========================= Mappings ===========================
6480

6581
/// <summary>
@@ -270,7 +286,7 @@ private void Initialize()
270286

271287
public static ProjectConfig Create(string content, ILogger logger, IErrorHandler errorHandler)
272288
{
273-
ProjectConfig config = JsonConvert.DeserializeObject<ProjectConfig>(content);
289+
ProjectConfig config = GetConfig(content);
274290

275291
config.Logger = logger;
276292
config.ErrorHandler = errorHandler;
@@ -280,6 +296,21 @@ public static ProjectConfig Create(string content, ILogger logger, IErrorHandler
280296
return config;
281297
}
282298

299+
private static ProjectConfig GetConfig(string configData)
300+
{
301+
if (configData == null)
302+
throw new ConfigParseException("Unable to parse null datafile.");
303+
304+
if (string.IsNullOrEmpty(configData))
305+
throw new ConfigParseException("Unable to parse empty datafile.");
306+
307+
var config = JsonConvert.DeserializeObject<ProjectConfig>(configData);
308+
309+
if (SupportedVersions.TrueForAll((supportedVersion) => !(((int)supportedVersion).ToString() == config.Version)))
310+
throw new ConfigParseException(string.Format(@"This version of the C# SDK does not support the given datafile version: {0}", config.Version));
311+
312+
return config;
313+
}
283314

284315
//========================= Getters ===========================
285316

0 commit comments

Comments
 (0)