Skip to content

Commit 38b3df3

Browse files
msohailhussainMichael Ng
authored and
Michael Ng
committed
3.2.x release (#177)
1 parent a2727e9 commit 38b3df3

File tree

13 files changed

+191
-37
lines changed

13 files changed

+191
-37
lines changed

CHANGELOG.md

+14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## 3.2.0
2+
July 22nd, 2019
3+
4+
### New Features:
5+
* Added support for automatic datafile management via `HttpProjectConfigManager` for framework 4.0 or above:
6+
* The [`HttpProjectConfigManager`](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Config/HttpProjectConfigManager.cs) is an implementation of the abstract
7+
[`PollingProjectConfigManager`](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Config/PollingProjectConfigManager.cs) class.
8+
- Users must first build the `HttpProjectConfigManager` with an SDK key and then and provide that instance to the `Optimizely`.
9+
- An initial datafile can be provided to the `HttpProjectConfigManager` to bootstrap before making http requests for the hosted datafile.
10+
- Requests for the datafile are made in a separate thread and are scheduled with fixed delay.
11+
- Configuration updates can be subscribed to via the NotificationCenter built with the `HttpProjectConfigManager`.
12+
- `Optimizely` instance must be disposed after the use or `HttpProjectConfigManager` must be disposed after the use to release resources.
13+
- The [`OptimizelyFactory`](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/OptimizelyFactory.cs) provides basic methods for instantiating the Optimizely SDK with a minimal number of parameters. Check [`README.md`](https://github.com/optimizely/csharp-sdk#use-optimizelyfactory) for more details.
14+
115
## 3.1.1
216
June 19th, 2019
317

OptimizelySDK.DemoApp/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@
3737
//
3838
// You can specify all the values or you can default the Revision and Build Numbers
3939
// by using the '*' as shown below:
40-
[assembly: AssemblyVersion("3.1.1.0")]
41-
[assembly: AssemblyFileVersion("3.1.1.0")]
42-
[assembly: AssemblyInformationalVersion("3.1.1")] // Used by Nuget.
40+
[assembly: AssemblyVersion("3.2.0.0")]
41+
[assembly: AssemblyFileVersion("3.2.0.0")]
42+
[assembly: AssemblyInformationalVersion("3.2.0")] // Used by Nuget.

OptimizelySDK.DemoApp/packages.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<package id="murmurhash-signed" version="1.0.2" targetFramework="net45" />
1818
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
1919
<package id="NJsonSchema" version="8.30.6304.31883" targetFramework="net45" />
20-
<package id="Optimizely.SDK" version="3.1.0" targetFramework="net45" />
20+
<package id="Optimizely.SDK" version="3.2.0" targetFramework="net45" />
2121
<package id="popper.js" version="1.12.9" targetFramework="net45" />
2222
<package id="Respond" version="1.2.0" targetFramework="net45" />
2323
<package id="WebGrease" version="1.5.2" targetFramework="net45" />

OptimizelySDK.Net35/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
// You can specify all the values or you can default the Build and Revision Numbers
3939
// by using the '*' as shown below:
4040
// [assembly: AssemblyVersion("1.0.*")]
41-
[assembly: AssemblyVersion("3.1.1.0")]
42-
[assembly: AssemblyFileVersion("3.1.1.0")]
43-
[assembly: AssemblyInformationalVersion("3.1.1")] // Used by Nuget.
41+
[assembly: AssemblyVersion("3.2.0.0")]
42+
[assembly: AssemblyFileVersion("3.2.0.0")]
43+
[assembly: AssemblyInformationalVersion("3.2.0")] // Used by Nuget.

OptimizelySDK.Net40/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
// You can specify all the values or you can default the Build and Revision Numbers
3939
// by using the '*' as shown below:
4040
// [assembly: AssemblyVersion("1.0.*")]
41-
[assembly: AssemblyVersion("3.1.1.0")]
42-
[assembly: AssemblyFileVersion("3.1.1.0")]
43-
[assembly: AssemblyInformationalVersion("3.1.1")] // Used by Nuget.
41+
[assembly: AssemblyVersion("3.2.0.0")]
42+
[assembly: AssemblyFileVersion("3.2.0.0")]
43+
[assembly: AssemblyInformationalVersion("3.2.0")] // Used by Nuget.

OptimizelySDK.NetStandard16/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
// You can specify all the values or you can default the Build and Revision Numbers
3939
// by using the '*' as shown below:
4040
// [assembly: AssemblyVersion("1.0.*")]
41-
[assembly: AssemblyVersion("3.1.1.0")]
42-
[assembly: AssemblyFileVersion("3.1.1.0")]
43-
[assembly: AssemblyInformationalVersion("3.1.1")] // Used by Nuget.
41+
[assembly: AssemblyVersion("3.2.0.0")]
42+
[assembly: AssemblyFileVersion("3.2.0.0")]
43+
[assembly: AssemblyInformationalVersion("3.2.0")] // Used by Nuget.

OptimizelySDK.Package/OptimizelySDK.nuspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<package>
33
<metadata>
44
<id>Optimizely.SDK</id>
5-
<version>3.1.0</version>
5+
<version>3.2.0</version>
66
<title>Optimizely C# SDK</title>
77
<authors>Optimizely Development Team</authors>
88
<owners>fullstack.optimizely</owners>

OptimizelySDK.Tests/ConfigTest/HttpProjectConfigManagerTest.cs

+50-3
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ public void TestHttpConfigManagerDoesNotWaitForTheConfigWhenDeferIsTrue()
130130
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
131131
.WithLogger(LoggerMock.Object)
132132
.WithPollingInterval(TimeSpan.FromSeconds(2))
133-
.WithBlockingTimeoutPeriod(TimeSpan.FromSeconds(0))
133+
// negligible timeout
134+
.WithBlockingTimeoutPeriod(TimeSpan.FromMilliseconds(50))
134135
.WithStartByDefault()
135136
.Build(false);
136137

@@ -176,6 +177,52 @@ public void TestHttpConfigManagerDoesNotSendConfigUpdateNotificationWhenDatafile
176177
NotificationCallbackMock.Verify(nc => nc.TestConfigUpdateCallback(), Times.Never);
177178
Assert.NotNull(httpManager.GetConfig()); Assert.NotNull(httpManager.GetConfig());
178179
}
179-
#endregion
180-
}
180+
181+
[Test]
182+
public void TestDefaultBlockingTimeoutWhileProvidingZero()
183+
{
184+
var httpManager = new HttpProjectConfigManager.Builder()
185+
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
186+
.WithDatafile(TestData.Datafile)
187+
.WithLogger(LoggerMock.Object)
188+
.WithPollingInterval(TimeSpan.FromMilliseconds(1000))
189+
.WithBlockingTimeoutPeriod(TimeSpan.FromMilliseconds(0))
190+
.WithStartByDefault(true)
191+
.Build(true);
192+
193+
LoggerMock.Verify(l => l.Log(LogLevel.INFO, $"Blocking timeout is not valid, using default blocking timeout {TimeSpan.FromSeconds(15).TotalMilliseconds}ms"));
194+
}
195+
196+
[Test]
197+
public void TestDefaultPeriodWhileProvidingZero()
198+
{
199+
var httpManager = new HttpProjectConfigManager.Builder()
200+
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
201+
.WithDatafile(TestData.Datafile)
202+
.WithLogger(LoggerMock.Object)
203+
.WithPollingInterval(TimeSpan.FromMilliseconds(0))
204+
.WithBlockingTimeoutPeriod(TimeSpan.FromMilliseconds(1000))
205+
.WithStartByDefault(true)
206+
.Build(true);
207+
208+
LoggerMock.Verify(l => l.Log(LogLevel.INFO, $"Period is not valid for periodic calls, using default period {TimeSpan.FromMinutes(5).TotalMilliseconds}ms"));
209+
}
210+
211+
[Test]
212+
public void TestDefaultPeriodWhileProvidingNegative()
213+
{
214+
var httpManager = new HttpProjectConfigManager.Builder()
215+
.WithSdkKey("QBw9gFM8oTn7ogY9ANCC1z")
216+
.WithDatafile(TestData.Datafile)
217+
.WithLogger(LoggerMock.Object)
218+
.WithPollingInterval(TimeSpan.FromMilliseconds(-1))
219+
.WithBlockingTimeoutPeriod(TimeSpan.FromMilliseconds(1000))
220+
.WithStartByDefault(true)
221+
.Build(true);
222+
223+
LoggerMock.Verify(l => l.Log(LogLevel.INFO, $"Period is not valid for periodic calls, using default period {TimeSpan.FromMinutes(5).TotalMilliseconds}ms"));
224+
}
225+
226+
#endregion
227+
}
181228
}

