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
5 changes: 5 additions & 0 deletions .changeset/tangy-pugs-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"PostHog": minor
---

Add `FeatureFlagEvaluations.IsEnabled(key, defaultValue)`, which returns the caller-supplied default when the flag has no value in the snapshot. A flag that has a value β€” including `false` β€” still wins over the default.
15 changes: 13 additions & 2 deletions src/PostHog/Features/FeatureFlagEvaluations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,21 @@ internal FeatureFlagEvaluations(
/// </summary>
/// <param name="key">The feature flag key.</param>
/// <returns><c>true</c> if the flag is present and enabled; otherwise <c>false</c>.</returns>
public bool IsEnabled(string key)
public bool IsEnabled(string key) => IsEnabled(key, defaultValue: false);

/// <summary>
/// Returns <c>true</c> when the named flag is present in the snapshot and enabled, <c>false</c> when
/// it is present and disabled, and <paramref name="defaultValue"/> when the flag is not present at all
/// (not loaded, a failed flags request, or no flag with that key). Records access on the snapshot and
/// fires <c>$feature_flag_called</c> on first access for a given (distinct id, key, value) tuple.
/// </summary>
/// <param name="key">The feature flag key.</param>
/// <param name="defaultValue">The value to return when the flag has no value in this snapshot.</param>
/// <returns>The flag's enabled state, or <paramref name="defaultValue"/> when the flag is not present.</returns>
public bool IsEnabled(string key, bool defaultValue)
{
var record = RecordAccess(key);
return record is { Enabled: true };
return record is null ? defaultValue : record.Enabled;
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions src/PostHog/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ PostHog.Api.FlagsResult.MinimalFlagCalledEvents.get -> bool
PostHog.Api.FlagsResult.MinimalFlagCalledEvents.init -> void
PostHog.Features.FeatureFlag.HasExperiment.get -> bool?
PostHog.Features.FeatureFlag.HasExperiment.init -> void
PostHog.Features.FeatureFlagEvaluations.IsEnabled(string! key, bool defaultValue) -> bool
PostHog.Json.PropertyFilterValue.IsPrefixOf(object? other, System.StringComparison stringComparison) -> bool
PostHog.Json.PropertyFilterValue.IsSuffixOf(object? other, System.StringComparison stringComparison) -> bool
PostHog.PostHogOptions.BeforeSend.get -> System.Func<PostHog.Api.CapturedEvent!, PostHog.Api.CapturedEvent?>?
Expand Down
23 changes: 23 additions & 0 deletions tests/UnitTests/Features/FeatureFlagEvaluationsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,29 @@ public async Task UnknownKeyReturnsFalsyValue(string accessor)
}
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task UnknownKeyReturnsCallerSuppliedDefaultValue(bool defaultValue)
{
var snapshot = await EvaluateAsync("""{"featureFlags": {}}""");

Assert.Equal(defaultValue, snapshot.IsEnabled("missing", defaultValue));
}

[Theory]
[InlineData("false", true, false)]
[InlineData("\"variant-a\"", false, true)]
public async Task PresentFlagValueWinsOverCallerSuppliedDefaultValue(
string flagValue,
bool defaultValue,
bool expected)
{
var snapshot = await EvaluateAsync($$$"""{"featureFlags": {"feature": {{{flagValue}}} }}""");

Assert.Equal(expected, snapshot.IsEnabled("feature", defaultValue));
}

[Fact]
public async Task GetFlagPayloadDoesNotFireFeatureFlagCalledEvent()
{
Expand Down
Loading