Skip to content

feat(entitlement): add grant balances to v2 entitlement value api#3922

Merged
GAlexIHU merged 2 commits into
mainfrom
feat/grant-balances-on-entitlement-value
Mar 11, 2026
Merged

feat(entitlement): add grant balances to v2 entitlement value api#3922
GAlexIHU merged 2 commits into
mainfrom
feat/grant-balances-on-entitlement-value

Conversation

@GAlexIHU
Copy link
Copy Markdown
Contributor

@GAlexIHU GAlexIHU commented Mar 4, 2026

Overview

Adds grant level balances to V2 entitlement value APIs

Summary by CodeRabbit

  • New Features

    • Added v2 customer entitlement endpoints returning an enhanced entitlement response with access state, optional balance/usage/overage, total available grant amount, config, and per-grant balances. Router now forwards time for v2 lookups so responses can be requested for a specific timestamp.
  • Tests

    • Strengthened end-to-end and multi-subject tests to require per-grant balances and assert they sum to the reported balance, ensuring parity across APIs.

@GAlexIHU GAlexIHU requested a review from a team as a code owner March 4, 2026 15:59
@GAlexIHU GAlexIHU marked this pull request as draft March 4, 2026 15:59
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Entitlement V2: new EntitlementValueV2 model (with per-grant grantBalances), V2 API mappings and handlers, wiring of grant balances through metered entitlement domain models, router switched to V2 handlers, and tests asserting grant-balance parity.

Changes

Cohort / File(s) Summary
API Spec
api/spec/src/entitlements/v2/customer.tsp
Replaced getCustomerEntitlementValue return type with EntitlementValueV2; added EntitlementValueV2 model including hasAccess, balance, usage, overage, totalAvailableGrantAmount, config, grantBalances.
Domain — Metered Entitlement
openmeter/entitlement/metered/balance.go, openmeter/entitlement/metered/connector.go, openmeter/entitlement/metered/reset.go
Added GrantBalances map[string]float64 to EntitlementBalance and MeteredEntitlementValue; populate and propagate grant balances from snapshot/balance/reset flows.
HTTP API Mapping
openmeter/customer/httpdriver/apimapping.go
Added EntitlementValueV2 and CustomerAccessV2 types; implemented MapEntitlementValueToAPIV2() and MapAccessToAPIV2() to map domain values (including conditional grantBalances) to V2 API shapes.
HTTP Handlers & Interface
openmeter/customer/httpdriver/customer.go, openmeter/customer/httpdriver/handler.go
Added V2 handler types and implementations: GetCustomerEntitlementValueV2() and GetCustomerAccessV2(); extended CustomerHandler interface; updated request/params to carry Time.
Router
openmeter/server/router/entitlement.go
Router now invokes V2 handlers for access and entitlement-value routes and forwards Time to the V2 entitlement value request.
Tests
e2e/entitlement_parity_test.go, e2e/multisubject_test.go
Added helper and assertions ensuring GrantBalances is present and non-empty, and that sum(grantBalances) equals reported balance in parity checks across v1/v2 APIs.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as "Client"
    participant Router as "Router\n(openmeter/server)"
    participant Handler as "CustomerHandler\nV2 Handler"
    participant Service as "EntitlementService"
    participant Metered as "Metered Connector\n(GetValue/GetEntitlementBalance)"
    participant Mapper as "APIMapping\nMapEntitlementValueToAPIV2"

    Client->>Router: GET /api/.../entitlement/value (V2)
    Router->>Handler: invoke GetCustomerEntitlementValueV2
    Handler->>Service: Fetch entitlement value for customer (with Time)
    Service->>Metered: GetValue / GetEntitlementBalance (includes GrantBalances)
    Metered-->>Service: EntitlementValue (with GrantBalances)
    Service-->>Handler: domain EntitlementValue (with GrantBalances)
    Handler->>Mapper: MapEntitlementValueToAPIV2(domain value)
    Mapper-->>Handler: EntitlementValueV2 (includes grantBalances)
    Handler-->>Client: 200 OK + EntitlementValueV2 JSON
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

kind/feature, area/entitlements

Suggested reviewers

  • tothandras
  • chrisgacsal
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 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: adding grant balances to the V2 entitlement value API, which is the core feature reflected throughout all file changes.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/grant-balances-on-entitlement-value

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.

@GAlexIHU GAlexIHU added the release-note/feature Release note: Exciting New Features label Mar 4, 2026
@GAlexIHU GAlexIHU marked this pull request as ready for review March 4, 2026 16:02
Copy link
Copy Markdown
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: 3

🧹 Nitpick comments (1)
openmeter/customer/httpdriver/customer.go (1)

430-481: Please extract the shared V1/V2 customer-resolution flow to avoid drift.

Line 432 onward and Line 550 onward repeat almost the same request/customer lookup and deleted-check flow as V1. Pulling that into small helpers would make these endpoints easier to maintain and less likely to diverge.

