Skip to content

fix(6905): replace dynamic serializer with SDKAnalyticsFlagsV1Serializer#6908

Merged
gagantrivedi merged 3 commits intomainfrom
refactor/remove-dynamic-serializer-v1-analytics
Mar 11, 2026
Merged

fix(6905): replace dynamic serializer with SDKAnalyticsFlagsV1Serializer#6908
gagantrivedi merged 3 commits intomainfrom
refactor/remove-dynamic-serializer-v1-analytics

Conversation

@gagantrivedi
Copy link
Member

@gagantrivedi gagantrivedi commented Mar 10, 2026

  • 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

Closes #6905, #5906

The V1 SDKAnalyticsFlags view dynamically generated a DRF serializer inside get_serializer_class. This caused dotted feature names (e.g. org.app.module:handler) to be split into nested dicts by DRF's set_value.

This PR replaces the dynamic serializer with a proper SDKAnalyticsFlagsV1Serializer.

Unknown feature names were already silently dropped by the old code (only known names had fields). The new serializer preserves this behaviour explicitly in validate. Non-integer values are now also silently skipped — the old code would have raised an unhandled AssertionError in this case since is_valid() was called without raise_exception=True, leaving validated_data unpopulated. This never surfaced because SDKs always send integers.

How did you test this code?

Existing and new unit tests:

python -m pytest tests/unit/app_analytics/test_unit_app_analytics_views.py -v

All 22 tests pass (20 passed, 2 skipped due to no analytics database configured).

@gagantrivedi gagantrivedi requested a review from a team as a code owner March 10, 2026 09:43
@gagantrivedi gagantrivedi requested review from Zaimwa9 and removed request for a team March 10, 2026 09:43
@vercel
Copy link

vercel bot commented Mar 10, 2026

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

3 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Mar 11, 2026 3:52am
flagsmith-frontend-preview Ignored Ignored Preview Mar 11, 2026 3:52am
flagsmith-frontend-staging Ignored Ignored Preview Mar 11, 2026 3:52am

Request Review

@github-actions github-actions bot added api Issue related to the REST API fix labels Mar 10, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 10, 2026

Docker builds report

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 10, 2026

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

passed  10 passed

Details

stats  10 tests across 7 suites
duration  29.4 seconds
commit  d422a84
info  🔄 Run: #15223 (attempt 1)

@github-actions
Copy link
Contributor

github-actions bot commented Mar 10, 2026

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

passed  10 passed

Details

stats  10 tests across 7 suites
duration  11.3 seconds
commit  1c9cd52
info  🔄 Run: #15224 (attempt 1)

@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.31%. Comparing base (a37f417) to head (1c9cd52).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6908      +/-   ##
==========================================
- Coverage   98.34%   98.31%   -0.03%     
==========================================
  Files        1335     1335              
  Lines       49707    49717      +10     
==========================================
- Hits        48882    48879       -3     
- Misses        825      838      +13     

☔ View full report in Codecov by Sentry.
📢 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.

@gagantrivedi gagantrivedi force-pushed the refactor/remove-dynamic-serializer-v1-analytics branch from c39d427 to a5e89f9 Compare March 10, 2026 09:51
@github-actions
Copy link
Contributor

github-actions bot commented Mar 10, 2026

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  1 minute, 2 seconds
commit  d422a84
info  🔄 Run: #15223 (attempt 1)

@github-actions github-actions bot added fix and removed fix labels Mar 10, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 10, 2026

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  1 minute, 12 seconds
commit  d422a84
info  🔄 Run: #15223 (attempt 1)

The V1 SDKAnalyticsFlags view dynamically generated a DRF serializer
inside get_serializer_class. Extract this into a proper
SDKAnalyticsFlagsV1Serializer so the view mirrors the clean V2 pattern
of serializer_class / get_serializer / is_valid / save.
@gagantrivedi gagantrivedi force-pushed the refactor/remove-dynamic-serializer-v1-analytics branch from a7e4367 to b8edc1d Compare March 11, 2026 03:35
@github-actions github-actions bot added fix and removed fix labels Mar 11, 2026
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Free Tier Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Boolean values pass isinstance(count, int) check
    • Changed the V1 analytics serializer to accept only exact int values and added a regression test confirming boolean counts are skipped.

Create PR

Or push these changes by commenting:

@cursor push 97bade19b0
Preview (97bade19b0)
diff --git a/api/app_analytics/serializers.py b/api/app_analytics/serializers.py
--- a/api/app_analytics/serializers.py
+++ b/api/app_analytics/serializers.py
@@ -137,7 +137,7 @@
         return {
             name: count
             for name, count in data.items()
-            if isinstance(name, str) and isinstance(count, int)
+            if isinstance(name, str) and type(count) is int
         }
 
     def validate(self, attrs: dict[str, int]) -> dict[str, int]:

diff --git a/api/tests/unit/app_analytics/test_unit_app_analytics_views.py b/api/tests/unit/app_analytics/test_unit_app_analytics_views.py
--- a/api/tests/unit/app_analytics/test_unit_app_analytics_views.py
+++ b/api/tests/unit/app_analytics/test_unit_app_analytics_views.py
@@ -110,6 +110,31 @@
     )
 
 
+def test_sdk_analytics_flags_v1__boolean_count__is_skipped(
+    mocker: MockerFixture,
+    environment: Environment,
+    feature: Feature,
+    api_client: APIClient,
+) -> None:
+    # Given
+    api_client.credentials(HTTP_X_ENVIRONMENT_KEY=environment.api_key)
+    data = {feature.name: True}
+    mocked_feature_eval_cache = mocker.patch(
+        "app_analytics.views.feature_evaluation_cache"
+    )
+
+    url = reverse("api-v1:analytics-flags")
+
+    # When
+    response = api_client.post(
+        url, data=json.dumps(data), content_type="application/json"
+    )
+
+    # Then
+    assert response.status_code == status.HTTP_200_OK
+    mocked_feature_eval_cache.track_feature_evaluation.assert_not_called()
+
+
 def test_get_usage_data(mocker, admin_client, organisation):  # type: ignore[no-untyped-def]
     # Given
     url = reverse("api-v1:organisations:usage-data", args=[organisation.id])

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@gagantrivedi gagantrivedi force-pushed the refactor/remove-dynamic-serializer-v1-analytics branch from dcd9372 to d422a84 Compare March 11, 2026 03:51
@github-actions github-actions bot added fix and removed fix labels Mar 11, 2026
Copy link
Contributor

@Zaimwa9 Zaimwa9 left a comment

Choose a reason for hiding this comment

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

Looks good! Indeed, I can get why linking the sentry issue with this code was sneaky

@gagantrivedi gagantrivedi merged commit f1082d4 into main Mar 11, 2026
33 checks passed
@gagantrivedi gagantrivedi deleted the refactor/remove-dynamic-serializer-v1-analytics branch March 11, 2026 06:06
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 fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeError: Field 'evaluation_count' expected a number but got {'abc': {'xyz:bcd': 2}}.

2 participants