Skip to content

feat(billing): add GET /api/billing/usage endpoint#3277

Merged
PierreBrisorgueil merged 3 commits intomasterfrom
feat/billing-usage-endpoint
Mar 19, 2026
Merged

feat(billing): add GET /api/billing/usage endpoint#3277
PierreBrisorgueil merged 3 commits intomasterfrom
feat/billing-usage-endpoint

Conversation

@PierreBrisorgueil
Copy link
Contributor

@PierreBrisorgueil PierreBrisorgueil commented Mar 19, 2026

Summary

  • Add GET /api/billing/usage endpoint returning current plan, period, usage counters, and flattened quota limits
  • Add read ability on BillingUsage CASL subject for authenticated users with org membership
  • Add route-to-subject mapping in policy middleware for /api/billing/usage

Closes #3272

Test plan

  • Unit tests: active subscription returns correct usage and limits
  • Unit tests: no subscription falls back to free plan
  • Unit tests: past_due subscription falls back to free plan
  • Unit tests: unknown plan returns empty limits
  • Unit tests: flattened limits format is correct
  • Unit tests: new org with no usage returns empty counters
  • Lint passes
  • All 252 tests pass

Summary by CodeRabbit

  • New Features
    • Added a billing usage endpoint that returns your organization's plan, current billing period, usage metrics, and quota limits.
  • Bug Fixes
    • Enabled organization members to view billing usage (access control updated for the new resource).
  • Tests
    • Added unit tests covering usage responses, plan/default behavior, quota flattening, and error handling.

Copilot AI review requested due to automatic review settings March 19, 2026 07:37
@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

Warning

Rate limit exceeded

@PierreBrisorgueil has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 2 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b24ba00f-9f0d-40e6-a49f-cb491c39a964

📥 Commits

Reviewing files that changed from the base of the PR and between 03cd794 and be70ae7.

📒 Files selected for processing (2)
  • modules/billing/controllers/billing.controller.js
  • modules/billing/tests/billing.usage.endpoint.unit.tests.js

Walkthrough

Adds a new authenticated endpoint GET /api/billing/usage plus policy mapping and controller logic to return an organization's billing plan, current usage counters, flattened quota limits, and billing period; includes unit tests and CASL BillingUsage read permission.

Changes

Cohort / File(s) Summary
Policy Mapping
lib/middlewares/policy.js
Map route /api/billing/usage to CASL subject type BillingUsage.
Billing Routes
modules/billing/routes/billing.routes.js
Register authenticated GET /api/billing/usage route with JWT, org resolution, and policy middleware; handler -> billing.getUsage.
Billing Abilities
modules/billing/policies/billing.policy.js
Grant read permission on BillingUsage to authenticated organization members.
Controller
modules/billing/controllers/billing.controller.js
Add getUsage handler: determines effective plan (defaults to free for none or past_due), fetches usage via BillingUsageService.get, flattens config quotas to {resource}.{action} keys (normalizing non-finite to null), and returns { plan, period, usage, limits } with 500 error handling.
Tests
modules/billing/tests/billing.usage.endpoint.unit.tests.js
New Jest suite covering active/no/past_due subscriptions, empty quotas, quota flattening, empty usage counters, period value, and error path; uses per-test mocks for subscription + usage services and config.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant API as "API /api/billing/usage"
  participant Auth as "JWT / policy"
  participant Org as "organization.resolveOrganization"
  participant Controller as "BillingController.getUsage"
  participant SubSvc as "BillingService"
  participant UsageSvc as "BillingUsageService"
  participant Config as "Config (quotas)"

  Client->>API: GET /api/billing/usage (JWT)
  API->>Auth: validate JWT & policy.isAllowed
  Auth-->>API: allowed
  API->>Org: resolve organization
  Org-->>Controller: org id
  Controller->>SubSvc: getSubscription(orgId)
  SubSvc-->>Controller: subscription | null
  Controller->>UsageSvc: get(orgId)
  UsageSvc-->>Controller: usage { counters, month }
  Controller->>Config: read billing.quotas[plan]
  Controller-->>Controller: flatten quotas, normalize limits
  Controller-->>Client: 200 { plan, period, usage, limits }
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: adding a GET endpoint for billing usage, which is the core feature introduced in this changeset.
Description check ✅ Passed The PR description covers the summary of changes and references the linked issue (#3272), but the template structure is not fully followed—missing explicit Scope, Validation checklist sections, and Guardrails check items.
Linked Issues check ✅ Passed The implementation fully addresses all requirements from linked issue #3272: endpoint returns correct response structure with plan, usage counters, limits, and period; JWT auth and org context enforced; CASL read ability granted; tests verify all acceptance criteria.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the billing usage endpoint per issue #3272; no unrelated modifications detected across policy, routes, controller, and test files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/billing-usage-endpoint
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new authenticated billing usage endpoint so the frontend can fetch current plan usage counters and quota limits in one call, with CASL authorization wired through the policy middleware.

Changes:

  • Add GET /api/billing/usage route guarded by JWT, org resolution, and CASL.
  • Add read ability for BillingUsage and map /api/billing/usageBillingUsage subject type.
  • Implement getUsage controller logic and add unit tests for plan fallback + quota flattening.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
modules/billing/controllers/billing.controller.js Adds getUsage controller to compute plan, period, usage counters, and flattened limits.
modules/billing/routes/billing.routes.js Registers GET /api/billing/usage with auth/org/policy middleware chain.
modules/billing/policies/billing.policy.js Grants read on BillingUsage for authenticated users with org membership.
lib/middlewares/policy.js Maps the new route path to the BillingUsage CASL subject.
modules/billing/tests/billing.usage.endpoint.unit.tests.js Adds unit coverage for subscription states, unknown plan, flattening, and error path.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@modules/billing/controllers/billing.controller.js`:
- Around line 7-8: The controller is calling SubscriptionRepository directly
(imported as SubscriptionRepository) which breaks the service-layer boundary;
add a subscription retrieval method on the service layer (e.g., add
getSubscriptionById or fetchSubscription in BillingUsageService) that uses
SubscriptionRepository internally, then update the controller to call
BillingUsageService.getSubscriptionById(...) instead of referencing
SubscriptionRepository, and remove the SubscriptionRepository import from the
controller so all repository access is encapsulated by BillingUsageService.
- Around line 89-99: The code copies planQuotas values directly into limits
which means non-finite values like Infinity will become null when
JSON-serialized; update the assignment where limits[`${resource}.${action}`] =
planQuotas[resource][action]; to detect non-finite numbers (e.g.,
Number.isFinite) and encode them explicitly (for example the string "unlimited"
or another agreed sentinel) before building the response so
responses.success(...) returns JSON-safe quota values that match the intended
meaning; touch the limits population logic and ensure any downstream consumers
expect the chosen sentinel, leaving usage.counters and responses.success calls
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bdb36d2e-4ff1-496a-b972-f84f05e16415

📥 Commits

Reviewing files that changed from the base of the PR and between 0f5347e and 3ccb712.

📒 Files selected for processing (5)
  • lib/middlewares/policy.js
  • modules/billing/controllers/billing.controller.js
  • modules/billing/policies/billing.policy.js
  • modules/billing/routes/billing.routes.js
  • modules/billing/tests/billing.usage.endpoint.unit.tests.js

- Move subscription lookup behind BillingService (service-layer rule)
- Remove SubscriptionRepository import from controller
- Use usage.month for period instead of recomputing (avoid drift)
- Normalize Infinity quotas to null for JSON-safe serialization
- Update tests to match new service boundary and null limits
@PierreBrisorgueil
Copy link
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@modules/billing/controllers/billing.controller.js`:
- Line 70: The current ternary that sets const plan = (!subscription ||
subscription.status === 'past_due') ? 'free' : (subscription.plan || 'free');
only guards one non-active status; change the logic so only subscription.status
values 'active' or 'trialing' permit the paid plan and all other statuses map to
'free' (e.g., if no subscription or status not in {'active','trialing'} =>
'free', else use subscription.plan); apply the identical fix in the similar
check inside billing.requireQuota.js (the middleware that determines plan/quota)
so both controller (plan assignment) and middleware use the same whitelist of
allowed statuses.

In `@modules/billing/tests/billing.usage.endpoint.unit.tests.js`:
- Around line 100-114: Add unit tests covering other non-active subscription
statuses by duplicating the existing test for 'past_due' and changing
mockBillingService.getSubscription to resolve values like { plan: 'starter',
status: 'canceled' } and { plan: 'starter', status: 'unpaid' } (or any
additional statuses your controller will treat as non-active); call
billingController.getUsage with the same mocked req/res and assert the response
contains plan: 'free' and the expected limits, reusing
mockBillingUsageService.get as in the current test to ensure the controller
behavior is validated for each non-active status.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 43f0adbb-a015-40ac-b214-3825e98d6738

📥 Commits

Reviewing files that changed from the base of the PR and between 0f5347e and 03cd794.

📒 Files selected for processing (5)
  • lib/middlewares/policy.js
  • modules/billing/controllers/billing.controller.js
  • modules/billing/policies/billing.policy.js
  • modules/billing/routes/billing.routes.js
  • modules/billing/tests/billing.usage.endpoint.unit.tests.js

- Replace past_due-only check with active/trialing allowlist
- All other statuses (canceled, unpaid, incomplete, etc.) now map to free
- Add test.each for all non-active statuses + trialing test
@PierreBrisorgueil PierreBrisorgueil merged commit 0c2efb2 into master Mar 19, 2026
3 checks passed
@PierreBrisorgueil PierreBrisorgueil deleted the feat/billing-usage-endpoint branch March 19, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(billing): GET /api/billing/usage endpoint

2 participants