Skip to content

fix: tx list names#4167

Merged
GAlexIHU merged 1 commit intomainfrom
fix/tx-list-names
Apr 17, 2026
Merged

fix: tx list names#4167
GAlexIHU merged 1 commit intomainfrom
fix/tx-list-names

Conversation

@GAlexIHU
Copy link
Copy Markdown
Contributor

@GAlexIHU GAlexIHU commented Apr 17, 2026

Overview

Fixes #(issue)

Notes for reviewer

Summary by CodeRabbit

  • New Features

    • Credit transactions now include description information populated from charge metadata
    • Enhanced transaction details with charge names and descriptions for improved visibility
  • Tests

    • Added test coverage for description population in transaction responses

@GAlexIHU GAlexIHU requested a review from a team as a code owner April 17, 2026 13:03
@GAlexIHU GAlexIHU added the release-note/ignore Ignore this change when generating release notes label Apr 17, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

This PR extends credit transactions with an optional Description field by adding a GetByIDs method to the charges service, fetching charge metadata during transaction listing, and applying charge descriptions to transactions before returning them to the API layer.

Changes

Cohort / File(s) Summary
API Handler & Test
api/v3/handlers/customers/credits/convert.go, api/v3/handlers/customers/credits/list_transactions_test.go
Maps the Description field from transaction objects to API responses; test now verifies the description is correctly populated and returned.
Charge Service Interface
openmeter/ledger/customerbalance/service.go
Added GetByIDs method to chargesService interface to enable bulk charge lookups by ID.
Transaction Domain & Metadata Logic
openmeter/ledger/customerbalance/transactions.go, openmeter/ledger/customerbalance/transactions_test.go
Added Description field to CreditTransaction; new applyChargeMetadataToCreditTransactions extracts charge IDs from transaction annotations, fetches charge details, and populates name and description. Test verifies metadata application behavior.
Charge Store Implementation
openmeter/ledger/customerbalance/testenv_test.go
Implements GetByIDs method for the test charge store, partitioning results by charge type and fetching details from flat-fee and usage-based services.

Sequence Diagram

sequenceDiagram
    participant Client as API Client
    participant Handler as API Handler
    participant Service as Credit Service
    participant ChargeService as Charge Service
    participant Ledger as Ledger

    Client->>Handler: List Credit Transactions
    Handler->>Service: ListCreditTransactions()
    Service->>Ledger: Fetch ledger transactions
    Ledger-->>Service: Return transactions
    Service->>Service: Extract charge IDs from annotations
    Service->>ChargeService: GetByIDs(charge IDs)
    ChargeService-->>Service: Return charge details
    Service->>Service: applyChargeMetadataToCreditTransactions()
    Note over Service: Populate Name and Description
    Service-->>Handler: Return enriched transactions
    Handler->>Handler: Map Description to API response
    Handler-->>Client: Return API transactions with descriptions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

area/api, area/billing, release-note/bug-fix

Suggested reviewers

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

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'fix: tx list names' accurately reflects the main change—populating transaction names and descriptions from charge metadata in the credit transaction list.

✏️ 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/tx-list-names

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

🧹 Nitpick comments (3)
openmeter/ledger/customerbalance/transactions_test.go (1)

74-124: Lovely focused test — consider adding the error path too.

The happy path and the "no charge ID annotation" path are both covered nicely. One gap worth filling while you're in here: what happens when ChargesService.GetByIDs returns an error? Given the current silent-swallow behavior in applyChargeMetadataToCreditTransactions, a regression that accidentally starts clobbering Name/Description on error would slip through.

A tiny extra case using a stub that returns an error from GetByIDs and asserting items stay untouched (Name == "IssueCustomerReceivableTemplate", Description == nil) would lock down the contract.

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

In `@openmeter/ledger/customerbalance/transactions_test.go` around lines 74 - 124,
Add a new sub-test that covers the error path for
applyChargeMetadataToCreditTransactions: create a Service whose ChargesService
stub (implementing GetByIDs) returns an error, call
Service.applyChargeMetadataToCreditTransactions with an item that has
ledger.AnnotationChargeID set and a pre-existing Name
("IssueCustomerReceivableTemplate") and nil Description, and assert after the
call that the item's Name remains "IssueCustomerReceivableTemplate" and
Description is still nil; ensure the stub is wired into the same test harness
(e.g., alongside staticChargeService) so the failing GetByIDs path is exercised
and items are not mutated on error.
openmeter/ledger/customerbalance/testenv_test.go (1)

369-423: GetByIDs is almost a carbon copy of ListCharges below it — worth a tiny helper?

The split into flat-fee/usage-based IDs, the two service fetches, building the chargesByID map, and the ordered rebuild are duplicated pretty much verbatim between GetByIDs (lines 369–423) and ListCharges (lines 425–483). A small private helper like hydrateChargesBySearchItems(ctx, items, input.Namespace, input.Expands) would collapse both into a few lines.

Also minor: neither switch handles chargemeta.ChargeTypeCreditPurchase. That's fine today because this test env only creates flat-fee and usage-based charges, but if a future test seeds a credit-purchase charge and queries through this chargeStore, it'll silently disappear from the results. Worth a // only flat-fee and usage-based supported by this test env comment if not hydrating them here.

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

In `@openmeter/ledger/customerbalance/testenv_test.go` around lines 369 - 423,
GetByIDs duplicates the flat-fee/usage-based split, service fetches, map
hydration, and ordered rebuild logic found in ListCharges; extract this into a
small private helper (e.g., hydrateChargesBySearchItems(ctx context.Context,
items []chargemeta.SearchItem, namespace string, expands []string)
(charges.Charges, error)) and call it from both GetByIDs and ListCharges to
remove the duplication; ensure the helper performs the ID partitioning (using
chargemeta.ChargeTypeFlatFee and chargemeta.ChargeTypeUsageBased), calls
flatFeeService.GetByIDs and usageBasedService.GetByIDs, builds the chargesByID
map with charges.NewCharge, and reorders results to match the input search
items; also add a brief comment in the helper noting that
ChargeTypeCreditPurchase is intentionally not handled in this test env so such
items would be skipped.
openmeter/ledger/customerbalance/transactions.go (1)

265-311: Small readability nit: two passes over items could be one.

Right now you iterate items once to collect charge IDs (lines 265–268) and again to apply metadata (lines 297–310). It's readable as-is, but since you're already caching chargeIDFromAnnotations(item.Annotations) once per item in both loops, you could stash per-index charge IDs in a slice during the first pass and avoid re-computing. Purely optional — the current shape is easy to follow.

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

