Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>1.0.0-beta.9</Version>
<Version>1.0.0-beta.10</Version>
<LangVersion>13.0</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
21 changes: 21 additions & 0 deletions samples/HogTied.Web/Pages/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,27 @@
</div>
</div>
</div>

<div class="mt-3">
<div class="row justify-content-center">
<div class="col-md-6">
<pre><code>
await posthog.GetRemoteConfigPayloadAsync(
"unencrypted-remote-config-setting");

> @Model.UnencryptedRemoteConfigSetting
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: The '>' character before @Model.UnencryptedRemoteConfigSetting seems unnecessary and inconsistent with the encrypted setting display below

</code></pre>
</div>
<div class="col-md-6">
<pre><code>
await posthog.GetRemoteConfigPayloadAsync(
"encrypted-remote-config-setting");

@Model.EncryptedRemoteConfigSetting
</code></pre>
</div>
</div>
</div>
}
</div>
</div>
8 changes: 8 additions & 0 deletions samples/HogTied.Web/Pages/Index.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public class IndexModel(IOptions<PostHogOptions> options, IPostHogClient posthog
[FromQuery]
public string? FeatureFlagKey { get; set; }

public string? UnencryptedRemoteConfigSetting { get; set; }

public string? EncryptedRemoteConfigSetting { get; set; }

public async Task OnGetAsync()
{
ApiKeyIsSet = options.Value.ProjectApiKey is not (null or []);
Expand Down Expand Up @@ -103,6 +107,10 @@ await posthog.IdentifyAsync(
}

NonExistentFlag = await posthog.IsFeatureEnabledAsync("non-existent-flag", UserId);

UnencryptedRemoteConfigSetting = (await posthog.GetRemoteConfigPayloadAsync("unencrypted-remote-config-setting", HttpContext.RequestAborted))?.RootElement.GetRawText();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: GetRawText() may throw if RootElement is invalid JSON. Consider wrapping in try-catch


EncryptedRemoteConfigSetting = (await posthog.GetRemoteConfigPayloadAsync("encrypted-remote-config-setting", HttpContext.RequestAborted))?.RootElement.GetRawText();
Comment on lines +111 to +113
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: These calls could be parallelized using Task.WhenAll for better performance

}
}

Expand Down
2 changes: 1 addition & 1 deletion src/PostHog/Generated/VersionConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ This is generated by an MSBuild task in PostHog.csproj
namespace PostHog.Versioning;
public static class VersionConstants
{
public const string Version = "1.0.0-beta.9";
public const string Version = "1.0.0-beta.10";
}
30 changes: 29 additions & 1 deletion src/PostHog/PostHogClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,41 @@ public async Task<bool> IsFeatureEnabledAsync(

try
{
return await _apiClient.GetRemoteConfigPayloadAsync(key, cancellationToken);
var document = await _apiClient.GetRemoteConfigPayloadAsync(key, cancellationToken);

// The remote config endpoint returns JSON encoded in a string.
// For example: "{\"foo\": \"bar\",\"baz\": 42}"
// Instead of: {"foo": "bar","baz": 42}
// However, we may change that in the future.
// So this is implemented in a forward-compatible way.
if (document is { RootElement.ValueKind: JsonValueKind.String } doc
&& doc.RootElement.GetString() is { } innerJson
&& TryParseJson(innerJson, out var parsedJson))
{
return parsedJson;
}

return document;
}
catch (Exception e) when (e is not ArgumentException and not NullReferenceException)
{
_logger.LogErrorUnableToGetRemoteConfigPayload(e);
return null;
}

static bool TryParseJson(string json, out JsonDocument? document)
{
try
{
document = JsonDocument.Parse(json);
return true;
}
catch (JsonException)
{
document = null;
return false;
}
}
}

bool CaptureFeatureFlagSentEvent(
Expand Down
58 changes: 58 additions & 0 deletions tests/UnitTests/Features/RemoteConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,64 @@ public async Task ReturnsJsonPayloadForKey()
var result = await client.GetRemoteConfigPayloadAsync("remote-config-key");

Assert.NotNull(result);
Assert.Equal("bar", result.RootElement.GetProperty("foo").GetString());
JsonAssert.Equal("""{"foo": "bar","baz": 42}""", result);
}

[Fact]
public async Task HandlesJsonEncodedInString()
{
var container = new TestContainer("fake-personal-api-key");
// Right now, the endpoint doesn't return JSON. It returns a string that contains JSON.
container.FakeHttpMessageHandler.AddRemoteConfigResponse(
"remote-config-key",
"""
"{\"an encrypted\": \"payload\"}"
"""
);
var client = container.Activate<PostHogClient>();

var result = await client.GetRemoteConfigPayloadAsync("remote-config-key");

Assert.NotNull(result);
Assert.Equal("payload", result.RootElement.GetProperty("an encrypted").GetString());
}

[Fact]
public async Task HandlesJsonStringPayload()
{
var container = new TestContainer("fake-personal-api-key");
// Right now, the endpoint doesn't return JSON. It returns a string that contains JSON.
container.FakeHttpMessageHandler.AddRemoteConfigResponse(
"remote-config-key",
"""
"Valid JSON string"
"""
);
var client = container.Activate<PostHogClient>();

var result = await client.GetRemoteConfigPayloadAsync("remote-config-key");

Assert.NotNull(result);
Assert.Equal("Valid JSON string", result.RootElement.GetString());
}

[Fact]
public async Task HandlesJsonEncodedStringPayload()
{
var container = new TestContainer("fake-personal-api-key");
// Right now, the endpoint doesn't return JSON. It returns a string that contains JSON.
container.FakeHttpMessageHandler.AddRemoteConfigResponse(
"remote-config-key",
"""
"\"Valid JSON string\""
"""
);
var client = container.Activate<PostHogClient>();

var result = await client.GetRemoteConfigPayloadAsync("remote-config-key");

Assert.NotNull(result);
Assert.Equal("Valid JSON string", result.RootElement.GetString());
}
}