Skip to content

Commit 8b2c333

Browse files
committed
Move request context overloads to ASP.NET Core
1 parent 7d37209 commit 8b2c333

7 files changed

Lines changed: 166 additions & 163 deletions

File tree

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using PostHog.Features;
2+
using static PostHog.Library.Ensure;
3+
4+
namespace PostHog;
5+
6+
/// <summary>
7+
/// Extension methods that use the current ASP.NET Core PostHog request context.
8+
/// </summary>
9+
public static class PostHogRequestContextExtensions
10+
{
11+
/// <summary>
12+
/// Captures an event using the current request context distinct ID, or as a personless event if none is set.
13+
/// </summary>
14+
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
15+
/// <param name="eventName">Human friendly name of the event.</param>
16+
/// <returns><c>true</c> if the event was successfully enqueued. Otherwise <c>false</c>.</returns>
17+
public static bool Capture(
18+
this IPostHogClient client,
19+
string eventName)
20+
{
21+
var checkedClient = NotNull(client);
22+
var context = PostHogContextHelper.ResolveCaptureContext(distinctId: null, properties: null);
23+
return checkedClient.Capture(
24+
context.DistinctId,
25+
eventName,
26+
context.Properties,
27+
groups: null,
28+
flags: null);
29+
}
30+
31+
/// <summary>
32+
/// Captures an event using the current request context distinct ID, or as a personless event if none is set.
33+
/// </summary>
34+
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
35+
/// <param name="eventName">Human friendly name of the event.</param>
36+
/// <param name="properties">Optional: The properties to send along with the event.</param>
37+
/// <returns><c>true</c> if the event was successfully enqueued. Otherwise <c>false</c>.</returns>
38+
public static bool Capture(
39+
this IPostHogClient client,
40+
string eventName,
41+
Dictionary<string, object>? properties)
42+
{
43+
var checkedClient = NotNull(client);
44+
var context = PostHogContextHelper.ResolveCaptureContext(distinctId: null, properties);
45+
return checkedClient.Capture(
46+
context.DistinctId,
47+
eventName,
48+
context.Properties,
49+
groups: null,
50+
flags: null);
51+
}
52+
53+
/// <summary>
54+
/// Captures an exception using the current request context distinct ID, or as a personless event if none is set.
55+
/// </summary>
56+
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
57+
/// <param name="exception">The exception to capture.</param>
58+
/// <param name="properties">Optional: The properties to send along with the event.</param>
59+
/// <returns><c>true</c> if the exception event was successfully enqueued. Otherwise <c>false</c>.</returns>
60+
public static bool CaptureException(
61+
this IPostHogClient client,
62+
Exception exception,
63+
Dictionary<string, object>? properties = null)
64+
{
65+
var checkedClient = NotNull(client);
66+
var context = PostHogContextHelper.ResolveCaptureContext(distinctId: null, properties);
67+
return checkedClient.CaptureException(
68+
exception,
69+
context.DistinctId,
70+
context.Properties,
71+
groups: null,
72+
flags: null);
73+
}
74+
75+
/// <summary>
76+
/// Evaluates all feature flags using the current request context distinct ID and returns a <see cref="FeatureFlagEvaluations"/> snapshot.
77+
/// </summary>
78+
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
79+
/// <returns>A snapshot of feature flag evaluations.</returns>
80+
public static Task<FeatureFlagEvaluations> EvaluateFlagsAsync(this IPostHogClient client)
81+
=> NotNull(client).EvaluateFlagsAsync(
82+
PostHogContextHelper.ResolveDistinctId() ?? string.Empty,
83+
options: null,
84+
CancellationToken.None);
85+
86+
/// <summary>
87+
/// Evaluates all feature flags using the current request context distinct ID and returns a <see cref="FeatureFlagEvaluations"/> snapshot.
88+
/// </summary>
89+
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
90+
/// <param name="options">Options used to control feature flag evaluation. <see cref="AllFeatureFlagsOptions.FlagKeysToEvaluate"/> scopes the underlying <c>/flags</c> request body.</param>
91+
/// <returns>A snapshot of feature flag evaluations.</returns>
92+
public static Task<FeatureFlagEvaluations> EvaluateFlagsAsync(
93+
this IPostHogClient client,
94+
AllFeatureFlagsOptions options)
95+
=> NotNull(client).EvaluateFlagsAsync(
96+
PostHogContextHelper.ResolveDistinctId() ?? string.Empty,
97+
options,
98+
CancellationToken.None);
99+
}

src/PostHog/Capture/CaptureExtensions.cs

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,48 +9,6 @@ namespace PostHog;
99
/// </summary>
1010
public static class CaptureExtensions
1111
{
12-
/// <summary>
13-
/// Captures an event using the current request context distinct ID, or as a personless event if none is set.
14-
/// </summary>
15-
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
16-
/// <param name="eventName">Human friendly name of the event.</param>
17-
/// <returns><c>true</c> if the event was successfully enqueued. Otherwise <c>false</c>.</returns>
18-
public static bool Capture(
19-
this IPostHogClient client,
20-
string eventName)
21-
{
22-
var checkedClient = NotNull(client);
23-
var context = PostHogContextHelper.ResolveCaptureContext(distinctId: null, properties: null);
24-
return checkedClient.Capture(
25-
context.DistinctId,
26-
eventName,
27-
context.Properties,
28-
groups: null,
29-
flags: null);
30-
}
31-
32-
/// <summary>
33-
/// Captures an event using the current request context distinct ID, or as a personless event if none is set.
34-
/// </summary>
35-
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
36-
/// <param name="eventName">Human friendly name of the event.</param>
37-
/// <param name="properties">Optional: The properties to send along with the event.</param>
38-
/// <returns><c>true</c> if the event was successfully enqueued. Otherwise <c>false</c>.</returns>
39-
public static bool Capture(
40-
this IPostHogClient client,
41-
string eventName,
42-
Dictionary<string, object>? properties)
43-
{
44-
var checkedClient = NotNull(client);
45-
var context = PostHogContextHelper.ResolveCaptureContext(distinctId: null, properties);
46-
return checkedClient.Capture(
47-
context.DistinctId,
48-
eventName,
49-
context.Properties,
50-
groups: null,
51-
flags: null);
52-
}
53-
5412
/// <summary>
5513
/// Captures an event.
5614
/// </summary>

src/PostHog/ErrorTracking/CaptureExceptionExtensions.cs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,6 @@ namespace PostHog;
99
/// </summary>
1010
public static class CaptureExceptionExtensions
1111
{
12-
/// <summary>
13-
/// Captures an exception using the current request context distinct ID, or as a personless event if none is set.
14-
/// </summary>
15-
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
16-
/// <param name="exception">The exception to capture.</param>
17-
/// <param name="properties">Optional: The properties to send along with the event.</param>
18-
/// <returns><c>true</c> if the exception event was successfully enqueued. Otherwise <c>false</c>.</returns>
19-
public static bool CaptureException(
20-
this IPostHogClient client,
21-
Exception exception,
22-
Dictionary<string, object>? properties = null)
23-
{
24-
var checkedClient = NotNull(client);
25-
var context = PostHogContextHelper.ResolveCaptureContext(distinctId: null, properties);
26-
return checkedClient.CaptureException(
27-
exception,
28-
context.DistinctId,
29-
context.Properties,
30-
groups: null,
31-
flags: null);
32-
}
33-
3412
/// <summary>
3513
/// Captures an exception event.
3614
/// </summary>

src/PostHog/Features/FeatureFlagExtensions.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -258,29 +258,6 @@ public static async Task<IReadOnlyDictionary<string, FeatureFlag>> GetAllFeature
258258
.GetAllFeatureFlagsAsync(distinctId, options: new AllFeatureFlagsOptions(), CancellationToken.None);
259259
}
260260

261-
/// <summary>
262-
/// Evaluates all feature flags using the current request context distinct ID and returns a <see cref="FeatureFlagEvaluations"/> snapshot.
263-
/// </summary>
264-
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
265-
public static Task<FeatureFlagEvaluations> EvaluateFlagsAsync(this IPostHogClient client)
266-
=> NotNull(client).EvaluateFlagsAsync(
267-
PostHogContextHelper.ResolveDistinctId() ?? string.Empty,
268-
options: null,
269-
CancellationToken.None);
270-
271-
/// <summary>
272-
/// Evaluates all feature flags using the current request context distinct ID and returns a <see cref="FeatureFlagEvaluations"/> snapshot.
273-
/// </summary>
274-
/// <param name="client">The <see cref="IPostHogClient"/>.</param>
275-
/// <param name="options">Options used to control feature flag evaluation. <see cref="AllFeatureFlagsOptions.FlagKeysToEvaluate"/> scopes the underlying <c>/flags</c> request body.</param>
276-
public static Task<FeatureFlagEvaluations> EvaluateFlagsAsync(
277-
this IPostHogClient client,
278-
AllFeatureFlagsOptions options)
279-
=> NotNull(client).EvaluateFlagsAsync(
280-
PostHogContextHelper.ResolveDistinctId() ?? string.Empty,
281-
options,
282-
CancellationToken.None);
283-
284261
/// <summary>
285262
/// Evaluates all feature flags for the user and returns a <see cref="FeatureFlagEvaluations"/> snapshot.
286263
/// </summary>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Text.Json;
2+
using Microsoft.Extensions.Logging;
3+
using PostHog;
4+
using UnitTests.Fakes;
5+
6+
namespace PostHogRequestContextExtensionsTests;
7+
8+
public class ThePostHogRequestContextExtensions
9+
{
10+
[Fact]
11+
public async Task EvaluateFlagsAsyncUsesCurrentRequestContextDistinctId()
12+
{
13+
var container = new TestContainer();
14+
var flagsHandler = container.FakeHttpMessageHandler.AddFlagsResponse("""{"featureFlags": {"flag-a": true}}""");
15+
var client = container.Activate<PostHogClient>();
16+
17+
using (PostHogContext.BeginScope(distinctId: "context-user", fresh: true))
18+
{
19+
await client.EvaluateFlagsAsync();
20+
}
21+
22+
var request = flagsHandler.ReceivedRequests.Single();
23+
var body = await request.Content!.ReadAsStringAsync();
24+
using var doc = JsonDocument.Parse(body);
25+
Assert.Equal("context-user", doc.RootElement.GetProperty("distinct_id").GetString());
26+
}
27+
28+
[Fact]
29+
public async Task MissingContextDistinctIdLogsWarningOnceAndAvoidsHttpCalls()
30+
{
31+
var container = new TestContainer();
32+
var flagsHandler = container.FakeHttpMessageHandler.AddFlagsResponse("""{"featureFlags": {"flag-a": true}}""");
33+
var client = container.Activate<PostHogClient>();
34+
35+
var first = await client.EvaluateFlagsAsync();
36+
var second = await client.EvaluateFlagsAsync();
37+
38+
Assert.Empty(first.Keys);
39+
Assert.Empty(second.Keys);
40+
Assert.Empty(flagsHandler.ReceivedRequests);
41+
var warning = Assert.Single(
42+
container.FakeLoggerProvider.GetAllEvents(minimumLevel: LogLevel.Warning),
43+
e => (e.Message ?? string.Empty).Contains("distinctId is required", StringComparison.Ordinal));
44+
Assert.Equal(LogLevel.Warning, warning.LogLevel);
45+
}
46+
}

tests/UnitTests/Features/FeatureFlagEvaluationsTests.cs

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -57,43 +57,6 @@ public async Task EmptyDistinctIdReturnsEmptySnapshotWithNoHttpCall()
5757
Assert.Empty(flagsHandler.ReceivedRequests);
5858
}
5959

60-
[Fact]
61-
public async Task MissingContextDistinctIdLogsWarningOnceAndAvoidsHttpCalls()
62-
{
63-
var container = new TestContainer();
64-
var flagsHandler = container.FakeHttpMessageHandler.AddFlagsResponse("""{"featureFlags": {"flag-a": true}}""");
65-
var client = container.Activate<PostHogClient>();
66-
67-
var first = await client.EvaluateFlagsAsync();
68-
var second = await client.EvaluateFlagsAsync();
69-
70-
Assert.Empty(first.Keys);
71-
Assert.Empty(second.Keys);
72-
Assert.Empty(flagsHandler.ReceivedRequests);
73-
var warning = Assert.Single(
74-
container.FakeLoggerProvider.GetAllEvents(minimumLevel: LogLevel.Warning),
75-
e => (e.Message ?? string.Empty).Contains("distinctId is required", StringComparison.Ordinal));
76-
Assert.Equal(LogLevel.Warning, warning.LogLevel);
77-
}
78-
79-
[Fact]
80-
public async Task UsesContextDistinctIdWhenEvaluateFlagsOmitsDistinctId()
81-
{
82-
var container = new TestContainer();
83-
var flagsHandler = container.FakeHttpMessageHandler.AddFlagsResponse("""{"featureFlags": {"flag-a": true}}""");
84-
var client = container.Activate<PostHogClient>();
85-
86-
using (PostHogContext.BeginScope(distinctId: "context-user", fresh: true))
87-
{
88-
await client.EvaluateFlagsAsync();
89-
}
90-
91-
var request = flagsHandler.ReceivedRequests.Single();
92-
var body = await request.Content!.ReadAsStringAsync();
93-
using var doc = JsonDocument.Parse(body);
94-
Assert.Equal("context-user", doc.RootElement.GetProperty("distinct_id").GetString());
95-
}
96-
9760
[Fact]
9861
public async Task ForwardsFlagKeysToFlagsRequestBody()
9962
{

0 commit comments

Comments
 (0)