In `@openmeter/ledger/customerbalance/transactions.go` around lines 265 - 311, The
code does two passes over items calling chargeIDFromAnnotations twice; change
the first pass (where chargeIDs is built via lo.FilterMap) to also record
per-index charge IDs (e.g., a slice or map like chargeIDByIndex[int] = string)
so you can reuse those values in the second loop instead of recomputing; keep
the existing flow that builds chargeIDs, calls s.ChargesService.GetByIDs, fills
chargeDisplayByID, and then iterate items using the cached chargeIDByIndex value
to look up metadata and set items[i].Name/Description.
🤖 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/ledger/customerbalance/transactions.go`:
- Around line 128-131: Call site s.applyChargeMetadataToCreditTransactions
currently ignores failures because applyChargeMetadataToCreditTransactions
returns no error; update the function signature to return error (func
applyChargeMetadataToCreditTransactions(... ) error), update its internal calls
to return errors, and change this call site to handle the returned error (either
propagate it from the surrounding function or log-and-continue per the sibling
comment). Ensure you update any callers of
applyChargeMetadataToCreditTransactions and tests accordingly, and choose
consistent behavior (fail hard by returning the error up or swallow with a
structured log) for observability.
- Around line 274-280: The code silently swallows errors from
ChargesService.GetByIDs (and similarly from GetChargeID and
chargeDisplayMetadataFromCharge) which results in blank transaction
names/descriptions; modify the error handling in the block that calls
s.ChargesService.GetByIDs (and the subsequent GetChargeID /
chargeDisplayMetadataFromCharge calls used by
creditTransactionFromLedgerTransaction) to surface failures: either return the
error up to the caller (so ListCreditTransactions can fail) or log the error
(use the service logger at warn/error) and attach a fallback marker to
transaction Name/Description so callers know data is incomplete; update the call
sites in the GetByIDs handling and the code path in
creditTransactionFromLedgerTransaction to propagate or log errors consistently.

---

Nitpick comments:
In `@openmeter/ledger/customerbalance/testenv_test.go`:
- Around line 369-423: GetByIDs duplicates the flat-fee/usage-based split,
service fetches, map hydration, and ordered rebuild logic found in ListCharges;
extract this into a small private helper (e.g., hydrateChargesBySearchItems(ctx
context.Context, items []chargemeta.SearchItem, namespace string, expands
[]string) (charges.Charges, error)) and call it from both GetByIDs and
ListCharges to remove the duplication; ensure the helper performs the ID
partitioning (using chargemeta.ChargeTypeFlatFee and
chargemeta.ChargeTypeUsageBased), calls flatFeeService.GetByIDs and
usageBasedService.GetByIDs, builds the chargesByID map with charges.NewCharge,
and reorders results to match the input search items; also add a brief comment
in the helper noting that ChargeTypeCreditPurchase is intentionally not handled
in this test env so such items would be skipped.

In `@openmeter/ledger/customerbalance/transactions_test.go`:
- Around line 74-124: Add a new sub-test that covers the error path for
applyChargeMetadataToCreditTransactions: create a Service whose ChargesService
stub (implementing GetByIDs) returns an error, call
Service.applyChargeMetadataToCreditTransactions with an item that has
ledger.AnnotationChargeID set and a pre-existing Name
("IssueCustomerReceivableTemplate") and nil Description, and assert after the
call that the item's Name remains "IssueCustomerReceivableTemplate" and
Description is still nil; ensure the stub is wired into the same test harness
(e.g., alongside staticChargeService) so the failing GetByIDs path is exercised
and items are not mutated on error.

In `@openmeter/ledger/customerbalance/transactions.go`:
- Around line 265-311: The code does two passes over items calling
chargeIDFromAnnotations twice; change the first pass (where chargeIDs is built
via lo.FilterMap) to also record per-index charge IDs (e.g., a slice or map like
chargeIDByIndex[int] = string) so you can reuse those values in the second loop
instead of recomputing; keep the existing flow that builds chargeIDs, calls
s.ChargesService.GetByIDs, fills chargeDisplayByID, and then iterate items using
the cached chargeIDByIndex value to look up metadata and set
items[i].Name/Description.
🪄 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: 560a0754-4a6e-4af7-8438-ecd9e2d6b1d9

📥 Commits

Reviewing files that changed from the base of the PR and between 2007c53 and 7529c12.

📒 Files selected for processing (6)
  • api/v3/handlers/customers/credits/convert.go
  • api/v3/handlers/customers/credits/list_transactions_test.go
  • openmeter/ledger/customerbalance/service.go
  • openmeter/ledger/customerbalance/testenv_test.go
  • openmeter/ledger/customerbalance/transactions.go
  • openmeter/ledger/customerbalance/transactions_test.go

Comment on lines 128 to +131
}

s.applyChargeMetadataToCreditTransactions(ctx, input.CustomerID.Namespace, items)

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

Heads up: applyChargeMetadataToCreditTransactions has no return value.

Tiny thing — since the function signature returns nothing, the current call site is fine, but it means the caller can't react to enrichment failures even if you later decide to add observability. If you do plumb in an error return (per the sibling comment), you'll want to decide here whether to fail hard or just log-and-continue.

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

In `@openmeter/ledger/customerbalance/transactions.go` around lines 128 - 131,
Call site s.applyChargeMetadataToCreditTransactions currently ignores failures
because applyChargeMetadataToCreditTransactions returns no error; update the
function signature to return error (func
applyChargeMetadataToCreditTransactions(... ) error), update its internal calls
to return errors, and change this call site to handle the returned error (either
propagate it from the surrounding function or log-and-continue per the sibling
comment). Ensure you update any callers of
applyChargeMetadataToCreditTransactions and tests accordingly, and choose
consistent behavior (fail hard by returning the error up or swallow with a
structured log) for observability.

Comment on lines +274 to +280
chargeEntities, err := s.ChargesService.GetByIDs(ctx, charges.GetByIDsInput{
Namespace: namespace,
IDs: chargeIDs,
})
if err != nil {
return
}
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

Silent swallow on GetByIDs failure leaves users staring at nameless transactions.

If ChargesService.GetByIDs errors out (timeout, DB hiccup, etc.), every returned transaction ends up with Name == "" and Description == nil — and the caller has no idea anything went wrong. Since creditTransactionFromLedgerTransaction now initializes Name: "" (line 220) and relies entirely on this step to populate it, a transient failure quietly degrades the UX.

A couple of options depending on how strict you want to be:

  • Log the error (even at warn) so operators can diagnose missing names.
  • Or propagate the error and let ListCreditTransactions decide whether to fail the request.

At minimum, the same concern applies to lines 284–287 and 289–292 where GetChargeID and chargeDisplayMetadataFromCharge errors are also swallowed — those indicate real data inconsistencies worth surfacing in logs.

💡 Sketch: add a logger and warn on failures
 	chargeEntities, err := s.ChargesService.GetByIDs(ctx, charges.GetByIDsInput{
 		Namespace: namespace,
 		IDs:       chargeIDs,
 	})
 	if err != nil {
+		s.Logger.WarnContext(ctx, "failed to fetch charges for credit transaction metadata",
+			"namespace", namespace, "error", err)
 		return
 	}

(would require plumbing a *slog.Logger through Config/Service).

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

In `@openmeter/ledger/customerbalance/transactions.go` around lines 274 - 280, The
code silently swallows errors from ChargesService.GetByIDs (and similarly from
GetChargeID and chargeDisplayMetadataFromCharge) which results in blank
transaction names/descriptions; modify the error handling in the block that
calls s.ChargesService.GetByIDs (and the subsequent GetChargeID /
chargeDisplayMetadataFromCharge calls used by
creditTransactionFromLedgerTransaction) to surface failures: either return the
error up to the caller (so ListCreditTransactions can fail) or log the error
(use the service logger at warn/error) and attach a fallback marker to
transaction Name/Description so callers know data is incomplete; update the call
sites in the GetByIDs handling and the code path in
creditTransactionFromLedgerTransaction to propagate or log errors consistently.

@GAlexIHU GAlexIHU merged commit a433d7e into main Apr 17, 2026
28 of 30 checks passed
@GAlexIHU GAlexIHU deleted the fix/tx-list-names branch April 17, 2026 13:09
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