Skip to content

feat(api): Evaluate segments for environment flags - #8576

Open
khvn26 wants to merge 12 commits into
stack/3-edge-identityfrom
stack/4-environment-flags
Open

khvn26 wants to merge 12 commits into
stack/3-edge-identityfrom
stack/4-environment-flags

Conversation

@khvn26

@khvn26 khvn26 commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Thanks for submitting a PR! Please check the boxes below:

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Contributes to #6654
Closes #8416

In this PR, we evaluate environment flags with the environment's segments, so that $.environment and $.flags conditions resolve without an identity. Segments reading traits or identity context are left out. Edge API is set to do the same in Flagsmith/edge-api#700.

This changes GET /flags/ without an identifier:

  1. A segment override applies when its rules only read $.environment or $.flags context values, e.g. $.environment.name.
  2. A latent bug discovered during development: with hide_disabled_flags, disabled flags are hidden after evaluating, so a disabled override hides its flag instead of letting the enabled environment default through.
  3. Another latent bug: with hide_disabled_flags, server-key-only flags are no longer served to client keys.

There are some query count / performance implications:

  • GET /flags/ now reads the environment's segments, which is one more query per request unless CACHE_ENVIRONMENT_SEGMENTS_SECONDS is set.
  • GET /flags/?feature=… now evaluates all flags and then filters, because dependent flags.

Flag engine is bumped to 11.1.0, enabling dependent flag evaluation.

How did you test this code?

Added tests.

@vercel

vercel Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated
docs Ignored Ignored Preview Sep 25, 2026 11:15am UTC
flagsmith-frontend-preview Ignored Ignored Preview Sep 25, 2026 11:15am UTC
flagsmith-frontend-staging Ignored Ignored Preview Sep 25, 2026 11:15am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: ff6b68c3-8ea1-4044-9b0e-91f4369d8784

📥 Commits

Reviewing files that changed from the base of the PR and between 5af2863 and 4b0d50a.

📒 Files selected for processing (2)
  • api/features/views.py
  • api/tests/unit/features/test_unit_features_views.py

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

Environment evaluation now receives cached segments and excludes segments with identity-dependent rules. The SDK endpoint evaluates feature states before selecting the requested feature by name. Client requests retain the server-key-only filter, and cache-populating queries always read from the replica. Tests cover feature filtering, segment overrides and client-key responses.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: 🟡 Moderate · up to 4b0d5

Flag-dependent segments can return incorrect values, and some flags requests retain response or performance regressions. Resolve these issues before merging unless their impact is explicitly accepted.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added api Issue related to the REST API feature New feature or request docs Documentation updates labels Sep 23, 2026
@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.82%. Comparing base (48e4a96) to head (9900822).

Additional details and impacted files
@@                  Coverage Diff                   @@
##           stack/3-edge-identity    #8576   +/-   ##
======================================================
  Coverage                  98.81%   98.82%           
======================================================
  Files                       1645     1645           
  Lines                      67478    67561   +83     
======================================================
+ Hits                       66681    66764   +83     
  Misses                       797      797           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@khvn26
khvn26 force-pushed the stack/4-environment-flags branch from 26e1f7b to e8aa0ac Compare September 23, 2026 09:00
@github-actions github-actions Bot added feature New feature or request docs Documentation updates and removed feature New feature or request docs Documentation updates labels Sep 23, 2026
@khvn26
khvn26 force-pushed the stack/4-environment-flags branch from 6abaac4 to edbad51 Compare September 23, 2026 09:17
@github-actions github-actions Bot added feature New feature or request docs Documentation updates and removed feature New feature or request docs Documentation updates labels Sep 23, 2026
@khvn26
khvn26 force-pushed the stack/4-environment-flags branch from d1d9376 to 4ce147b Compare September 23, 2026 09:37
@github-actions github-actions Bot added feature New feature or request docs Documentation updates and removed feature New feature or request docs Documentation updates labels Sep 23, 2026
@khvn26
khvn26 force-pushed the stack/4-environment-flags branch from 5d5c9b8 to 82b24a8 Compare September 23, 2026 10:25
@khvn26
khvn26 force-pushed the stack/4-environment-flags branch from d41d2a2 to 3d82111 Compare September 23, 2026 14:06
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request docs Documentation updates labels Sep 23, 2026
@khvn26
khvn26 force-pushed the stack/4-environment-flags branch from 3d82111 to cfe9ee8 Compare September 23, 2026 18:00
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Sep 23, 2026
@khvn26 khvn26 changed the title feat(api): Evaluate environment flags through flag-engine feat(api): Evaluate segments for environment flags Sep 23, 2026
@khvn26
khvn26 force-pushed the stack/4-environment-flags branch from cfe9ee8 to 37506d3 Compare September 23, 2026 18:18
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-e2e:pr-8576 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-frontend:pr-8576 Finished ✅ Results ✅
ghcr.io/flagsmith/flagsmith-api:pr-8576 Finished ✅ Results ✅
ghcr.io/flagsmith/flagsmith-api-test:pr-8576 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith:pr-8576 Finished ✅ Results ✅
ghcr.io/flagsmith/flagsmith-private-cloud:pr-8576 Finished ✅ Results ✅

@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor
✅ private-cloud · depot-ubuntu-latest-16 — run #20841 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  55.5 seconds
commit  9900822
info  🔄 Run: #20841 (attempt 1)

🗂️ Previous results
✅ private-cloud · depot-ubuntu-latest-arm-16 — run #20841 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  1 minute, 2 seconds
commit  9900822
info  🔄 Run: #20841 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #20841 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  37.4 seconds
commit  9900822
info  🔄 Run: #20841 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #20841 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  38 seconds
commit  9900822
info  🔄 Run: #20841 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #20839 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  1 minute, 12 seconds
commit  981b0ef
info  🔄 Run: #20839 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-16 — run #20839 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  48.5 seconds
commit  981b0ef
info  🔄 Run: #20839 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #20839 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  37.7 seconds
commit  981b0ef
info  🔄 Run: #20839 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #20839 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  38.9 seconds
commit  981b0ef
info  🔄 Run: #20839 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-16 — run #20838 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  55.2 seconds
commit  1704bdb
info  🔄 Run: #20838 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #20838 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  36.9 seconds
commit  1704bdb
info  🔄 Run: #20838 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #20838 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  34.7 seconds
commit  1704bdb
info  🔄 Run: #20838 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #20838 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  31.2 seconds
commit  1704bdb
info  🔄 Run: #20838 (attempt 1)

@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Visual Regression

19 screenshots compared. See report for details.
View full report

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 3152f251-00cb-40ec-896f-970485f3f521

📥 Commits

Reviewing files that changed from the base of the PR and between c6f0aa3 and 991ccf2.

📒 Files selected for processing (3)
  • api/evaluation/services.py
  • api/features/views.py
  • api/tests/unit/features/test_unit_features_views.py

Included review availability: Your plan provides up to 8 included reviews per hour; 4 remain after this review.

Comment thread api/evaluation/services.py
Comment thread api/features/views.py
Comment thread api/features/views.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🔵 Trivial · Add a feature-filter regression test for dependent flags. · test_unit_features_views.py:720-765

api/tests/unit/features/test_unit_features_views.py:720-765
🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a feature-filter regression test for dependent flags.

SDKFeatureStates.get evaluates all flags before selecting feature, because segment rules can read another flag through $.flags. The matching feature-filter test has no segment dependency, and the segment test does not use ?feature=. The current tests can therefore pass if the endpoint prefilters flags again, causing the requested flag to miss its segment override.

Add a test with an enabled dependency flag, a segment rule on $.flags.<dependency>.enabled, and a filtered request for the overridden flag.

Suggested fix
 def test_sdk_feature_states_get__existing_feature_filter__returns_feature(
     api_client: APIClient,
     environment: Environment,
     feature: Feature,
     feature_state: FeatureState,
@@
     assert response.json() == {
         "id": feature_state.id,
         "enabled": feature.default_enabled,
         "environment": environment.id,
         "feature": mocker.ANY,
         "feature_segment": None,
         "feature_state_value": None,
         "identity": None,
     }
 
 
+def test_sdk_feature_states_get__feature_filter__evaluates_dependent_flags(
+    api_client: APIClient,
+    environment: Environment,
+    project: Project,
+) -> None:
+    # Given
+    dependency = Feature.objects.create(
+        name="dependency_feature",
+        project=project,
+        default_enabled=True,
+    )
+    requested = Feature.objects.create(
+        name="requested_feature",
+        project=project,
+        initial_value="environment",
+    )
+    segment = Segment.objects.create(name="dependent_segment", project=project)
+    Condition.objects.create(
+        rule=SegmentRule.objects.create(segment=segment, type=SegmentRule.ALL_RULE),
+        property=f"$.flags.{dependency.name}.enabled",
+        operator=EQUAL,
+        value=True,
+    )
+    feature_segment = FeatureSegment.objects.create(
+        segment=segment,
+        feature=requested,
+        environment=environment,
+    )
+    segment_override = FeatureState.objects.create(
+        feature=requested,
+        feature_segment=feature_segment,
+        environment=environment,
+    )
+    segment_override.feature_state_value.string_value = "segment"
+    segment_override.feature_state_value.save()
+    api_client.credentials(HTTP_X_ENVIRONMENT_KEY=environment.api_key)
+
+    # When
+    response = api_client.get(f"/api/v1/flags/?feature={requested.name}")
+
+    # Then
+    assert response.status_code == status.HTTP_200_OK
+    assert response.json()["feature_state_value"] == "segment"
+
+
 def test_sdk_feature_states_get__missing_feature_filter__returns_404(
     api_client: APIClient,
     environment: Environment,
 ) -> None:

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: ba65dc8e-5ae0-4a9b-a715-850fcead740f

📥 Commits

Reviewing files that changed from the base of the PR and between 306113f and c718192.

📒 Files selected for processing (3)
  • api/evaluation/services.py
  • api/features/views.py
  • api/tests/unit/features/test_unit_features_views.py

Included review availability: Your plan provides up to 8 included reviews per hour; 2 remain after this review.

Comment thread api/features/views.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 9c4fea17-648c-446e-b419-f03e772f52b6

📥 Commits

Reviewing files that changed from the base of the PR and between c718192 and 5af2863.

📒 Files selected for processing (2)
  • api/evaluation/services.py
  • api/tests/unit/features/test_unit_features_views.py

Included review availability: Your plan provides up to 8 included reviews per hour; 1 remains after this review.

Comment thread api/evaluation/services.py Outdated

@emyller emyller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One blocking comment.

Comment thread api/evaluation/services.py Outdated
from util.engine_models.features.models import FeatureStateModel


_IDENTITY_FREE_PROPERTY_PREFIXES = ("$.environment.", "$.flags.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we'll need to check for /^\$\.flags(?:\[|\.)/ — i.e. accept a feature name quoted within square brackets — because feature names are very flexible in their allowed characters.

For that reason, #8570 adds prerequisites using $.flags[{json_quoted_feature_name}].

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch! Coordinated with edge-api#731 in 58d72ea.

Comment on lines +708 to +717
@pytest.fixture()
def environment_name_segment(environment: Environment, project: Project) -> Segment:
segment: Segment = Segment.objects.create(name="This environment", project=project)
Condition.objects.create(
rule=SegmentRule.objects.create(segment=segment, type=SegmentRule.ALL_RULE),
property="$.environment.name",
operator=EQUAL,
value=environment.name,
)
return segment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Can you please move fixture to the top of the test module, or to a conftest module? Adding fixtures mid-tests has been a consistent — and IMO myopic — behaviour of LLMs lately.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to the top of the module in 1704bdb.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

note: Some of the unit tests were conceived to cover how — e.g. segments now being counted in evaluation — but fail to convey why to the reader. Consider using flag dependency examples in such tests, and how the hide_disabled_flags affects it under both client and server-type API keys.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I hope the examples here are useful.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Reworked in 1704bdb.

Comment thread api/tests/unit/features/test_unit_features_views.py

This branch was successfully deployed

1 active (outdated) deployment
Preview – docs — ad78da6a Deployed Sep 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Issue related to the REST API feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support $.flags context values in segment conditions

3 participants