Skip to content

Commit

Permalink
Merge branch 'main' into chore/#38_PackageMetadata
Browse files Browse the repository at this point in the history
  • Loading branch information
thompson-tomo authored Jul 18, 2024
2 parents 670bf71 + dd4a4ec commit c955976
Show file tree
Hide file tree
Showing 20 changed files with 287 additions and 105 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ on:

jobs:
build:
timeout-minutes: 10
runs-on: windows-latest
steps:
- uses: actions/checkout@v2

- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v2
with:
dotnet-version: "6.x.x"

- name: Install dependencies
run: dotnet restore

- name: Build
run: dotnet build --configuration Release --no-restore
timeout-minutes: 3

- name: Test
run: dotnet test --no-restore --verbosity normal
timeout-minutes: 5
env:
test_api_key: ${{ secrets.SDK_CONSISTENCY_TEST_COMPANY_API_KEY }}
test_client_key: ${{ secrets.SDK_CLIENT_KEY }}
3 changes: 1 addition & 2 deletions dotnet-statsig-tests/Client/ClientOptionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ await StatsigClient.Initialize
Assert.False(StatsigClient.CheckGate("test_gate"));
var endTime = DateTime.Now;


Assert.True(endTime.Subtract(TimeSpan.FromMilliseconds(300)) < startTime); // make sure it took less than 200 ms to complete
Assert.True(endTime.Subtract(TimeSpan.FromMilliseconds(600)) < startTime); // make sure it took less than 600 ms to complete
await StatsigClient.Shutdown();

startTime = DateTime.Now;
Expand Down
4 changes: 3 additions & 1 deletion dotnet-statsig-tests/Common/EventLoggerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Statsig;
using Statsig.Lib;
using Statsig.Network;
using WireMock;
using WireMock.RequestBuilders;
Expand Down Expand Up @@ -33,7 +34,8 @@ public Task InitializeAsync()

var sdkDetails = SDKDetails.GetClientSDKDetails();
var dispatcher = new RequestDispatcher("a-key", new StatsigOptions(apiUrlBase: _server.Urls[0]), sdkDetails, "my-session");
_logger = new EventLogger(dispatcher, sdkDetails, maxQueueLength: 3, maxThresholdSecs: ThresholdSeconds);
var errorBoundary = new ErrorBoundary("a-key", SDKDetails.GetServerSDKDetails());
_logger = new EventLogger(dispatcher, sdkDetails, maxQueueLength: 3, maxThresholdSecs: ThresholdSeconds, errorBoundary);
_onLogCountdown = new CountdownEvent(1);
return Task.CompletedTask;
}
Expand Down
2 changes: 1 addition & 1 deletion dotnet-statsig-tests/Common/StatsigTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class StatsigTest : IAsyncLifetime
{
WireMockServer _server;

private const String ExpectedSdkVersion = "1.25.0.0";
private const String ExpectedSdkVersion = "1.27.2.0";

Task IAsyncLifetime.InitializeAsync()
{
Expand Down
2 changes: 1 addition & 1 deletion dotnet-statsig-tests/Server/ErrorBoundaryUsageTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ Task IAsyncLifetime.InitializeAsync()
_requests = new List<RequestMessage>();
_server = WireMockServer.Start();
_server.Given(Request.Create().WithPath("*").UsingAnyMethod()).RespondWith(this);
ErrorBoundary.ExceptionEndpoint = $"{_server.Urls[0]}/v1/sdk_exception";

_statsig = new ServerDriver("secret-key");
_statsig._errorBoundary.ExceptionEndpoint = $"{_server.Urls[0]}/v1/sdk_exception";

return Task.CompletedTask;
}
Expand Down
6 changes: 6 additions & 0 deletions dotnet-statsig-tests/Server/GetFeatureGateTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,16 @@ public async void GetFeatureGate()
Assert.True(gate.Value);
Assert.Equal("7w9rbTSffLT89pxqpyhuqK", gate.RuleID);
Assert.Equal(EvaluationReason.Network, gate.Reason);
Assert.Equal(EvaluationReason.Network, gate.EvaluationDetails?.Reason);
Assert.Equal(1631638014811, gate.EvaluationDetails?.ConfigSyncTime);
Assert.Equal(1631638014811, gate.EvaluationDetails?.InitTime);

var gate2 = StatsigServer.GetFeatureGateWithExposureLoggingDisabled(user, "fake_gate");
Assert.False(gate2.Value);
Assert.Equal(EvaluationReason.Unrecognized, gate2.Reason);
Assert.Equal(EvaluationReason.Unrecognized, gate2.EvaluationDetails?.Reason);
Assert.Equal(1631638014811, gate.EvaluationDetails?.ConfigSyncTime);
Assert.Equal(1631638014811, gate.EvaluationDetails?.InitTime);

await StatsigServer.Shutdown();

Expand Down
9 changes: 6 additions & 3 deletions dotnet-statsig/dotnet-statsig.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net471</TargetFrameworks>
Expand All @@ -7,7 +7,7 @@
<RootNamespace>statsig_dotnet</RootNamespace>
<PackOnBuild>true</PackOnBuild>
<PackageId>Statsig</PackageId>
<Version>1.25.0</Version>
<Version>1.27.2</Version>
<Authors>Statsig Inc.</Authors>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RepositoryUrl>https://github.com/statsig-io/dotnet-sdk.git</RepositoryUrl>
Expand All @@ -23,8 +23,11 @@
<Folder Include="src\" />
</ItemGroup>

<ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net471' or '$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="IP3Country" Version="1.1.0" />
Expand Down
8 changes: 5 additions & 3 deletions dotnet-statsig/src/Statsig/Client/ClientDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Statsig.Client.Storage;
using Statsig.Network;
Expand Down Expand Up @@ -64,6 +65,7 @@ public ClientDriver(string clientKey, StatsigOptions? options = null)
sdkDetails,
clientOpts?.LoggingBufferMaxSize ?? Constants.CLIENT_MAX_LOGGER_QUEUE_LENGTH,
clientOpts?.LoggingIntervalSeconds ?? Constants.CLIENT_MAX_LOGGER_WAIT_TIME_IN_SEC,
null,
Constants.CLIENT_DEDUPE_INTERVAL
);
_user = new StatsigUser();
Expand All @@ -90,11 +92,11 @@ public async Task Initialize(StatsigUser? user)
_user.statsigEnvironment = _options.StatsigEnvironment.Values;
var response = await _requestDispatcher.Fetch(
"initialize",
new Dictionary<string, object>
JsonConvert.SerializeObject(new Dictionary<string, object>
{
["user"] = _user,
["statsigMetadata"] = GetStatsigMetadata(),
},
}),
timeoutInMs: _options.ClientRequestTimeoutMs
).ConfigureAwait(false);
if (response == null)
Expand Down Expand Up @@ -404,7 +406,7 @@ IReadOnlyDictionary<string, string> GetStatsigMetadata()
["sessionID"] = _sessionID,
["stableID"] = PersistentStore.StableID,
["locale"] = CultureInfo.CurrentUICulture.Name,
["appVersion"] = Assembly.GetEntryAssembly()!.GetName()!.Version!.ToString()!,
["appVersion"] = Assembly.GetEntryAssembly()?.GetName().Version!.ToString() ?? "unknown",
["systemVersion"] = Environment.OSVersion.Version.ToString(),
["systemName"] = systemName,
["sdkType"] = sdkDetails.SDKType,
Expand Down
7 changes: 6 additions & 1 deletion dotnet-statsig/src/Statsig/DynamicConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Newtonsoft.Json;
using Statsig.Lib;
using Statsig.Server;
using Statsig.Server.Evaluation;

namespace Statsig
{
Expand All @@ -26,6 +27,8 @@ public class DynamicConfig
[JsonProperty("is_user_in_experiment")]
public bool IsUserInExperiment { get; private set; }

public EvaluationDetails? EvaluationDetails { get; }


static DynamicConfig? _defaultConfig;

Expand All @@ -50,7 +53,8 @@ public DynamicConfig(
List<IReadOnlyDictionary<string, string>>? secondaryExposures = null,
List<string>? explicitParameters = null,
bool isInLayer = false,
bool isUserInExperiment = false
bool isUserInExperiment = false,
EvaluationDetails? details = null
)
{
ConfigName = configName ?? "";
Expand All @@ -61,6 +65,7 @@ public DynamicConfig(
ExplicitParameters = explicitParameters ?? new List<string>();
IsInLayer = isInLayer;
IsUserInExperiment = isUserInExperiment;
EvaluationDetails = details;
}

public T? Get<T>(string key, T? defaultValue = default(T))
Expand Down
6 changes: 4 additions & 2 deletions dotnet-statsig/src/Statsig/FeatureGate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public class FeatureGate
public string RuleID { get; }
[JsonProperty("secondary_exposures")]
public List<IReadOnlyDictionary<string, string>> SecondaryExposures { get; }
public EvaluationReason? Reason { get; }
public string? Reason { get; }
public EvaluationDetails? EvaluationDetails { get; }

static FeatureGate? _defaultConfig;

Expand All @@ -31,13 +32,14 @@ public static FeatureGate Default
}
}

public FeatureGate(string? name = null, bool value = false, string? ruleID = null, List<IReadOnlyDictionary<string, string>>? secondaryExposures = null, EvaluationReason? reason = null)
public FeatureGate(string? name = null, bool value = false, string? ruleID = null, List<IReadOnlyDictionary<string, string>>? secondaryExposures = null, string? reason = null, EvaluationDetails? details = null)
{
Name = name ?? "";
Value = value;
RuleID = ruleID ?? "";
SecondaryExposures = secondaryExposures ?? new List<IReadOnlyDictionary<string, string>>();
Reason = reason ?? EvaluationReason.Uninitialized;
EvaluationDetails = details;
}

internal static FeatureGate? FromJObject(string name, JObject? jobj)
Expand Down
7 changes: 6 additions & 1 deletion dotnet-statsig/src/Statsig/Layer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Newtonsoft.Json;
using Statsig.Lib;
using Statsig.Server;
using Statsig.Server.Evaluation;