♻️ Suggested extraction sketch
+func (h *handler) buildEntitlementValueRequest(
+	ctx context.Context,
+	params GetCustomerEntitlementValueParams,
+) (GetCustomerEntitlementValueRequest, error) {
+	ns, err := h.resolveNamespace(ctx)
+	if err != nil {
+		return GetCustomerEntitlementValueRequest{}, err
+	}
+
+	cus, err := h.service.GetCustomer(ctx, customer.GetCustomerInput{
+		CustomerIDOrKey: &customer.CustomerIDOrKey{
+			IDOrKey:   params.CustomerIDOrKey,
+			Namespace: ns,
+		},
+	})
+	if err != nil {
+		return GetCustomerEntitlementValueRequest{}, err
+	}
+	if cus != nil && cus.IsDeleted() {
+		return GetCustomerEntitlementValueRequest{},
+			models.NewGenericPreConditionFailedError(
+				fmt.Errorf("customer is deleted [namespace=%s customer.id=%s]", cus.Namespace, cus.ID),
+			)
+	}
+
+	return GetCustomerEntitlementValueRequest{
+		FeatureKey: params.FeatureKey,
+		CustomerID: cus.GetID(),
+	}, nil
+}
+
+func (h *handler) buildCustomerAccessRequest(
+	ctx context.Context,
+	params GetCustomerAccessParams,
+) (GetCustomerAccessRequest, error) {
+	ns, err := h.resolveNamespace(ctx)
+	if err != nil {
+		return GetCustomerAccessRequest{}, err
+	}
+	return GetCustomerAccessRequest{
+		CustomerIDOrKey: &customer.CustomerIDOrKey{
+			Namespace: ns,
+			IDOrKey:   params.CustomerIDOrKey,
+		},
+	}, nil
+}

