Skip to content

feat(api): added price types#3982

Open
rolosp wants to merge 3 commits intomainfrom
feat/productcatalog-v3-price
Open

feat(api): added price types#3982
rolosp wants to merge 3 commits intomainfrom
feat/productcatalog-v3-price

Conversation

@rolosp
Copy link
Collaborator

@rolosp rolosp commented Mar 20, 2026

Overview

Adds price and unit-config types to the productcatalog package
as a preparatory step toward the full product catalog API (plans, addons, rate
cards).

No new API surface is introduced (the types are not yet reachable)

Summary by CodeRabbit

  • New Features
    • Added product catalog pricing: free, flat, per-unit, graduated tiers, and volume pricing, including tiered pricing support and spend commitment fields.
    • Added usage-based pricing variants and payment term options (in advance / in arrears).
    • Introduced unit conversion/configuration for billing with multiply/divide operations, rounding modes, precision, and display unit.
    • Added invoice usage details: raw, converted, and invoiced quantities plus applied unit-config snapshots.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

Adds a ProductCatalog TypeSpec module (pricing primitives, price discriminated unions and tiers, payment terms, spend commitments) and unit conversion/rounding models with invoice quantity detail. The OpenMeter entrypoint now imports the ProductCatalog module so its types are included in the generated API spec.

Changes

Cohort / File(s) Summary
Main Entrypoint
api/spec/packages/aip/src/openmeter.tsp
Imported the productcatalog module so its types are merged into the OpenMeter API spec.
ProductCatalog index
api/spec/packages/aip/src/productcatalog/index.tsp
Registered new feature files: price.tsp and unitconfig.tsp.
Pricing types
api/spec/packages/aip/src/productcatalog/price.tsp
Added ProductCatalog.PriceType enum, ProductCatalog.Price and PriceUsageBased discriminated unions (no envelope, discriminator type), concrete price models (PriceFree, PriceFlat, PriceUnit, PriceGraduated, PriceVolume), PriceTier, PricePaymentTerm, and SpendCommitments with visibility/summary metadata.
Unit & invoice types
api/spec/packages/aip/src/productcatalog/unitconfig.tsp
Added UnitConfigOperation and UnitConfigRoundingMode enums, UnitConfig model (operation, conversion_factor, rounding, precision, display_unit), and InvoiceUsageQuantityDetail (raw/converted/invoiced quantities and applied unit config).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • tothandras
  • turip
  • hekike
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(api): added price types' accurately summarizes the main change: introducing new price-related types to the productcatalog package.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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/productcatalog-v3-price
📝 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.

@rolosp rolosp added the release-note/feature Release note: Exciting New Features label Mar 20, 2026
@rolosp rolosp force-pushed the feat/productcatalog-v3-price branch from d0a15f5 to 3c90261 Compare March 20, 2026 10:19
@rolosp rolosp marked this pull request as ready for review March 20, 2026 10:19
@rolosp rolosp requested a review from a team as a code owner March 20, 2026 10:19
Copy link
Contributor

@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: 1

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

Inline comments:
In `@api/spec/packages/aip/src/productcatalog/price.tsp`:
- Around line 190-214: The PriceTier TypeSpec model currently allows
up_to_amount, flat_price, and unit_price to all be optional even though the
server enforces "At least one price component (flat_price or unit_price) must be
set" in Validate() (openmeter/productcatalog/price.go::Validate); update the
PriceTier doc comment to explicitly state this invariant is enforced server-side
and must be validated by the service (mentioning Validate()), and if desired add
a custom TypeSpec decorator or flag to surface this constraint in generated
schemas; ensure no code changes remove the existing Validate() logic and that
consumers are directed to rely on server-side validation for the one-of
requirement between flat_price and unit_price.
🪄 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: CHILL

Plan: Pro

Run ID: 4434bf59-5cf6-46b2-abe4-4151107a12fc

📥 Commits

Reviewing files that changed from the base of the PR and between 51db29b and 3c90261.

📒 Files selected for processing (4)
  • api/spec/packages/aip/src/openmeter.tsp
  • api/spec/packages/aip/src/productcatalog/index.tsp
  • api/spec/packages/aip/src/productcatalog/price.tsp
  • api/spec/packages/aip/src/productcatalog/unitconfig.tsp

Comment on lines +190 to +214
model PriceTier {
/**
* Up to and including this quantity will be contained in the tier.
* If undefined, the tier is open-ended (the last tier).
*/
@visibility(Lifecycle.Read, Lifecycle.Create, Lifecycle.Update)
@summary("Up to quantity")
up_to_amount?: Shared.Numeric;

/**
* The flat price component of the tier.
* Charged once when the tier is entered.
*/
@visibility(Lifecycle.Read, Lifecycle.Create, Lifecycle.Update)
@summary("Flat price component")
flat_price?: PriceFlat;

/**
* The unit price component of the tier.
* Charged per billing unit within the tier.
*/
@visibility(Lifecycle.Read, Lifecycle.Create, Lifecycle.Update)
@summary("Unit price component")
unit_price?: PriceUnit;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

PriceTier validation rule not enforced in schema.

The doc comment on line 185 correctly states "At least one price component (flat_price or unit_price) must be set", and the Go implementation (openmeter/productcatalog/price.go:749-750) enforces this in Validate(). However, the TypeSpec model allows all three fields to be optional without any constraint.

TypeSpec doesn't have a native "oneOf required" decorator, so this invariant will need server-side validation. Just flagging so it's on the radar - might be worth adding a more prominent note in the doc string that this is a server-enforced constraint, or exploring if a custom decorator could help here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/spec/packages/aip/src/productcatalog/price.tsp` around lines 190 - 214,
The PriceTier TypeSpec model currently allows up_to_amount, flat_price, and
unit_price to all be optional even though the server enforces "At least one
price component (flat_price or unit_price) must be set" in Validate()
(openmeter/productcatalog/price.go::Validate); update the PriceTier doc comment
to explicitly state this invariant is enforced server-side and must be validated
by the service (mentioning Validate()), and if desired add a custom TypeSpec
decorator or flag to surface this constraint in generated schemas; ensure no
code changes remove the existing Validate() logic and that consumers are
directed to rely on server-side validation for the one-of requirement between
flat_price and unit_price.

Copy link
Contributor

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

🧹 Nitpick comments (1)
api/spec/packages/aip/src/productcatalog/price.tsp (1)

52-56: Consider adding @summary decorators for consistency.

The Price union above has @summary decorators on each member (lines 28-41), but PriceUsageBased doesn't. Adding them would keep the generated docs consistent across both unions.

✨ Suggested improvement
 union PriceUsageBased {
+  `@summary`("Unit price")
   unit: PriceUnit,
+  `@summary`("Graduated price")
   graduated: PriceGraduated,
+  `@summary`("Volume price")
   volume: PriceVolume,
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/spec/packages/aip/src/productcatalog/price.tsp` around lines 52 - 56, The
PriceUsageBased union is missing `@summary` decorators on its members (unit,
graduated, volume); add matching `@summary` annotations for each variant in the
PriceUsageBased union (same style/phrasing used in the Price union's members) so
generated docs remain consistent across both unions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@api/spec/packages/aip/src/productcatalog/price.tsp`:
- Around line 52-56: The PriceUsageBased union is missing `@summary` decorators on
its members (unit, graduated, volume); add matching `@summary` annotations for
each variant in the PriceUsageBased union (same style/phrasing used in the Price
union's members) so generated docs remain consistent across both unions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bd60ebed-beef-45ab-bbcd-c24f7d6a2558

📥 Commits

Reviewing files that changed from the base of the PR and between 745a037 and f66d7f2.

📒 Files selected for processing (1)
  • api/spec/packages/aip/src/productcatalog/price.tsp

@rolosp rolosp force-pushed the feat/productcatalog-v3-price branch from f66d7f2 to 3a2f4a6 Compare March 20, 2026 15:06
Copy link
Contributor

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

🧹 Nitpick comments (2)
api/spec/packages/aip/src/productcatalog/price.tsp (2)

52-56: Consider adding @summary decorators for consistency.

The Price union (lines 28-41) includes @summary decorators on each variant, but PriceUsageBased doesn't. For consistent API documentation, you might want to add summaries here too:

📝 Suggested improvement
 union PriceUsageBased {
+  `@summary`("Unit price")
   unit: PriceUnit,
+  `@summary`("Graduated price")
   graduated: PriceGraduated,
+  `@summary`("Volume price")
   volume: PriceVolume,
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/spec/packages/aip/src/productcatalog/price.tsp` around lines 52 - 56, Add
`@summary` decorators to each variant of the PriceUsageBased union to match the
existing style used in the Price union: annotate the unit, graduated, and volume
variants in PriceUsageBased with concise `@summary` strings describing each
variant (e.g., "Per-unit pricing", "Graduated pricing by tiers", "Volume-based
pricing") so documentation is consistent; update the PriceUsageBased union
declaration to include these `@summary` decorators for unit, graduated, and
volume.

221-240: Consider documenting the min ≤ max constraint.

When both minimum_amount and maximum_amount are set, there's an implicit business rule that minimum should not exceed maximum. This will need server-side validation - might be worth mentioning in the doc comment so API consumers know what to expect.

📝 Suggested doc improvement
 /**
  * Spend commitments for a rate card.
  * The customer is committed to spend at least the minimum amount and at most the maximum amount.
+ *
+ * When both are specified, minimum_amount must not exceed maximum_amount (validated server-side).
  */
 `@friendlyName`("BillingSpendCommitments")
 model SpendCommitments {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/spec/packages/aip/src/productcatalog/price.tsp` around lines 221 - 240,
Add a clear note in the SpendCommitments model doc comment that when both
minimum_amount and maximum_amount are provided the invariant minimum_amount ≤
maximum_amount must hold, and implement server-side validation enforcing this
rule (e.g., in the create/update handlers or validation layer that processes
SpendCommitments) to return a descriptive error when the constraint is violated;
reference the SpendCommitments model and the minimum_amount and maximum_amount
fields so consumers and implementers see the requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@api/spec/packages/aip/src/productcatalog/price.tsp`:
- Around line 52-56: Add `@summary` decorators to each variant of the
PriceUsageBased union to match the existing style used in the Price union:
annotate the unit, graduated, and volume variants in PriceUsageBased with
concise `@summary` strings describing each variant (e.g., "Per-unit pricing",
"Graduated pricing by tiers", "Volume-based pricing") so documentation is
consistent; update the PriceUsageBased union declaration to include these
`@summary` decorators for unit, graduated, and volume.
- Around line 221-240: Add a clear note in the SpendCommitments model doc
comment that when both minimum_amount and maximum_amount are provided the
invariant minimum_amount ≤ maximum_amount must hold, and implement server-side
validation enforcing this rule (e.g., in the create/update handlers or
validation layer that processes SpendCommitments) to return a descriptive error
when the constraint is violated; reference the SpendCommitments model and the
minimum_amount and maximum_amount fields so consumers and implementers see the
requirement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a2531349-76bd-4049-b498-0cbc61dfac91

📥 Commits

Reviewing files that changed from the base of the PR and between f66d7f2 and 3a2f4a6.

📒 Files selected for processing (4)
  • api/spec/packages/aip/src/openmeter.tsp
  • api/spec/packages/aip/src/productcatalog/index.tsp
  • api/spec/packages/aip/src/productcatalog/price.tsp
  • api/spec/packages/aip/src/productcatalog/unitconfig.tsp
✅ Files skipped from review due to trivial changes (2)
  • api/spec/packages/aip/src/openmeter.tsp
  • api/spec/packages/aip/src/productcatalog/index.tsp
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/spec/packages/aip/src/productcatalog/unitconfig.tsp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/feature Release note: Exciting New Features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants