Skip to content

feat(usage): expose API limit restriction, grace period and overage billing state - #8641

Open
Zaimwa9 wants to merge 6 commits into
mainfrom
feat/grace-period-state-8256
Open

Zaimwa9 wants to merge 6 commits into
mainfrom
feat/grace-period-state-8256

Conversation

@Zaimwa9

@Zaimwa9 Zaimwa9 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

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 #8256. Supersedes #8584 and #8492.

Four read-only fields on the organisation response:

Field Meaning
stop_serving_flags Flag serving is paused (existing model field)
api_limit_restriction_enabled Free plan with at least one api_limiting_* flag on
api_limit_grace_period_used A breached grace period row exists
overage_billing_eligible The billing task would charge this organisation: Start-Up or Scale-Up, not cancelled, current monthly term, api_usage_overage_charges on

The two computed fields are false when ENABLE_API_USAGE_ALERTING is off, since the tasks aren't registered then.

The restriction task shares get_api_limit_restrictions(). is_overage_billing_eligible() mirrors the billing task's eligibility, with a parity test running the real task against it. Neither task's behaviour changes.

The viewset select_related subscription, subscription_information_cache and breached_grace_period, so the fields add no per-organisation queries.

How did you test this code?

  • Unit tests for the services, the serializer, the restriction task with both flags off, and the billing-eligibility parity test.
  • Manually against a local API with seeded free and paid organisations, with and without a grace row.

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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: 649883a6-1d7f-4605-bd34-17cafc177fac
📥 Commits

Reviewing files that changed from the base of the PR and between 385de5f and 4ee3862.

📒 Files selected for processing (3)
  • api/organisations/services.py
  • infrastructure/aws/production/ecs-task-definition-admin-api.json
  • infrastructure/aws/staging/ecs-task-definition-admin-api.json

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The changes add API limit restriction evaluation for free-plan organisations and use the result in the grace-period task. The organisation serializer exposes restriction status, grace-period usage and overage billing eligibility as read-only fields. The overage eligibility check uses subscription, billing-period and feature-flag conditions. The frontend organisation type and OpenAPI schemas include the response fields. Unit tests cover the evaluator, task behaviour and serializer output.

Priority: ➖ Normal

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

Merge Risk: ⚪ Minimal · up to 4ee38

The API adds read-only organization status fields. No concrete behavior violating the repository’s response or billing contract is established, so no material merge-blocking risk remains.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 04a37

The new fields remain read-only and organisation-scoped, without granting billing or enforcement authority. The reviewed enforcement lifecycle appears preserved. Disabled-alerting responses do not fully match the stated all-false behavior, and incomplete evidence prevents a minimal-risk assessment.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The reviewed status exposure is bounded to organisations available to the authenticated caller. Restriction outcomes remain organisation-level flag-serving and admin-access state; no broader write authority follows from serializing these values.

Trust Boundaries and Controls

  • observed — Client-supplied status values do not become enforcement or billing commands. The fields are read-only, and the update test verifies that submitted computed status fields are excluded from validated write data.

Resilience and Maintainability Implications

  • observed — The enforcement lifecycle retains an access-block ownership marker and recovery that clears restriction fields before deleting that marker. Notification, organisation persistence and marker creation remain separate steps; the lifecycle probe identified this as pre-existing behavior rather than a newly introduced failure-containment weakness.
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

@vercel

vercel Bot commented Oct 1, 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 Oct 7, 2026 9:00am UTC
flagsmith-frontend-preview Ignored Ignored Preview Oct 7, 2026 9:00am UTC
flagsmith-frontend-staging Ignored Ignored Preview Oct 7, 2026 9:00am UTC

Request Review

@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.85%. Comparing base (8fae0a4) to head (4ee3862).
⚠️ Report is 21 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #8641    +/-   ##
========================================
  Coverage   98.84%   98.85%            
========================================
  Files        1658     1666     +8     
  Lines       68447    68788   +341     
========================================
+ Hits        67657    67999   +342     
+ Misses        790      789     -1     

☔ 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.

@Zaimwa9

Zaimwa9 commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

@themis-blindfold review

Comment thread api/organisations/serializers.py
Comment thread api/organisations/serializers.py
@themis-blindfold

Copy link
Copy Markdown
Contributor

⚖️ Themis review: 🟠 Fix before merge

This adds the requested organisation state, but api_limit_grace_period_used cannot distinguish a free-plan restriction grace record from a paid overage grace record after an upgrade. The organisation list also gains one database lookup per returned organisation. The Vercel – docs check failed; the diff contains no documentation change that establishes a connection.

Targeted tests could not run locally because the sandbox cannot fetch the project dependencies.

