Skip to content

fix(api): entitlements static config#4512

Merged
tothandras merged 3 commits into
mainfrom
fix/entitlement-v3-ratecard-entitlement-static
Jun 11, 2026
Merged

fix(api): entitlements static config#4512
tothandras merged 3 commits into
mainfrom
fix/entitlement-v3-ratecard-entitlement-static

Conversation

@tothandras

@tothandras tothandras commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes

    • Preserve client-provided JSON for static entitlements (unwrap legacy string-wrapped JSON, keep non-wrapped payloads) and avoid hard-failing reads.
    • Malformed or missing usage periods now yield proper validation errors; billing cadence is parsed once and applied consistently during conversions.
    • Boolean and other entitlement types now round-trip reliably.
  • Tests

    • Added unit and end-to-end tests for static-config round-trips, legacy token handling, metered-entitlement validation, and cross-version serialization.

@tothandras tothandras added the release-note/bug-fix Release note: Bug Fixes label Jun 11, 2026
@tothandras tothandras requested a review from a team as a code owner June 11, 2026 16:02
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2060b718-8b82-45d9-ab70-8f35ee4efabe

📥 Commits

Reviewing files that changed from the base of the PR and between 5e4731b and 8c8ba8c.

📒 Files selected for processing (3)
  • api/v3/handlers/addons/convert.go
  • api/v3/handlers/addons/convert_test.go
  • api/v3/handlers/plans/convert.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • api/v3/handlers/addons/convert_test.go
  • api/v3/handlers/addons/convert.go
  • api/v3/handlers/plans/convert.go

📝 Walkthrough

Walkthrough

Unwraps legacy JSON-string-wrapped static entitlement configs for v3 reads, re-encodes configs as string tokens for domain storage on writes, centralizes billing-cadence parsing, standardizes metered usage-period validation errors, and adds unit and E2E tests covering cross-version behavior.

Changes

Entitlement config and billing cadence handling

Layer / File(s) Summary
Static entitlement config unwrapping (domain → API)
api/v3/handlers/addons/convert.go, api/v3/handlers/plans/convert.go
ToAPIBillingRateCardEntitlement detects and unwraps legacy JSON-string-wrapped configs into json.RawMessage, with fallback to stored bytes for non-wrapped values.
Static entitlement config encoding (API → domain)
api/v3/handlers/addons/convert.go, api/v3/handlers/plans/convert.go
FromAPIBillingRateCardEntitlement marshals the v3 union, extracts raw config bytes, normalizes empty to JSON null, and re-encodes as a JSON-string token for domain storage.
Billing cadence parsing optimization
api/v3/handlers/addons/convert.go, api/v3/handlers/plans/convert.go
Rate-card conversion parses BillingCadence once up front and reuses the parsed value for flat and usage-based constructions and when converting entitlements.
Metered entitlement validation improvements
api/v3/handlers/addons/convert.go, api/v3/handlers/plans/convert.go
usage_period parse failures now return models.NewGenericValidationError(...) for consistent validation error handling.
Addon entitlement conversion tests
api/v3/handlers/addons/convert_test.go
New tests with roundTripStaticConfig helper covering static/metered/boolean entitlements, JSON shape round-trips, legacy token handling, billing-cadence defaulting, and validation errors.
Plan conversion test extensions
api/v3/handlers/plans/convert_test.go
Extended tests validating v1 string-token preservation, large-integer fidelity, legacy stored-config handling, and additional metered entitlement validation/round-trips.
E2E cross-version static-config test
e2e/plan_entitlement_template_v3_test.go
New E2E test asserting v3 JSON object static configs and v1 string-token configs round-trip across versions, plus helpers for constructing v3 rate cards and extracting v1 configs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • openmeterio/openmeter#4493: Modifies the v3 rate-card entitlement conversion layer; this PR extends entitlement JSON/static-config and billing-cadence parsing behavior introduced there.

Suggested labels

area/api

Suggested reviewers

  • rolosp
  • chrisgacsal
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing how static entitlement configs are handled in the API layer across v3 and v1 conversions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/entitlement-v3-ratecard-entitlement-static

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
api/v3/handlers/plans/convert_test.go (1)

