diff --git a/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj b/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj index 8f9b63b5..376c3828 100644 --- a/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj +++ b/OptimizelySDK.Net40/OptimizelySDK.Net40.csproj @@ -39,6 +39,7 @@ + @@ -230,6 +231,9 @@ Config\PollingProjectConfigManager + + + ClientConfigHandler Config\HttpProjectConfigManager diff --git a/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj b/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj index 35e8e8b6..b4ca1a8a 100644 --- a/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj +++ b/OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -72,6 +72,9 @@ Config\ProjectConfigManager.cs + + + Config\ClientConfigHandler.cs Entity\Attribute.cs @@ -257,6 +260,7 @@ + diff --git a/OptimizelySDK.Tests/App.config b/OptimizelySDK.Tests/App.config index e629f34e..efdfcc4f 100644 --- a/OptimizelySDK.Tests/App.config +++ b/OptimizelySDK.Tests/App.config @@ -4,10 +4,31 @@ that apply only to the Test project. --> - - + +
+ + + + + + + + + + + diff --git a/OptimizelySDK.Tests/ClientConfigHandlerTest.cs b/OptimizelySDK.Tests/ClientConfigHandlerTest.cs new file mode 100644 index 00000000..be8adda6 --- /dev/null +++ b/OptimizelySDK.Tests/ClientConfigHandlerTest.cs @@ -0,0 +1,57 @@ +/* + * Copyright 2019, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !NETSTANDARD1_6 && !NET35 + +using NUnit.Framework; +using System.Configuration; + +namespace OptimizelySDK.Tests +{ + [TestFixture] + public class ClientConfigHandlerTest + { + + [Test] + public void TestHTTPAppConfigSection() + { + var configSection = ConfigurationManager.GetSection("optlySDKConfigSection") as OptimizelySDKConfigSection; + var httpSetting = configSection.HttpProjectConfig; + Assert.IsNotNull(httpSetting); + Assert.IsTrue(httpSetting.AutoUpdate); + Assert.AreEqual(httpSetting.BlockingTimeOutPeriod, 10000); + Assert.AreEqual(httpSetting.Format, "https://cdn.optimizely.com/data/{0}.json"); + Assert.IsTrue(httpSetting.DefaultStart); + Assert.AreEqual(httpSetting.PollingInterval, 2000); + Assert.AreEqual(httpSetting.SDKKey, "43214321"); + Assert.AreEqual(httpSetting.Url, "www.testurl.com"); + } + + [Test] + public void TestBatchEventAppConfigSection() + { + var configSection = ConfigurationManager.GetSection("optlySDKConfigSection") as OptimizelySDKConfigSection; + var batchSetting = configSection.BatchEventProcessor; + Assert.IsNotNull(batchSetting); + Assert.AreEqual(batchSetting.BatchSize, 10); + Assert.AreEqual(batchSetting.FlushInterval, 2000); + Assert.AreEqual(batchSetting.TimeoutInterval, 10000); + Assert.IsTrue(batchSetting.DefaultStart); + } + + } +} +#endif diff --git a/OptimizelySDK.Tests/ConfigTest/HttpProjectConfigManagerTest.cs b/OptimizelySDK.Tests/ConfigTest/HttpProjectConfigManagerTest.cs index b7bea2e3..ffe3229d 100644 --- a/OptimizelySDK.Tests/ConfigTest/HttpProjectConfigManagerTest.cs +++ b/OptimizelySDK.Tests/ConfigTest/HttpProjectConfigManagerTest.cs @@ -55,6 +55,22 @@ public void TestHttpConfigManagerRetreiveProjectConfigByURL() Assert.NotNull(httpManager.GetConfig()); } + [Test] + public void TestHttpConfigManagerRetreiveProjectConfigGivenEmptyFormatUseDefaultFormat() + { + HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder() + .WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z") + .WithFormat("") + .WithLogger(LoggerMock.Object) + .WithPollingInterval(TimeSpan.FromMilliseconds(1000)) + .WithBlockingTimeoutPeriod(TimeSpan.FromMilliseconds(500)) + .WithStartByDefault() + .Build(); + + httpManager.OnReady().Wait(System.Threading.Timeout.Infinite); + Assert.NotNull(httpManager.GetConfig()); + } + [Test] public void TestHttpConfigManagerRetreiveProjectConfigBySDKKey() { diff --git a/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj b/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj index 85e05edc..c777eb91 100644 --- a/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj +++ b/OptimizelySDK.Tests/OptimizelySDK.Tests.csproj @@ -86,6 +86,7 @@ + diff --git a/OptimizelySDK/ClientConfigHandler.cs b/OptimizelySDK/ClientConfigHandler.cs new file mode 100644 index 00000000..a32bea97 --- /dev/null +++ b/OptimizelySDK/ClientConfigHandler.cs @@ -0,0 +1,108 @@ +/* + * Copyright 2019, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Configuration; + +namespace OptimizelySDK +{ + public class HttpProjectConfigElement : ConfigurationElement + { + [ConfigurationProperty("sdkKey", IsRequired = true, IsKey = true)] + public string SDKKey + { + get { return (string)base["sdkKey"]; } + } + + [ConfigurationProperty("url")] + public string Url + { + get { return (string)base["url"]; } + } + + [ConfigurationProperty("format")] + public string Format + { + get { return (string)base["format"]; } + } + + [ConfigurationProperty("pollingInterval")] + public int PollingInterval + { + get { return base["pollingInterval"] is int ? (int)base["pollingInterval"] : 0; } + } + + [ConfigurationProperty("blockingTimeOutPeriod")] + public int BlockingTimeOutPeriod + { + get { return base["blockingTimeOutPeriod"] is int ? (int)base["blockingTimeOutPeriod"] : 0; } + } + + [ConfigurationProperty("autoUpdate")] + public bool AutoUpdate + { + get { return (bool)base["autoUpdate"]; } + } + + [ConfigurationProperty("defaultStart")] + public bool DefaultStart + { + get { return (bool)base["defaultStart"]; } + } + } + + public class BatchEventProcessorElement : ConfigurationElement + { + [ConfigurationProperty("batchSize")] + public int BatchSize + { + get { return (int)base["batchSize"]; } + } + + [ConfigurationProperty("flushInterval")] + public int FlushInterval + { + get { return base["flushInterval"] is int ? (int)base["flushInterval"] : 0; } + } + + [ConfigurationProperty("timeoutInterval")] + public int TimeoutInterval + { + get { return base["timeoutInterval"] is int ? (int)base["timeoutInterval"] : 0; } + } + + [ConfigurationProperty("defaultStart")] + public bool DefaultStart + { + get { return (bool)base["defaultStart"]; } + } + } + + public class OptimizelySDKConfigSection : ConfigurationSection + { + [ConfigurationProperty("HttpProjectConfig")] + public HttpProjectConfigElement HttpProjectConfig + { + get { return (HttpProjectConfigElement)base["HttpProjectConfig"]; } + set { base["HttpProjectConfig"] = value; } + } + + [ConfigurationProperty("BatchEventProcessor")] + public BatchEventProcessorElement BatchEventProcessor { + get { return (BatchEventProcessorElement)(base["BatchEventProcessor"]); } + set { base["BatchEventProcessor"] = value; } + } + } +} diff --git a/OptimizelySDK/Config/HttpProjectConfigManager.cs b/OptimizelySDK/Config/HttpProjectConfigManager.cs index 8768a69e..22a0d20d 100644 --- a/OptimizelySDK/Config/HttpProjectConfigManager.cs +++ b/OptimizelySDK/Config/HttpProjectConfigManager.cs @@ -124,11 +124,12 @@ public class Builder private const long MAX_MILLISECONDS_LIMIT = 4294967294; private readonly TimeSpan DEFAULT_PERIOD = TimeSpan.FromMinutes(5); private readonly TimeSpan DEFAULT_BLOCKINGOUT_PERIOD = TimeSpan.FromSeconds(15); + private readonly string DEFAULT_FORMAT = "https://cdn.optimizely.com/datafiles/{0}.json"; private string Datafile; private string SdkKey; private string Url; - private string Format = "https://cdn.optimizely.com/datafiles/{0}.json"; + private string Format; private ILogger Logger; private IErrorHandler ErrorHandler; private TimeSpan Period; @@ -247,6 +248,10 @@ public HttpProjectConfigManager Build(bool defer) if (ErrorHandler == null) ErrorHandler = new DefaultErrorHandler(); + if (string.IsNullOrEmpty(Format)) { + Format = DEFAULT_FORMAT; + } + if (string.IsNullOrEmpty(Url) && string.IsNullOrEmpty(SdkKey)) { ErrorHandler.HandleError(new Exception("SdkKey cannot be null")); diff --git a/OptimizelySDK/OptimizelyFactory.cs b/OptimizelySDK/OptimizelyFactory.cs index 559646e7..e077b412 100644 --- a/OptimizelySDK/OptimizelyFactory.cs +++ b/OptimizelySDK/OptimizelyFactory.cs @@ -14,6 +14,10 @@ * limitations under the License. */ using System; +#if !NETSTANDARD1_6 && !NET35 +using System.Configuration; +#endif + using OptimizelySDK.Bucketing; using OptimizelySDK.Config; using OptimizelySDK.ErrorHandler; @@ -32,6 +36,7 @@ public static class OptimizelyFactory private static int MaxEventBatchSize; private static TimeSpan MaxEventFlushInterval; private static ILogger OptimizelyLogger; + private const string ConfigSectionName = "optlySDKConfigSection"; #if !NETSTANDARD1_6 && !NET35 public static void SetBatchSize(int batchSize) @@ -48,7 +53,61 @@ public static void SetLogger(ILogger logger) { OptimizelyLogger = logger; } + + public static Optimizely NewDefaultInstance() + { + var logger = OptimizelyLogger ?? new DefaultLogger(); + OptimizelySDKConfigSection OptlySDKConfigSection = null; + try + { + OptlySDKConfigSection = ConfigurationManager.GetSection(ConfigSectionName) as OptimizelySDKConfigSection; + } + catch (ConfigurationErrorsException ex) + { + logger.Log(LogLevel.ERROR, "Invalid App.Config. Unable to initialize optimizely instance" + ex.Message); + return null; + } + + HttpProjectConfigElement httpProjectConfigElement = OptlySDKConfigSection.HttpProjectConfig; + + if (httpProjectConfigElement == null) return null; + + var errorHandler = new DefaultErrorHandler(); + var eventDispatcher = new DefaultEventDispatcher(logger); + var builder = new HttpProjectConfigManager.Builder(); + var notificationCenter = new NotificationCenter(); + + var configManager = builder + .WithSdkKey(httpProjectConfigElement.SDKKey) + .WithUrl(httpProjectConfigElement.Url) + .WithFormat(httpProjectConfigElement.Format) + .WithPollingInterval(TimeSpan.FromMilliseconds(httpProjectConfigElement.PollingInterval)) + .WithBlockingTimeoutPeriod(TimeSpan.FromMilliseconds(httpProjectConfigElement.BlockingTimeOutPeriod)) + .WithLogger(logger) + .WithErrorHandler(errorHandler) + .WithNotificationCenter(notificationCenter) + .Build(true); + + EventProcessor eventProcessor = null; + + var batchEventProcessorElement = OptlySDKConfigSection.BatchEventProcessor; + + if (batchEventProcessorElement == null) return null; + + eventProcessor = new BatchEventProcessor.Builder() + .WithMaxBatchSize(batchEventProcessorElement.BatchSize) + .WithFlushInterval(TimeSpan.FromMilliseconds(batchEventProcessorElement.FlushInterval)) + .WithTimeoutInterval(TimeSpan.FromMilliseconds(batchEventProcessorElement.TimeoutInterval)) + .WithLogger(logger) + .WithEventDispatcher(eventDispatcher) + .WithNotificationCenter(notificationCenter) + .Build(); + + return NewDefaultInstance(configManager, notificationCenter, eventDispatcher, errorHandler, logger, eventProcessor: eventProcessor); + + } #endif + public static Optimizely NewDefaultInstance(string sdkKey) { return NewDefaultInstance(sdkKey, null); @@ -70,12 +129,6 @@ public static Optimizely NewDefaultInstance(string sdkKey, string fallback) .WithNotificationCenter(notificationCenter) .Build(true); - return NewDefaultInstance(configManager, notificationCenter, eventDispatcher, errorHandler, logger); - } - - public static Optimizely NewDefaultInstance(ProjectConfigManager configManager, NotificationCenter notificationCenter = null, IEventDispatcher eventDispatcher = null, - IErrorHandler errorHandler = null, ILogger logger = null, UserProfileService userprofileService = null) - { EventProcessor eventProcessor = null; #if !NETSTANDARD1_6 && !NET35 @@ -87,6 +140,13 @@ public static Optimizely NewDefaultInstance(ProjectConfigManager configManager, .WithNotificationCenter(notificationCenter) .Build(); #endif + + return NewDefaultInstance(configManager, notificationCenter, eventDispatcher, errorHandler, logger, eventProcessor: eventProcessor); + } + + public static Optimizely NewDefaultInstance(ProjectConfigManager configManager, NotificationCenter notificationCenter = null, IEventDispatcher eventDispatcher = null, + IErrorHandler errorHandler = null, ILogger logger = null, UserProfileService userprofileService = null, EventProcessor eventProcessor = null) + { return new Optimizely(configManager, notificationCenter, eventDispatcher, logger, errorHandler, userprofileService, eventProcessor); } } diff --git a/OptimizelySDK/OptimizelySDK.csproj b/OptimizelySDK/OptimizelySDK.csproj index ebda9ab5..2d1593be 100644 --- a/OptimizelySDK/OptimizelySDK.csproj +++ b/OptimizelySDK/OptimizelySDK.csproj @@ -51,6 +51,7 @@ True + @@ -70,6 +71,7 @@ + diff --git a/README.md b/README.md index a14d612c..68c4cf35 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,42 @@ You can also provide default datafile with the SDK key. ``` Optimizely optimizely = OptimizelyFactory.newDefaultInstance(<>, <>); ``` +##### Using App.config in OptimizelyFactory + +OptimizelyFactory provides support of setting configuration variables in App.config: +User can provide variables using following procedure: +1. In App.config file of your project in **** add following: +``` + +
+ +``` +2. Now add **optlySDKConfigSection** below ****. In this section you can add and set following **HttpProjectConfigManager** and **BatchEventProcessor** variables: +``` + + + + + + + + + +``` +3. After setting these variables you can instantiate the Optimizely SDK using function: +``` +Optimizely optimizely = OptimizelyFactory.newDefaultInstance(); +``` #### HttpProjectConfigManager