Area Score
🎯 Correctness 2/5
🧪 Test coverage 3/5
📐 Code quality 3/5
🚀 Product impact 3/5

🟠 Majors

  • api/organisations/serializers.py:120 — reports paid grace-period use after a free-plan breach and upgrade.
  • api/organisations/serializers.py:61 — adds a reverse-relation query for every organisation in the list response.
📝 Walkthrough
  • Organisation response - adds three read-only API fields for restriction and grace-period state.
  • API limit service - centralises free-plan OpenFeature restriction evaluation for the API and enforcement task.
  • Restriction task - consumes the shared restriction state without changing its enforcement flow.
  • API contracts - updates the frontend type and generated OpenAPI specifications.
🧪 How to verify
  1. Breach the free-plan limit, upgrade the organisation, and confirm the paid grace state is reset or independently represented before any paid overage.
  2. With a user belonging to many organisations, request GET /api/v1/organisations/ and assert relation queries remain constant rather than scaling with the result count.
  3. Exercise the restriction task with each individual restriction flag enabled and with both disabled.
  4. Run cd api && uv run pytest tests/unit/organisations/test_unit_organisations_services.py tests/unit/organisations/test_unit_organisations_serializers.py tests/unit/organisations/test_unit_organisations_tasks.py -q.
    Automate: add regression coverage for free-to-paid grace-state transitions and a list-query-count assertion.

Product take: The new response fields would make API-limit state visible to the usage experience, a solid product improvement.
Until the grace record has unambiguous billing semantics, it can give upgraded customers the wrong charge-coverage status.

🧭 Assumptions & unverified claims

No unverified assumptions or claims.

One grace-period row cannot do two jobs without a name tag · reviewed at 023fcf4

@Zaimwa9

Zaimwa9 commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

@themis-blindfold review

Comment thread frontend/common/types/responses.ts
@themis-blindfold

Copy link
Copy Markdown
Contributor

⚖️ Themis review: 🟠 Fix before merge

The API now exposes the required state, but the dashboard never reads it: it still infers possible charges from the plan name and billing period. As a result, the usage page cannot tell an over-limit customer whether grace covers them or a charge applies, nor surface the new restriction state. All completed CI checks passed.

Area Score
🎯 Correctness 2/5
🧪 Test coverage 3/5
📐 Code quality 4/5
🚀 Product impact 2/5

🟠 Majors

  • frontend/common/types/responses.ts:558 — the usage page never consumes the state it adds.

⚖️ Acknowledged

  • A free-plan breach can be shown as paid grace usage after an upgrade — thread resolved by @Zaimwa9
  • Organisation-list serialisation now preloads the grace-period relation — thread resolved by @Zaimwa9
📝 Walkthrough
  • Organisation API - adds four read-only fields for restriction, grace, and overage-billing state.
  • Restriction and billing services - centralise the predicates used to calculate those fields.
  • Usage dashboard - adds response typings but does not use the new values to change customer-facing guidance.
🧪 How to verify
  1. Create a Start-Up or Scale-Up organisation at 100–199% of its allowance with overage billing enabled and no grace record; confirm the usage page says the current period is covered.
  2. Repeat with a grace record, then at 200% usage; confirm both cases say that overage charges apply.
  3. Disable overage eligibility and confirm an over-limit organisation is not presented as chargeable.
  4. Enable each free-plan restriction flag independently and confirm the usage page communicates the corresponding restriction state.
    Automate: Add UsageDashboardPage tests covering eligibility, grace use, the 200% threshold, and both restriction flags.

Product take: The server now supplies the facts the usage experience needs, but customers still receive the old generic guidance. Until the dashboard consumes them, this is a partial implementation of a customer-facing billing feature.

🧭 Assumptions & unverified claims

No unverified assumptions or claims.

The API has brought the facts; the dashboard still needs to read the briefing. · reviewed at cc07346

@Zaimwa9 Zaimwa9 changed the title feat(usage): return grace period and restriction state from the API feat(usage): expose API limit restriction, grace period and overage billing state Oct 2, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Oct 2, 2026
@Zaimwa9
Zaimwa9 requested a review from a team as a code owner October 2, 2026 13:27
@Zaimwa9
Zaimwa9 requested review from khvn26 and talissoncosta and removed request for a team October 2, 2026 13:27
@github-actions github-actions Bot removed the feature New feature or request label Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Docker builds report

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

@github-actions github-actions Bot added the feature New feature or request label Oct 2, 2026

@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: e27ea962-cd51-40b1-9e17-b7c02abe9cf2

📥 Commits

Reviewing files that changed from the base of the PR and between 023fcf4 and 04a37b3.