namespace Statsig
{
Expand Down Expand Up @@ -31,6 +32,8 @@ public class Layer

static Layer? _default;

public EvaluationDetails? EvaluationDetails { get; }

public static Layer Default
{
get
Expand All @@ -50,7 +53,8 @@ public Layer(string? name = null,
string? allocatedExperimentName = null,
List<string>? explicitParameters = null,
Action<Layer, string>? onExposure = null,
string? groupName = null)
string? groupName = null,
EvaluationDetails? details = null)
{
Name = name ?? "";
Value = value ?? new Dictionary<string, JToken>();
Expand All @@ -61,6 +65,7 @@ public Layer(string? name = null,
ExplicitParameters = explicitParameters ?? new List<string>();
AllocatedExperimentName = allocatedExperimentName ?? "";
GroupName = groupName;
EvaluationDetails = details;
}

public T? Get<T>(string key, T? defaultValue = default(T))
Expand Down
8 changes: 4 additions & 4 deletions dotnet-statsig/src/Statsig/Lib/ErrorBoundary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Statsig.Lib
{
public class ErrorBoundary
{
internal static string ExceptionEndpoint = "https://statsigapi.net/v1/sdk_exception";
public string ExceptionEndpoint = "https://statsigapi.net/v1/sdk_exception";

private string _sdkKey;
private SDKDetails _sdkDetails;
Expand Down Expand Up @@ -81,7 +81,7 @@ private T OnCaught<T>(string tag, Exception ex, Func<T> recover)
return recover();
}

public async void LogException(string tag, Exception ex, Dictionary<String, String>? extra = null)
public async void LogException(string tag, Exception ex, Dictionary<String, object>? extra = null, bool force = false)
{
try
{
Expand All @@ -91,7 +91,7 @@ public async void LogException(string tag, Exception ex, Dictionary<String, Stri
}

var name = ex?.GetType().FullName ?? "No Name";
if (_seen.Contains(name))
if (_seen.Contains(name) && !force)
{
return;
}
Expand All @@ -110,7 +110,7 @@ public async void LogException(string tag, Exception ex, Dictionary<String, Stri
{ "exception", name },
{ "info", info },
{ "statsigMetadata", _sdkDetails.StatsigMetadata },
{ "extra", extra ?? new Dictionary<string, string>()}
{ "extra", extra ?? new Dictionary<string, object>()}
});
request.Content = new StringContent(body, Encoding.UTF8, "application/json");

Expand Down
27 changes: 25 additions & 2 deletions dotnet-statsig/src/Statsig/Network/EventLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Statsig.Lib;

namespace Statsig.Network
{
Expand All @@ -14,6 +16,7 @@ public class EventLogger
private readonly SDKDetails _sdkDetails;
private readonly RequestDispatcher _dispatcher;
private readonly Dictionary<string, string> _statsigMetadata;
private readonly ErrorBoundary? _errorBoundary;

private readonly Task _backgroundPeriodicFlushTask;
private readonly CancellationTokenSource _shutdownCTS;
Expand All @@ -28,7 +31,7 @@ public class EventLogger
private DateTime _dedupeStartTime;

public EventLogger(RequestDispatcher dispatcher, SDKDetails sdkDetails, int maxQueueLength,
int maxThresholdSecs, int dedupeInterval = 60 * 1000)
int maxThresholdSecs, ErrorBoundary? errorBoundary = null, int dedupeInterval = 60 * 1000)
{
_sdkDetails = sdkDetails;
_maxQueueLength = maxQueueLength;
Expand All @@ -38,6 +41,7 @@ public EventLogger(RequestDispatcher dispatcher, SDKDetails sdkDetails, int maxQ
["sdkType"] = _sdkDetails.SDKType,
["sdkVersion"] = _sdkDetails.SDKVersion,
};
_errorBoundary = errorBoundary;

_eventLogQueue = new List<EventLog>();
_errorsLogged = new HashSet<string>();
Expand Down Expand Up @@ -157,7 +161,26 @@ internal async Task FlushEvents()
["events"] = snapshot
};

await _dispatcher.Fetch("log_event", body, 5, 1);
var additionalHeaders = new Dictionary<string, string>
{
["STATSIG-EVENT-COUNT"] = String.Format("{0}", snapshot.Count)
};

var status = await _dispatcher.FetchStatus("log_event", JsonConvert.SerializeObject(body), 5, 1, 0, additionalHeaders, true);
if (status != InitializeResult.Success)
{
var message = String.Format("Failed to post {0} logs after {1} retries, dropping the request", snapshot.Count, 5);
System.Diagnostics.Debug.WriteLine(message);
if (this._errorBoundary != null)
{
var extra = new Dictionary<string, object>
{
["eventCount"] = snapshot.Count,
["error"] = message
};
this._errorBoundary.LogException("statsig::log_event_failed", new Exception(message), extra, true);
}
}
}

public async Task Shutdown()
Expand Down
Loading

0 comments on commit c955976

Please sign in to comment.