Skip to content

Commit 2367f9b

Browse files
authored
refactor: reduce dry4dotnet duplication (#222)
* refactor: reduce dry4dotnet duplication * address pr review feedback * add changeset for duplication refactor * test: cover duplication refactor paths * test: make invalid source path stack frame test robust * address pr review feedback
1 parent bd768fd commit 2367f9b

11 files changed

Lines changed: 444 additions & 927 deletions

File tree

.changeset/dry-owls-refactor.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"PostHog": patch
3+
---
4+
5+
Refactor duplicate internal SDK code paths without changing public API behavior.

samples/PostHog.Example.Console/Program.cs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,20 @@ static string Prompt(string message)
149149
return Console.ReadLine()?.Trim() ?? string.Empty;
150150
}
151151

152-
static async Task RunCaptureExamples(PostHogClient posthog)
152+
static string StartExampleSection(string title)
153153
{
154154
Console.WriteLine("\n" + new string('=', 60));
155-
Console.WriteLine("CAPTURE EVENTS");
155+
Console.WriteLine(title);
156156
Console.WriteLine(new string('=', 60));
157157

158158
var distinctId = $"user_{Guid.NewGuid():N}";
159159
Console.WriteLine($"\nUsing distinct ID: {distinctId}");
160+
return distinctId;
161+
}
162+
163+
static async Task RunCaptureExamples(PostHogClient posthog)
164+
{
165+
var distinctId = StartExampleSection("CAPTURE EVENTS");
160166

161167
// Simple capture
162168
Console.WriteLine("\n📤 Capturing 'page_view' event…");
@@ -191,12 +197,7 @@ static async Task RunCaptureExamples(PostHogClient posthog)
191197

192198
static async Task RunIdentifyExamples(PostHogClient posthog)
193199
{
194-
Console.WriteLine("\n" + new string('=', 60));
195-
Console.WriteLine("IDENTIFY USERS");
196-
Console.WriteLine(new string('=', 60));
197-
198-
var distinctId = $"user_{Guid.NewGuid():N}";
199-
Console.WriteLine($"\nUsing distinct ID: {distinctId}");
200+
var distinctId = StartExampleSection("IDENTIFY USERS");
200201

201202
// Identify with properties
202203
Console.WriteLine("\n👤 Identifying user with properties…");
@@ -232,12 +233,7 @@ await posthog.IdentifyAsync(
232233

233234
static async Task RunFeatureFlagExamples(PostHogClient posthog)
234235
{
235-
Console.WriteLine("\n" + new string('=', 60));
236-
Console.WriteLine("FEATURE FLAGS");
237-
Console.WriteLine(new string('=', 60));
238-
239-
var distinctId = $"user_{Guid.NewGuid():N}";
240-
Console.WriteLine($"\nUsing distinct ID: {distinctId}");
236+
var distinctId = StartExampleSection("FEATURE FLAGS");
241237

242238
// Check a simple boolean flag
243239
Console.WriteLine("\n🚩 Checking feature flag 'new-dashboard'…");

src/PostHog/Capture/CaptureExtensions.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,15 +331,22 @@ public static bool CapturePageView(
331331
string pagePath,
332332
Dictionary<string, object>? properties,
333333
bool sendFeatureFlags)
334+
{
335+
if (!sendFeatureFlags)
336+
{
337+
return client.CapturePageView(distinctId, pagePath, properties);
338+
}
339+
334340
#pragma warning disable CS0618
335-
=> NotNull(client).CaptureSpecialEvent(
341+
return NotNull(client).CaptureSpecialEvent(
336342
distinctId,
337343
eventName: "$pageview",
338344
eventPropertyName: "$current_url",
339345
eventPropertyValue: pagePath,
340346
properties,
341-
sendFeatureFlags);
347+
sendFeatureFlags: true);
342348
#pragma warning restore CS0618
349+
}
343350

344351
/// <summary>
345352
/// Captures a Page View ($pageview) event.
@@ -516,12 +523,15 @@ public static bool CaptureSurveyDismissed(
516523
string distinctId,
517524
string surveyId,
518525
Dictionary<string, object>? properties)
519-
=> NotNull(client).CaptureSpecialEvent(
526+
{
527+
const string eventName = "survey dismissed";
528+
return NotNull(client).CaptureSpecialEvent(
520529
distinctId,
521-
eventName: "survey dismissed",
530+
eventName,
522531
eventPropertyName: "$survey_id",
523532
eventPropertyValue: surveyId,
524533
properties);
534+
}
525535

526536
static bool CaptureSpecialEvent(
527537
this IPostHogClient client,

src/PostHog/Extensions/GroupIdentifyAsyncExtensions.cs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ public static async Task<ApiResult> GroupIdentifyAsync(
2525
StringOrValue<int> key,
2626
string name,
2727
Dictionary<string, object>? properties)
28-
{
29-
properties ??= new Dictionary<string, object>();
30-
properties["name"] = name;
31-
return await NotNull(client).GroupIdentifyAsync(type, key, properties, CancellationToken.None);
32-
}
28+
=> await client.GroupIdentifyAsync(type, key, name, properties, CancellationToken.None);
3329

3430
/// <summary>
3531
/// Sets a groups properties, which allows asking questions like "Who are the most active companies"
@@ -49,11 +45,7 @@ public static async Task<ApiResult> GroupIdentifyAsync(
4945
StringOrValue<int> key,
5046
string name,
5147
Dictionary<string, object>? properties)
52-
{
53-
properties ??= new Dictionary<string, object>();
54-
properties["name"] = name;
55-
return await NotNull(client).GroupIdentifyAsync(distinctId, type, key, properties, CancellationToken.None);
56-
}
48+
=> await client.GroupIdentifyAsync(distinctId, type, key, name, properties, CancellationToken.None);
5749

5850
/// <summary>
5951
/// Sets a groups properties, which allows asking questions like "Who are the most active companies"
@@ -73,11 +65,7 @@ public static async Task<ApiResult> GroupIdentifyAsync(
7365
string name,
7466
Dictionary<string, object>? properties,
7567
CancellationToken cancellationToken)
76-
{
77-
properties ??= new Dictionary<string, object>();
78-
properties["name"] = name;
79-
return await NotNull(client).GroupIdentifyAsync(type, key, properties, cancellationToken);
80-
}
68+
=> await GroupIdentifyWithNameAsync(NotNull(client), distinctId: null, type, key, name, properties, cancellationToken);
8169

8270
/// <summary>
8371
/// Sets a groups properties, which allows asking questions like "Who are the most active companies"
@@ -99,11 +87,7 @@ public static async Task<ApiResult> GroupIdentifyAsync(
9987
string name,
10088
Dictionary<string, object>? properties,
10189
CancellationToken cancellationToken)
102-
{
103-
properties ??= new Dictionary<string, object>();
104-
properties["name"] = name;
105-
return await NotNull(client).GroupIdentifyAsync(distinctId, type, key, properties, cancellationToken);
106-
}
90+
=> await GroupIdentifyWithNameAsync(NotNull(client), distinctId, type, key, name, properties, cancellationToken);
10791

10892
/// <summary>
10993
/// Sets a groups properties, which allows asking questions like "Who are the most active companies"
@@ -147,4 +131,21 @@ public static async Task<ApiResult> GroupIdentifyAsync(
147131
key,
148132
name,
149133
properties: new Dictionary<string, object>());
150-
}
134+
135+
static async Task<ApiResult> GroupIdentifyWithNameAsync(
136+
IPostHogClient client,
137+
string? distinctId,
138+
string type,
139+
StringOrValue<int> key,
140+
string name,
141+
Dictionary<string, object>? properties,
142+
CancellationToken cancellationToken)
143+
{
144+
properties ??= new Dictionary<string, object>();
145+
properties["name"] = name;
146+
147+
return distinctId is null
148+
? await client.GroupIdentifyAsync(type, key, properties, cancellationToken)
149+
: await client.GroupIdentifyAsync(distinctId, type, key, properties, cancellationToken);
150+
}
151+
}

src/PostHog/NoOpPostHogClient.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace PostHog.Sdk;
99
internal sealed class NoOpPostHogClient : IPostHogClient, IFeatureFlagEvaluationsHost
1010
{
1111
static readonly IReadOnlyDictionary<string, FeatureFlag> EmptyFeatureFlags = new Dictionary<string, FeatureFlag>();
12+
static readonly Task<ApiResult> NoOpApiResultTask = Task.FromResult(new ApiResult(0));
1213

1314
NoOpPostHogClient()
1415
{
@@ -17,29 +18,29 @@ internal sealed class NoOpPostHogClient : IPostHogClient, IFeatureFlagEvaluation
1718
internal static NoOpPostHogClient Instance { get; } = new();
1819

1920
public Task<ApiResult> AliasAsync(string previousId, string newId, CancellationToken cancellationToken)
20-
=> Task.FromResult(new ApiResult(0));
21+
=> NoOpApiResultTask;
2122

2223
public Task<ApiResult> IdentifyAsync(
2324
string distinctId,
2425
Dictionary<string, object>? personPropertiesToSet,
2526
Dictionary<string, object>? personPropertiesToSetOnce,
2627
CancellationToken cancellationToken)
27-
=> Task.FromResult(new ApiResult(0));
28+
=> NoOpApiResultTask;
2829

2930
public Task<ApiResult> GroupIdentifyAsync(
3031
string type,
3132
StringOrValue<int> key,
3233
Dictionary<string, object>? properties,
3334
CancellationToken cancellationToken)
34-
=> Task.FromResult(new ApiResult(0));
35+
=> NoOpApiResultTask;
3536

3637
public Task<ApiResult> GroupIdentifyAsync(
3738
string distinctId,
3839
string type,
3940
StringOrValue<int> key,
4041
Dictionary<string, object>? properties,
4142
CancellationToken cancellationToken)
42-
=> Task.FromResult(new ApiResult(0));
43+
=> NoOpApiResultTask;
4344

4445
public bool Capture(
4546
string distinctId,
@@ -48,7 +49,7 @@ public bool Capture(
4849
GroupCollection? groups,
4950
bool sendFeatureFlags,
5051
DateTimeOffset? timestamp = null)
51-
=> false;
52+
=> Capture(distinctId, eventName, properties, groups, flags: null, timestamp);
5253

5354
public bool Capture(
5455
string distinctId,
@@ -66,7 +67,7 @@ public bool CaptureException(
6667
GroupCollection? groups,
6768
bool sendFeatureFlags,
6869
DateTimeOffset? timestamp = null)
69-
=> false;
70+
=> CaptureException(exception, distinctId, properties, groups, flags: null, timestamp);
7071

7172
public bool CaptureException(
7273
Exception exception,

src/PostHog/PostHogClient.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -216,22 +216,7 @@ public async Task<ApiResult> GroupIdentifyAsync(
216216
StringOrValue<int> key,
217217
Dictionary<string, object>? properties,
218218
CancellationToken cancellationToken)
219-
{
220-
if (CheckDisabledAndLog(nameof(GroupIdentifyAsync)))
221-
{
222-
return NoOpApiResult;
223-
}
224-
225-
try
226-
{
227-
return await _apiClient.GroupIdentifyAsync(type, key, properties, cancellationToken);
228-
}
229-
catch (Exception e) when (e is not ArgumentException and not NullReferenceException and not OperationCanceledException)
230-
{
231-
_logger.LogErrorApiCallFailed(e, nameof(GroupIdentifyAsync));
232-
return NoOpApiResult;
233-
}
234-
}
219+
=> await GroupIdentifyCoreAsync(type, key, properties, distinctId: null, cancellationToken);
235220

236221
/// <inheritdoc/>
237222
public async Task<ApiResult> GroupIdentifyAsync(
@@ -240,6 +225,14 @@ public async Task<ApiResult> GroupIdentifyAsync(
240225
StringOrValue<int> key,
241226
Dictionary<string, object>? properties,
242227
CancellationToken cancellationToken)
228+
=> await GroupIdentifyCoreAsync(type, key, properties, distinctId, cancellationToken);
229+
230+
async Task<ApiResult> GroupIdentifyCoreAsync(
231+
string type,
232+
StringOrValue<int> key,
233+
Dictionary<string, object>? properties,
234+
string? distinctId,
235+
CancellationToken cancellationToken)
243236
{
244237
if (CheckDisabledAndLog(nameof(GroupIdentifyAsync)))
245238
{

tests/TestLibrary/Fakes/FakeHttpMessageHandlerExtensions.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ internal static class FakeHttpMessageHandlerExtensions
1212
static readonly Uri FlagsUrl = new("https://us.i.posthog.com/flags/?v=2");
1313

1414
public static FakeHttpMessageHandler.RequestHandler AddCaptureResponse(this FakeHttpMessageHandler handler) =>
15-
handler.AddResponse(
16-
new Uri("https://us.i.posthog.com/capture"),
17-
HttpMethod.Post,
18-
responseBody: new { status = 1 });
15+
handler.AddIngestionResponse("capture");
1916

2017
public static FakeHttpMessageHandler.RequestHandler AddBatchResponse(this FakeHttpMessageHandler handler) =>
18+
handler.AddIngestionResponse("batch");
19+
20+
static FakeHttpMessageHandler.RequestHandler AddIngestionResponse(
21+
this FakeHttpMessageHandler handler,
22+
string endpoint) =>
2123
handler.AddResponse(
22-
new Uri("https://us.i.posthog.com/batch"),
24+
new Uri($"https://us.i.posthog.com/{endpoint}"),
2325
HttpMethod.Post,
2426
responseBody: new { status = 1 });
2527

@@ -156,10 +158,7 @@ public static FakeHttpMessageHandler.RequestHandler AddDecryptedPayloadResponse(
156158
this FakeHttpMessageHandler handler,
157159
string key,
158160
string responseBody) =>
159-
handler.AddResponse(
160-
new Uri($"https://us.i.posthog.com/api/projects/@current/feature_flags/{key}/remote_config?token=fake-project-token"),
161-
HttpMethod.Get,
162-
responseBody: responseBody);
161+
handler.AddRemoteConfigResponse(key, responseBody);
163162

164163
static T Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json, JsonSerializerHelper.Options)
165164
?? throw new ArgumentException("Json is invalid and deserializes to null", nameof(json));

0 commit comments

Comments
 (0)