📒 Files selected for processing (12)
  • api/organisations/constants.py
  • api/organisations/dataclasses.py
  • api/organisations/serializers.py
  • api/organisations/services.py
  • api/organisations/tasks.py
  • api/organisations/views.py
  • api/tests/unit/organisations/test_unit_organisations_serializers.py
  • api/tests/unit/organisations/test_unit_organisations_services.py
  • api/tests/unit/organisations/test_unit_organisations_tasks.py
  • frontend/common/types/responses.ts
  • mcp/src/flagsmith_mcp/openapi.json
  • openapi.yaml

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

Comment thread api/organisations/serializers.py
@github-actions

github-actions Bot commented Oct 2, 2026 •

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

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

passed  4 passed

Details

stats  4 tests across 4 suites
duration  46.8 seconds
commit  4ee3862
info  🔄 Run: #21279 (attempt 1)

🗂️ Previous results
✅ private-cloud · depot-ubuntu-latest-16 — run #21279 (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  4ee3862
info  🔄 Run: #21279 (attempt 1)

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

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  37.5 seconds
commit  4ee3862
info  🔄 Run: #21279 (attempt 1)

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

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

passed  19 passed
skipped  1 skipped

Details

stats  20 tests across 13 suites
duration  1 minute, 5 seconds
commit  4ee3862
info  🔄 Run: #21279 (attempt 1)

Skipped tests

firefox › tests/onboarding-tests.pw.ts › Onboarding › New user connects via the single-page onboarding flow @oss

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

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  1 minute, 11 seconds
commit  2829efe
info  🔄 Run: #21278 (attempt 1)

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

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

passed  3 passed

Details

stats  3 tests across 3 suites
duration  33.5 seconds
commit  2829efe
info  🔄 Run: #21278 (attempt 1)

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

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  38.2 seconds
commit  2829efe
info  🔄 Run: #21278 (attempt 1)

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

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  32.8 seconds
commit  2829efe
info  🔄 Run: #21278 (attempt 1)

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

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  36.7 seconds
commit  385de5f
info  🔄 Run: #21251 (attempt 1)

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

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

passed  3 passed

Details

stats  3 tests across 3 suites
duration  33.3 seconds
commit  385de5f
info  🔄 Run: #21251 (attempt 1)

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

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  31.9 seconds
commit  385de5f
info  🔄 Run: #21251 (attempt 1)

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

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  4.6 seconds
commit  385de5f
info  🔄 Run: #21251 (attempt 1)

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Visual Regression

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

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.

such simply named functions, such a lot of code 😄

Comment on lines +26 to +39
openfeature_client = get_openfeature_client()
evaluation_context = organisation.openfeature_evaluation_context
return APILimitRestrictions(
stop_serving_flags=openfeature_client.get_boolean_value(
"api_limiting_stop_serving_flags",
default_value=False,
evaluation_context=evaluation_context,
),
block_access_to_admin=openfeature_client.get_boolean_value(
"api_limiting_block_access_to_admin",
default_value=False,
evaluation_context=evaluation_context,
),
)

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'm not sure I understand why we're using Flagsmith for these versus the stop_serving_flags and block_access_to_admin attributes on the Organisation model?

@Zaimwa9 Zaimwa9 Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, the naming was confusing. The model fields say if the org is blocked right now, and these fields were already in the response. The flags decide what it'll do once the grace period is over, so this is more "would we cut them off if they go over".
FE needs that to warn free orgs before it happens. I renamed it to APILimitEnforcement so it doesn't confict with the model fields anymore wdyt ?

Cf the comment above 😅

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.

Should we not just have the FE access the flags directly in that case? Since we merged the projects, I think that's feasible, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's feasible but do we really want to? This way it's living close to the task where it decides whether the org should be cut-off, so it avoids drifting and duplicating the (small, I agree) logic that combines the free plan check.
This way the frontend just blindly applies what the BE is sending.

But re-looking at it, i realized ENABLE_API_USAGE_ALERTING is only set in the task processor as we speak. I'll add it now

Comment thread api/organisations/services.py
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Oct 6, 2026
@Zaimwa9
Zaimwa9 requested a review from matthewelwell October 6, 2026 16:38
@Zaimwa9
Zaimwa9 requested a review from a team as a code owner October 7, 2026 08:55
@github-actions github-actions Bot added infrastructure feature New feature or request and removed feature New feature or request infrastructure labels Oct 7, 2026
@github-actions github-actions Bot added infrastructure feature New feature or request and removed feature New feature or request infrastructure labels Oct 7, 2026

This branch was successfully deployed

2 active (outdated) deployments
Preview – flagsmith-frontend-preview — cc07346e Deployed Oct 1, 2026 by vercel[bot]
Preview – flagsmith-frontend-staging — cc07346e Deployed Oct 1, 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 front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants