8000 feat: allow marshalling of big ints in string using a header by gfyrag · Pull Request #959 · formancehq/ledger · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: allow marshalling of big ints in string using a header #959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 11, 2025

Conversation

gfyrag
Copy link
Contributor
@gfyrag gfyrag commented Jun 11, 2025

Fixes LX-54

@gfyrag gfyrag requested a review from a team as a code owner June 11, 2025 12:34
Copy link
coderabbitai bot commented Jun 11, 2025

Warning

Rate limit exceeded

@gfyrag has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits 8000 than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 15ea8a4 and af20ac1.

📒 Files selected for processing (16)
  • internal/README.md (51 hunks)
  • internal/api/v2/controllers_accounts_list.go (2 hunks)
  • internal/api/v2/controllers_accounts_read.go (1 hunks)
  • internal/api/v2/controllers_balances.go (1 hunks)
  • internal/api/v2/controllers_logs_list.go (2 hunks)
  • internal/api/v2/controllers_transactions_create.go (2 hunks)
  • internal/api/v2/controllers_transactions_list.go (2 hunks)
  • internal/api/v2/controllers_transactions_read.go (1 hunks)
  • internal/api/v2/controllers_transactions_revert.go (1 hunks)
  • internal/api/v2/controllers_volumes.go (2 hunks)
  • internal/api/v2/views.go (1 hunks)
  • internal/api/v2/views_test.go (1 hunks)
  • internal/controller/ledger/controller_default_test.go (1 hunks)
  • internal/log.go (6 hunks)
  • internal/transaction.go (3 hunks)
  • internal/volumes.go (1 hunks)

Walkthrough

This update introduces a new rendering layer for API responses, enabling big integers to be serialized as strings when requested via an HTTP header. Several controller handlers now use these rendering functions to transform ledger entities before responding. Supporting methods and types are added to the ledger domain, and related documentation and tests are updated accordingly.

Changes

Files / Group Change Summary
internal/api/v2/views.go, internal/api/v2/views_test.go Introduced new rendering types and functions for ledger entities with custom JSON marshaling of big integers; added comprehensive tests for rendering logic.
internal/api/v2/controllers_accounts_list.go,
internal/api/v2/controllers_accounts_read.go,
internal/api/v2/controllers_balances.go,
internal/api/v2/controllers_logs_list.go,
internal/api/v2/controllers_transactions_create.go,
internal/api/v2/controllers_transactions_list.go,
internal/api/v2/controllers_transactions_read.go,
internal/api/v2/controllers_transactions_revert.go,
internal/api/v2/controllers_volumes.go
Updated handlers to use new rendering functions, transforming ledger entities before sending API responses.
internal/README.md Updated documentation to reflect new methods and renamed constants in internal packages.
internal/log.go Renamed NewLogType to NewTransactionLogType and updated all references; added WithDate method to Log.
internal/transaction.go Refactored JSON marshaling to use new SubtractPostings method; added WithPostCommitVolumes method.
internal/volumes.go Added SubtractPostings method to PostCommitVolumes for computing pre-commit volumes.
internal/controller/ledger/controller_default_test.go Updated test to expect NewTransactionLogType instead of NewLogType.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API Controller
    participant Renderer
    participant Ledger

    Client->>API Controller: HTTP Request (with/without Formance-Bigint-As-String header)
    API Controller->>Ledger: Fetch ledger entity (e.g., transaction, account)
    Ledger-->>API Controller: Return entity
    API Controller->>Renderer: Call render function (entity, request)
    Renderer-->>API Controller: Return rendered entity (big ints as string if header set)
    API Controller-->>Client: HTTP Response (JSON, big ints as string or number)
Loading

Suggested labels

build-images

Suggested reviewers

  • ascandone
  • flemzord

Poem

In the land of ledgers, numbers grow tall,
Now rendered as strings, not floats, not small.
With headers in tow, the API shines bright,
Big ints as strings, oh what a delight!
Tests hop in place, all snug and precise—
This rabbit approves, the code looks nice! 🐇

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Commit Unit Tests in branch feat/bigints-as-string
  • Post Copyable Unit Tests in Comment

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

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
@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 (12)
internal/api/v2/controllers_accounts_read.go (1)

45-45: Avoid unnecessary struct copy

renderAccount(r, *acc) copies the whole Account struct (potentially large slices & big.Ints).
Passing the pointer keeps GC pressure lower:

- api.Ok(w, renderAccount(r, *acc))
+ api.Ok(w, renderAccount(r, acc))

