From 41cd4124a059b81c18019975bf7192429bb94988 Mon Sep 17 00:00:00 2001 From: "posthog[bot]" <206114724+posthog[bot]@users.noreply.github.com> Date: Sun, 11 Oct 2026 06:05:42 +0000 Subject: [PATCH] feat(flags): accept a caller default in FeatureFlagEvaluations.IsEnabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an `IsEnabled(string key, bool defaultValue)` overload that returns the caller-supplied default when the flag has no value in the snapshot, while a flag that has a value — including `false` — still wins over the default. The existing single-argument overload delegates with `defaultValue: false`, so its behavior is unchanged. Co-Authored-By: Claude Opus 5 Generated-By: PostHog Desktop Task-Id: bcbae7c3-319c-4322-aacc-a2838b43287d --- .changeset/tangy-pugs-smile.md | 5 ++++ .../Features/FeatureFlagEvaluations.cs | 15 ++++++++++-- src/PostHog/PublicAPI.Unshipped.txt | 1 + .../Features/FeatureFlagEvaluationsTests.cs | 23 +++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .changeset/tangy-pugs-smile.md diff --git a/.changeset/tangy-pugs-smile.md b/.changeset/tangy-pugs-smile.md new file mode 100644 index 00000000..6258cb50 --- /dev/null +++ b/.changeset/tangy-pugs-smile.md @@ -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. diff --git a/src/PostHog/Features/FeatureFlagEvaluations.cs b/src/PostHog/Features/FeatureFlagEvaluations.cs index a23db9c7..e09a8770 100644 --- a/src/PostHog/Features/FeatureFlagEvaluations.cs +++ b/src/PostHog/Features/FeatureFlagEvaluations.cs @@ -81,10 +81,21 @@ internal FeatureFlagEvaluations( /// /// The feature flag key. /// true if the flag is present and enabled; otherwise false. - public bool IsEnabled(string key) + public bool IsEnabled(string key) => IsEnabled(key, defaultValue: false); + + /// + /// Returns true when the named flag is present in the snapshot and enabled, false when + /// it is present and disabled, and 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 $feature_flag_called on first access for a given (distinct id, key, value) tuple. + /// + /// The feature flag key. + /// The value to return when the flag has no value in this snapshot. + /// The flag's enabled state, or when the flag is not present. + public bool IsEnabled(string key, bool defaultValue) { var record = RecordAccess(key); - return record is { Enabled: true }; + return record is null ? defaultValue : record.Enabled; } /// diff --git a/src/PostHog/PublicAPI.Unshipped.txt b/src/PostHog/PublicAPI.Unshipped.txt index d8caf3f7..b1c7c623 100644 --- a/src/PostHog/PublicAPI.Unshipped.txt +++ b/src/PostHog/PublicAPI.Unshipped.txt @@ -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? diff --git a/tests/UnitTests/Features/FeatureFlagEvaluationsTests.cs b/tests/UnitTests/Features/FeatureFlagEvaluationsTests.cs index ccd6bbcc..a6ff6490 100644 --- a/tests/UnitTests/Features/FeatureFlagEvaluationsTests.cs +++ b/tests/UnitTests/Features/FeatureFlagEvaluationsTests.cs @@ -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() {