Skip to content
3 changes: 2 additions & 1 deletion Flagsmith.Client.Test/Fixtures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ public static string ApiFlagResponseWithTenFlags
environment = 1,
};
flags.Add(flag);
};
}
;
var json = JsonConvert.SerializeObject(flags);
// Return the JSON string representation of the flags list
return json;
Expand Down
22 changes: 22 additions & 0 deletions Flagsmith.Client.Test/SdkVersionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Flagsmith;
using Xunit;

namespace Flagsmith.FlagsmithClientTest
{
public class SdkVersionTest
{
[Fact]
public void TestGetUserAgentReturnsExpectedVersion()
{
// x-release-please-start-version
string expectedVersion = "8.0.2";
// x-release-please-end

// When
var userAgent = SdkVersion.GetUserAgent();

// Then
Assert.Equal($"flagsmith-dotnet-sdk/{expectedVersion}", userAgent);
}
}
}
134 changes: 134 additions & 0 deletions Flagsmith.Client.Test/UserAgentTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Flagsmith;
using Moq;
using Xunit;

namespace Flagsmith.FlagsmithClientTest
{
public class UserAgentTest
{
[Fact]
public async Task TestUserAgentHeaderIsSentInGetEnvironmentFlags()
{
// Given
HttpRequestMessage capturedRequest = null!;

var httpClientMock = new Mock<HttpClient>();
httpClientMock.Setup(x => x.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
.Callback<HttpRequestMessage, CancellationToken>((request, token) =>
{
capturedRequest = request;
})
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(Fixtures.ApiFlagResponse)
});

var config = new FlagsmithConfiguration
{
EnvironmentKey = Fixtures.ApiKey,
HttpClient = httpClientMock.Object
};

var client = new FlagsmithClient(config);

// When
await client.GetEnvironmentFlags();

// Then
Assert.NotNull(capturedRequest);
Assert.True(capturedRequest.Headers.Contains("User-Agent"));

var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList();
Assert.Single(userAgentValues);

var userAgent = userAgentValues[0];
Assert.Equal(SdkVersion.GetUserAgent(), userAgent);
}

[Fact]
public async Task TestUserAgentHeaderIsSentInGetIdentityFlags()
{
// Given
HttpRequestMessage capturedRequest = null!;

var httpClientMock = new Mock<HttpClient>();
httpClientMock.Setup(x => x.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
.Callback<HttpRequestMessage, CancellationToken>((request, token) =>
{
capturedRequest = request;
})
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(Fixtures.ApiIdentityResponse)
});

var config = new FlagsmithConfiguration
{
EnvironmentKey = Fixtures.ApiKey,
HttpClient = httpClientMock.Object
};

var client = new FlagsmithClient(config);

// When
await client.GetIdentityFlags("test-identity");

// Then
Assert.NotNull(capturedRequest);
Assert.True(capturedRequest.Headers.Contains("User-Agent"));

var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList();
Assert.Single(userAgentValues);

var userAgent = userAgentValues[0];
Assert.Equal(SdkVersion.GetUserAgent(), userAgent);
}

[Fact]
public async Task TestUserAgentHeaderIsSentInAnalyticsFlush()
{
// Given
HttpRequestMessage capturedRequest = null!;

var httpClientMock = new Mock<HttpClient>();
httpClientMock.Setup(x => x.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
.Callback<HttpRequestMessage, CancellationToken>((request, token) =>
{
capturedRequest = request;
})
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});

var analyticsProcessor = new AnalyticsProcessor(
httpClientMock.Object,
Fixtures.ApiKey,
Fixtures.ApiUrl
);

// When
await analyticsProcessor.TrackFeature("test_feature");
await analyticsProcessor.Flush();

// Then
Assert.NotNull(capturedRequest);
Assert.True(capturedRequest.Headers.Contains("User-Agent"));

var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList();
Assert.Single(userAgentValues);

var userAgent = userAgentValues[0];
Assert.Equal(SdkVersion.GetUserAgent(), userAgent);
}
}
}
3 changes: 2 additions & 1 deletion Flagsmith.Engine/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ public Dictionary<FeatureModel, FeatureStateModel> GetIdentityFeatureStatesMappi
}

featureStates[feature] = featureState;
};
}
;
}
identity.IdentityFeatures?.ForEach(x =>
{
Expand Down
3 changes: 2 additions & 1 deletion Flagsmith.FlagsmithClient/AnalyticsProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ private async Task FlushWithoutLock()
{
Headers =
{
{ "X-Environment-Key", _EnvironmentKey }
{ "X-Environment-Key", _EnvironmentKey },
{ "User-Agent", SdkVersion.GetUserAgent() }
},
Content = new StringContent(analyticsJson, Encoding.UTF8, "application/json")
};
Expand Down
3 changes: 2 additions & 1 deletion Flagsmith.FlagsmithClient/FlagsmithClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ private async Task<string> GetJson(HttpMethod method, string url, string? body =
{
Headers =
{
{ "X-Environment-Key", _config.EnvironmentKey }
{ "X-Environment-Key", _config.EnvironmentKey },
{ "User-Agent", SdkVersion.GetUserAgent() }
}
};
_config.CustomHeaders?.ForEach(kvp => request.Headers.Add(kvp.Key, kvp.Value));
Expand Down
22 changes: 22 additions & 0 deletions Flagsmith.FlagsmithClient/SdkVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#nullable enable

namespace Flagsmith
{
/// <summary>
/// Provides SDK version information for User-Agent header
/// </summary>
public static class SdkVersion
{
// x-release-please-start-version
private const string Version = "8.0.2";
// x-release-please-end

/// <summary>
/// Gets the SDK version in the format "flagsmith-dotnet-sdk/version"
/// </summary>
public static string GetUserAgent()
{
return $"flagsmith-dotnet-sdk/{Version}";
}
}
}
4 changes: 3 additions & 1 deletion release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"type": "xml",
"path": "Flagsmith.FlagsmithClient/Flagsmith.FlagsmithClient.csproj",
"xpath": "//Project/PropertyGroup/Version"
}
},
"Flagsmith.FlagsmithClient/SdkVersion.cs",
"Flagsmith.Client.Test/SdkVersionTest.cs"
]
}
},
Expand Down
Loading