feat(billing): add generic BillingUsage model for quota tracking#3273
feat(billing): add generic BillingUsage model for quota tracking#3273PierreBrisorgueil merged 2 commits intomasterfrom
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
WalkthroughA new generic billing usage tracking system is introduced with a Mongoose model, Zod validation schema, repository data-access layer, service coordinator, and comprehensive unit tests. Supports atomic counter increments per organization and month without project-specific logic. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
Pull request overview
Adds a generic billing usage/quota tracking layer to the billing module, providing a monthly bucketed BillingUsage model with atomic counter increments and a thin service wrapper for downstream quota enforcement.
Changes:
- Introduces
BillingUsageMongoose model (organizationId,month,counters) with a compound unique index for per-org monthly usage. - Adds repository + service APIs to
get,increment(atomic$incwith upsert), andresetusage counters. - Adds Zod schema validation and a new unit test suite covering schema defaults/validation and service delegation behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/billing/models/billing.usage.model.mongoose.js | New Mongoose model for monthly per-organization usage counters with unique index. |
| modules/billing/models/billing.usage.schema.js | New Zod schema for validating usage payloads and defaulting counters. |
| modules/billing/repositories/billing.usage.repository.js | New data access layer for get/increment/reset usage documents. |
| modules/billing/services/billing.usage.service.js | New service wrapper that computes current month and delegates to repository. |
| modules/billing/tests/billing.usage.unit.tests.js | New unit tests for the Zod schema and service behavior via mocked repository. |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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/models/billing.usage.model.mongoose.js`:
- Around line 13-23: The schema currently defines separate indexes on
organizationId and month but also creates a compound unique index {
organizationId: 1, month: 1 }; remove the redundant individual index on
organizationId to avoid duplicate indexes and write overhead while keeping the
compound unique index and the individual index on month if you still need
month-only queries; update the billing usage schema by deleting the index: true
(or index definition) on the organizationId field (leave month.index if
required) and ensure the compound unique index ({ organizationId: 1, month: 1 })
remains defined.
In `@modules/billing/models/billing.usage.schema.js`:
- Line 13: The month field's regex in the billing usage schema (the month:
z.string().trim().regex(...) entry) only checks format and allows invalid months
like 00 or 13; update the validation to enforce that the MM portion is between
01 and 12 (i.e., restrict months to 01–12) and keep the YYYY- MM format, e.g.,
replace the current generic YYYY-MM pattern with one that constrains the month
range so downstream code can assume valid calendar months.
In `@modules/billing/repositories/billing.usage.repository.js`:
- Around line 29-33: The increment function interpolates the `key` directly into
the update path (`counters.${key}`) which allows dots or other characters to
create unintended nested documents; add validation/sanitization in the increment
function (or a shared validator used by BillingUsage repository) to reject or
normalize keys containing dots or other unsafe characters (e.g., allow only a
safe pattern like alphanumerics, underscore, hyphen) before calling
BillingUsage.findOneAndUpdate, and throw a clear error if the key is invalid so
callers cannot trigger nested paths via `counters.${key}`.
- Around line 29-33: The increment function omits ObjectId validation for
organizationId which can cause a Mongoose cast error on upsert; add the same
validation used in get/reset (e.g., using mongoose.Types.ObjectId.isValid or
mongoose.isValidObjectId) at the start of the increment function and throw or
return a rejected promise with a clear error when invalid, before calling
BillingUsage.findOneAndUpdate so BillingUsage.findOneAndUpdate({ organizationId,
month }, ...) is only invoked with a valid ObjectId.
In `@modules/billing/services/billing.usage.service.js`:
- Around line 10-15: The currentMonth function uses local time (new
Date().getMonth()) which is timezone-dependent; change currentMonth to construct
the date in UTC by using getUTCFullYear() and getUTCMonth() (add 1 and pad with
String(...).padStart(2,'0')) so the returned YYYY-MM is based on UTC and
consistent across servers and DST changes.
- Around line 31-34: The function get calls currentMonth() twice which can race
across a month boundary; capture the month once into a local variable (e.g.,
const month = currentMonth()) and use that variable in the call to
UsageRepository.get(organizationId, month) and in the fallback object ({
organizationId, month, counters: {} }) so both paths use the same month value;
update the get function to reference month instead of calling currentMonth()
again.
In `@modules/billing/tests/billing.usage.unit.tests.js`:
- Line 4: The test file imports jest, beforeEach, and afterEach but omits the
Jest globals used elsewhere; update the import from '@jest/globals' to also
import describe, test, and expect so the file explicitly imports describe, test,
expect (in addition to jest, beforeEach, afterEach) to match other tests and
avoid relying on implicit globals.
🪄 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: cce0f1d6-e43c-473e-bd99-9df8745e9325
📒 Files selected for processing (5)
modules/billing/models/billing.usage.model.mongoose.jsmodules/billing/models/billing.usage.schema.jsmodules/billing/repositories/billing.usage.repository.jsmodules/billing/services/billing.usage.service.jsmodules/billing/tests/billing.usage.unit.tests.js
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
- Validate organizationId in increment(), sanitize counter key against injection - Handle E11000 duplicate key race on concurrent upserts (retry once) - Add runValidators to reset() for consistency with increment() - Tighten month regex to reject invalid months (00, 13) - Use UTC for currentMonth() to avoid timezone-dependent boundaries - Capture month once in get() to prevent cross-boundary inconsistency - Remove redundant individual index on organizationId (compound covers it) - Add missing Jest globals imports in test file - Add tests for semantically invalid month values
Summary
BillingUsageMongoose model withorganizationId,month(YYYY-MM), and free-formcounters(Mixed), with compound unique index on{ organizationId, month }get,increment(atomic$incwith upsert), andresetoperationsBillingUsageCloses #3269
Test plan
npm run lintpasses (0 errors)billing.usage.unit.tests.js— 11 tests covering schema + service layerSummary by CodeRabbit
New Features
Tests