1354-1367: ⚡ Quick win

Tighten the large-integer assertion to verify JSON shape, not just substring presence.

Current check on Line 1366 proves the digits are present, but it doesn’t fully prove the returned payload shape. Consider asserting exact JSON (or decoding and checking the id token explicitly) so this test fails on subtle representation regressions too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v3/handlers/plans/convert_test.go` around lines 1354 - 1367, The test
currently only checks that the digits "9007199254740993" appear in raw JSON;
instead decode the resulting JSON (out.MarshalJSON result in raw) into a small
struct or map and assert the id field equals the integer 9007199254740993 (or
assert exact expected JSON string) to verify shape and type; locate the test
block using FromAPIBillingRateCardEntitlement, ToAPIBillingRateCardEntitlement,
apiEnt and out and replace the assert.Contains on raw with a strict
decode-and-equal assertion against the id field.
api/v3/handlers/addons/convert_test.go (2)

150-159: ⚡ Quick win

Assert validation error classification, not just presence.

On Line 157 you already assert an error exists; it’s worth also asserting models.IsGenericValidationError(err) so this test protects the intended API validation contract (not just any error).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v3/handlers/addons/convert_test.go` around lines 150 - 159, The test
currently only asserts an error was returned when converting a metered
entitlement; update the case inside the t.Run ("metered — missing usage period
with no billing cadence is a validation error") to also assert the error is
classified as a generic validation error by calling
models.IsGenericValidationError(err) (keep the existing require.Error(t, err)
and assert.Nil(t, domain) checks). Locate the conversion call
FromAPIBillingRateCardEntitlement and add an assertion that
models.IsGenericValidationError(err) is true to ensure the validation contract
is enforced.

38-101: ⚡ Quick win

Add the same large-integer static-config round-trip case here for addon parity.

Nice coverage here overall. One gap: this suite doesn’t pin the 9007199254740993-style case, so addon and plan converter tests can drift on numeric preservation behavior. Adding that one case would lock in the no-float64-loss contract on the addon path too (same conversion logic, same regression risk).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v3/handlers/addons/convert_test.go` around lines 38 - 101, Add a t.Run
case in TestFromToAPIBillingRateCardEntitlement that verifies a large integer
preserves identity through the static-config round-trip: use
roundTripStaticConfig(t, 9007199254740993) (or as a numeric literal convertible
to interface{}) and assert equality with the original value, mirroring the
existing scalar/string/array tests; place it alongside the other "static — ..."
subtests so the addon conversion path (roundTripStaticConfig,
FromAPIBillingRateCardEntitlement/ToAPIBillingRateCardEntitlement) is pinned to
preserve large-integer semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/v3/handlers/addons/convert.go`:
- Around line 525-536: The current unwrap logic for static.Config misinterprets
legacy JSON string values like "\"true\"" as wrapped JSON because
json.Unmarshal(..., &text) + json.Valid(text) will match primitives; change the
heuristic so after successfully unmarshalling into text you only unwrap when the
inner text is clearly structured JSON (e.g., its first non-space character is
'{' or '[') — otherwise keep config = json.RawMessage(static.Config); update the
block handling static.Config/config in convert.go accordingly (look for the
variables static.Config, text, config).

---

Nitpick comments:
In `@api/v3/handlers/addons/convert_test.go`:
- Around line 150-159: The test currently only asserts an error was returned
when converting a metered entitlement; update the case inside the t.Run
("metered — missing usage period with no billing cadence is a validation error")
to also assert the error is classified as a generic validation error by calling
models.IsGenericValidationError(err) (keep the existing require.Error(t, err)
and assert.Nil(t, domain) checks). Locate the conversion call
FromAPIBillingRateCardEntitlement and add an assertion that
models.IsGenericValidationError(err) is true to ensure the validation contract
is enforced.
- Around line 38-101: Add a t.Run case in
TestFromToAPIBillingRateCardEntitlement that verifies a large integer preserves
identity through the static-config round-trip: use roundTripStaticConfig(t,
9007199254740993) (or as a numeric literal convertible to interface{}) and
assert equality with the original value, mirroring the existing
scalar/string/array tests; place it alongside the other "static — ..." subtests
so the addon conversion path (roundTripStaticConfig,
FromAPIBillingRateCardEntitlement/ToAPIBillingRateCardEntitlement) is pinned to
preserve large-integer semantics.

In `@api/v3/handlers/plans/convert_test.go`:
- Around line 1354-1367: The test currently only checks that the digits
"9007199254740993" appear in raw JSON; instead decode the resulting JSON
(out.MarshalJSON result in raw) into a small struct or map and assert the id
field equals the integer 9007199254740993 (or assert exact expected JSON string)
to verify shape and type; locate the test block using
FromAPIBillingRateCardEntitlement, ToAPIBillingRateCardEntitlement, apiEnt and
out and replace the assert.Contains on raw with a strict decode-and-equal
assertion against the id field.
🪄 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: dbe4e1e7-2a50-4973-868f-6b56706c39c6

📥 Commits

Reviewing files that changed from the base of the PR and between fffcde6 and 31e0fd6.

📒 Files selected for processing (5)
  • api/v3/handlers/addons/convert.go
  • api/v3/handlers/addons/convert_test.go
  • api/v3/handlers/plans/convert.go
  • api/v3/handlers/plans/convert_test.go
  • e2e/plan_entitlement_template_v3_test.go

Comment thread api/v3/handlers/addons/convert.go

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
e2e/plan_entitlement_template_v3_test.go (1)

188-236: ⚡ Quick win

Use given/when/then comments in these subtests.

These are non-trivial lifecycle subtests, but the inline commentary is still free-form. Please switch the section comments to concise given/when/then intent markers so the setup/action/assert flow is easier to scan.

As per coding guidelines, "For service and lifecycle subtests in Go, start each subtest body with concise intent comments using given-when-then format when the scenario is non-trivial".

Also applies to: 238-307

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/plan_entitlement_template_v3_test.go` around lines 188 - 236, The subtest
starting with t.Run("write via v3 (JSON object) -> read via v1 (JSON string
token)", ...) (and the similar subtest at 238-307) lacks structured intent
comments; update the subtest body to use concise given/when/then markers: add a
"given" comment before setup steps (uniqueKey, v3.CreateFeature, unmarshalling
configObj, ent.FromBillingRateCardStaticEntitlement, and CreatePlan), a "when"
comment before the action (the v1.GetPlanWithResponse call), and a "then"
comment before assertions (v1StaticConfig usage, json.Unmarshal into inner and
assert.JSONEq). Keep comments short and placed immediately above the appropriate
code blocks to make the lifecycle flow clear.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@e2e/plan_entitlement_template_v3_test.go`:
- Around line 188-236: The subtest starting with t.Run("write via v3 (JSON
object) -> read via v1 (JSON string token)", ...) (and the similar subtest at
238-307) lacks structured intent comments; update the subtest body to use
concise given/when/then markers: add a "given" comment before setup steps
(uniqueKey, v3.CreateFeature, unmarshalling configObj,
ent.FromBillingRateCardStaticEntitlement, and CreatePlan), a "when" comment
before the action (the v1.GetPlanWithResponse call), and a "then" comment before
assertions (v1StaticConfig usage, json.Unmarshal into inner and assert.JSONEq).
Keep comments short and placed immediately above the appropriate code blocks to
make the lifecycle flow clear.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9c11dc11-e438-4b9d-b399-013d599f25ef

📥 Commits

Reviewing files that changed from the base of the PR and between 31e0fd6 and 5e4731b.

📒 Files selected for processing (1)
  • e2e/plan_entitlement_template_v3_test.go

@tothandras tothandras added release-note/ignore Ignore this change when generating release notes and removed release-note/bug-fix Release note: Bug Fixes labels Jun 11, 2026
@tothandras tothandras enabled auto-merge (squash) June 11, 2026 18:18
@tothandras tothandras merged commit 50c8999 into main Jun 11, 2026
44 of 48 checks passed
@tothandras tothandras deleted the fix/entitlement-v3-ratecard-entitlement-static branch June 11, 2026 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/ignore Ignore this change when generating release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants