diff --git a/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj b/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
index e63b3923..dce9010a 100644
--- a/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
+++ b/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj
@@ -104,6 +104,7 @@
+
diff --git a/OptimizelySDK.Tests/OptimizelyTest.cs b/OptimizelySDK.Tests/OptimizelyTest.cs
index 490ef7e7..bab40b22 100644
--- a/OptimizelySDK.Tests/OptimizelyTest.cs
+++ b/OptimizelySDK.Tests/OptimizelyTest.cs
@@ -29,6 +29,7 @@
using OptimizelySDK.Notifications;
using OptimizelySDK.Tests.NotificationTests;
using OptimizelySDK.Utils;
+using Newtonsoft.Json;
namespace OptimizelySDK.Tests
{
@@ -185,7 +186,31 @@ public void TestValidateInputsInvalidFileJsonValidationSkipped()
{
string datafile = "{\"name\":\"optimizely\"}";
Optimizely optimizely = new Optimizely(datafile, null, null, null, skipJsonValidation: true);
- Assert.IsTrue(optimizely.IsValid);
+ Assert.IsFalse(optimizely.IsValid);
+ }
+
+ [Test]
+ public void TestErrorHandlingWithNullDatafile()
+ {
+ var optimizelyNullDatafile = new Optimizely(null, null, LoggerMock.Object, ErrorHandlerMock.Object, null, true);
+ LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Unable to parse null datafile."), Times.Once);
+ ErrorHandlerMock.Verify(e => e.HandleError(It.Is(ex => ex.Message == "Unable to parse null datafile.")), Times.Once);
+ }
+
+ [Test]
+ public void TestErrorHandlingWithEmptyDatafile()
+ {
+ var optimizelyEmptyDatafile = new Optimizely("", null, LoggerMock.Object, ErrorHandlerMock.Object, null, true);
+ LoggerMock.Verify(l => l.Log(LogLevel.ERROR, "Unable to parse empty datafile."), Times.Once);
+ ErrorHandlerMock.Verify(e => e.HandleError(It.Is(ex => ex.Message == "Unable to parse empty datafile.")), Times.Once);
+ }
+
+ [Test]
+ public void TestErrorHandlingWithUnsupportedConfigVersion()
+ {
+ var optimizelyUnsupportedVersion = new Optimizely(TestData.UnsupportedVersionDatafile, null, LoggerMock.Object, ErrorHandlerMock.Object, null, true);
+ LoggerMock.Verify(l => l.Log(LogLevel.ERROR, $"This version of the C# SDK does not support the given datafile version: 5"), Times.Once);
+ ErrorHandlerMock.Verify(e => e.HandleError(It.Is(ex => ex.Message == $"This version of the C# SDK does not support the given datafile version: 5")), Times.Once);
}
[Test]
diff --git a/OptimizelySDK.Tests/ProjectConfigTest.cs b/OptimizelySDK.Tests/ProjectConfigTest.cs
index 8dece0f8..f2b4381e 100644
--- a/OptimizelySDK.Tests/ProjectConfigTest.cs
+++ b/OptimizelySDK.Tests/ProjectConfigTest.cs
@@ -863,5 +863,32 @@ public void TestGetAttributeIdWithInvalidAttributeKey()
Assert.Null(Config.GetAttributeId("invalid_attribute"));
LoggerMock.Verify(l => l.Log(LogLevel.ERROR, @"Attribute key ""invalid_attribute"" is not in datafile."));
}
+
+ [Test]
+ public void TestCreateThrowsWithNullDatafile()
+ {
+ var exception = Assert.Throws(() => ProjectConfig.Create(null, null, null));
+ Assert.AreEqual("Unable to parse null datafile.", exception.Message);
+ }
+
+ [Test]
+ public void TestCreateThrowsWithEmptyDatafile()
+ {
+ var exception = Assert.Throws(() => ProjectConfig.Create("", null, null));
+ Assert.AreEqual("Unable to parse empty datafile.", exception.Message);
+ }
+
+ [Test]
+ public void TestCreateThrowsWithUnsupportedDatafileVersion()
+ {
+ var exception = Assert.Throws(() => ProjectConfig.Create(TestData.UnsupportedVersionDatafile, null, null));
+ Assert.AreEqual($"This version of the C# SDK does not support the given datafile version: 5", exception.Message);
+ }
+
+ [Test]
+ public void TestCreateDoesNotThrowWithValidDatafile()
+ {
+ Assert.DoesNotThrow(() => ProjectConfig.Create(TestData.Datafile, null, null));
+ }
}
}
diff --git a/OptimizelySDK.Tests/TestData.cs b/OptimizelySDK.Tests/TestData.cs
index ab4e6302..0bee0dbf 100644
--- a/OptimizelySDK.Tests/TestData.cs
+++ b/OptimizelySDK.Tests/TestData.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2017, Optimizely
+ * Copyright 2017-2018, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ public class TestData
{
private static string cachedDataFile = null;
private static string simpleABExperimentsDatafile = null;
+ private static string unsupportedVersionDatafile = null;
public static string Datafile
{
@@ -41,7 +42,15 @@ public static string SimpleABExperimentsDatafile
return simpleABExperimentsDatafile ?? (simpleABExperimentsDatafile = LoadJsonData("simple_ab_experiments.json"));
}
}
-
+
+ public static string UnsupportedVersionDatafile
+ {
+ get
+ {
+ return unsupportedVersionDatafile ?? (unsupportedVersionDatafile = LoadJsonData("unsupported_version_datafile.json"));
+ }
+ }
+
private static string LoadJsonData(string fileName = "TestData.json")
{
var assembly = Assembly.GetExecutingAssembly();
diff --git a/OptimizelySDK.Tests/unsupported_version_datafile.json b/OptimizelySDK.Tests/unsupported_version_datafile.json
new file mode 100644
index 00000000..4830b21f
--- /dev/null
+++ b/OptimizelySDK.Tests/unsupported_version_datafile.json
@@ -0,0 +1,42 @@
+{
+ "version": "5",
+ "rollouts": [],
+ "projectId": "10431130345",
+ "variables": [],
+ "featureFlags": [],
+ "experiments": [{
+ "status": "Running",
+ "key": "ab_running_exp_untargeted",
+ "layerId": "10417730432",
+ "trafficAllocation": [{
+ "entityId": "10418551353",
+ "endOfRange": 10000
+ }],
+ "audienceIds": [],
+ "variations": [{
+ "variables": [],
+ "id": "10418551353",
+ "key": "all_traffic_variation"
+ },
+ {
+ "variables": [],
+ "id": "10418510624",
+ "key": "no_traffic_variation"
+ }
+ ],
+ "forcedVariations": {},
+ "id": "10420810910"
+ }],
+ "audiences": [],
+ "groups": [],
+ "attributes": [],
+ "accountId": "10367498574",
+ "events": [{
+ "experimentIds": [
+ "10420810910"
+ ],
+ "id": "10404198134",
+ "key": "winning"
+ }],
+ "revision": "1337"
+}
\ No newline at end of file
diff --git a/OptimizelySDK/Exceptions/OptimizelyException.cs b/OptimizelySDK/Exceptions/OptimizelyException.cs
index a3770aaf..56c7b3ed 100644
--- a/OptimizelySDK/Exceptions/OptimizelyException.cs
+++ b/OptimizelySDK/Exceptions/OptimizelyException.cs
@@ -105,4 +105,13 @@ public InvalidRolloutException(string message)
{
}
}
+
+ public class ConfigParseException : OptimizelyException
+ {
+ public ConfigParseException(string message)
+ : base(message)
+ {
+
+ }
+ }
}
\ No newline at end of file
diff --git a/OptimizelySDK/Optimizely.cs b/OptimizelySDK/Optimizely.cs
index 62df0dd3..182cf9e3 100644
--- a/OptimizelySDK/Optimizely.cs
+++ b/OptimizelySDK/Optimizely.cs
@@ -18,6 +18,7 @@
using OptimizelySDK.ErrorHandler;
using OptimizelySDK.Event.Builder;
using OptimizelySDK.Event.Dispatcher;
+using OptimizelySDK.Exceptions;
using OptimizelySDK.Logger;
using OptimizelySDK.Utils;
using OptimizelySDK.Notifications;
@@ -113,7 +114,14 @@ public Optimizely(string datafile,
}
catch (Exception ex)
{
- Logger.Log(LogLevel.ERROR, "Provided 'datafile' is in an invalid format. " + ex.Message);
+ string error = String.Empty;
+ if (ex.GetType() == typeof(ConfigParseException))
+ error = ex.Message;
+ else
+ error = "Provided 'datafile' is in an invalid format. " + ex.Message;
+
+ Logger.Log(LogLevel.ERROR, error);
+ ErrorHandler.HandleError(ex);
}
}
diff --git a/OptimizelySDK/ProjectConfig.cs b/OptimizelySDK/ProjectConfig.cs
index 54f5c45a..c6a5c0db 100644
--- a/OptimizelySDK/ProjectConfig.cs
+++ b/OptimizelySDK/ProjectConfig.cs
@@ -16,6 +16,7 @@
using Newtonsoft.Json;
using OptimizelySDK.Entity;
using OptimizelySDK.ErrorHandler;
+using OptimizelySDK.Exceptions;
using OptimizelySDK.Logger;
using OptimizelySDK.Utils;
using System.Collections.Generic;
@@ -25,6 +26,13 @@ namespace OptimizelySDK
{
public class ProjectConfig
{
+ public enum OPTLYSDKVersion
+ {
+ V2 = 2,
+ V3 = 3,
+ V4 = 4
+ }
+
public const string RESERVED_ATTRIBUTE_PREFIX = "$opt_";
///
@@ -60,6 +68,14 @@ public class ProjectConfig
///
public bool? BotFiltering { get; set; }
+
+ private static List SupportedVersions = new List {
+ OPTLYSDKVersion.V2,
+ OPTLYSDKVersion.V3,
+ OPTLYSDKVersion.V4
+ };
+
+
//========================= Mappings ===========================
///
@@ -270,7 +286,7 @@ private void Initialize()
public static ProjectConfig Create(string content, ILogger logger, IErrorHandler errorHandler)
{
- ProjectConfig config = JsonConvert.DeserializeObject(content);
+ ProjectConfig config = GetConfig(content);
config.Logger = logger;
config.ErrorHandler = errorHandler;
@@ -280,6 +296,21 @@ public static ProjectConfig Create(string content, ILogger logger, IErrorHandler
return config;
}
+ private static ProjectConfig GetConfig(string configData)
+ {
+ if (configData == null)
+ throw new ConfigParseException("Unable to parse null datafile.");
+
+ if (string.IsNullOrEmpty(configData))
+ throw new ConfigParseException("Unable to parse empty datafile.");
+
+ var config = JsonConvert.DeserializeObject(configData);
+
+ if (SupportedVersions.TrueForAll((supportedVersion) => !(((int)supportedVersion).ToString() == config.Version)))
+ throw new ConfigParseException(string.Format(@"This version of the C# SDK does not support the given datafile version: {0}", config.Version));
+
+ return config;
+ }
//========================= Getters ===========================