(adjust renderAccount signature accordingly).
Not blocking, but worth considering.

internal/api/v2/controllers_transactions_read.go (1)

45-45: Same copy concern as in account handler

renderTransaction(r, *tx) copies the whole transaction, including postings & volumes maps.
If renderTransaction doesn’t need value semantics, prefer passing the pointer.

internal/api/v2/controllers_transactions_revert.go (1)

47-47: Consider pointer to avoid copying reverted transaction

renderTransaction(r, ret.RevertTransaction) currently copies the struct.
Pass the pointer if mutability isn’t required, matching the other handlers.

internal/api/v2/controllers_logs_list.go (1)

34-36: Extract mapped cursor into a variable for readability
Inlining bunpaginate.MapCursor inside the RenderCursor call forces an extra pointer dereference and makes the control-flow harder to scan.

-		api.RenderCursor(w, *bunpaginate.MapCursor(cursor, func(log ledger.Log) any {
-			return renderLog(r, log)
-		}))
+		mapped := bunpaginate.MapCursor(cursor, func(log ledger.Log) any {
+			return renderLog(r, log)
+		})
+		api.RenderCursor(w, *mapped)
internal/api/v2/controllers_transactions_list.go (1)

45-47: Mirror the cursor-mapping refactor for consistency

Same micro-readability win as in controllers_logs_list.go; consider the short variable extraction pattern:

-		api.RenderCursor(w, *bunpaginate.MapCursor(cursor, func(tx ledger.Transaction) any {
-			return renderTransaction(r, tx)
-		}))
+		mapped := bunpaginate.MapCursor(cursor, func(tx ledger.Transaction) any {
+			return renderTransaction(r, tx)
+		})
+		api.RenderCursor(w, *mapped)
internal/api/v2/controllers_volumes.go (1)

66-68: Apply the same small refactor for symmetry with other handlers

-		api.RenderCursor(w, *bunpaginate.MapCursor(cursor, func(volumes ledger.VolumesWithBalanceByAssetByAccount) any {
-			return renderVolumesWithBalances(r, volumes)
-		}))
+		mapped := bunpaginate.MapCursor(cursor, func(v ledger.VolumesWithBalanceByAssetByAccount) any {
+			return renderVolumesWithBalances(r, v)
+		})
+		api.RenderCursor(w, *mapped)
internal/transaction.go (1)

208-212: Receiver should be pointer for a fluent builder
WithPostCommitVolumes copies the whole Transaction struct. That’s consistent with the existing builder methods, but note that the new PostCommitVolumes map can be large – every call copies it. If you plan to chain many setters, consider using a pointer receiver to avoid unintended copies.

internal/log.go (1)

98-102: WithDate copies the value; was that intended?
Unlike most “mutator” helpers, WithDate operates on a copy of Log, so:

log := existing.WithDate(t) // original unchanged

If the goal is mutation in place, use a pointer receiver; otherwise document that this is purely functional.

internal/api/v2/views_test.go (3)

15-60: Use deterministic timestamps for reproducible tests
now := time.Now() can introduce flakiness across slow CI machines (nanosecond drift between value capture and JSON marshal). Prefer a fixed time.Date(...) literal.


66-152: Validate a “large” bigint scenario
All numeric-mode expectations use 100, which fits safely into a float64. Add a case using a value > 2^53 to ensure the "bigint as number" path is still precise enough or fails obviously – that is the whole point of the new header.


243-261: Helper could DRY out repeated render/assert boilerplate
Every test block repeats the request creation, marshal/unmarshal, and map comparison. A small helper (e.g. assertRender(t, headers, expected, func(*http.Request) any)) would shrink this file by ~40 %.

internal/api/v2/views.go (1)

218-221: Consider using a switch statement for better readability.

While the current implementation works correctly, a switch statement would be more idiomatic and readable.

Apply this diff to improve readability:

-func needBigIntAsString(r *http.Request) bool {
-	v := strings.ToLower(r.Header.Get(HeaderBigIntAsString))
-	return v == "true" || v == "yes" || v == "y" || v == "1"
-}
+func needBigIntAsString(r *http.Request) bool {
+	switch strings.ToLower(r.Header.Get(HeaderBigIntAsString)) {
+	case "true", "yes", "y", "1":
+		return true
+	default:
+		return false
+	}
+}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9b8f14 and 30f395c.

📒 Files selected for processing (16)
  • internal/README.md (51 hunks)
  • internal/api/v2/controllers_accounts_list.go (2 hunks)
  • internal/api/v2/controllers_accounts_read.go (1 hunks)
  • internal/api/v2/controllers_balances.go (1 hunks)
  • internal/api/v2/controllers_logs_list.go (2 hunks)
  • internal/api/v2/controllers_transactions_create.go (2 hunks)
  • internal/api/v2/controllers_transactions_list.go (2 hunks)
  • internal/api/v2/controllers_transactions_read.go (1 hunks)
  • internal/api/v2/controllers_transactions_revert.go (1 hunks)
  • internal/api/v2/controllers_volumes.go (2 hunks)
  • internal/api/v2/views.go (1 hunks)
  • internal/api/v2/views_test.go (1 hunks)
  • internal/controller/ledger/controller_default_test.go (1 hunks)
  • internal/log.go (6 hunks)
  • internal/transaction.go (2 hunks)
  • internal/volumes.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
internal/api/v2/controllers_transactions_revert.go (1)
internal/controller/ledger/controller.go (1)
  • RevertTransaction (99-103)
internal/api/v2/controllers_transactions_list.go (1)
internal/transaction.go (2)
  • Transaction (37-49)
  • Transaction (51-58)
internal/api/v2/controllers_logs_list.go (1)
internal/log.go (1)
  • Log (84-96)
internal/api/v2/controllers_volumes.go (1)
internal/volumes.go (1)
  • VolumesWithBalanceByAssetByAccount (63-67)
internal/log.go (1)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/api/v2/views.go (5)
internal/volumes.go (6)
  • Volumes (12-15)
  • Volumes (40-43)
  • VolumesByAssets (98-98)
  • PostCommitVolumes (118-118)
  • VolumesWithBalanceByAssetByAccount (63-67)
  • BalancesByAssets (96-96)
internal/transaction.go (2)
  • Transaction (37-49)
  • Transaction (51-58)
internal/posting.go (1)
  • Postings (35-35)
internal/account.go (1)
  • Account (14-24)
internal/log.go (5)
  • CreatedTransaction (199-202)
  • RevertedTransaction (329-332)
  • Log (84-96)
  • NewTransactionLogType (22-22)
  • RevertedTransactionLogType (23-23)
🪛 LanguageTool
internal/README.md

[duplication] ~74-~74: Possible typo: you repeated a word.
Context: ...a PostCommitVolumes) SubtractPostings(postings Postings) PostCommitVolumes](<#PostCommitVolume...

(ENGLISH_WORD_REPEAT_RULE)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Dirty
  • GitHub Check: Tests
🔇 Additional comments (34)
internal/api/v2/controllers_balances.go (1)

37-37: LGTM – consistent with new rendering layer

Using renderBalancesByAssets keeps API output homogeneous with the bigint-stringification feature.

internal/controller/ledger/controller_default_test.go (1)

64-65: Constant update looks correct
Expectation now matches the renamed domain constant NewTransactionLogType. Test semantics stay intact.

internal/api/v2/controllers_logs_list.go (1)

7-8: Import alias justified
The new ledger import is required for the generic type argument below—no issues.

internal/api/v2/controllers_volumes.go (1)

6-8: Added imports are required
bunpaginate and the ledger alias are now used after mapping. Looks good.

internal/api/v2/controllers_transactions_create.go (2)

6-7: Import re-ordering is gofmt-compliant
No functional impact.


71-72: Output now passes through renderer – good alignment
Switching to renderTransaction guarantees consistent big-int handling across endpoints. No issues spotted.

internal/api/v2/controllers_accounts_list.go (1)

6-8: Imports look good – ledger alias is now required
The added bunpaginate and ledger imports are both used below, satisfying the compiler and aligning this handler with the other v2 controllers.

internal/transaction.go (1)

182-197: Nil-map safety when subtracting postings
tx.PostCommitVolumes (a map) may legally be nil. Calling a method that iterates over a nil map is fine, but if SubtractPostings itself assumes a non-nil receiver, you will hit a panic. Double-check the implementation or guard here.

internal/log.go (3)

22-23: Renamed constant – confirm DB/back-compat
Behaviour stays identical, but double-check any migrations/seed data that referenced NEW_LOG_TYPE or relied on the previous enum ordering.


205-206: Consistency achieved with NewTransactionLogType
The CreatedTransaction.Type() switch-case now matches the renamed constant. Good catch.


368-372: HydrateLog switch updated – no other code path missing
The new case guarantees proper unmarshalling of "NEW_TRANSACTION" logs. Looks complete.

internal/api/v2/views.go (4)

1-10: LGTM!

Clean and well-organized imports.


12-12: LGTM!

Well-named constant following Go conventions.


14-26: LGTM!

Clean implementation of MarshalJSON for volumes with proper string conversion of big integers.


79-84:

✅ Verification successful

Verify the SubtractPostings method exists on PostCommitVolumes.

The code calls SubtractPostings on PostCommitVolumes to compute pre-commit volumes. Ensure this method is properly implemented.


🏁 Script executed:

#!/bin/bash
# Description: Verify that SubtractPostings method exists on PostCommitVolumes type

# Search for SubtractPostings method definition
ast-grep --pattern 'func ($_ PostCommitVolumes) SubtractPostings($_) $_'

Length of output: 734


No action needed – SubtractPostings method exists

The SubtractPostings method is defined on PostCommitVolumes in internal/volumes.go (starting at line 120), so the calls in internal/api/v2/views.go are valid.

internal/README.md (19)

53-53: Approve new index entry for Log.WithDate
The documentation now includes the Log.WithDate(date time.Time) Log method, matching the implementation in internal/log.go.


253-253: Approve addition of AccountMetadata type
The new AccountMetadata map[string]metadata.Metadata type is correctly documented and aligns with its declaration in internal/log.go.


278-278: Approve addition of AggregatedVolumes type
The AggregatedVolumes type is properly listed and reflects the structure in internal/volumes.go.


347-347: Approve addition of CreatedTransaction type
The CreatedTransaction type entry is correctly documented, matching its definition in internal/log.go.


359-359: Approve addition of CreatedTransaction.GetMemento
The GetMemento() method entry aligns with the implementation in internal/log.go.


368-368: Approve addition of CreatedTransaction.Type
The Type() LogType method is documented as expected and matches its code in internal/log.go.


377-377: Approve addition of DeletedMetadata type
The DeletedMetadata type entry is correct and consistent with the code in internal/log.go.


390-390: Approve addition of DeletedMetadata.Type
The Type() LogType method for DeletedMetadata is accurately documented.


399-399: Approve addition of DeletedMetadata.UnmarshalJSON
The UnmarshalJSON(data []byte) error signature is correctly reflected in the docs.


549-549: Approve addition of NewLog factory function
The NewLog(payload LogPayload) Log entry matches the new constructor in internal/log.go.


558-558: Approve addition of Log.ChainLog
The ChainLog(previous *Log) Log method is now documented and aligns with its implementation.


567-567: Approve addition of Log.ComputeHash
The ComputeHash(previous *Log) entry correctly matches the code change.


576-576: Approve addition of Log.UnmarshalJSON
The pointer receiver UnmarshalJSON(data []byte) error method is accurately included.


584-589: Approve full doc entry for Log.WithDate
Both the anchor and method signature for WithDate(date time.Time) Log are present and correct.


645-645: Approve rename to NewTransactionLogType
The constant NewTransactionLogType reflects the code rename from NewLogType in internal/log.go.


800-805: Approve addition of PostCommitVolumes.SubtractPostings
The SubtractPostings(postings Postings) PostCommitVolumes method is correctly documented and matches the new implementation.


1067-1072: Approve addition of Transaction.WithPostCommitVolumes
The new WithPostCommitVolumes(volumes PostCommitVolumes) Transaction method is properly listed and consistent with internal/transaction.go.


1128-1132: Approve addition of NewTransactionData
The NewTransactionData() TransactionData constructor is now included in the index as expected.


1137-1141: Approve addition of TransactionData.WithPostings
The WithPostings(postings ...Posting) TransactionData method entry correctly mirrors the code.

Comment on lines +120 to +131
func (a PostCommitVolumes) SubtractPostings(postings Postings) PostCommitVolumes {
if len(a) == 0 {
return PostCommitVolumes{}
}
ret := a.Copy()
for _, posting := range postings {
ret.AddOutput(posting.Source, posting.Asset, big.NewInt(0).Neg(posting.Amount))
ret.AddInput(posting.Destination, posting.Asset, big.NewInt(0).Neg(posting.Amount))
}

return ret
}
Copy link
@coderabbitai coderabbitai bot Jun 11, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential panic when source/destination account or asset is missing

AddInput/AddOutput assume that a[account] and a[account][asset] are already initialised.
If SubtractPostings is called with a posting whose source/destination didn’t exist in the original PostCommitVolumes, the index operation on a nil map will dereference a nil value and panic at runtime.

volumes := a[account][asset].Copy()   // ← panics when a[account] == nil

Recommended fix: lazily initialise the nested maps before accessing them.

 func (a PostCommitVolumes) AddInput(account, asset string, input *big.Int) {
-    volumes := a[account][asset].Copy()
+    if _, ok := a[account]; !ok {
+        a[account] = VolumesByAssets{}
+    }
+    if _, ok := a[account][asset]; !ok {
+        a[account][asset] = NewEmptyVolumes()
+    }
+    volumes := a[account][asset].Copy()
     volumes.Input.Add(volumes.Input, input)
     a[account][asset] = volumes
 }

Do the same for AddOutput.

Minor perf-nit: big.NewInt(0).Neg(posting.Amount) allocates each loop; reuse a scratch variable to avoid N allocations on large posting slices.

🤖 Prompt for AI Agents
In internal/volumes.go around lines 120 to 131, the SubtractPostings method can
panic because AddInput and AddOutput assume nested maps are initialized, but
they may be nil for new accounts or assets. Fix this by lazily initializing the
nested maps inside AddInput and AddOutput before accessing them. Also, optimize
the loop by creating a single big.Int variable outside the loop to hold the
negated amount and reuse it for each posting instead of allocating a new big.Int
every iteration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If PostCommitVolumes is not empty, it must contains required data.
We don't want to ignore the errors if it does not contains the required data.

Copy link

Choose a reason for hiding this comment

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

@gfyrag Got it—if the surrounding logic guarantees that every (account, asset) pair referenced by a posting is already present whenever PostCommitVolumes is non-empty, leaving the current implementation as-is is perfectly reasonable.

You might still consider one of the following (purely optional) hardening steps:

• Add an explicit check with a descriptive panic/log.Fatalf so that, if the invariant is ever violated, you get a clearer message than “invalid memory address or nil pointer dereference”.
• Add a short comment above AddInput/AddOutput documenting the pre-condition (“volume entries must already exist”).

Both keep the fail-fast behavior you want while improving debuggability, but I’m fine if you prefer the current minimal version.

Comment on lines +35 to +37
api.RenderCursor(w, *bunpaginate.MapCursor(cursor, func(account ledger.Account) any {
return renderAccount(r, account)
}))
Copy link
@coderabbitai coderabbitai bot Jun 11, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Potential panic if bunpaginate.MapCursor returns nil
MapCursor returns *bunpaginate.Cursor.

api.RenderCursor(w, *bunpaginate.MapCursor(...))

If, for any reason, MapCursor returns nil, dereferencing it will panic. A defensive check (or keeping the pointer and letting RenderCursor accept it) would eliminate this foot-gun.

-		api.RenderCursor(w, *bunpaginate.MapCursor(cursor, func(account ledger.Account) any {
-			return renderAccount(r, account)
-		}))
+		mapped := bunpaginate.MapCursor(cursor, func(account ledger.Account) any {
+			return renderAccount(r, account)
+		})
+		if mapped == nil {
+			api.BadRequest(w, common.ErrValidation, errors.New("failed to map cursor"))
+			return
+		}
+		api.RenderCursor(w, *mapped)
🤖 Prompt for AI Agents
In internal/api/v2/controllers_accounts_list.go around lines 35 to 37, the code
dereferences the pointer returned by bunpaginate.MapCursor without checking if
it is nil, which can cause a panic. Modify the code to first assign the result
of MapCursor to a variable, check if it is nil, and handle that case
appropriately before dereferencing. Alternatively, adjust RenderCursor to accept
a pointer and pass the pointer directly without dereferencing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

MapCursor return a cursor, the method should return a value, and not a pointer. It should be fixed on the library.

Copy link

Choose a reason for hiding this comment

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

@gfyrag Understood—thanks for clarifying. If MapCursor is intended to return a value (not a *Cursor), then the ideally safe fix belongs in bunpaginate itself so every caller benefits and we can keep the concise dereference-free call here. I’ll drop the earlier suggestion and leave this handler as-is.

Let me know if you’d like help opening an issue or PR on go-libs to adjust the return type; otherwise I’m all set. 🐇

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems legit, you can open a pr.

Copy link

Choose a reason for hiding this comment

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

A summary of the edits CodeRabbit can apply:

I will open a PR in the go-libs repository (not in this repo) that changes bunpaginate.MapCursor to return a non-pointer Cursor value so callers no longer need to dereference it. No change is needed in the current file.

  • ✅ Create PR with these edits
  • 📌 Create commit in current branch
  • 📋 Get copyable edits

Comment on lines +198 to +205
switch l.Type {
case ledger.NewTransactionLogType:
return createdTransaction(l.Data.(ledger.CreatedTransaction))
case ledger.RevertedTransactionLogType:
return revertedTransaction(l.Data.(ledger.RevertedTransaction))
default:
return l.Data
}
Copy link
@coderabbitai coderabbitai bot Jun 11, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add safety checks for type assertions to prevent panics.

The type assertions on lines 200 and 202 don't check the second return value, which could cause panics if the assertions fail.

Apply this diff to add safety checks:

-			switch l.Type {
-			case ledger.NewTransactionLogType:
-				return createdTransaction(l.Data.(ledger.CreatedTransaction))
-			case ledger.RevertedTransactionLogType:
-				return revertedTransaction(l.Data.(ledger.RevertedTransaction))
-			default:
-				return l.Data
-			}
+			switch l.Type {
+			case ledger.NewTransactionLogType:
+				if ct, ok := l.Data.(ledger.CreatedTransaction); ok {
+					return createdTransaction(ct)
+				}
+				return l.Data
+			case ledger.RevertedTransactionLogType:
+				if rt, ok := l.Data.(ledger.RevertedTransaction); ok {
+					return revertedTransaction(rt)
+				}
+				return l.Data
+			default:
+				return l.Data
+			}
📝 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
switch l.Type {
case ledger.NewTransactionLogType:
return createdTransaction(l.Data.(ledger.CreatedTransaction))
case ledger.RevertedTransactionLogType:
return revertedTransaction(l.Data.(ledger.RevertedTransaction))
default:
return l.Data
}
switch l.Type {
case ledger.NewTransactionLogType:
if ct, ok := l.Data.(ledger.CreatedTransaction); ok {
return createdTransaction(ct)
}
return l.Data
case ledger.RevertedTransactionLogType:
if rt, ok := l.Data.(ledger.RevertedTransaction); ok {
return revertedTransaction(rt)
}
return l.Data
default:
return l.Data
}
🤖 Prompt for AI Agents
In internal/api/v2/views.go around lines 198 to 205, the type assertions on
l.Data for ledger.CreatedTransaction and ledger.RevertedTransaction lack safety
checks, risking panics if the assertion fails. Modify the code to use the
two-value form of type assertion, check the boolean result, and handle the
failure case gracefully, such as returning an error or a default value, to
prevent runtime panics.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't want to ignore a conversion error. The types should be these types. If not, we are ok with the fact than the code will panic, as it would be a development error.

Copy link

Choose a reason for hiding this comment

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

@gfyrag Got it—thanks for clarifying. I’ll keep in mind that, for these internal assertions, a panic is the intended behaviour when the data doesn’t match the expected type.

(⁎ᵕᴗᵕ⁎)🪃

Copy link
codecov bot commented Jun 11, 2025

Codecov Report

Attention: Patch coverage is 96.04520% with 7 lines in your changes missing coverage. Please review.

Project coverage is 82.82%. Comparing base (4b7f773) to head (af20ac1).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
internal/api/v2/views.go 94.73% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #959      +/-   ##
==========================================
+ Coverage   82.43%   82.82%   +0.38%     
==========================================
  Files         141      142       +1     
  Lines        7886     8028     +142     
==========================================
+ Hits         6501     6649     +148     
+ Misses       1059     1054       -5     
+ Partials      326      325       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gfyrag gfyrag force-pushed the feat/bigints-as-string branch from 30f395c to 15ea8a4 Compare June 11, 2025 12:39
@gfyrag gfyrag enabled auto-merge June 11, 2025 14:30
@gfyrag gfyrag added this pull request to the merge queue Jun 11, 2025
@gfyrag gfyrag removed this pull request from the merge queue due to a manual request Jun 11, 2025
@gfyrag gfyrag force-pushed the feat/bigints-as-string branch from 15ea8a4 to af20ac1 Compare June 11, 2025 14:31
@gfyrag gfyrag enabled auto-merge June 11, 2025 14:31
@gfyrag gfyrag added this pull request to the merge queue Jun 11, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jun 11, 2025
@gfyrag gfyrag added this pull request to the merge queue Jun 11, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jun 11, 2025
@gfyrag gfyrag added this pull request to the merge queue Jun 11, 2025
Merged via the queue into main with commit 739e4b4 Jun 11, 2025
10 checks passed
@gfyrag gfyrag deleted the feat/bigints-as-string branch June 11, 2025 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0