As per coding guidelines, **/*.go: In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them.

Also applies to: 548-596

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

In `@openmeter/customer/httpdriver/customer.go` around lines 430 - 481, The
customer resolution, lookup and deleted-check logic repeated in
GetCustomerEntitlementValueV2 (and the V1 handler) should be extracted into a
small helper to avoid drift: create a helper function (e.g.
resolveAndValidateCustomer(ctx context.Context, idOrKey string)
(customer.CustomerID, error)) that calls h.resolveNamespace, calls
h.service.GetCustomer with customer.GetCustomerInput/customer.CustomerIDOrKey,
checks cus != nil && cus.IsDeleted() and returns
models.NewGenericPreConditionFailedError when deleted, and returns the canonical
customer ID on success; then replace the inline blocks in
GetCustomerEntitlementValueV2 (and the V1 handler) to call this helper and use
its returned CustomerID for entitlementService.GetEntitlementValue to keep
behavior identical and reduce duplication.
🤖 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/src/entitlements/v2/customer.tsp`:
- Around line 235-236: Replace the floating-point types used for monetary/credit
fields with the shared precise numeric type: update the type of balance, usage,
overage, totalAvailableGrantAmount, and grantBalances from float64 to
Shared.Numeric in the customer model/type definitions (e.g., where the fields
balance?: float64; appears). Ensure the TypeSpec file imports/uses the Shared
namespace so Shared.Numeric is resolvable and adjust any array/map annotations
for grantBalances to use Shared.Numeric elements as needed.

In `@openmeter/customer/httpdriver/apimapping.go`:
- Around line 17-25: The JSON tag on the HasAccess field in EntitlementValueV2
currently uses `omitempty` which drops false and violates the API contract;
update the struct definition for EntitlementValueV2 by removing `omitempty` from
the `HasAccess` json tag so the boolean is always serialized (leave other fields
unchanged) and run tests to verify serialization matches the contract.

In `@openmeter/server/router/entitlement.go`:
- Around line 223-227: GetCustomerEntitlementValueV2 currently ignores the
incoming params (so query time filters like the `time` param are dropped);
update Router.GetCustomerEntitlementValueV2 to populate and pass the received
`params` into customerdriver.GetCustomerEntitlementValueParams (e.g., attach the
relevant fields from the `params` argument such as time/start/end or the entire
params struct) when calling
a.customerHandler.GetCustomerEntitlementValueV2().With(...).ServeHTTP so the
handler receives and honors historical/time-based lookup parameters.

---

Nitpick comments:
In `@openmeter/customer/httpdriver/customer.go`:
- Around line 430-481: The customer resolution, lookup and deleted-check logic
repeated in GetCustomerEntitlementValueV2 (and the V1 handler) should be
extracted into a small helper to avoid drift: create a helper function (e.g.
resolveAndValidateCustomer(ctx context.Context, idOrKey string)
(customer.CustomerID, error)) that calls h.resolveNamespace, calls
h.service.GetCustomer with customer.GetCustomerInput/customer.CustomerIDOrKey,
checks cus != nil && cus.IsDeleted() and returns
models.NewGenericPreConditionFailedError when deleted, and returns the canonical
customer ID on success; then replace the inline blocks in
GetCustomerEntitlementValueV2 (and the V1 handler) to call this helper and use
its returned CustomerID for entitlementService.GetEntitlementValue to keep
behavior identical and reduce duplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0af068b1-88b1-4f97-b85d-0ba29a6fb8b9

📥 Commits

Reviewing files that changed from the base of the PR and between 02cd2a7 and c9a2a9f.

⛔ Files ignored due to path filters (8)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/javascript/src/client/schemas.ts is excluded by !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/models/__init__.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/models/_models.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (10)
  • api/api.gen.go
  • api/spec/src/entitlements/v2/customer.tsp
  • e2e/entitlement_parity_test.go
  • e2e/multisubject_test.go
  • openmeter/customer/httpdriver/apimapping.go
  • openmeter/customer/httpdriver/customer.go
  • openmeter/customer/httpdriver/handler.go
  • openmeter/entitlement/metered/balance.go
  • openmeter/entitlement/metered/connector.go
  • openmeter/server/router/entitlement.go

Comment on lines +235 to +236
balance?: float64;

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.

⚠️ Potential issue | 🟠 Major

Use Shared.Numeric for these financial fields instead of float64.

balance, usage, overage, totalAvailableGrantAmount, and grantBalances values are monetary/credit quantities; using float64 here risks precision drift across generated clients.

💡 Proposed TypeSpec adjustment
-  balance?: float64;
+  balance?: Shared.Numeric;

-  usage?: float64;
+  usage?: Shared.Numeric;

-  overage?: float64;
+  overage?: Shared.Numeric;

-  totalAvailableGrantAmount?: float64;
+  totalAvailableGrantAmount?: Shared.Numeric;

-  grantBalances?: Record<float64>;
+  grantBalances?: Record<Shared.Numeric>;

Based on learnings: In TypeSpec API specs, prefer using Shared.Numeric for financial values (e.g., exchange rates, amounts, prices) instead of native numeric types like decimal128 or float64.

Also applies to: 242-243, 249-250, 256-257, 270-270

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

In `@api/spec/src/entitlements/v2/customer.tsp` around lines 235 - 236, Replace
the floating-point types used for monetary/credit fields with the shared precise
numeric type: update the type of balance, usage, overage,
totalAvailableGrantAmount, and grantBalances from float64 to Shared.Numeric in
the customer model/type definitions (e.g., where the fields balance?: float64;
appears). Ensure the TypeSpec file imports/uses the Shared namespace so
Shared.Numeric is resolvable and adjust any array/map annotations for
grantBalances to use Shared.Numeric elements as needed.

Comment on lines +17 to +25
type EntitlementValueV2 struct {
HasAccess bool `json:"hasAccess,omitempty"`
Balance *float64 `json:"balance,omitempty"`
Config *string `json:"config,omitempty"`
Overage *float64 `json:"overage,omitempty"`
TotalAvailableGrantAmount *float64 `json:"totalAvailableGrantAmount,omitempty"`
Usage *float64 `json:"usage,omitempty"`
GrantBalances *map[string]float64 `json:"grantBalances,omitempty"`
}
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.

⚠️ Potential issue | 🟠 Major

hasAccess is required but can be omitted due omitempty.

The generated API contract expects hasAccess to always be present; with omitempty, false gets dropped from JSON.

💡 Proposed fix
 type EntitlementValueV2 struct {
-	HasAccess                 bool                `json:"hasAccess,omitempty"`
+	HasAccess                 bool                `json:"hasAccess"`
 	Balance                   *float64            `json:"balance,omitempty"`
 	Config                    *string             `json:"config,omitempty"`
 	Overage                   *float64            `json:"overage,omitempty"`
 	TotalAvailableGrantAmount *float64            `json:"totalAvailableGrantAmount,omitempty"`
 	Usage                     *float64            `json:"usage,omitempty"`
 	GrantBalances             *map[string]float64 `json:"grantBalances,omitempty"`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type EntitlementValueV2 struct {
HasAccess bool `json:"hasAccess,omitempty"`
Balance *float64 `json:"balance,omitempty"`
Config *string `json:"config,omitempty"`
Overage *float64 `json:"overage,omitempty"`
TotalAvailableGrantAmount *float64 `json:"totalAvailableGrantAmount,omitempty"`
Usage *float64 `json:"usage,omitempty"`
GrantBalances *map[string]float64 `json:"grantBalances,omitempty"`
}
type EntitlementValueV2 struct {
HasAccess bool `json:"hasAccess"`
Balance *float64 `json:"balance,omitempty"`
Config *string `json:"config,omitempty"`
Overage *float64 `json:"overage,omitempty"`
TotalAvailableGrantAmount *float64 `json:"totalAvailableGrantAmount,omitempty"`
Usage *float64 `json:"usage,omitempty"`
GrantBalances *map[string]float64 `json:"grantBalances,omitempty"`
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openmeter/customer/httpdriver/apimapping.go` around lines 17 - 25, The JSON
tag on the HasAccess field in EntitlementValueV2 currently uses `omitempty`
which drops false and violates the API contract; update the struct definition
for EntitlementValueV2 by removing `omitempty` from the `HasAccess` json tag so
the boolean is always serialized (leave other fields unchanged) and run tests to
verify serialization matches the contract.

Comment thread openmeter/server/router/entitlement.go
Copy link
Copy Markdown
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 `@openmeter/entitlement/metered/balance.go`:
- Around line 25-31: The EntitlementBalance struct's GrantBalances map must be
initialized on every return path so responses never omit/null that field; modify
the reset flow in the EntitlementBalance creation/update in reset.go (the code
that constructs or returns an EntitlementBalance after reset) to ensure
GrantBalances is set to an empty map (map[string]float64{}) when no grants
exist, and similarly add initialization wherever EntitlementBalance is
constructed or returned (look for functions/methods that return
EntitlementBalance or modify its fields, including the reset handler in reset.go
and any factory/constructor for EntitlementBalance) so GrantBalances is always
non-nil.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a0f85e82-3e0c-452d-8253-a3c2a83b2930

📥 Commits

Reviewing files that changed from the base of the PR and between c9a2a9f and 4630edb.

📒 Files selected for processing (2)
  • openmeter/entitlement/metered/balance.go
  • openmeter/entitlement/metered/connector.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • openmeter/entitlement/metered/connector.go

Comment thread openmeter/entitlement/metered/balance.go
Copy link
Copy Markdown
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

🧹 Nitpick comments (1)
openmeter/customer/httpdriver/customer.go (1)

437-491: Consider extracting shared customer-read flow used by v1/v2 handlers.

The v1/v2 entitlement-value and access handlers are almost identical; pulling customer resolution + validation into a helper would reduce drift and make future fixes safer.

As per coding guidelines, “In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them.”

Also applies to: 558-606

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

In `@openmeter/customer/httpdriver/customer.go` around lines 437 - 491, The
customer-resolution and validation logic in GetCustomerEntitlementValueV2
(resolveNamespace, h.service.GetCustomer call, deleted check and building
GetEntitlementValueInput) is duplicated across v1/v2 handlers; extract it into a
single helper (e.g., handler.resolveAndValidateCustomerForEntitlement or
loadCustomerForEntitlement) that returns the namespace, customer ID wrapper
(customer.CustomerIDOrKey/GetEntitlementValueInput) and any error, reuse it from
GetCustomerEntitlementValueV2 and the corresponding v1 handler (the
near-duplicate block around lines 558-606) and update calls to use the helper
before calling h.entitlementService.GetEntitlementValue; keep existing error
types and precondition check (models.NewGenericPreConditionFailedError)
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openmeter/customer/httpdriver/customer.go`:
- Around line 407-410: The code dereferences the variable cus when building
GetEntitlementValueInput (and at the other uses around the same handler), but
cus can be nil if the lookup returned (nil, nil); add a nil-check immediately
after the customer lookup that returns an appropriate error/HTTP 404 (or the
handler's not-found response) instead of proceeding, and apply the same guard
before any other dereference sites referenced (the occurrences around the
GetEntitlementValueInput construction and the uses at the other two locations).
Ensure the check is placed before any use of cus.GetID() or cus fields so
GetEntitlementValueInput and other code never dereference a nil pointer.

---

Nitpick comments:
In `@openmeter/customer/httpdriver/customer.go`:
- Around line 437-491: The customer-resolution and validation logic in
GetCustomerEntitlementValueV2 (resolveNamespace, h.service.GetCustomer call,
deleted check and building GetEntitlementValueInput) is duplicated across v1/v2
handlers; extract it into a single helper (e.g.,
handler.resolveAndValidateCustomerForEntitlement or loadCustomerForEntitlement)
that returns the namespace, customer ID wrapper
(customer.CustomerIDOrKey/GetEntitlementValueInput) and any error, reuse it from
GetCustomerEntitlementValueV2 and the corresponding v1 handler (the
near-duplicate block around lines 558-606) and update calls to use the helper
before calling h.entitlementService.GetEntitlementValue; keep existing error
types and precondition check (models.NewGenericPreConditionFailedError)
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c1148714-ba59-44eb-bb39-be34b977bcd0

📥 Commits

Reviewing files that changed from the base of the PR and between 4630edb and 9ae4109.

📒 Files selected for processing (4)
  • openmeter/customer/httpdriver/customer.go
  • openmeter/entitlement/metered/balance.go
  • openmeter/entitlement/metered/reset.go
  • openmeter/server/router/entitlement.go

Comment on lines +407 to +410
GetEntitlementValueInput: customer.GetEntitlementValueInput{
FeatureKey: params.FeatureKey,
CustomerID: cus.GetID(),
},
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.

⚠️ Potential issue | 🔴 Critical

Guard against nil cus before dereferencing.

Line 410, Line 465, and Line 588 dereference cus without a cus == nil check. If lookup returns (nil, nil) on not-found, this can panic.

💡 Suggested guard pattern
@@
-            return GetCustomerEntitlementValueRequest{
+            if cus == nil {
+                return GetCustomerEntitlementValueRequest{}, fmt.Errorf("customer not found")
+            }
+
+            return GetCustomerEntitlementValueRequest{
                 GetEntitlementValueInput: customer.GetEntitlementValueInput{
                     FeatureKey: params.FeatureKey,
                     CustomerID: cus.GetID(),
                 },
                 At: defaultx.WithDefault(params.Time, clock.Now()),
             }, nil
@@
-            access, err := h.entitlementService.GetAccess(ctx, cus.Namespace, cus.ID)
+            if cus == nil {
+                return GetCustomerAccessV2Response{}, fmt.Errorf("customer not found")
+            }
+
+            access, err := h.entitlementService.GetAccess(ctx, cus.Namespace, cus.ID)

Also applies to: 462-466, 588-589

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

In `@openmeter/customer/httpdriver/customer.go` around lines 407 - 410, The code
dereferences the variable cus when building GetEntitlementValueInput (and at the
other uses around the same handler), but cus can be nil if the lookup returned
(nil, nil); add a nil-check immediately after the customer lookup that returns
an appropriate error/HTTP 404 (or the handler's not-found response) instead of
proceeding, and apply the same guard before any other dereference sites
referenced (the occurrences around the GetEntitlementValueInput construction and
the uses at the other two locations). Ensure the check is placed before any use
of cus.GetID() or cus fields so GetEntitlementValueInput and other code never
dereference a nil pointer.

@GAlexIHU GAlexIHU force-pushed the feat/grant-balances-on-entitlement-value branch from 9ae4109 to 6140c1e Compare March 5, 2026 14:44
Copy link
Copy Markdown
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: 2

♻️ Duplicate comments (1)
openmeter/customer/httpdriver/customer.go (1)

399-412: ⚠️ Potential issue | 🔴 Critical

Guard cus == nil before dereferencing in the new/updated GET handlers.

Line 409, Line 465, and Line 588 can panic when lookup returns (nil, nil).

💡 Suggested fix pattern
@@
-			if cus != nil && cus.IsDeleted() {
+			if cus == nil {
+				return GetCustomerEntitlementValueRequest{}, fmt.Errorf("failed to get customer")
+			}
+			if cus.IsDeleted() {
 				return GetCustomerEntitlementValueRequest{},
 					models.NewGenericPreConditionFailedError(
 						fmt.Errorf("customer is deleted [namespace=%s customer.id=%s]", cus.Namespace, cus.ID),
 					)
 			}
@@
-			if cus != nil && cus.IsDeleted() {
+			if cus == nil {
+				return GetCustomerEntitlementValueRequest{}, fmt.Errorf("failed to get customer")
+			}
+			if cus.IsDeleted() {
 				return GetCustomerEntitlementValueRequest{},
 					models.NewGenericPreConditionFailedError(
 						fmt.Errorf("customer is deleted [namespace=%s customer.id=%s]", cus.Namespace, cus.ID),
 					)
 			}
@@
-			if cus != nil && cus.IsDeleted() {
+			if cus == nil {
+				return GetCustomerAccessV2Response{}, fmt.Errorf("failed to get customer")
+			}
+			if cus.IsDeleted() {
 				return GetCustomerAccessV2Response{},
 					models.NewGenericPreConditionFailedError(
 						fmt.Errorf("customer is deleted [namespace=%s customer.id=%s]", cus.Namespace, cus.ID),
 					)
 			}

Also applies to: 455-468, 581-589

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

In `@openmeter/customer/httpdriver/customer.go` around lines 399 - 412, Guard
against cus being nil before any dereference in the GET handlers: check "if cus
== nil" before calling cus.IsDeleted() or accessing cus.Namespace, cus.ID, or
cus.GetID(), and if nil return an appropriate not-found error (e.g.,
models.NewGenericNotFoundError) along with an empty
GetCustomerEntitlementValueRequest; keep the existing return path that builds
GetCustomerEntitlementValueRequest (using customer.GetEntitlementValueInput,
params.FeatureKey, defaultx.WithDefault(params.Time, clock.Now())) only after
the nil check passes.
🧹 Nitpick comments (1)
openmeter/customer/httpdriver/customer.go (1)

437-491: Recommended: extract shared customer-resolution logic used by v1/v2 handlers.

GetCustomerEntitlementValue vs GetCustomerEntitlementValueV2 and GetCustomerAccess vs GetCustomerAccessV2 are mostly duplicated; a shared helper would reduce drift and prevent bug duplication.

As per coding guidelines "In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them."

Also applies to: 558-606

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

In `@openmeter/customer/httpdriver/customer.go` around lines 437 - 491, The
handler contains duplicated customer-resolution logic across
GetCustomerEntitlementValueV2 (and similar blocks in
GetCustomerEntitlementValue, GetCustomerAccess, GetCustomerAccessV2); extract
that into a shared helper (e.g., resolveCustomerForHandler or
buildCustomerRequest) that performs h.resolveNamespace(ctx), calls
h.service.GetCustomer with customer.GetCustomerInput / customer.CustomerIDOrKey,
checks cus != nil && cus.IsDeleted() and returns
models.NewGenericPreConditionFailedError, and computes defaults like At via
defaultx.WithDefault(params.Time, clock.Now()); then replace the duplicated
request-construction code in the request factory functions (which currently
build GetCustomerEntitlementValueRequest and similar) to call this helper and
return its result so all handlers reuse the same customer resolution path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/multisubject_test.go`:
- Around line 263-267: The test currently compares floating sums with
assert.Equal which is brittle; change the final assertion to a tolerant float
comparison (e.g., use testify's assert.InDelta) comparing *ent.Balance and
grantBalancesSum with a small delta (for example 1e-6) so tiny rounding
differences won't fail the test—replace the assert.Equal(t, *ent.Balance,
grantBalancesSum) line with an assert.InDelta(t, *ent.Balance, grantBalancesSum,
1e-6) (or equivalent) while keeping the sum computation using grantBalancesSum
and ent.GrantBalances as-is.

In `@openmeter/server/router/entitlement.go`:
- Around line 137-139: The route is calling
customerHandler.GetCustomerAccessV2() which returns the v2 schema (including
GrantBalances) and breaks the existing v1 contract; replace the call with
customerHandler.GetCustomerAccess().With(customerdriver.GetCustomerAccessParams{
CustomerIDOrKey: customerIdOrKey }).ServeHTTP(w, r) so the v1 CustomerAccess
handler is used, or if you intentionally want v2, update the route/operation
spec to reflect the new response schema instead of swapping handlers silently.

---

Duplicate comments:
In `@openmeter/customer/httpdriver/customer.go`:
- Around line 399-412: Guard against cus being nil before any dereference in the
GET handlers: check "if cus == nil" before calling cus.IsDeleted() or accessing
cus.Namespace, cus.ID, or cus.GetID(), and if nil return an appropriate
not-found error (e.g., models.NewGenericNotFoundError) along with an empty
GetCustomerEntitlementValueRequest; keep the existing return path that builds
GetCustomerEntitlementValueRequest (using customer.GetEntitlementValueInput,
params.FeatureKey, defaultx.WithDefault(params.Time, clock.Now())) only after
the nil check passes.

---

Nitpick comments:
In `@openmeter/customer/httpdriver/customer.go`:
- Around line 437-491: The handler contains duplicated customer-resolution logic
across GetCustomerEntitlementValueV2 (and similar blocks in
GetCustomerEntitlementValue, GetCustomerAccess, GetCustomerAccessV2); extract
that into a shared helper (e.g., resolveCustomerForHandler or
buildCustomerRequest) that performs h.resolveNamespace(ctx), calls
h.service.GetCustomer with customer.GetCustomerInput / customer.CustomerIDOrKey,
checks cus != nil && cus.IsDeleted() and returns
models.NewGenericPreConditionFailedError, and computes defaults like At via
defaultx.WithDefault(params.Time, clock.Now()); then replace the duplicated
request-construction code in the request factory functions (which currently
build GetCustomerEntitlementValueRequest and similar) to call this helper and
return its result so all handlers reuse the same customer resolution path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 29dc2af4-ce12-483e-99dc-1c0641b53c11

📥 Commits

Reviewing files that changed from the base of the PR and between 9ae4109 and 6140c1e.

⛔ Files ignored due to path filters (8)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/javascript/src/client/schemas.ts is excluded by !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/models/__init__.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/models/_models.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (11)
  • api/api.gen.go
  • api/spec/src/entitlements/v2/customer.tsp
  • e2e/entitlement_parity_test.go
  • e2e/multisubject_test.go
  • openmeter/customer/httpdriver/apimapping.go
  • openmeter/customer/httpdriver/customer.go
  • openmeter/customer/httpdriver/handler.go
  • openmeter/entitlement/metered/balance.go
  • openmeter/entitlement/metered/connector.go
  • openmeter/entitlement/metered/reset.go
  • openmeter/server/router/entitlement.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • e2e/entitlement_parity_test.go
  • openmeter/customer/httpdriver/handler.go
  • openmeter/entitlement/metered/balance.go
  • openmeter/customer/httpdriver/apimapping.go
  • api/spec/src/entitlements/v2/customer.tsp

Comment thread e2e/multisubject_test.go
Comment on lines +263 to +267
var grantBalancesSum float64
for _, grantBalance := range *ent.GrantBalances {
grantBalancesSum += grantBalance
}
assert.Equal(t, *ent.Balance, grantBalancesSum)
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.

⚠️ Potential issue | 🟡 Minor

Use tolerant float comparison for sum parity.

This exact equality can get flaky with fractional balances; better to assert within a tiny delta.

💡 Suggested tweak
-		assert.Equal(t, *ent.Balance, grantBalancesSum)
+		assert.InDelta(t, *ent.Balance, grantBalancesSum, 1e-9)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var grantBalancesSum float64
for _, grantBalance := range *ent.GrantBalances {
grantBalancesSum += grantBalance
}
assert.Equal(t, *ent.Balance, grantBalancesSum)
var grantBalancesSum float64
for _, grantBalance := range *ent.GrantBalances {
grantBalancesSum += grantBalance
}
assert.InDelta(t, *ent.Balance, grantBalancesSum, 1e-9)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/multisubject_test.go` around lines 263 - 267, The test currently compares
floating sums with assert.Equal which is brittle; change the final assertion to
a tolerant float comparison (e.g., use testify's assert.InDelta) comparing
*ent.Balance and grantBalancesSum with a small delta (for example 1e-6) so tiny
rounding differences won't fail the test—replace the assert.Equal(t,
*ent.Balance, grantBalancesSum) line with an assert.InDelta(t, *ent.Balance,
grantBalancesSum, 1e-6) (or equivalent) while keeping the sum computation using
grantBalancesSum and ent.GrantBalances as-is.

Comment on lines +137 to 139
a.customerHandler.GetCustomerAccessV2().With(customerdriver.GetCustomerAccessParams{
CustomerIDOrKey: customerIdOrKey,
}).ServeHTTP(w, r)
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check for GetCustomerAccess in generated API files and identify v1 vs v2 contracts
find . -name "*.go" -type f | head -20

Repository: openmeterio/openmeter

Length of output: 509


🏁 Script executed:

# Search for GetCustomerAccess definitions and their signatures
rg -n 'GetCustomerAccess' --type go | grep -E '(api\.gen|httpdriver|entitlement)' | head -30

Repository: openmeterio/openmeter

Length of output: 3481


🏁 Script executed:

# Look for the actual response types/structures
rg -n 'type.*CustomerAccess|type.*EntitlementValue' --type go -C 2 | head -50

Repository: openmeterio/openmeter

Length of output: 3152


🏁 Script executed:

# Find CustomerAccessV2 type definition
rg -n 'type CustomerAccessV2' --type go -A 10

Repository: openmeterio/openmeter

Length of output: 876


🏁 Script executed:

# Check the OpenAPI/generated route for GetCustomerAccess in api.gen.go
rg -n 'GetCustomerAccess' openmeter/customer/httpdriver/customer.go -C 5 | head -40

Repository: openmeterio/openmeter

Length of output: 1593


🏁 Script executed:

# Check if there's an OpenAPI spec defining the original GetCustomerAccess contract
find . -name "*.yaml" -o -name "*.yml" -o -name "*.json" | xargs grep -l "GetCustomerAccess\|get.*customer.*access" 2>/dev/null | head -5

Repository: openmeterio/openmeter

Length of output: 47


🏁 Script executed:

# Find api.CustomerAccess type definition
rg -n 'type CustomerAccess struct' --type go -A 15 api/

Repository: openmeterio/openmeter

Length of output: 2083


🏁 Script executed:

# Compare the two structures side by side
echo "=== CustomerAccessV2 ===" && \
rg -n 'type CustomerAccessV2 struct' --type go -A 5 openmeter/customer/httpdriver/apimapping.go && \
echo "" && \
echo "=== api.CustomerAccess ===" && \
rg -n 'type CustomerAccess struct' --type go -A 15 api/types/

Repository: openmeterio/openmeter

Length of output: 340


🏁 Script executed:

# Check EntitlementValue vs EntitlementValueV2 type definitions
rg -n 'type EntitlementValue' openmeter/customer/httpdriver/ --type go -A 10

Repository: openmeterio/openmeter

Length of output: 1189


🏁 Script executed:

# Also check api.EntitlementValue
rg -n 'type EntitlementValue struct' api/api.gen.go -A 10

Repository: openmeterio/openmeter

Length of output: 971


🏁 Script executed:

# Let's see the full EntitlementValue structure in api.gen.go
rg -n 'type EntitlementValue struct' api/api.gen.go -A 25

Repository: openmeterio/openmeter

Length of output: 2052


🏁 Script executed:

# Also check the router to confirm the wiring is indeed swapped
sed -n '130,145p' openmeter/server/router/entitlement.go

Repository: openmeterio/openmeter

Length of output: 588


Keep GetCustomerAccess handler here, not GetCustomerAccessV2.

The endpoint is wired to call GetCustomerAccessV2(), which returns a different response schema. The v2 version adds a GrantBalances field to the entitlement values that doesn't exist in v1. Clients expecting the v1 CustomerAccess schema will get the v2 one instead—that's a silent contract change even though JSON unmarshaling is forgiving about extra fields.

Either swap back to the v1 handler, or update the route spec/operation definition alongside this change so the API contract stays clear.

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

In `@openmeter/server/router/entitlement.go` around lines 137 - 139, The route is
calling customerHandler.GetCustomerAccessV2() which returns the v2 schema
(including GrantBalances) and breaks the existing v1 contract; replace the call
with
customerHandler.GetCustomerAccess().With(customerdriver.GetCustomerAccessParams{
CustomerIDOrKey: customerIdOrKey }).ServeHTTP(w, r) so the v1 CustomerAccess
handler is used, or if you intentionally want v2, update the route/operation
spec to reflect the new response schema instead of swapping handlers silently.

tothandras
tothandras previously approved these changes Mar 5, 2026
@GAlexIHU GAlexIHU force-pushed the feat/grant-balances-on-entitlement-value branch from 6140c1e to 5d7a8fb Compare March 11, 2026 12:32
Copy link
Copy Markdown
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.

♻️ Duplicate comments (3)
openmeter/customer/httpdriver/apimapping.go (1)

17-25: ⚠️ Potential issue | 🟠 Major

HasAccess should not use omitempty.

The JSON tag on HasAccess includes omitempty, which will drop the field when it's false. Since hasAccess is required in the API contract (not optional), it should always be serialized.

💡 Proposed fix
 type EntitlementValueV2 struct {
-	HasAccess                 bool                `json:"hasAccess,omitempty"`
+	HasAccess                 bool                `json:"hasAccess"`
 	Balance                   *float64            `json:"balance,omitempty"`
 	Config                    *string             `json:"config,omitempty"`
 	Overage                   *float64            `json:"overage,omitempty"`
 	TotalAvailableGrantAmount *float64            `json:"totalAvailableGrantAmount,omitempty"`
 	Usage                     *float64            `json:"usage,omitempty"`
 	GrantBalances             *map[string]float64 `json:"grantBalances,omitempty"`
 }

,

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

In `@openmeter/customer/httpdriver/apimapping.go` around lines 17 - 25, The JSON
tag for the field HasAccess in type EntitlementValueV2 should not include
"omitempty" because hasAccess is required by the API contract; update the struct
field tag on HasAccess to `json:"hasAccess"` (remove ",omitempty") so the
boolean is always serialized, and then run/adjust any serialization tests or
callers that assume the field is present.
openmeter/customer/httpdriver/customer.go (2)

445-465: ⚠️ Potential issue | 🟠 Major

Add nil guard before dereferencing cus.

If GetCustomer returns (nil, nil) for a not-found case, line 465 (cus.GetID()) will panic. The same pattern exists in the v1 handler, so this might be a pre-existing issue, but worth addressing in the new code.

💡 Suggested guard
 			if err != nil {
 				return GetCustomerEntitlementValueRequest{}, err
 			}
+
+			if cus == nil {
+				return GetCustomerEntitlementValueRequest{}, commonhttp.NewHTTPError(http.StatusNotFound, fmt.Errorf("customer not found"))
+			}

 			if cus != nil && cus.IsDeleted() {

,

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

In `@openmeter/customer/httpdriver/customer.go` around lines 445 - 465, The code
dereferences cus (returned from h.service.GetCustomer) without checking for nil,
which can panic if GetCustomer returns (nil, nil); add a nil guard after the
GetCustomer call and before using cus (e.g., before cus.IsDeleted() and before
building GetCustomerEntitlementValueRequest) to return an appropriate not-found
or precondition error when cus == nil; update the logic around the cus variable
in the handler that builds GetCustomerEntitlementValueRequest so all uses
(cus.IsDeleted(), cus.GetID(), cus.Namespace, cus.ID) are only accessed after
confirming cus != nil.

574-588: ⚠️ Potential issue | 🟠 Major

Same nil guard needed here for cus.

Line 588 dereferences cus.Namespace without a nil check. Apply the same guard pattern as suggested for GetCustomerEntitlementValueV2.

💡 Suggested guard
 			if err != nil {
 				return GetCustomerAccessV2Response{}, err
 			}
+
+			if cus == nil {
+				return GetCustomerAccessV2Response{}, commonhttp.NewHTTPError(http.StatusNotFound, fmt.Errorf("customer not found"))
+			}

 			if cus != nil && cus.IsDeleted() {

,

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

In `@openmeter/customer/httpdriver/customer.go` around lines 574 - 588, Add a
nil-check for cus before dereferencing cus.Namespace/cus.ID when calling
h.entitlementService.GetAccess: after the call to h.service.GetCustomer (the
variable cus returned there) return early with the same
not-found/precondition-failed style used in GetCustomerEntitlementValueV2 if cus
== nil, so you never call h.entitlementService.GetAccess(ctx, cus.Namespace,
cus.ID) on a nil pointer.
🧹 Nitpick comments (1)
openmeter/server/router/entitlement.go (1)

134-140: Section header may be misleading.

The route comment says /api/v2/customers/{customerId}/access but it's under the "Customer Entitlement APIs (V1)" section header. If this is actually a v2 endpoint, it might be cleaner to move it under the V2 section (after line 156) for clarity. Otherwise, LGTM on the handler swap since the route is v2.

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

In `@openmeter/server/router/entitlement.go` around lines 134 - 140, The
comment/header for the GetCustomerAccess route is inconsistent: the route
comment shows a v2 path but the function is placed under the "Customer
Entitlement APIs (V1)" section. Fix by either moving the GetCustomerAccess
function (Router.GetCustomerAccess) and its route comment into the V2 section
where other v2 handlers live, or if this is intended to be V1, change the route
comment to the correct v1 path; ensure the section header and the comment for
GetCustomerAccess (CustomerIDOrKey route) match the intended API version.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@openmeter/customer/httpdriver/apimapping.go`:
- Around line 17-25: The JSON tag for the field HasAccess in type
EntitlementValueV2 should not include "omitempty" because hasAccess is required
by the API contract; update the struct field tag on HasAccess to
`json:"hasAccess"` (remove ",omitempty") so the boolean is always serialized,
and then run/adjust any serialization tests or callers that assume the field is
present.

In `@openmeter/customer/httpdriver/customer.go`:
- Around line 445-465: The code dereferences cus (returned from
h.service.GetCustomer) without checking for nil, which can panic if GetCustomer
returns (nil, nil); add a nil guard after the GetCustomer call and before using
cus (e.g., before cus.IsDeleted() and before building
GetCustomerEntitlementValueRequest) to return an appropriate not-found or
precondition error when cus == nil; update the logic around the cus variable in
the handler that builds GetCustomerEntitlementValueRequest so all uses
(cus.IsDeleted(), cus.GetID(), cus.Namespace, cus.ID) are only accessed after
confirming cus != nil.
- Around line 574-588: Add a nil-check for cus before dereferencing
cus.Namespace/cus.ID when calling h.entitlementService.GetAccess: after the call
to h.service.GetCustomer (the variable cus returned there) return early with the
same not-found/precondition-failed style used in GetCustomerEntitlementValueV2
if cus == nil, so you never call h.entitlementService.GetAccess(ctx,
cus.Namespace, cus.ID) on a nil pointer.

---

Nitpick comments:
In `@openmeter/server/router/entitlement.go`:
- Around line 134-140: The comment/header for the GetCustomerAccess route is
inconsistent: the route comment shows a v2 path but the function is placed under
the "Customer Entitlement APIs (V1)" section. Fix by either moving the
GetCustomerAccess function (Router.GetCustomerAccess) and its route comment into
the V2 section where other v2 handlers live, or if this is intended to be V1,
change the route comment to the correct v1 path; ensure the section header and
the comment for GetCustomerAccess (CustomerIDOrKey route) match the intended API
version.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 220920ea-4cc7-47c0-bb2e-c6f9ddc027f2

📥 Commits

Reviewing files that changed from the base of the PR and between 6140c1e and 5d7a8fb.

⛔ Files ignored due to path filters (8)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/javascript/src/client/schemas.ts is excluded by !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/models/__init__.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/models/_models.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (11)
  • api/api.gen.go
  • api/spec/src/entitlements/v2/customer.tsp
  • e2e/entitlement_parity_test.go
  • e2e/multisubject_test.go
  • openmeter/customer/httpdriver/apimapping.go
  • openmeter/customer/httpdriver/customer.go
  • openmeter/customer/httpdriver/handler.go
  • openmeter/entitlement/metered/balance.go
  • openmeter/entitlement/metered/connector.go
  • openmeter/entitlement/metered/reset.go
  • openmeter/server/router/entitlement.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • openmeter/entitlement/metered/balance.go
  • e2e/entitlement_parity_test.go
  • openmeter/entitlement/metered/connector.go

@GAlexIHU GAlexIHU merged commit 56a331f into main Mar 11, 2026
36 of 38 checks passed
@GAlexIHU GAlexIHU deleted the feat/grant-balances-on-entitlement-value branch March 11, 2026 12:44
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.

3 participants