OptimizelySDK.Tests/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@
3131
//
3232
// You can specify all the values or you can default the Revision and Build Numbers
3333
// by using the '*' as shown below:
34-
[assembly: AssemblyVersion("3.1.1.0")]
35-
[assembly: AssemblyFileVersion("3.1.1.0")]
36-
[assembly: AssemblyInformationalVersion("3.1.1")] // Used by Nuget.
34+
[assembly: AssemblyVersion("3.2.0.0")]
35+
[assembly: AssemblyFileVersion("3.2.0.0")]
36+
[assembly: AssemblyInformationalVersion("3.2.0")] // Used by Nuget.

OptimizelySDK/Config/HttpProjectConfigManager.cs

+31-5
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,18 @@ protected override ProjectConfig Poll()
122122

123123
public class Builder
124124
{
125+
private const long MAX_MILLISECONDS_LIMIT = 4294967294;
126+
private readonly TimeSpan DEFAULT_PERIOD = TimeSpan.FromMinutes(5);
127+
private readonly TimeSpan DEFAULT_BLOCKINGOUT_PERIOD = TimeSpan.FromSeconds(15);
128+
125129
private string Datafile;
126130
private string SdkKey;
127131
private string Url;
128132
private string Format = "https://cdn.optimizely.com/datafiles/{0}.json";
129133
private ILogger Logger;
130134
private IErrorHandler ErrorHandler;
131-
private TimeSpan Period = TimeSpan.FromMinutes(5);
132-
private TimeSpan BlockingTimeoutSpan = TimeSpan.FromSeconds(15);
135+
private TimeSpan Period;
136+
private TimeSpan BlockingTimeoutSpan;
133137
private bool AutoUpdate = true;
134138
private bool StartByDefault;
135139
private NotificationCenter NotificationCenter;
@@ -143,42 +147,49 @@ public Builder WithBlockingTimeoutPeriod(TimeSpan blockingTimeoutSpan)
143147
public Builder WithDatafile(string datafile)
144148
{
145149
Datafile = datafile;
150+
146151
return this;
147152
}
148153

149154
public Builder WithSdkKey(string sdkKey)
150155
{
151156
SdkKey = sdkKey;
157+
152158
return this;
153159
}
154160

155161
public Builder WithUrl(string url)
156162
{
157163
Url = url;
164+
158165
return this;
159166
}
160167

161168
public Builder WithPollingInterval(TimeSpan period)
162-
{
169+
{
163170
Period = period;
171+
164172
return this;
165173
}
166174

167175
public Builder WithFormat(string format)
168176
{
169177
Format = format;
178+
170179
return this;
171180
}
172181

173182
public Builder WithLogger(ILogger logger)
174183
{
175184
Logger = logger;
185+
176186
return this;
177187
}
178188

179189
public Builder WithErrorHandler(IErrorHandler errorHandler)
180190
{
181191
ErrorHandler = errorHandler;
192+
182193
return this;
183194
}
184195

@@ -222,20 +233,35 @@ public HttpProjectConfigManager Build()
222233
public HttpProjectConfigManager Build(bool defer)
223234
{
224235
HttpProjectConfigManager configManager = null;
236+
225237
if (Logger == null)
226238
Logger = new DefaultLogger();
227239

240+
if (ErrorHandler == null)
241+
ErrorHandler = new DefaultErrorHandler();
242+
228243
if (string.IsNullOrEmpty(Url) && string.IsNullOrEmpty(SdkKey))
229244
{
230-
ErrorHandler.HandleError(new Exception("SdkKey cannot be null"));
231-
throw new Exception("SdkKey cannot be null");
245+
ErrorHandler.HandleError(new Exception("SdkKey cannot be null"));
232246
}
233247
else if (!string.IsNullOrEmpty(SdkKey))
234248
{
235249
Url = string.Format(Format, SdkKey);
236250
}
251+
252+
if (Period.TotalMilliseconds <= 0 || Period.TotalMilliseconds > MAX_MILLISECONDS_LIMIT) {
253+
Logger.Log(LogLevel.INFO, $"Period is not valid for periodic calls, using default period {DEFAULT_PERIOD.TotalMilliseconds}ms");
254+
Period = DEFAULT_PERIOD;
255+
}
237256

238257

258+
if (BlockingTimeoutSpan.TotalMilliseconds <= 0 || BlockingTimeoutSpan.TotalMilliseconds > MAX_MILLISECONDS_LIMIT) {
259+
Logger.Log(LogLevel.INFO, $"Blocking timeout is not valid, using default blocking timeout {DEFAULT_BLOCKINGOUT_PERIOD.TotalMilliseconds}ms");
260+
BlockingTimeoutSpan = DEFAULT_BLOCKINGOUT_PERIOD;
261+
}
262+
263+
264+
239265
configManager = new HttpProjectConfigManager(Period, Url, BlockingTimeoutSpan, AutoUpdate, Logger, ErrorHandler);
240266

241267
if (Datafile != null)

OptimizelySDK/Config/PollingProjectConfigManager.cs

+8-9
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,7 @@ public ProjectConfig GetConfig()
136136
/// <param name="projectConfig">ProjectConfig</param>
137137
/// <returns>true if the ProjectConfig saved successfully, false otherwise</returns>
138138
public bool SetConfig(ProjectConfig projectConfig)
139-
{
140-
// trigger now, due because of delayed latency response
141-
if (scheduleWhenFinished && IsStarted) {
142-
// Can't directly call Run, it will be part of previous thread then.
143-
// Call immediately, because it's due now.
144-
scheduleWhenFinished = false;
145-
SchedulerService.Change(TimeSpan.FromSeconds(0), PollingInterval);
146-
}
147-
139+
{
148140
if (projectConfig == null)
149141
return false;
150142

@@ -191,6 +183,13 @@ public virtual void Run()
191183
Logger.Log(LogLevel.ERROR, "Unable to get project config. Error: " + exception.GetAllMessages());
192184
} finally {
193185
Interlocked.Exchange(ref resourceInUse, 0);
186+
187+
// trigger now, due because of delayed latency response
188+
if (scheduleWhenFinished && IsStarted) {
189+
// Call immediately, because it's due now.
190+
scheduleWhenFinished = false;
191+
SchedulerService.Change(TimeSpan.FromSeconds(0), PollingInterval);
192+
}
194193
}
195194
}
196195
else {

OptimizelySDK/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
// You can specify all the values or you can default the Build and Revision Numbers
3939
// by using the '*' as shown below:
4040
// [assembly: AssemblyVersion("1.0.*")]
41-
[assembly: AssemblyVersion("3.1.1.0")]
42-
[assembly: AssemblyFileVersion("3.1.1.0")]
43-
[assembly: AssemblyInformationalVersion("3.1.1")] // Used by Nuget.
41+
[assembly: AssemblyVersion("3.2.0.0")]
42+
[assembly: AssemblyFileVersion("3.2.0.0")]
43+
[assembly: AssemblyInformationalVersion("3.2.0")] // Used by Nuget.

README.md

+68
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,77 @@ The Optimizely client object accepts the following plug-ins:
6767
1. `IEventDispatcher` handles the HTTP requests to Optimizely. The default implementation is an asynchronous "fire and forget".
6868
2. `ILogger` exposes a single method, Log, to record activity in the SDK. An example of a class to bridge the SDK's Log to Log4Net is provided in the Demo Application.
6969
3. `IErrorHandler` allows you to implement custom logic when Exceptions are thrown. Note that Exception information is already included in the Log.
70+
4. `ProjectConfigManager` exposes method for retrieving ProjectConfig instance. Examples include `FallbackProjectConfigManager` and `HttpProjectConfigManager`.
7071

7172
These are optional plug-ins and default behavior is implement if none are provided.
7273

74+
#### OptimizelyFactory
75+
76+
[`OptimizelyFactory`](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/OptimizelyFactory.cs)
77+
provides basic utility to instantiate the Optimizely SDK with a minimal number of configuration options.
78+
79+
`OptimizelyFactory` does not capture all configuration and initialization options. For more use cases,
80+
build the resources via their respective builder classes.
81+
82+
##### Use OptimizelyFactory
83+
84+
You must provide the SDK key at runtime, either directly via the factory method:
85+
```
86+
Optimizely optimizely = OptimizelyFactory.newDefaultInstance(<<SDK_KEY>>);
87+
```
88+
89+
You can also provide default datafile with the SDK key.
90+
```
91+
Optimizely optimizely = OptimizelyFactory.newDefaultInstance(<<SDK_KEY>>, <<Fallback>>);
92+
```
93+
94+
#### HttpProjectConfigManager
95+
96+
[`HttpProjectConfigManager`](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Config/HttpProjectConfigManager.cs)
97+
is an implementation of the abstract [`PollingProjectConfigManager`](https://github.com/optimizely/csharp-sdk/blob/master/OptimizelySDK/Config/PollingProjectConfigManager.cs).
98+
The `Poll` method is extended and makes an HTTP GET request to the configured URL to asynchronously download the
99+
project datafile and initialize an instance of the ProjectConfig.
100+
101+
By default, `HttpProjectConfigManager` will block until the first successful datafile retrieval, up to a configurable timeout.
102+
Set the frequency of the polling method and the blocking timeout with `HttpProjectConfigManager.Builder`.
103+
104+
##### Use HttpProjectConfigManager
105+
106+
```
107+
HttpProjectConfigManager httpManager = new HttpProjectConfigManager.Builder()
108+
.WithSdkKey(sdkKey)
109+
.WithPollingInterval(TimeSpan.FromMinutes(1))
110+
.Build();
111+
```
112+
113+
##### SDK key
114+
115+
The SDK key is used to compose the outbound HTTP request to the default datafile location on the Optimizely CDN.
116+
117+
##### Polling interval
118+
119+
The polling interval is used to specify a fixed delay between consecutive HTTP requests for the datafile. Between 1 to 4294967294 miliseconds is valid duration. Otherwise default 5 minutes will be used.
120+
121+
##### Blocking Timeout Period
122+
123+
The blocking timeout period is used to specify a maximum time to wait for initial bootstrapping. Between 1 to 4294967294 miliseconds is valid blocking timeout period. Otherwise default value 15 seconds will be used.
124+
125+
##### Initial datafile
126+
127+
You can provide an initial datafile via the builder to bootstrap the `ProjectConfigManager` so that it can be used immediately without blocking execution.
128+
129+
##### URL
130+
131+
The URL is used to specify the location of datafile.
132+
133+
##### Format
134+
135+
This option enables user to provide a custom URL format to fetch the datafile.
136+
137+
##### Start by default
138+
139+
This option is used to specify whether to start the config manager on initialization.
140+
73141
## Development
74142

75143
### Unit tests

0 commit comments

Comments
 (0)