Skip to content

Conversation

@gfyrag
Copy link
Contributor

@gfyrag gfyrag commented Nov 24, 2025

  • feat: return idempotency hit from controller
  • feat: add Idempotency-Hit header

@gfyrag gfyrag requested a review from a team as a code owner November 24, 2025 11:40
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Controller methods gained an idempotencyHit boolean return; handlers inspect it and set an Idempotency-Hit response header when true. Mocks were regenerated to typed per-call wrappers and tests updated. Client models/docs expose response Headers and some requests accept Idempotency-Key. New e2e tests verify header behavior.

Changes

Cohort / File(s) Summary
API Docs
docs/api/README.md, pkg/client/docs/models/operations/*.md
Added Idempotency-Key request parameter entries and Idempotency-Hit response header docs; added Headers documentation rows to many response models.
Controller interfaces & implementations
internal/controller/ledger/controller.go, internal/controller/ledger/controller_default.go, internal/controller/ledger/controller_with_events.go, internal/controller/ledger/controller_with_traces.go, internal/controller/ledger/controller_with_too_many_client_handling.go, internal/controller/ledger/log_process.go, internal/controller/ledger/log_process_test.go, internal/controller/ledger/*_generated_test.go, internal/controller/ledger/*_test.go
Extended signatures for CreateTransaction, RevertTransaction, Save*/Delete*Metadata to return an additional bool (idempotencyHit); propagated the flag through implementations and tests; forgeLog now returns the idempotency boolean and callers/tests updated.
API Handlers (v1 & v2)
internal/api/v1/...controllers_*.go, internal/api/v2/...controllers_*.go, internal/api/v1/..._test.go, internal/api/v2/..._test.go
Unpacked new idempotencyHit return values from controller calls; set Idempotency-Hit: true header when flag is true; updated related tests.
Bulking & facades / state tracker
internal/api/bulking/bulker.go, internal/api/bulking/bulker_test.go, internal/api/bulking/mocks.go, internal/controller/system/state_tracker.go
Adjusted controller call sites to capture extra return value (sometimes ignored); tests/mocks updated to include additional boolean in Return(...) calls; state tracker/facade methods propagate idempotencyHit.
Mocks: generation directives & generated mocks
internal/api/bulking/mocks.go, internal/api/v1/mocks.go, internal/api/v2/mocks.go, internal/api/*/mocks_*.go, internal/controller/*_generated_test.go, various *_generated_test.go
Added -typed to go:generate directives; regenerated/edited mocks to introduce typed per-call wrapper types and adapted recorder Return/Do/DoAndReturn helpers to new signatures (many mock methods now include the additional bool).
Client models & implementation
pkg/client/models/operations/*.go, pkg/client/v1.go, pkg/client/v2.go
Added Headers map[string][]string fields and GetHeaders() accessors to multiple response structs; added IdempotencyKey to RevertTransaction request model; populated response structs with HTTP headers on successful 200/201/204 paths.
End-to-end tests
test/e2e/api_idempotency_hit_header_test.go
New e2e test suite exercising idempotency keys across CreateTransaction, metadata add/delete, revert, and account metadata operations; asserts Idempotency-Hit header absent on first call and "true" on replay.
Minor
internal/log.go, internal/README.md
Minor grammar/comment tweaks only.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Handler as API Handler
    participant Controller
    participant LogProcessor as Log Processor

    Client->>Handler: HTTP request (may include Idempotency-Key)
    Handler->>Controller: call Create/Revert/Save/Delete(...)
    Controller->>LogProcessor: forgeLog / find by idempotency key
    alt first request
        LogProcessor-->>Controller: (log, output, false, nil)
    else idempotent replay
        LogProcessor-->>Controller: (log, output, true, nil)
    end
    Controller-->>Handler: (log, output, idempotencyHit, err)
    alt idempotencyHit == true
        Handler->>Handler: set header "Idempotency-Hit: true"
    end
    Handler-->>Client: HTTP response (body + Headers)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay special attention to:
    • Controller signature changes and consistent propagation of idempotencyHit across DefaultController, wrappers, facades, and wrappers with events/traces.
    • forgeLog idempotency branches and ensuring boolean semantics on success vs error paths.
    • Mock regeneration (typed wrappers): verify Return/Do helpers match new signatures and tests updated accordingly.
    • Client code paths that populate response Headers for 200/201/204 and any consumers of those response structs.
    • Bulking and other call sites that ignore the new return value—ensure no unintended behavior loss.

Possibly related PRs

  • backport: feat/idempotency-hit #1156 — implements the same idempotencyHit propagation, Idempotency-Hit header emission, mock updates, and client/docs changes; strong code-level overlap.

Poem

🐰 With keys upon my twitching nose,

I hop the request where replay grows.
First hop is fresh, the second says "true" —
Headers sing that I returned to you.
A rabbit nods: idempotent muse.

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding an Idempotency-Hit header feature to the system.
Description check ✅ Passed The description relates to the changeset by describing the two key aspects of the implementation: returning idempotency hit from controller and adding the Idempotency-Hit header.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/idempotency-hit

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Nov 24, 2025

Codecov Report

❌ Patch coverage is 74.34783% with 59 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.25%. Comparing base (f8b2138) to head (5229294).

Files with missing lines Patch % Lines
...ledger/controller_with_too_many_client_handling.go 68.75% 0 Missing and 10 partials ⚠️
internal/controller/system/state_tracker.go 73.68% 0 Missing and 10 partials ⚠️
...ternal/controller/ledger/controller_with_traces.go 84.48% 0 Missing and 9 partials ⚠️
internal/controller/ledger/controller_default.go 57.14% 0 Missing and 6 partials ⚠️
...ternal/controller/ledger/controller_with_events.go 75.00% 0 Missing and 6 partials ⚠️
internal/api/v1/controllers_transactions_create.go 16.66% 2 Missing and 3 partials ⚠️
...al/api/v1/controllers_transactions_add_metadata.go 40.00% 1 Missing and 2 partials ⚠️
...ternal/api/v1/controllers_accounts_add_metadata.go 33.33% 1 Missing and 1 partial ⚠️
...nal/api/v1/controllers_accounts_delete_metadata.go 60.00% 1 Missing and 1 partial ⚠️
...api/v1/controllers_transactions_delete_metadata.go 60.00% 1 Missing and 1 partial ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1155      +/-   ##
==========================================
- Coverage   82.09%   81.25%   -0.84%     
==========================================
  Files         190      190              
  Lines        9319     9417      +98     
==========================================
+ Hits         7650     7652       +2     
- Misses       1216     1235      +19     
- Partials      453      530      +77     

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/api/v2/controllers_transactions_create_test.go (1)

390-440: CreateTransaction mock returns are correct, but the test still captures the loop variable improperly.

The updated expectations:

  • success: expect.Return(&ledger.Log{}, &ledger.CreatedTransaction{Transaction: expectedTx}, false, nil)
  • error: expect.Return(nil, nil, false, tc.returnError)

correctly match the new (*ledger.Log, *ledger.CreatedTransaction, bool, error) signature.

However, inside the loop you still mix testCase and tc inside the subtest closure, which means several fields read from the range variable instead of the copied value and can end up using the last test’s data. It’s safer to always use tc inside the closure.

for _, testCase := range testCases {
-   tc := testCase
-   t.Run(tc.name, func(t *testing.T) {
-       if testCase.expectedStatusCode == 0 {
-           testCase.expectedStatusCode = http.StatusOK
-       }
+   tc := testCase
+   t.Run(tc.name, func(t *testing.T) {
+       if tc.expectedStatusCode == 0 {
+           tc.expectedStatusCode = http.StatusOK
        }
        ...
-       systemController, ledgerController := newTestingSystemController(t, true)
-       if testCase.expectControllerCall {
-           testCase.expectedRunScript.Timestamp = time.Time{}
+       systemController, ledgerController := newTestingSystemController(t, true)
+       if tc.expectControllerCall {
+           tc.expectedRunScript.Timestamp = time.Time{}
            expect := ledgerController.EXPECT().
                CreateTransaction(gomock.Any(), ledgercontroller.Parameters[ledgercontroller.CreateTransaction]{
-                   DryRun: tc.expectedDryRun,
+                   DryRun: tc.expectedDryRun,
                    Input: ledgercontroller.CreateTransaction{
-                       RunScript: testCase.expectedRunScript,
+                       RunScript: tc.expectedRunScript,
                    },
                })
        ...
-       require.Equal(t, testCase.expectedStatusCode, rec.Code)
-       if testCase.expectedStatusCode < 300 && testCase.expectedStatusCode >= 200 {
+       require.Equal(t, tc.expectedStatusCode, rec.Code)
+       if tc.expectedStatusCode < 300 && tc.expectedStatusCode >= 200 {
            ...
-       } else {
+       } else {
            ...
-           require.EqualValues(t, testCase.expectedErrorCode, err.ErrorCode)
+           require.EqualValues(t, tc.expectedErrorCode, err.ErrorCode)
        }
    })
}
🧹 Nitpick comments (14)
pkg/client/models/operations/v2createtransaction.go (1)

62-88: Headers exposure looks good; consider tightening nil-handling semantics

The added Headers field and GetHeaders accessor are consistent with the rest of the response model and give callers straightforward access to HTTP headers; nil-safe handling on the receiver is good.

If you want more predictable semantics for consumers (never having to special‑case nil vs empty maps), you could also treat a nil Headers field as empty:

func (o *V2CreateTransactionResponse) GetHeaders() map[string][]string {
-	if o == nil {
-		return map[string][]string{}
-	}
-	return o.Headers
+	if o == nil || o.Headers == nil {
+		return map[string][]string{}
+	}
+	return o.Headers
}

Not strictly necessary, but it avoids the subtle difference between o == nil and o.Headers == nil and may simplify caller code.

pkg/client/models/operations/v2reverttransaction.go (1)

87-92: Consider returning nil in GetHeaders for a nil receiver

Right now a nil receiver returns a fresh empty map. That’s safe, but slightly surprising because:

  • Callers cannot distinguish “no response” from “no headers”.
  • Any mutation on the returned map (when receiver is nil) won’t persist anywhere.

If you prefer clearer semantics, you could return nil instead:

func (o *V2RevertTransactionResponse) GetHeaders() map[string][]string {
-	if o == nil {
-		return map[string][]string{}
-	}
+	if o == nil {
+		return nil
+	}
	return o.Headers
}

Not critical, just a minor clarity tweak (would need to be done in the generator).

Also applies to: 108-113

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

31-41: Tests correctly updated to the 4‑tuple CreateTransaction signature

The expectations and call sites now match CreateTransaction’s (log, created, bool, error) shape, and the retry behavior is still asserted via the error path. If you later add tests around the idempotency flag itself, it may be worth adding an assertion on the returned bool, but that’s optional for this “too many clients” behavior.

Also applies to: 51-52, 65-68, 79-80

pkg/client/docs/models/operations/v2reverttransactionrequest.md (1)

13-13: IdempotencyKey field doc looks correct; consider clarifying usage.

The new IdempotencyKey field is consistent with the existing table structure and typing. If you want to be extra explicit, you could mention the exact HTTP header name (e.g. “Sent via the Idempotency-Key header”) so callers immediately see how to supply it.

internal/api/v1/controllers_transactions_create.go (1)

87-90: Both CreateTransaction call sites are correctly adapted to the new signature.

Using _, res, _, err := l.CreateTransaction(...) in both the postings and script branches aligns with the new 4‑value return and preserves existing v1 behavior. If you later decide to expose Idempotency-Hit on v1 as well, this is where you’d start wiring the boolean through to response headers.

Also applies to: 118-125

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

73-73: Mock signature correctly updated.

The mock expectation now returns three values (*ledger.Log, bool, error) to match the updated DeleteAccountMetadata interface.

Consider adding test cases to verify the Idempotency-Hit header is set correctly when idempotencyHit is true:

{
    name:              "idempotency hit",
    expectBackendCall: true,
    account:           "account0",
    returnIdempotencyHit: true, // Add this field to testCase
},

Then verify in the test:

if tc.returnIdempotencyHit {
    require.Equal(t, "true", rec.Header().Get("Idempotency-Hit"))
}
internal/api/v1/controllers_transactions_add_metadata.go (1)

33-44: SaveTransactionMetadata call correctly adjusted; consider idempotency header for v1

The call site is correctly updated for the new (*ledger.Log, bool, error) signature, and existing error handling is preserved.

If the intent is for v1 endpoints to also surface idempotent replays, you may want to capture the boolean and optionally set an Idempotency-Hit response header here (mirroring the v2 behavior); if v1 is not supposed to expose this, the current implementation is fine.

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

69-89: Add Idempotency-Hit header assertions in the happy path.

This suite now depends on the handler surfacing Idempotency-Hit, but none of the cases assert it. Please add a success case with idempotencyHit = true and verify the response header so a future regression is caught.

@@
 	type testCase struct {
 		name               string
 		queryParams        url.Values
 		returnErr          error
 		expectedStatusCode int
 		expectedErrorCode  string
 		expectBackendCall  bool
+		idempotencyHit     bool
 	}
@@
 		{
 			name:              "nominal",
 			expectBackendCall: true,
 		},
+		{
+			name:              "idempotency hit",
+			expectBackendCall: true,
+			idempotencyHit:    true,
+		},
@@
-					Return(&ledger.Log{}, false, tc.returnErr)
+					Return(&ledger.Log{}, tc.idempotencyHit, tc.returnErr)
@@
 			router.ServeHTTP(rec, req)
 
 			if tc.expectedStatusCode == 0 || tc.expectedStatusCode == http.StatusOK {
 				require.Equal(t, http.StatusNoContent, rec.Code)
 			} else {
 				require.Equal(t, tc.expectedStatusCode, rec.Code)
 				errorResponse := api.ErrorResponse{}
 				require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errorResponse))
 				require.Equal(t, tc.expectedErrorCode, errorResponse.ErrorCode)
 			}
+
+			header := rec.Header().Get("Idempotency-Hit")
+			if rec.Code == http.StatusNoContent && tc.idempotencyHit {
+				require.Equal(t, "true", header)
+			} else {
+				require.Empty(t, header)
+			}
internal/api/v2/controllers_transactions_add_metadata_test.go (1)

91-115: Assert Idempotency-Hit in the metadata POST tests.

We expose the Idempotency-Hit header from this handler now, but the suite never checks it. Add a case where the mock returns idempotencyHit = true and assert the header to guarantee the new contract.

@@
 	type testCase struct {
 		name              string
 		queryParams       url.Values
 		expectStatusCode  int
 		expectedErrorCode string
 		body              any
 		id                any
 		expectBackendCall bool
 		returnErr         error
+		idempotencyHit    bool
 	}
@@
 		{
 			name: "nominal",
 			body: metadata.Metadata{
 				"foo": "bar",
 			},
 			expectBackendCall: true,
 		},
+		{
+			name: "idempotency hit",
+			body: metadata.Metadata{
+				"foo": "bar",
+			},
+			expectBackendCall: true,
+			idempotencyHit:    true,
+		},
@@
-					Return(nil, false, testCase.returnErr)
+					Return(nil, testCase.idempotencyHit, testCase.returnErr)
@@
 			router.ServeHTTP(rec, req)
 
 			require.Equal(t, testCase.expectStatusCode, rec.Code)
 			if testCase.expectStatusCode >= 300 || testCase.expectStatusCode < 200 {
 				err := api.ErrorResponse{}
 				api.Decode(t, rec.Body, &err)
 				require.EqualValues(t, testCase.expectedErrorCode, err.ErrorCode)
 			}
+
+			header := rec.Header().Get("Idempotency-Hit")
+			if rec.Code == http.StatusNoContent && testCase.idempotencyHit {
+				require.Equal(t, "true", header)
+			} else {
+				require.Empty(t, header)
+			}
test/e2e/api_idempotency_hit_header_test.go (1)

1-477: Comprehensive Idempotency-Hit e2e coverage; only minor possible DRY refactors

The scenarios exercise all main write operations with/without prior calls and correctly assert that Idempotency-Hit is absent on first use and "true" on replay with the same idempotency key, which aligns well with the intended semantics. If you want to reduce repetition later, you could factor small helpers for building common requests and asserting the header value, but that's purely optional given this file’s clarity.

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

43-154: Idempotency flag propagation looks correct; consider event emission on idempotent replays

The wrappers correctly pass through idempotencyHit from the underlying controller and force it to false on error, preserving existing behavior while extending the signature.

One thing to double‑check: all six methods (CreateTransaction, RevertTransaction, Save*Metadata, Delete*Metadata) still schedule listener events whenever !parameters.DryRun, even when idempotencyHit == true. That means repeated idempotent calls will re‑emit CommittedTransactions/SavedMetadata/DeletedMetadata events to listeners.

If the desired contract is “no additional side‑effects on idempotent replays”, you may want to gate event emission on !idempotencyHit as well:

if !parameters.DryRun && !idempotencyHit {
    c.handleEvent(ctx, func() {
        // existing listener call
    })
}

If, instead, duplicate notifications are acceptable (or relied upon), current behavior is fine but should probably be documented.

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

365-411: Traced Create/Revert correctly capture and propagate idempotencyHit

CreateTransaction and RevertTransaction now record idempotencyHit inside the TraceWithMetric closure and return (nil, nil, false, err) on failure, (log, payload, idempotencyHit, nil) on success. Using RET = any purely as a vehicle for tracing (and discarding the value with _) is fine here.

If you want to tighten things stylistically, you could mirror the metadata methods and use a more specific RET type instead of any, but functionally this is sound.

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

19-75: Documentation for boolean return values recommended

Verification confirms all six methods and their implementations have been correctly updated with the new bool return type across all Controller implementations (DefaultController, ControllerWithEvents, ControllerWithTraces, ControllerWithTooManyClientHandling, and the generated mock). No signature mismatches found.

The signature changes are complete and consistent. For clarity, consider adding brief doc comments explaining what the boolean represents (e.g., idempotency flag, whether state changed) on these six methods in the interface:

  • CreateTransaction
  • RevertTransaction
  • SaveTransactionMetadata
  • SaveAccountMetadata
  • DeleteTransactionMetadata
  • DeleteAccountMetadata
internal/controller/ledger/controller_with_too_many_client_handling.go (1)

18-18: Clean up duplicate -typed flag in go:generate

The go:generate line includes -typed twice, once as a flag and once trailing after DelayCalculator. That extra token is unnecessary and may be confusing for future maintainers or tooling.

Consider simplifying to a single flag:

-//go:generate mockgen -write_source_comment=false -typed -write_package_comment=false -source controller_with_too_many_client_handling.go -destination controller_with_too_many_client_handling_generated_test.go -package ledger . DelayCalculator -typed
+//go:generate mockgen -write_source_comment=false -typed -write_package_comment=false -source controller_with_too_many_client_handling.go -destination controller_with_too_many_client_handling_generated_test.go -package ledger . DelayCalculator
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3700da0 and 1cab802.

⛔ Files ignored due to path filters (5)
  • openapi.yaml is excluded by !**/*.yaml
  • openapi/v2.yaml is excluded by !**/*.yaml
  • pkg/client/.speakeasy/gen.lock is excluded by !**/*.lock, !**/*.lock
  • pkg/client/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
  • pkg/client/speakeasyusagegen/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
📒 Files selected for processing (66)
  • docs/api/README.md (7 hunks)
  • internal/README.md (1 hunks)
  • internal/api/bulking/bulker.go (6 hunks)
  • internal/api/bulking/bulker_test.go (13 hunks)
  • internal/api/bulking/mocks.go (1 hunks)
  • internal/api/bulking/mocks_ledger_controller_test.go (20 hunks)
  • internal/api/common/mocks_ledger_controller_test.go (6 hunks)
  • internal/api/v1/controllers_accounts_add_metadata.go (1 hunks)
  • internal/api/v1/controllers_accounts_add_metadata_test.go (1 hunks)
  • internal/api/v1/controllers_accounts_delete_metadata.go (1 hunks)
  • internal/api/v1/controllers_accounts_delete_metadata_test.go (1 hunks)
  • internal/api/v1/controllers_transactions_add_metadata.go (1 hunks)
  • internal/api/v1/controllers_transactions_add_metadata_test.go (1 hunks)
  • internal/api/v1/controllers_transactions_create.go (2 hunks)
  • internal/api/v1/controllers_transactions_create_test.go (1 hunks)
  • internal/api/v1/controllers_transactions_delete_metadata.go (1 hunks)
  • internal/api/v1/controllers_transactions_delete_metadata_test.go (1 hunks)
  • internal/api/v1/controllers_transactions_revert.go (1 hunks)
  • internal/api/v1/controllers_transactions_revert_test.go (1 hunks)
  • internal/api/v1/mocks.go (1 hunks)
  • internal/api/v1/mocks_ledger_controller_test.go (20 hunks)
  • internal/api/v1/mocks_system_controller_test.go (33 hunks)
  • internal/api/v2/controllers_accounts_add_metadata.go (1 hunks)
  • internal/api/v2/controllers_accounts_add_metadata_test.go (1 hunks)
  • internal/api/v2/controllers_accounts_delete_metadata.go (1 hunks)
  • internal/api/v2/controllers_accounts_delete_metadata_test.go (1 hunks)
  • internal/api/v2/controllers_bulk_test.go (13 hunks)
  • internal/api/v2/controllers_transactions_add_metadata.go (2 hunks)
  • internal/api/v2/controllers_transactions_add_metadata_test.go (1 hunks)
  • internal/api/v2/controllers_transactions_create.go (2 hunks)
  • internal/api/v2/controllers_transactions_create_test.go (1 hunks)
  • internal/api/v2/controllers_transactions_delete_metadata.go (1 hunks)
  • internal/api/v2/controllers_transactions_delete_metadata_test.go (1 hunks)
  • internal/api/v2/controllers_transactions_revert.go (2 hunks)
  • internal/api/v2/controllers_transactions_revert_test.go (1 hunks)
  • internal/api/v2/mocks.go (1 hunks)
  • internal/api/v2/mocks_ledger_controller_test.go (20 hunks)
  • internal/api/v2/mocks_system_controller_test.go (33 hunks)
  • internal/controller/ledger/controller.go (2 hunks)
  • internal/controller/ledger/controller_default.go (6 hunks)
  • internal/controller/ledger/controller_default_test.go (4 hunks)
  • internal/controller/ledger/controller_generated_test.go (20 hunks)
  • internal/controller/ledger/controller_with_events.go (6 hunks)
  • internal/controller/ledger/controller_with_too_many_client_handling.go (2 hunks)
  • internal/controller/ledger/controller_with_too_many_client_handling_generated_test.go (2 hunks)
  • internal/controller/ledger/controller_with_too_many_client_handling_test.go (4 hunks)
  • internal/controller/ledger/controller_with_traces.go (1 hunks)
  • internal/controller/ledger/log_process.go (2 hunks)
  • internal/controller/ledger/log_process_test.go (2 hunks)
  • internal/controller/system/state_tracker.go (1 hunks)
  • internal/log.go (1 hunks)
  • pkg/client/docs/models/operations/v2addmetadataontransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2addmetadatatoaccountresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2createtransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2deleteaccountmetadataresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2deletetransactionmetadataresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2reverttransactionrequest.md (1 hunks)
  • pkg/client/docs/models/operations/v2reverttransactionresponse.md (1 hunks)
  • pkg/client/models/operations/v2addmetadataontransaction.go (2 hunks)
  • pkg/client/models/operations/v2addmetadatatoaccount.go (2 hunks)
  • pkg/client/models/operations/v2createtransaction.go (2 hunks)
  • pkg/client/models/operations/v2deleteaccountmetadata.go (2 hunks)
  • pkg/client/models/operations/v2deletetransactionmetadata.go (2 hunks)
  • pkg/client/models/operations/v2reverttransaction.go (4 hunks)
  • pkg/client/v2.go (7 hunks)
  • test/e2e/api_idempotency_hit_header_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (30)
internal/api/v2/controllers_accounts_add_metadata_test.go (1)
internal/log.go (1)
  • Log (84-96)
internal/api/bulking/bulker.go (2)
internal/controller/ledger/controller.go (6)
  • CreateTransaction (96-100)
  • SaveAccountMetadata (114-117)
  • SaveTransactionMetadata (109-112)
  • RevertTransaction (102-107)
  • DeleteAccountMetadata (124-127)
  • DeleteTransactionMetadata (119-122)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/api/v1/controllers_accounts_delete_metadata_test.go (2)
internal/log.go (1)
  • Log (84-96)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/api/v2/controllers_transactions_add_metadata.go (1)
internal/controller/ledger/controller.go (1)
  • SaveTransactionMetadata (109-112)
internal/api/v1/controllers_transactions_delete_metadata.go (1)
internal/controller/ledger/controller.go (1)
  • DeleteTransactionMetadata (119-122)
internal/controller/ledger/controller_default_test.go (2)
internal/controller/ledger/controller.go (4)
  • CreateTransaction (96-100)
  • RevertTransaction (102-107)
  • SaveTransactionMetadata (109-112)
  • DeleteTransactionMetadata (119-122)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/controller/ledger/controller_with_too_many_client_handling_test.go (4)
internal/controller/ledger/errors.go (1)
  • ErrTooManyClient (16-16)
internal/log.go (2)
  • Log (84-96)
  • CreatedTransaction (199-202)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/transaction.go (1)
  • NewTransaction (228-232)
internal/api/v1/controllers_transactions_delete_metadata_test.go (2)
internal/log.go (1)
  • Log (84-96)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/api/v2/controllers_accounts_delete_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • DeleteAccountMetadata (124-127)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/api/v1/controllers_accounts_add_metadata.go (1)
internal/controller/ledger/controller.go (1)
  • SaveAccountMetadata (114-117)
internal/controller/ledger/controller_with_traces.go (4)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/log.go (2)
  • Log (84-96)
  • CreatedTransaction (199-202)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/tracing/tracing.go (1)
  • TraceWithMetric (101-129)
internal/api/v2/controllers_transactions_revert.go (1)
internal/controller/ledger/controller.go (1)
  • RevertTransaction (102-107)
internal/controller/ledger/controller_default.go (3)
internal/controller/ledger/controller.go (6)
  • CreateTransaction (96-100)
  • RevertTransaction (102-107)
  • SaveTransactionMetadata (109-112)
  • SaveAccountMetadata (114-117)
  • DeleteTransactionMetadata (119-122)
  • DeleteAccountMetadata (124-127)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/log.go (3)
  • Log (84-96)
  • CreatedTransaction (199-202)
  • RevertedTransaction (329-332)
internal/api/v1/controllers_transactions_revert.go (1)
internal/controller/ledger/controller.go (1)
  • RevertTransaction (102-107)
internal/api/v1/controllers_transactions_create.go (1)
internal/controller/ledger/controller.go (1)
  • CreateTransaction (96-100)
internal/controller/system/state_tracker.go (3)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/log.go (3)
  • Log (84-96)
  • CreatedTransaction (199-202)
  • RevertedTransaction (329-332)
internal/controller/system/controller.go (1)
  • Controller (41-55)
internal/api/v2/controllers_accounts_add_metadata.go (4)
internal/controller/ledger/controller.go (1)
  • SaveAccountMetadata (114-117)
internal/machine/address.go (1)
  • Address (7-7)
internal/machine/vm/machine.go (1)
  • Metadata (52-52)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/controller/ledger/log_process.go (2)
internal/log.go (1)
  • Log (84-96)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/api/v1/controllers_accounts_add_metadata_test.go (1)
internal/log.go (1)
  • Log (84-96)
internal/api/v2/controllers_transactions_delete_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • DeleteTransactionMetadata (119-122)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/api/v1/controllers_transactions_add_metadata.go (1)
internal/controller/ledger/controller.go (1)
  • SaveTransactionMetadata (109-112)
internal/api/v2/controllers_transactions_create.go (1)
internal/controller/ledger/controller.go (1)
  • CreateTransaction (96-100)
internal/api/common/mocks_ledger_controller_test.go (3)
internal/controller/ledger/controller.go (1)
  • CreateTransaction (96-100)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/controller/ledger/controller.go (2)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/log.go (2)
  • CreatedTransaction (199-202)
  • RevertedTransaction (329-332)
internal/api/v2/controllers_transactions_delete_metadata_test.go (2)
internal/log.go (1)
  • Log (84-96)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/controller/ledger/controller_with_too_many_client_handling.go (4)
internal/controller/ledger/controller.go (7)
  • CreateTransaction (96-100)
  • Controller (21-83)
  • RevertTransaction (102-107)
  • SaveTransactionMetadata (109-112)
  • SaveAccountMetadata (114-117)
  • DeleteTransactionMetadata (119-122)
  • DeleteAccountMetadata (124-127)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/log.go (3)
  • Log (84-96)
  • CreatedTransaction (199-202)
  • RevertedTransaction (329-332)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/api/v1/mocks_ledger_controller_test.go (4)
internal/api/common/mocks_ledger_controller_test.go (2)
  • LedgerControllerMockRecorder (33-35)
  • LedgerController (26-30)
internal/controller/ledger/controller.go (7)
  • Controller (21-83)
  • CreateTransaction (96-100)
  • DeleteAccountMetadata (124-127)
  • DeleteTransactionMetadata (119-122)
  • RevertTransaction (102-107)
  • SaveAccountMetadata (114-117)
  • SaveTransactionMetadata (109-112)
internal/volumes.go (2)
  • BalancesByAssets (97-97)
  • VolumesWithBalanceByAssetByAccount (64-68)
internal/controller/ledger/stats.go (1)
  • Stats (10-13)
internal/api/v2/mocks_ledger_controller_test.go (7)
internal/controller/ledger/controller.go (3)
  • Controller (21-83)
  • CreateTransaction (96-100)
  • RevertTransaction (102-107)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/account.go (1)
  • Account (16-26)
internal/volumes.go (1)
  • BalancesByAssets (97-97)
internal/controller/ledger/stats.go (1)
  • Stats (10-13)
internal/storage/common/pagination.go (1)
  • PaginatedQuery (26-29)
internal/ledger.go (1)
  • Ledger (21-30)
internal/api/v1/mocks_system_controller_test.go (5)
internal/api/common/mocks_system_controller_test.go (3)
  • MockReplicationBackendMockRecorder (30-32)
  • MockReplicationBackend (23-27)
  • SystemController (221-225)
internal/ledger.go (2)
  • Configuration (96-100)
  • Ledger (21-30)
internal/controller/system/controller.go (1)
  • Controller (41-55)
internal/storage/common/pagination.go (1)
  • PaginatedQuery (26-29)
internal/storage/system/queries.go (1)
  • ListLedgersQueryPayload (3-7)
internal/controller/ledger/controller_generated_test.go (2)
internal/controller/ledger/controller.go (7)
  • Controller (21-83)
  • CreateTransaction (96-100)
  • DeleteAccountMetadata (124-127)
  • DeleteTransactionMetadata (119-122)
  • RevertTransaction (102-107)
  • SaveAccountMetadata (114-117)
  • SaveTransactionMetadata (109-112)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/models/operations/v2deletetransactionmetadataresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/v2addmetadataontransactionresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/v2deleteaccountmetadataresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/v2addmetadatatoaccountresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/v2createtransactionresponse.md

10-10: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/v2reverttransactionresponse.md

10-10: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

🔇 Additional comments (53)
internal/README.md (1)

727-727: Documentation comment update is clear and appropriate.

The updated comment for IdempotencyHash clearly explains its purpose in validating idempotency key usage. This aligns with the PR objective of adding idempotency support and is an accurate reflection of the field's role in the feature.

pkg/client/models/operations/v2deleteaccountmetadata.go (2)

50-50: LGTM! Headers field properly typed.

The Headers field uses the standard Go type for HTTP headers and aligns with the PR's objective to expose response headers for idempotency tracking.


60-65: LGTM! Proper nil-safety in accessor.

The GetHeaders() method correctly handles nil receivers by returning an empty map, preventing potential nil pointer dereferences for callers.

pkg/client/models/operations/v2addmetadataontransaction.go (2)

72-72: LGTM! Headers field correctly added.

The Headers field uses the standard Go type for HTTP headers (map[string][]string) and appropriately lacks a JSON tag since headers are transport metadata rather than response body content.


82-87: LGTM! GetHeaders() method correctly implemented.

The method provides nil-safe access to the Headers field and follows the same pattern as other getters in this file (e.g., GetHTTPMeta(), GetRequestBody()). Returning an empty map when the receiver is nil is the appropriate default behavior.

pkg/client/models/operations/v2deletetransactionmetadata.go (2)

63-63: Headers field added to support idempotency tracking.

The new Headers field correctly exposes HTTP response headers, aligning with the PR objective to propagate the Idempotency-Hit header. Note that unlike HTTPMeta (line 62), this field lacks a JSON struct tag, meaning it may be included in JSON serialization using the default field name "Headers". This is likely intentional based on the generator configuration, but verify if this matches the expected SDK behavior.


73-78: LGTM! Nil-safe accessor follows established pattern.

The GetHeaders() method correctly implements nil-safety by returning an empty map when the receiver is nil, consistent with the pattern used by other getters in this struct (GetHTTPMeta, GetLedger, etc.).

pkg/client/v2.go (1)

2201-2201: LGTM: Idempotency header support correctly implemented

The changes consistently add idempotency header support across mutation endpoints:

  1. Outbound headers: utils.PopulateHeaders correctly extracts idempotency-related headers from the request object and applies them to the HTTP request
  2. Inbound headers: res.Headers = httpRes.Header correctly propagates response headers (including Idempotency-Hit) from the HTTP response to the public response object
  3. Success-only propagation: Headers are only propagated on success status codes (200, 201, 204), which is correct
  4. Complete pairing: Every endpoint that populates outbound headers also propagates inbound headers

The implementation is thread-safe and follows the established patterns in this generated code.

Also applies to: 2315-2316, 2398-2398, 2508-2509, 3227-3227, 3341-3342, 3662-3662, 3776-3777, 3859-3859, 3969-3970, 4058-4059, 4172-4173

internal/log.go (1)

92-92: LGTM: Grammar correction

Minor grammatical improvement to the comment.

internal/api/bulking/mocks.go (1)

1-1: LGTM: Typed mock generation enabled

The -typed flag addition enables type-safe mock wrappers, consistent with mock generation updates across the codebase.

pkg/client/docs/models/operations/v2createtransactionresponse.md (1)

9-10: LGTM: Headers field documentation added

The documentation correctly reflects the new Headers field. The static analysis warning about a missing link definition for "string" is a false positive—this is type notation, not a markdown link.

pkg/client/models/operations/v2addmetadatatoaccount.go (1)

63-78: LGTM: Headers support added correctly

The Headers field and GetHeaders method are implemented correctly with proper nil safety.

pkg/client/docs/models/operations/v2reverttransactionresponse.md (1)

9-10: LGTM: Headers field documentation added

Consistent with other response model documentation updates. The static analysis warning is a false positive.

docs/api/README.md (1)

975-980: LGTM: Comprehensive idempotency documentation

The documentation consistently describes the Idempotency-Hit header behavior across all operations that support idempotency (status codes 204, 200, and 201). The Idempotency-Key request header parameters are also properly documented.

Also applies to: 1034-1039, 1491-1496, 1687-1692, 1838-1843

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

26-39: LGTM: Idempotency hit handling implemented correctly

The handler now captures the idempotencyHit boolean and sets the Idempotency-Hit header appropriately before writing the response. Ignoring the first return value (the log) is acceptable since this endpoint returns a 204 No Content response.

internal/api/v1/controllers_accounts_delete_metadata_test.go (1)

77-77: LGTM: Test mock updated for new signature

The mock correctly reflects the expanded DeleteAccountMetadata return signature (Log, bool, error).

pkg/client/models/operations/v2reverttransaction.go (1)

21-25: Idempotency key field and accessor look consistent

IdempotencyKey is correctly wired as an optional header and the getter mirrors the existing accessor patterns with a nil‑receiver guard. Nothing to change here.

Also applies to: 73-79

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

5-5: Typed Next mock wrapper is consistent with the interface

MockDelayCalculatorNextCall and its Return/Do/DoAndReturn helpers correctly mirror Next(int) time.Duration and keep chained expectations (EXPECT().Next(0).Return(...)) working. No issues spotted.

Also applies to: 50-77

internal/api/bulking/mocks_ledger_controller_test.go (1)

5-5: Typed LedgerController mocks and idempotency bool plumbing look correct

The generated mocks:

  • Match the ledger0.Controller interface for all methods, including the new (…, bool, error) signatures on CreateTransaction, RevertTransaction, and the metadata operations.
  • Use per‑method wrapper types (LedgerController<Method>Call) whose Return/Do/DoAndReturn signatures correctly mirror the underlying methods.

Given this is generated code and aligns with the interface, I don’t see any issues here. Just ensure the mockgen invocation is tracked so future regenerations stay in sync with the interface.

Also applies to: 59-1028

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

5-5: MockController typed wrappers and bool return extensions are consistent

For CreateTransaction, RevertTransaction, and the metadata methods, the mocks:

  • Return (log, result, bool, error) / (log, bool, error) with indices and types matching the Controller interface.
  • Expose MockController<Method>Call wrappers whose Return/Do/DoAndReturn function types line up with the concrete method signatures, including the new bool.

Everything here looks coherent with the interface and other mocks; no changes needed.

Also applies to: 59-1027

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

1-2: Typed mock generation directives look good.

Using -typed here is consistent with the rest of the API mocks and should improve mock type safety.

internal/api/v1/controllers_accounts_delete_metadata.go (1)

22-31: Correct adaptation to new DeleteAccountMetadata signature.

Unpacking the extra boolean return and ignoring it here is fine for the v1 API, since this endpoint doesn’t expose idempotency headers.

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

25-32: RevertTransaction call correctly updated for extra return value.

Capturing ret while discarding log and idempotency flag matches the expanded signature and preserves existing v1 behavior.

internal/api/v1/controllers_transactions_delete_metadata.go (1)

27-33: DeleteTransactionMetadata call updated correctly for new triple return.

The extra boolean return is safely discarded here, keeping v1 semantics unchanged while aligning with the new controller signature.

internal/api/v1/mocks.go (1)

1-2: v1 mock generation updated consistently with typed mocks.

The added -typed flag for both system and ledger controller mocks is consistent with the rest of the codebase’s mock updates.

internal/api/v1/controllers_transactions_delete_metadata_test.go (1)

70-70: Mock signature correctly updated.

The mock expectation now returns three values (*ledger.Log, bool, error) to match the updated DeleteTransactionMetadata interface.

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

26-36: LGTM! Idempotency-Hit header correctly implemented.

The handler now captures the idempotencyHit boolean from DeleteTransactionMetadata and sets the Idempotency-Hit response header appropriately in the success path.

internal/api/bulking/bulker.go (6)

143-143: LGTM! Controller call updated correctly.

The CreateTransaction call now captures the additional boolean return value (idempotency hit flag), which is appropriately ignored in bulk operations.


167-167: LGTM! Controller call updated correctly.

The SaveAccountMetadata call now captures the additional boolean return value, which is appropriately ignored in bulk operations.


180-180: LGTM! Controller call updated correctly.

The SaveTransactionMetadata call now captures the additional boolean return value, which is appropriately ignored in bulk operations.


199-199: LGTM! Controller call updated correctly.

The RevertTransaction call now captures the additional boolean return value, which is appropriately ignored in bulk operations.


228-228: LGTM! Controller call updated correctly.

The DeleteAccountMetadata call now captures the additional boolean return value, which is appropriately ignored in bulk operations.


242-242: LGTM! Controller call updated correctly.

The DeleteTransactionMetadata call now captures the additional boolean return value, which is appropriately ignored in bulk operations.

internal/api/v1/controllers_accounts_add_metadata.go (1)

38-41: Controller call signature updated correctly.

The SaveAccountMetadata call now captures the additional boolean return value, which is currently ignored.

Confirm whether v1 API handlers should set the Idempotency-Hit header like v2 handlers, or if this feature is intentionally v2-only. The verification script from the test file review will help clarify this.

internal/api/v1/controllers_transactions_add_metadata_test.go (1)

56-66: SaveTransactionMetadata mock expectation correctly updated

The gomock Return(&ledger.Log{}, false, nil) matches the new (*ledger.Log, bool, error) signature and keeps the idempotency flag explicitly false for the nominal path. No further changes needed here.

internal/api/v1/controllers_transactions_revert_test.go (1)

75-85: RevertTransaction mock return matches updated signature

The expectation now returns (*ledger.Log, *ledger.RevertedTransaction, bool, error) with false for the new idempotency flag. This aligns with the updated RevertTransaction API and keeps the tests’ behavior intact.

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

75-84: v2 RevertTransaction mock correctly updated for idempotency flag

The gomock return values now conform to the new (*ledger.Log, *ledger.RevertedTransaction, bool, error) signature, with the idempotency flag set to false. This keeps the existing test logic valid under the new API.

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

40-64: Idempotency-Hit handling in createTransaction is wired correctly

The handler now consumes the new idempotencyHit boolean from CreateTransaction and, on success, sets Idempotency-Hit: true before writing the response body. This cleanly preserves existing error handling while exposing idempotent replays to clients as intended.

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

22-37: Idempotency-Hit header propagation from controller looks correct

The handler cleanly threads the new idempotencyHit flag from DeleteAccountMetadata and only sets Idempotency-Hit: true on the successful, idempotent replay path, leaving error handling and first-call behavior unchanged.

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

45-52: Tests updated to new forgeLog signature without behavior change

Both tests now capture the extra return value from forgeLog via _ while preserving the existing assertions on err, which is exactly what's needed for the signature change here.

Also applies to: 87-95

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

89-133: forgeLog’s idempotencyHit flag is wired correctly across all paths

The updated forgeLog cleanly distinguishes between: (a) pre-existing logs found by idempotency key and conflicts resolved via fetchLogWithIK (returns idempotencyHit=true), and (b) newly forged logs (idempotencyHit=false), while ensuring all error paths return idempotencyHit=false. This gives callers a reliable signal for header behavior without changing the underlying deadlock/idempotency handling.

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

71-77: Controller default tests correctly adapted to new idempotency-aware signatures

The tests now destructure the extended controller returns (extra bool before error) while preserving their original success assertions, which keeps them aligned with the new API surface without altering behavior.

Also applies to: 127-133, 172-178, 215-221

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

76-88: Bulk controller mocks correctly updated for idempotency boolean

The bulk tests’ expectations now match the extended controller signatures, returning false for idempotencyHit on fresh operations and threading the flag through all success and error cases without changing existing assertions on bulk results or error handling.

Also applies to: 120-132, 150-162, 179-203, 227-238, 277-300, 351-386, 425-455, 515-540

internal/controller/system/state_tracker.go (1)

114-197: State tracker correctly threads idempotencyHit through handleState

The facade methods (CreateTransaction, RevertTransaction, Save*Metadata, Delete*Metadata) now consistently capture idempotencyHit from the underlying ledgercontroller.Controller inside handleState and return it alongside the existing outputs.

The control flow (lock, optional TX, commit/rollback) is unchanged apart from the extra boolean, so external behavior should match the previous implementation plus the new flag. No concurrency or transaction‑lifecycle issues introduced here.

internal/controller/ledger/controller_with_traces.go (2)

413-456: Save*Metadata traced wrappers preserve behavior while surfacing idempotencyHit

Both SaveTransactionMetadata and SaveAccountMetadata wrap the underlying calls in TraceWithMetric[*ledger.Log], assign log and idempotencyHit inside the closure, and on exit return (nil, false, err) or (log, idempotencyHit, nil).

This preserves previous tracing/latency behavior and simply adds the extra boolean without changing DryRun or error semantics.


458-501: Delete*Metadata traced wrappers match the new signature and error semantics

DeleteTransactionMetadata and DeleteAccountMetadata follow the same pattern as the save variants: local log, idempotencyHit, wrapped in TraceWithMetric[*ledger.Log], and they zero the outputs on error.

Propagation of idempotencyHit from underlying through the tracing layer looks consistent and correct.

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

415-565: DefaultController wrappers cleanly surface idempotencyHit from logProcessor

The public methods now delegate as follows:

  • CreateTransaction / RevertTransaction return the full tuple from createTransactionLp / revertTransactionLp.forgeLog, which now includes the bool.
  • Save*Metadata and Delete*Metadata unwrap forgeLog as (log, _, idempotencyHit, err) and expose (log, idempotencyHit, err).

Assuming forgeLog’s new signature is (*ledger.Log, *Payload, bool, error) (as implied by usage), this is a straightforward and consistent way to surface idempotencyHit without touching the core business logic in the helpers.

No functional issues spotted here.

internal/api/common/mocks_ledger_controller_test.go (1)

109-422: Mocks updated to match new controller signatures (including idempotencyHit)

The LedgerController mock methods now mirror the updated controller interface:

  • CreateTransaction / RevertTransaction return four values and correctly unpack ret[2] as bool and ret[3] as error.
  • SaveAccountMetadata, SaveTransactionMetadata, DeleteAccountMetadata, and DeleteTransactionMetadata all return (*ledger.Log, bool, error) and unpack the new boolean at index 1.

This keeps tests aligned with the new API surface and ensures the idempotencyHit flag can be exercised in expectations and returns. No issues from the gomock usage itself.

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

55-506: Typed gomock wrappers for v2 SystemController/ReplicationBackend look consistent

Recorder methods and the *...Call wrapper types correctly mirror the underlying method signatures (params and return types) and provide typed Return/Do/DoAndReturn helpers. This should improve test ergonomics without changing behavior.

Also applies to: 532-1299

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

46-58: Idempotency hit flag is correctly threaded through retry wrapper

For CreateTransaction, RevertTransaction, and the Save*/Delete* metadata methods, the new idempotencyHit bool is:

  • Declared alongside the existing results,
  • Assigned inside the handleRetry closure from the underlying Controller call, and
  • Returned from the wrapper in the same positional order as defined on the Controller interface.

This preserves the retry behavior while exposing idempotency information cleanly.

Also applies to: 60-73, 75-88, 89-102, 103-116, 117-129

internal/api/v1/mocks_system_controller_test.go (1)

55-506: v1 SystemController/ReplicationBackend typed mocks match interface surface

The recorder methods and *...Call wrapper types correctly mirror the v1 system controller and replication backend interfaces and expose typed Return/Do/DoAndReturn helpers. The generated code looks consistent and safe to rely on in tests.

Also applies to: 532-1299

internal/api/v1/mocks_ledger_controller_test.go (1)

205-245: v1 LedgerController mocks correctly incorporate idempotency boolean

For CreateTransaction, RevertTransaction, and the Save*/Delete* metadata methods, the mock methods and their LedgerController*Call wrappers:

  • Use the updated return shapes ((*ledger.Log, *ledger.CreatedTransaction, bool, error) and (*ledger.Log, bool, error)),
  • Read the bool from the correct index in ret[...], and
  • Expose matching typed Return/Do/DoAndReturn signatures.

This keeps the mocks in sync with the updated controller interface and should work smoothly with idempotency-aware tests.

Also applies to: 246-325, 871-910, 950-1028

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

205-245: v2 LedgerController mocks are correctly updated for idempotency-aware signatures

The v2 mocks mirror the controller interface changes:

  • CreateTransaction / RevertTransaction return (*ledger.Log, *ledger.CreatedTransaction|*ledger.RevertedTransaction, bool, error) and read the bool and error from the correct positions.
  • Save/Delete metadata methods return (*ledger.Log, bool, error) with matching indices.
  • The associated LedgerController*Call wrappers expose typed Return/Do/DoAndReturn with the same signatures.

This keeps the v2 mock surface consistent with the new idempotency semantics.

Also applies to: 246-325, 871-910, 950-1028

@flemzord
Copy link
Member

@cubic-dev-ai review this pull request

@cubic-dev-ai
Copy link

cubic-dev-ai bot commented Nov 27, 2025

@cubic-dev-ai review this pull request

@flemzord I've started the AI code review. It'll take a few minutes to complete.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 71 files

Prompt for AI agents (all 4 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="internal/api/v1/controllers_transactions_delete_metadata.go">

<violation number="1" location="internal/api/v1/controllers_transactions_delete_metadata.go:27">
The idempotency-hit flag returned by DeleteTransactionMetadata is ignored here, so the v1 endpoint never emits the new Idempotency-Hit header even when a request replays a previous command.</violation>
</file>

<file name="internal/api/v1/controllers_accounts_add_metadata.go">

<violation number="1" location="internal/api/v1/controllers_accounts_add_metadata.go:38">
The idempotency hit flag returned by `SaveAccountMetadata` is discarded, so this v1 endpoint never sets the required Idempotency-Hit response header.</violation>
</file>

<file name="internal/api/v1/controllers_transactions_revert.go">

<violation number="1" location="internal/api/v1/controllers_transactions_revert.go:25">
The controller drops the new `idempotencyHit` return value, so the v1 revert endpoint never emits the required `Idempotency-Hit` response header.</violation>
</file>

<file name="internal/api/v1/controllers_transactions_add_metadata.go">

<violation number="1" location="internal/api/v1/controllers_transactions_add_metadata.go:33">
Discarding the idempotencyHit return value means this controller never sets the Idempotency-Hit response header, so clients cannot tell when their request was served from an idempotency cache.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
pkg/client/models/operations/createtransaction.go (1)

63-68: Consider normalizing nil Headers to an empty map for stronger nil-safety

GetHeaders returns an empty map only when the receiver is nil; if o is non‑nil but Headers is nil, callers still get nil and must handle that case separately. If you want fully nil‑safe semantics, you could also normalize nil o.Headers to an empty map:

func (o *CreateTransactionResponse) GetHeaders() map[string][]string {
-	if o == nil {
-		return map[string][]string{}
-	}
-	return o.Headers
+	if o == nil {
+		return map[string][]string{}
+	}
+	if o.Headers == nil {
+		return map[string][]string{}
+	}
+	return o.Headers
}

This is optional and mainly about ergonomic consistency for callers.

pkg/client/models/operations/addmetadataontransaction.go (1)

64-69: Nil‑receiver behavior in GetHeaders is fine; consider consistency on nil vs empty maps

GetHeaders safely handles a nil receiver and returns an empty map, which is convenient for callers that iterate without nil checks. Note that if o != nil but o.Headers == nil, this method will return nil rather than an empty map, so callers that mutate the returned map might still need to defensively initialize it. If you want fully uniform behavior, you could normalize nil to map[string][]string{} in the non‑nil branch as well, but this is optional.

internal/api/v1/controllers_accounts_delete_metadata.go (1)

22-37: Idempotency-Hit header correctly added for deleteAccountMetadata

DeleteAccountMetadata’s new idempotencyHit flag is captured, errors are handled first, and Idempotency-Hit is only set on successful idempotent hits. This is consistent with the other controllers in the PR.

If you touch this again, you could optionally pull ctx := r.Context() and l := common.LedgerFromContext(ctx) for slightly clearer flow, but it’s not required.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1cab802 and 6bcf605.

⛔ Files ignored due to path filters (5)
  • openapi.yaml is excluded by !**/*.yaml
  • openapi/v1.yaml is excluded by !**/*.yaml
  • pkg/client/.speakeasy/gen.lock is excluded by !**/*.lock, !**/*.lock
  • pkg/client/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
  • pkg/client/speakeasyusagegen/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
📒 Files selected for processing (15)
  • internal/api/v1/controllers_accounts_add_metadata.go (1 hunks)
  • internal/api/v1/controllers_accounts_delete_metadata.go (1 hunks)
  • internal/api/v1/controllers_transactions_add_metadata.go (2 hunks)
  • internal/api/v1/controllers_transactions_create.go (4 hunks)
  • internal/api/v1/controllers_transactions_delete_metadata.go (1 hunks)
  • internal/api/v1/controllers_transactions_revert.go (2 hunks)
  • pkg/client/docs/models/operations/addmetadataontransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/addmetadatatoaccountresponse.md (1 hunks)
  • pkg/client/docs/models/operations/createtransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/reverttransactionresponse.md (1 hunks)
  • pkg/client/models/operations/addmetadataontransaction.go (2 hunks)
  • pkg/client/models/operations/addmetadatatoaccount.go (2 hunks)
  • pkg/client/models/operations/createtransaction.go (2 hunks)
  • pkg/client/models/operations/reverttransaction.go (2 hunks)
  • pkg/client/v1.go (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • pkg/client/v1.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/api/v1/controllers_transactions_add_metadata.go
  • internal/api/v1/controllers_transactions_create.go
  • internal/api/v1/controllers_accounts_add_metadata.go
🧰 Additional context used
🧬 Code graph analysis (3)
internal/api/v1/controllers_accounts_delete_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • DeleteAccountMetadata (124-127)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/api/v1/controllers_transactions_revert.go (1)
internal/controller/ledger/controller.go (1)
  • RevertTransaction (102-107)
internal/api/v1/controllers_transactions_delete_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • DeleteTransactionMetadata (119-122)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/models/operations/addmetadataontransactionresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/createtransactionresponse.md

10-10: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (7)
pkg/client/models/operations/createtransaction.go (1)

42-47: Headers field addition looks consistent with response modeling

Adding Headers map[string][]string on CreateTransactionResponse aligns with the pattern of exposing HTTP headers on client responses and keeps types straightforward for consumers. No issues from a correctness or API-design standpoint here.

pkg/client/models/operations/addmetadataontransaction.go (1)

52-55: Adding Headers field on the response is reasonable and consistent

Exposing Headers map[string][]string on AddMetadataOnTransactionResponse fits the use case of surfacing HTTP headers (e.g., idempotency information) to SDK consumers and aligns with the existing HTTPMeta pattern. No issues from a type/visibility perspective.

pkg/client/models/operations/addmetadatatoaccount.go (2)

45-45: LGTM! Headers field properly added.

The new Headers field correctly captures HTTP response headers to support the idempotency-hit feature. The lack of struct tags is consistent with the existing HTTPMeta field pattern.


55-60: LGTM! Getter method follows best practices.

The GetHeaders() method is correctly implemented with nil-safe behavior and follows the same pattern as other getters in this file. Returning an empty map instead of nil is good defensive programming.

internal/api/v1/controllers_transactions_delete_metadata.go (1)

27-37: Idempotency-Hit propagation for deleteTransactionMetadata looks correct

DeleteTransactionMetadata’s new idempotencyHit return is captured, errors are handled first, and Idempotency-Hit: true is only set on successful idempotent hits. This matches the intended pattern used elsewhere in the PR.

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

25-48: RevertTransaction idempotency wiring is consistent and safe

The handler now unpacks idempotencyHit from RevertTransaction and sets Idempotency-Hit: true only after a successful call. Error mapping and the 201 response body remain unchanged, so behavior is backward compatible apart from the new header.

pkg/client/models/operations/reverttransaction.go (1)

56-57: Generated Headers field and accessor look consistent with the new API surface

Adding Headers map[string][]string to RevertTransactionResponse and exposing it via GetHeaders() (with a nil-safe receiver check) matches the intended API change and aligns with how other response metadata is modeled. Given this file is generated, leaving the implementation as-is is appropriate.

Also applies to: 73-78

@gfyrag gfyrag force-pushed the feat/idempotency-hit branch from 6bcf605 to 5229294 Compare November 28, 2025 10:45
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (7)
pkg/client/docs/models/operations/reverttransactionresponse.md (1)

10-10: Fix Headers type formatting and align with Go model (duplicate from prior review).

This is a duplicate of the earlier review comment. The Headers type documentation at line 10 shows map[string][]*string*, which:

  1. Triggers a markdownlint MD052 error (undefined reference [string])
  2. Mismatches the actual Go client model: Headers map[string][]string

Apply the suggested fix from the prior review:

-| `Headers`                                                                         | map[string][]*string*                                                             | :heavy_check_mark:                                                                | N/A                                                                               |
+| `Headers`                                                                         | `map[string][]string`                                                             | :heavy_check_mark:                                                                | N/A                                                                               |

This syncs the docs with the code and resolves the markdown formatting issue.

pkg/client/docs/models/operations/addmetadatatoaccountresponse.md (1)

9-9: Improve the Headers field documentation — duplicate of past review comment.

As flagged in the prior review, the Headers field description remains "N/A", which doesn't explain its purpose to API consumers. Given the PR objective to expose the Idempotency-Hit header, clarify the field's content.

If this file is auto-generated, update the generator or template to emit a meaningful description for the Headers field. If manually maintained, update the description to:

Response HTTP headers, including Idempotency-Hit when the request matched a previous idempotent operation.
pkg/client/docs/models/operations/v2deleteaccountmetadataresponse.md (1)

9-9: Fix markdown formatting for the type syntax.

The asterisks in *string* are being interpreted as markdown italic markers, causing a linting error. The type should be formatted as a proper code token without unescaped special characters.

Apply this diff to fix the formatting (assuming the intended type is map[string][]string for standard HTTP headers):

-| `Headers`                                                          | map[string][]*string*                                              | :heavy_check_mark:                                                 | N/A                                                                |
+| `Headers`                                                          | map[string][]string                                                | :heavy_check_mark:                                                 | N/A                                                                |

If the type is intended to be pointers to strings, escape the asterisks:

-| `Headers`                                                          | map[string][]*string*                                              | :heavy_check_mark:                                                 | N/A                                                                |
+| `Headers`                                                          | map[string][]\*string                                              | :heavy_check_mark:                                                 | N/A                                                                |
pkg/client/docs/models/operations/v2deletetransactionmetadataresponse.md (1)

9-9: Fix markdown formatting for Headers type.

The Headers type annotation has formatting issues: map[string][]*string* contains a stray trailing asterisk and triggers markdown linting errors. Wrap the entire type in backticks: `map[string][]*string`.

pkg/client/docs/models/operations/createtransactionresponse.md (1)

10-10: Fix markdown formatting for Headers type.

The Headers type annotation has a stray trailing asterisk (map[string][]*string*) that causes markdown linting errors. Wrap the type in backticks and remove the trailing asterisk: `map[string][]*string`.

pkg/client/docs/models/operations/addmetadataontransactionresponse.md (1)

8-9: Fix Headers type formatting - markdown interprets [string] as a reference link.

The type map[string][]*string* has formatting issues: [string] triggers markdownlint MD052 and the asterisks render as italics. The Go client uses map[string][]string.

This was flagged in a previous review. Apply the fix:

-| `Headers`                                                          | map[string][]*string*                                              | :heavy_check_mark:                                                 | N/A                                                                |
+| `Headers`                                                          | `map[string][]string`                                              | :heavy_check_mark:                                                 | N/A                                                                |
pkg/client/docs/models/operations/v2addmetadatatoaccountresponse.md (1)

8-9: Fix Headers type formatting.

Same markdown formatting issue as other response docs - map[string][]*string* should be `map[string][]string` to match the Go client type and avoid markdownlint MD052.

This was flagged in a previous review.

🧹 Nitpick comments (7)
internal/api/v1/controllers_transactions_delete_metadata_test.go (1)

70-70: Consider adding test coverage for idempotency hit behavior.

While the mock is correctly updated to match the new signature, there are no test cases that verify the behavior when idempotency-hit is true. This leaves the header-setting logic untested in this endpoint.

Consider adding a test case that verifies the Idempotency-Hit header is set correctly:

+		{
+			name:              "with idempotency hit",
+			expectBackendCall: true,
+			returnIdempotencyHit: true,
+		},

Then in the test body:

 if tc.expectBackendCall {
 	ledgerController.EXPECT().
 		DeleteTransactionMetadata(gomock.Any(), ledgercontroller.Parameters[ledgercontroller.DeleteTransactionMetadata]{
 			Input: ledgercontroller.DeleteTransactionMetadata{
 				TransactionID: 1,
 				Key:           "foo",
 			},
 		}).
-		Return(&ledger.Log{}, false, tc.returnErr)
+		Return(&ledger.Log{}, tc.returnIdempotencyHit, tc.returnErr)
 }

And verify the header:

 if tc.expectedStatusCode == 0 || tc.expectedStatusCode == http.StatusNoContent {
 	require.Equal(t, http.StatusNoContent, rec.Code)
+	if tc.returnIdempotencyHit {
+		require.Equal(t, "true", rec.Header().Get("Idempotency-Hit"))
+	}
internal/api/v2/controllers_transactions_create_test.go (1)

415-417: Consider adding test coverage for idempotency hit behavior.

Similar to other endpoints, there are no test cases that verify the Idempotency-Hit header is set correctly when idempotency-hit is true. Consider adding a test case that mocks a successful idempotency hit and verifies the response header.

internal/api/v1/controllers_transactions_revert_test.go (1)

84-84: Consider adding test coverage for idempotency hit behavior.

The mock is correctly updated to match the new signature, but there are no test cases that verify the Idempotency-Hit header behavior for this endpoint.

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

83-83: Consider adding test coverage for idempotency hit behavior.

The mock is correctly updated to match the new signature, but there are no test cases that verify the Idempotency-Hit header is set correctly when idempotency-hit is true.

internal/api/v1/controllers_accounts_add_metadata_test.go (1)

100-100: Consider adding test coverage for idempotency hit scenario.

The test mocks now return false for idempotencyHit, but there's no test case that verifies the Idempotency-Hit header is set when idempotencyHit is true. Consider adding a test case that:

  • Mocks SaveAccountMetadata to return true for idempotencyHit
  • Asserts that the response includes the Idempotency-Hit: true header
internal/api/v2/controllers_accounts_add_metadata_test.go (1)

72-72: Consider adding test coverage for idempotency hit scenario.

Similar to the v1 test, consider adding a test case that verifies the Idempotency-Hit header is set when idempotencyHit is true.

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

44-52: Consider documenting the new idempotencyHit return value.

The existing comments document possible errors but don't explain the new boolean return. Consider adding a brief note, e.g.:

 // CreateTransaction accept a numscript script and returns a transaction
 // It can return following errors:
 //  * ErrCompilationFailed
 //  * ErrMetadataOverride
 //  * ErrInvalidVars
 //  * ErrTransactionReferenceConflict
 //  * ErrIdempotencyKeyConflict
 //  * ErrInsufficientFunds
+// The bool return indicates whether this was an idempotent cache hit (true) or a fresh operation (false).
 CreateTransaction(ctx context.Context, parameters Parameters[CreateTransaction]) (*ledger.Log, *ledger.CreatedTransaction, bool, error)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6bcf605 and 5229294.

⛔ Files ignored due to path filters (6)
  • openapi.yaml is excluded by !**/*.yaml
  • openapi/v1.yaml is excluded by !**/*.yaml
  • openapi/v2.yaml is excluded by !**/*.yaml
  • pkg/client/.speakeasy/gen.lock is excluded by !**/*.lock, !**/*.lock
  • pkg/client/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
  • pkg/client/speakeasyusagegen/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
📒 Files selected for processing (75)
  • docs/api/README.md (7 hunks)
  • internal/README.md (1 hunks)
  • internal/api/bulking/bulker.go (6 hunks)
  • internal/api/bulking/bulker_test.go (13 hunks)
  • internal/api/bulking/mocks.go (1 hunks)
  • internal/api/bulking/mocks_ledger_controller_test.go (20 hunks)
  • internal/api/common/mocks_ledger_controller_test.go (6 hunks)
  • internal/api/v1/controllers_accounts_add_metadata.go (1 hunks)
  • internal/api/v1/controllers_accounts_add_metadata_test.go (1 hunks)
  • internal/api/v1/controllers_accounts_delete_metadata.go (1 hunks)
  • internal/api/v1/controllers_accounts_delete_metadata_test.go (1 hunks)
  • internal/api/v1/controllers_transactions_add_metadata.go (2 hunks)
  • internal/api/v1/controllers_transactions_add_metadata_test.go (1 hunks)
  • internal/api/v1/controllers_transactions_create.go (4 hunks)
  • internal/api/v1/controllers_transactions_create_test.go (1 hunks)
  • internal/api/v1/controllers_transactions_delete_metadata.go (1 hunks)
  • internal/api/v1/controllers_transactions_delete_metadata_test.go (1 hunks)
  • internal/api/v1/controllers_transactions_revert.go (2 hunks)
  • internal/api/v1/controllers_transactions_revert_test.go (1 hunks)
  • internal/api/v1/mocks.go (1 hunks)
  • internal/api/v1/mocks_ledger_controller_test.go (20 hunks)
  • internal/api/v1/mocks_system_controller_test.go (33 hunks)
  • internal/api/v2/controllers_accounts_add_metadata.go (1 hunks)
  • internal/api/v2/controllers_accounts_add_metadata_test.go (1 hunks)
  • internal/api/v2/controllers_accounts_delete_metadata.go (1 hunks)
  • internal/api/v2/controllers_accounts_delete_metadata_test.go (1 hunks)
  • internal/api/v2/controllers_bulk_test.go (13 hunks)
  • internal/api/v2/controllers_transactions_add_metadata.go (2 hunks)
  • internal/api/v2/controllers_transactions_add_metadata_test.go (1 hunks)
  • internal/api/v2/controllers_transactions_create.go (2 hunks)
  • internal/api/v2/controllers_transactions_create_test.go (1 hunks)
  • internal/api/v2/controllers_transactions_delete_metadata.go (1 hunks)
  • internal/api/v2/controllers_transactions_delete_metadata_test.go (1 hunks)
  • internal/api/v2/controllers_transactions_revert.go (2 hunks)
  • internal/api/v2/controllers_transactions_revert_test.go (1 hunks)
  • internal/api/v2/mocks.go (1 hunks)
  • internal/api/v2/mocks_ledger_controller_test.go (20 hunks)
  • internal/api/v2/mocks_system_controller_test.go (33 hunks)
  • internal/controller/ledger/controller.go (2 hunks)
  • internal/controller/ledger/controller_default.go (6 hunks)
  • internal/controller/ledger/controller_default_test.go (4 hunks)
  • internal/controller/ledger/controller_generated_test.go (20 hunks)
  • internal/controller/ledger/controller_with_events.go (6 hunks)
  • internal/controller/ledger/controller_with_too_many_client_handling.go (2 hunks)
  • internal/controller/ledger/controller_with_too_many_client_handling_generated_test.go (2 hunks)
  • internal/controller/ledger/controller_with_too_many_client_handling_test.go (4 hunks)
  • internal/controller/ledger/controller_with_traces.go (1 hunks)
  • internal/controller/ledger/log_process.go (2 hunks)
  • internal/controller/ledger/log_process_test.go (2 hunks)
  • internal/controller/system/state_tracker.go (1 hunks)
  • internal/log.go (1 hunks)
  • pkg/client/docs/models/operations/addmetadataontransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/addmetadatatoaccountresponse.md (1 hunks)
  • pkg/client/docs/models/operations/createtransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/reverttransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2addmetadataontransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2addmetadatatoaccountresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2createtransactionresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2deleteaccountmetadataresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2deletetransactionmetadataresponse.md (1 hunks)
  • pkg/client/docs/models/operations/v2reverttransactionrequest.md (1 hunks)
  • pkg/client/docs/models/operations/v2reverttransactionresponse.md (1 hunks)
  • pkg/client/models/operations/addmetadataontransaction.go (2 hunks)
  • pkg/client/models/operations/addmetadatatoaccount.go (2 hunks)
  • pkg/client/models/operations/createtransaction.go (2 hunks)
  • pkg/client/models/operations/reverttransaction.go (2 hunks)
  • pkg/client/models/operations/v2addmetadataontransaction.go (2 hunks)
  • pkg/client/models/operations/v2addmetadatatoaccount.go (2 hunks)
  • pkg/client/models/operations/v2createtransaction.go (2 hunks)
  • pkg/client/models/operations/v2deleteaccountmetadata.go (2 hunks)
  • pkg/client/models/operations/v2deletetransactionmetadata.go (2 hunks)
  • pkg/client/models/operations/v2reverttransaction.go (4 hunks)
  • pkg/client/v1.go (4 hunks)
  • pkg/client/v2.go (7 hunks)
  • test/e2e/api_idempotency_hit_header_test.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • internal/log.go
🚧 Files skipped from review as they are similar to previous changes (22)
  • pkg/client/models/operations/v2createtransaction.go
  • internal/api/v2/controllers_transactions_add_metadata_test.go
  • pkg/client/models/operations/v2deleteaccountmetadata.go
  • pkg/client/models/operations/v2reverttransaction.go
  • internal/README.md
  • pkg/client/models/operations/addmetadatatoaccount.go
  • internal/api/v1/controllers_transactions_create_test.go
  • internal/api/bulking/bulker.go
  • internal/api/v2/controllers_transactions_create.go
  • internal/api/v2/controllers_accounts_delete_metadata_test.go
  • pkg/client/v2.go
  • internal/api/bulking/bulker_test.go
  • pkg/client/models/operations/v2addmetadataontransaction.go
  • pkg/client/docs/models/operations/v2addmetadataontransactionresponse.md
  • internal/api/v2/controllers_transactions_delete_metadata_test.go
  • internal/controller/ledger/controller_default_test.go
  • pkg/client/models/operations/reverttransaction.go
  • internal/controller/ledger/log_process_test.go
  • pkg/client/docs/models/operations/v2reverttransactionrequest.md
  • internal/api/v1/mocks.go
  • internal/api/v1/controllers_accounts_delete_metadata_test.go
  • pkg/client/v1.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-04-29T11:24:28.923Z
Learnt from: gfyrag
Repo: formancehq/ledger PR: 892
File: internal/controller/ledger/controller_default.go:196-196
Timestamp: 2025-04-29T11:24:28.923Z
Learning: In the ledger Import function, it's critical to maintain proper log ID tracking by updating lastLogID with the current log.ID after each processed log, rather than setting it to nil. This ensures the system can properly validate the ordering of logs and prevent duplicate or out-of-order processing, which is essential for maintaining data integrity in the ledger.

Applied to files:

  • internal/controller/ledger/log_process.go
🧬 Code graph analysis (24)
internal/api/v1/controllers_transactions_add_metadata_test.go (1)
internal/log.go (1)
  • Log (84-96)
internal/api/v2/controllers_accounts_delete_metadata.go (3)
internal/controller/ledger/controller.go (1)
  • DeleteAccountMetadata (124-127)
internal/machine/address.go (1)
  • Address (7-7)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/api/v2/controllers_accounts_add_metadata_test.go (1)
internal/log.go (1)
  • Log (84-96)
internal/api/v1/controllers_transactions_add_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • SaveTransactionMetadata (109-112)
internal/machine/vm/machine.go (1)
  • Metadata (52-52)
internal/api/v1/controllers_accounts_add_metadata_test.go (1)
internal/log.go (1)
  • Log (84-96)
internal/api/v1/controllers_accounts_delete_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • DeleteAccountMetadata (124-127)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/api/v2/controllers_transactions_add_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • SaveTransactionMetadata (109-112)
internal/machine/vm/machine.go (1)
  • Metadata (52-52)
internal/api/v1/controllers_transactions_create.go (1)
internal/controller/ledger/controller.go (1)
  • CreateTransaction (96-100)
internal/api/v1/controllers_transactions_delete_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • DeleteTransactionMetadata (119-122)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/api/v2/controllers_transactions_delete_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • DeleteTransactionMetadata (119-122)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/controller/ledger/controller.go (3)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/log.go (3)
  • Log (84-96)
  • CreatedTransaction (199-202)
  • RevertedTransaction (329-332)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/controller/ledger/controller_with_traces.go (5)
internal/controller/ledger/controller.go (6)
  • CreateTransaction (96-100)
  • RevertTransaction (102-107)
  • SaveTransactionMetadata (109-112)
  • SaveAccountMetadata (114-117)
  • DeleteTransactionMetadata (119-122)
  • DeleteAccountMetadata (124-127)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/log.go (3)
  • Log (84-96)
  • CreatedTransaction (199-202)
  • RevertedTransaction (329-332)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/tracing/tracing.go (1)
  • TraceWithMetric (101-129)
internal/api/v2/mocks_system_controller_test.go (4)
internal/api/common/mocks_system_controller_test.go (4)
  • MockReplicationBackendMockRecorder (30-32)
  • MockReplicationBackend (23-27)
  • SystemControllerMockRecorder (228-230)
  • SystemController (221-225)
internal/ledger.go (2)
  • Configuration (96-100)
  • Ledger (21-30)
internal/controller/ledger/controller.go (1)
  • Controller (21-83)
internal/controller/system/controller.go (1)
  • Controller (41-55)
internal/api/v1/controllers_accounts_add_metadata.go (4)
internal/controller/ledger/controller.go (1)
  • SaveAccountMetadata (114-117)
internal/machine/address.go (1)
  • Address (7-7)
internal/machine/vm/machine.go (1)
  • Metadata (52-52)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/api/common/mocks_ledger_controller_test.go (3)
internal/controller/ledger/controller.go (1)
  • CreateTransaction (96-100)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/api/v2/controllers_transactions_revert.go (1)
internal/controller/ledger/controller.go (1)
  • RevertTransaction (102-107)
internal/controller/ledger/controller_with_too_many_client_handling.go (3)
internal/controller/ledger/controller.go (7)
  • CreateTransaction (96-100)
  • Controller (21-83)
  • RevertTransaction (102-107)
  • SaveTransactionMetadata (109-112)
  • SaveAccountMetadata (114-117)
  • DeleteTransactionMetadata (119-122)
  • DeleteAccountMetadata (124-127)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/log.go (3)
  • Log (84-96)
  • CreatedTransaction (199-202)
  • RevertedTransaction (329-332)
internal/api/v2/controllers_accounts_add_metadata.go (2)
internal/controller/ledger/controller.go (1)
  • SaveAccountMetadata (114-117)
internal/api/common/errors.go (1)
  • HandleCommonWriteErrors (45-56)
internal/api/v1/controllers_transactions_revert.go (1)
internal/controller/ledger/controller.go (1)
  • RevertTransaction (102-107)
internal/api/v1/mocks_system_controller_test.go (5)
internal/api/common/mocks_system_controller_test.go (3)
  • MockReplicationBackendMockRecorder (30-32)
  • MockReplicationBackend (23-27)
  • SystemController (221-225)
internal/ledger.go (2)
  • Configuration (96-100)
  • Ledger (21-30)
internal/controller/system/controller.go (1)
  • Controller (41-55)
internal/storage/common/pagination.go (1)
  • PaginatedQuery (26-29)
internal/storage/system/queries.go (1)
  • ListLedgersQueryPayload (3-7)
internal/api/bulking/mocks_ledger_controller_test.go (6)
internal/api/common/mocks_ledger_controller_test.go (2)
  • LedgerControllerMockRecorder (33-35)
  • LedgerController (26-30)
internal/api/v1/mocks_ledger_controller_test.go (2)
  • LedgerControllerMockRecorder (33-35)
  • LedgerController (26-30)
internal/controller/ledger/controller.go (7)
  • Controller (21-83)
  • CreateTransaction (96-100)
  • DeleteAccountMetadata (124-127)
  • DeleteTransactionMetadata (119-122)
  • RevertTransaction (102-107)
  • SaveAccountMetadata (114-117)
  • SaveTransactionMetadata (109-112)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
internal/storage/common/pagination.go (1)
  • PaginatedQuery (26-29)
internal/ledger.go (1)
  • Ledger (21-30)
internal/api/v2/mocks_ledger_controller_test.go (2)
internal/api/common/mocks_ledger_controller_test.go (2)
  • LedgerControllerMockRecorder (33-35)
  • LedgerController (26-30)
internal/controller/ledger/controller.go (7)
  • Controller (21-83)
  • CreateTransaction (96-100)
  • DeleteAccountMetadata (124-127)
  • DeleteTransactionMetadata (119-122)
  • RevertTransaction (102-107)
  • SaveAccountMetadata (114-117)
  • SaveTransactionMetadata (109-112)
internal/controller/ledger/log_process.go (2)
internal/log.go (1)
  • Log (84-96)
internal/storage/ledger/logs.go (1)
  • Log (20-26)
internal/controller/ledger/controller_generated_test.go (2)
internal/controller/ledger/controller.go (7)
  • Controller (21-83)
  • CreateTransaction (96-100)
  • DeleteAccountMetadata (124-127)
  • DeleteTransactionMetadata (119-122)
  • RevertTransaction (102-107)
  • SaveAccountMetadata (114-117)
  • SaveTransactionMetadata (109-112)
internal/controller/ledger/parameters.go (1)
  • Parameters (3-7)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/models/operations/v2createtransactionresponse.md

10-10: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/createtransactionresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/addmetadataontransactionresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/reverttransactionresponse.md

10-10: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/v2deleteaccountmetadataresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/v2deletetransactionmetadataresponse.md

9-9: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

pkg/client/docs/models/operations/v2reverttransactionresponse.md

10-10: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Tests
  • GitHub Check: Dirty
🔇 Additional comments (68)
internal/api/bulking/mocks.go (1)

1-1: LGTM: Typed mock generation enabled.

The addition of the -typed flag improves type safety by generating per-call wrapper types for the mocks, which aligns with the broader mock updates across the PR.

internal/api/v1/controllers_accounts_delete_metadata.go (2)

22-29: LGTM: Idempotency indicator correctly unpacked.

The handler now correctly receives the idempotencyHit boolean from DeleteAccountMetadata, maintaining proper error handling flow.


35-37: LGTM: Idempotency header correctly set.

The Idempotency-Hit header is properly set before the response is written, following the correct HTTP header emission pattern.

pkg/client/models/operations/createtransaction.go (2)

46-46: LGTM: Headers field appropriately added.

The Headers field follows standard Go conventions for exposing HTTP response headers in client models.


63-68: LGTM: Nil-safe accessor follows conventions.

The GetHeaders method provides appropriate nil-safe access to the headers, consistent with other accessor patterns in this generated code.

pkg/client/models/operations/addmetadataontransaction.go (2)

54-54: LGTM: Headers field consistently added.

The Headers field follows the same pattern used across other response models in the client, providing standard HTTP header access.


64-69: LGTM: Nil-safe accessor matches established pattern.

The GetHeaders method provides consistent nil-safe access, matching the pattern used throughout the generated client code.

pkg/client/models/operations/v2deletetransactionmetadata.go (1)

61-78: LGTM - Generated code with proper nil-safety.

The added Headers field and GetHeaders() accessor follow the established pattern for exposing HTTP response headers. The nil-safe implementation returning an empty map is appropriate for a generated client model.

test/e2e/api_idempotency_hit_header_test.go (7)

1-47: Well-structured e2e test setup for idempotency header verification.

Good use of Ginkgo's deferred setup patterns with UseTemplatedDatabase() and DeferTestServer(). The BeforeEach correctly creates the ledger before each spec.


49-98: LGTM - CreateTransaction idempotency tests.

The test correctly verifies:

  • First call returns no Idempotency-Hit header
  • Second call with same key returns Idempotency-Hit: true

100-171: LGTM - AddMetadataOnTransaction idempotency tests.

Good setup creating a transaction in BeforeEach before testing metadata operations. Uses distinct idempotency keys (metadata-idempotency-key-header-1 vs -2) for the two test cases to ensure isolation.


173-258: LGTM - DeleteTransactionMetadata idempotency tests.

Proper setup chain: creates transaction, adds metadata, then tests delete operations with idempotency keys.


260-348: LGTM - RevertTransaction idempotency tests.

Correctly creates separate transactions within each It block since a transaction can only be reverted once. This isolation is necessary for the revert operation semantics.


350-406: LGTM - AddMetadataToAccount idempotency tests.

Uses distinct account addresses (account-header-test-1 vs -2) and idempotency keys per test case for proper isolation.


408-478: LGTM - DeleteAccountMetadata idempotency tests.

Proper setup adds metadata in BeforeEach, then tests delete operations. The shared account address account-delete-header-test works here since the BeforeEach runs before each It block.

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

1-3: Slightly imprecise description but correct implementation.

The review comment states methods return (result, idempotencyHit bool, error), but the actual controller signatures show unnamed bool returns in methods like:

  • CreateTransaction(ctx context.Context, parameters Parameters[CreateTransaction]) (*ledger.Log, *ledger.CreatedTransaction, bool, error)
  • SaveTransactionMetadata(ctx context.Context, parameters Parameters[SaveTransactionMetadata]) (*ledger.Log, bool, error)

The -typed flag is correctly added and beneficial for these multi-return-value methods, enabling type-safe per-call return value expectations in tests. The implementation is sound; only the variable naming description in the comment is inaccurate.

internal/api/v1/controllers_accounts_add_metadata.go (1)

38-48: Correct implementation of idempotency-hit header.

The implementation correctly unpacks the idempotency-hit value and sets the response header when appropriate. This matches the pattern used in other endpoints.

pkg/client/models/operations/v2addmetadatatoaccount.go (1)

63-78: Correct implementation of headers exposure.

The addition of the Headers field and GetHeaders method correctly exposes response headers to API clients. The nil check in GetHeaders prevents potential panics.

internal/api/v1/controllers_transactions_add_metadata.go (1)

33-48: Correct implementation of idempotency-hit header.

The implementation correctly unpacks the idempotency-hit value and sets the response header when appropriate. The error handling remains unchanged and correct.

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

22-37: LGTM!

The idempotency handling is correctly implemented:

  • The additional idempotencyHit return value is properly unpacked
  • The Idempotency-Hit header is conditionally set when true
  • Header is set before writing the response body
internal/api/v2/controllers_accounts_add_metadata.go (1)

26-36: LGTM!

The idempotency handling follows the same correct pattern as other endpoints.

docs/api/README.md (1)

975-979: LGTM!

The API documentation properly documents the Idempotency-Hit response header and Idempotency-Key request header across all relevant endpoints.

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

39-63: LGTM!

The idempotency handling is correctly implemented, following the same pattern as other endpoints.

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

25-48: LGTM!

The idempotency handling is correctly implemented. The previous issue flagged in review comments has been properly addressed.

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

27-42: LGTM!

The idempotency handling follows the correct pattern consistently applied across all endpoints.

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

26-38: LGTM!

The idempotency handling is correctly implemented. The header is set only on the success path after error handling, and w.Header().Set() is called before api.NoContent(w), ensuring the header is properly included in the response.

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

87-114: LGTM!

The idempotency handling for the postings-based transaction creation path is correctly implemented. The header is set after successful execution and before the response is sent.


123-152: LGTM!

The script-based transaction creation path mirrors the postings-based path with consistent idempotency handling. Both branches are properly updated.

internal/api/v1/controllers_transactions_delete_metadata.go (1)

27-39: LGTM!

The idempotency handling is now correctly implemented, consistent with the v2 controller and other v1 endpoints. The previously flagged issue has been properly addressed.

internal/controller/ledger/controller_with_too_many_client_handling_test.go (2)

31-52: LGTM!

The mock expectations and test assertions are correctly updated to handle the additional idempotencyHit return value. The mock returns false appropriately since retry scenarios aren't idempotency hits.


65-81: LGTM!

The "finally failing" test case is correctly updated with the same pattern. The mock expectations properly return false for idempotencyHit in error scenarios.

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

89-133: LGTM!

The forgeLog method correctly signals idempotency hits:

  • Returns true when an existing log is reused via idempotency key lookup (lines 101, 126)
  • Returns false for fresh log creation (line 132) or error paths (lines 98, 120, 128)

The boolean semantics are clear and the implementation properly distinguishes between reusing a cached result versus creating a new one.

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

1-77: LGTM - Generated mock file with typed wrappers.

This is an auto-generated file with the -typed flag for mockgen, which correctly produces per-call wrapper types for type-safe mock configuration. The pattern is consistent with other mock files in the PR.

Note: The -typed flag appears twice in the generate command (line 5), which is redundant but harmless.

internal/api/v2/controllers_bulk_test.go (9)

60-106: LGTM - Mock expectations correctly updated for new return signature.

The test expectations are properly updated to include the idempotencyHit boolean (false) before the error return value, consistent with the updated CreateTransaction method signature.


119-136: LGTM - SaveTransactionMetadata mock updated correctly.


149-166: LGTM - SaveAccountMetadata mock updated correctly.


178-216: LGTM - RevertTransaction mock updated correctly.


227-242: LGTM - DeleteTransactionMetadata mock updated correctly.


277-312: LGTM - Error scenario mocks correctly include the idempotencyHit boolean.

The error test cases properly return false for idempotencyHit alongside the error, maintaining consistency with the success cases.


351-396: LGTM - Continue-on-failure test case mocks updated correctly.


425-465: LGTM - Atomic transaction test mocks updated correctly.


514-558: LGTM - Custom content type test mock updated correctly.

internal/controller/ledger/controller_with_traces.go (6)

365-387: LGTM - CreateTransaction correctly propagates idempotencyHit through tracing.

The implementation properly captures idempotencyHit from the underlying controller call and propagates it through the return, with false returned on error paths.


389-411: LGTM - RevertTransaction correctly propagates idempotencyHit through tracing.


413-433: LGTM - SaveTransactionMetadata correctly propagates idempotencyHit through tracing.


435-456: LGTM - SaveAccountMetadata correctly propagates idempotencyHit through tracing.


458-479: LGTM - DeleteTransactionMetadata correctly propagates idempotencyHit through tracing.


481-502: LGTM - DeleteAccountMetadata correctly propagates idempotencyHit through tracing.

internal/controller/ledger/controller_with_events.go (6)

43-55: LGTM - CreateTransaction correctly propagates idempotencyHit through events wrapper.

The implementation captures and propagates idempotencyHit while preserving existing event listener behavior.


57-74: LGTM - RevertTransaction correctly propagates idempotencyHit through events wrapper.


76-94: LGTM - SaveTransactionMetadata correctly propagates idempotencyHit through events wrapper.


96-114: Consider: Should events be suppressed on idempotent hits?

Events are currently fired regardless of idempotencyHit status. If an operation returns cached results due to idempotency, downstream consumers may receive duplicate events for the same logical operation.

If this is intentional (e.g., for at-least-once delivery semantics), please disregard. Otherwise, consider guarding event firing with !idempotencyHit.


116-134: LGTM - DeleteTransactionMetadata correctly propagates idempotencyHit through events wrapper.


136-154: LGTM - DeleteAccountMetadata correctly propagates idempotencyHit through events wrapper.

internal/controller/ledger/controller.go (2)

19-19: LGTM - Mock generation updated with -typed flag.

The mockgen directive correctly includes the -typed flag for generating type-safe mock wrappers.


53-75: LGTM - Remaining method signatures correctly updated.

All mutating operations that can leverage idempotency (RevertTransaction, SaveTransactionMetadata, SaveAccountMetadata, DeleteTransactionMetadata, DeleteAccountMetadata) now return the idempotencyHit boolean. The signature changes are consistent and idiomatic.

internal/controller/system/state_tracker.go (1)

114-197: Idempotency flag is cleanly forwarded through the state-tracking facade

The new signatures and closure pattern correctly propagate idempotencyHit from the underlying controller through handleState, without altering the existing locking/transaction behavior. Looks good.

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

415-565: DefaultController correctly surfaces idempotencyHit from log processing

Create/Revert and the Save*/Delete* metadata methods now consistently return the extra idempotencyHit flag from logProcessor.forgeLog, while preserving previous return shapes and behavior. The separation between the internal Saved/Deleted payload and the public API remains clear and correct.

internal/api/common/mocks_ledger_controller_test.go (1)

109-428: LedgerController common mocks match updated controller signatures

The generated mocks correctly add the bool return for CreateTransaction, RevertTransaction, and the Save*/Delete* metadata methods, preserving order and types. This keeps tests aligned with the new idempotency-aware interface.

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

5-1299: Typed gomock wrappers for v2 system mocks look consistent and type-safe

The new per-method *...Call wrappers and -typed generation integrate cleanly with existing mocks, improving type safety on expectations without altering runtime behavior. No issues spotted.

internal/api/v1/mocks_system_controller_test.go (1)

5-1299: v1 SystemController typed mocks correctly wrap gomock.Call

Recorder methods now returning typed SystemController*Call wrappers are consistent with the interface and other mocks, and the added Return/Do/DoAndReturn helpers are well-typed. Generated code looks fine.

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

46-129: Retry wrapper now correctly forwards idempotencyHit from the underlying controller

The TooManyClient-handling wrapper methods simply add an idempotencyHit local, invoke the underlying controller via handleRetry, and return the extended tuple. This keeps retry semantics unchanged while exposing the new flag. Implementation looks solid.

internal/api/v1/mocks_ledger_controller_test.go (1)

5-1028: v1 LedgerController mocks align with the new idempotency-aware interface

The generated mocks correctly add the bool return for transactional and metadata operations and wrap recorder calls in typed LedgerController*Call helpers. Types and ordering all match the controller interface; nothing problematic stands out.

internal/api/bulking/mocks_ledger_controller_test.go (1)

5-1028: Bulking LedgerController typed mocks correctly reflect extended return values

These gomock-generated bulking mocks now expose the extra bool return for idempotent operations and use typed LedgerController*Call wrappers, in line with the main controller interface and other mock sets. No issues detected.

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

5-5: LGTM: Typed mock generation enabled.

The addition of the -typed flag to mockgen is a good improvement. This generates type-safe wrapper types for each mocked method, providing better compile-time safety and IDE support compared to generic *gomock.Call returns.


206-213: LGTM: Mock signatures correctly reflect interface changes.

The generated mocks correctly include the new bool return parameter (idempotencyHit) for methods that now support idempotency tracking. The signatures match the Controller interface definition shown in the relevant code snippets.

Also applies to: 247-253, 287-293, 872-879, 951-957, 991-997

internal/controller/ledger/controller_generated_test.go (2)

5-5: LGTM: Typed mock generation enabled.

Consistent with the v2 API mocks, this file now uses typed mock generation for better type safety and developer experience.


205-212: LGTM: Mock signatures correctly reflect interface changes.

The generated mocks correctly include the new bool return parameter for idempotency tracking, matching the Controller interface. The changes are consistent with the v2 API mocks.

Also applies to: 246-252, 286-292, 871-878, 950-956, 990-996

)

//go:generate mockgen -write_source_comment=false -write_package_comment=false -source controller_with_too_many_client_handling.go -destination controller_with_too_many_client_handling_generated_test.go -package ledger . DelayCalculator -typed
//go:generate mockgen -write_source_comment=false -typed -write_package_comment=false -source controller_with_too_many_client_handling.go -destination controller_with_too_many_client_handling_generated_test.go -package ledger . DelayCalculator -typed
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix duplicated -typed flag in go:generate directive

The go:generate line ends with an extra -typed after . DelayCalculator, so mockgen will likely treat it as a positional argument rather than an option and may fail when running go generate.

Consider dropping the trailing -typed so the command matches the other mockgen invocations:

-//go:generate mockgen -write_source_comment=false -typed -write_package_comment=false -source controller_with_too_many_client_handling.go -destination controller_with_too_many_client_handling_generated_test.go -package ledger . DelayCalculator -typed
+//go:generate mockgen -write_source_comment=false -typed -write_package_comment=false -source controller_with_too_many_client_handling.go -destination controller_with_too_many_client_handling_generated_test.go -package ledger . DelayCalculator
📝 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
//go:generate mockgen -write_source_comment=false -typed -write_package_comment=false -source controller_with_too_many_client_handling.go -destination controller_with_too_many_client_handling_generated_test.go -package ledger . DelayCalculator -typed
//go:generate mockgen -write_source_comment=false -typed -write_package_comment=false -source controller_with_too_many_client_handling.go -destination controller_with_too_many_client_handling_generated_test.go -package ledger . DelayCalculator
🤖 Prompt for AI Agents
In internal/controller/ledger/controller_with_too_many_client_handling.go around
line 18, the go:generate directive contains a duplicated trailing "-typed" flag
which will be treated as a positional argument by mockgen; remove the extra
"-typed" at the end so the directive matches other mockgen invocations (keep the
initial -typed flag before -write_package_comment=false) and ensure the rest of
the flags/order remain unchanged.

| `HTTPMeta` | [components.HTTPMetadata](../../models/components/httpmetadata.md) | :heavy_check_mark: | N/A |
| `V2CreateTransactionResponse` | [*components.V2CreateTransactionResponse](../../models/components/v2createtransactionresponse.md) | :heavy_minus_sign: | OK |
| `V2CreateTransactionResponse` | [*components.V2CreateTransactionResponse](../../models/components/v2createtransactionresponse.md) | :heavy_minus_sign: | OK |
| `Headers` | map[string][]*string* | :heavy_check_mark: | N/A |
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix markdown formatting for Headers type.

The Headers type contains a stray trailing asterisk (map[string][]*string*) that triggers markdown linting errors. Wrap the type in backticks and remove the trailing asterisk: `map[string][]*string`.

Apply this diff:

-| `Headers`                                                                                         | map[string][]*string*                                                                             | :heavy_check_mark:                                                                                | N/A                                                                                               |
+| `Headers`                                                                                         | `map[string][]*string`                                                                            | :heavy_check_mark:                                                                                | N/A                                                                                               |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

10-10: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

🤖 Prompt for AI Agents
In pkg/client/docs/models/operations/v2createtransactionresponse.md around line
10, the Headers type is incorrectly formatted as map[string][]*string* (with a
stray trailing asterisk) which breaks markdown linting; change it to a properly
backticked code span and remove the trailing asterisk so it reads
`map[string][]*string`.

| `HTTPMeta` | [components.HTTPMetadata](../../models/components/httpmetadata.md) | :heavy_check_mark: | N/A |
| `V2CreateTransactionResponse` | [*components.V2CreateTransactionResponse](../../models/components/v2createtransactionresponse.md) | :heavy_minus_sign: | OK |
| `V2CreateTransactionResponse` | [*components.V2CreateTransactionResponse](../../models/components/v2createtransactionresponse.md) | :heavy_minus_sign: | OK |
| `Headers` | map[string][]*string* | :heavy_check_mark: | N/A |
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix markdown formatting for Headers type.

The Headers type contains a stray trailing asterisk (map[string][]*string*) that triggers markdown linting errors. Wrap the type in backticks and remove the trailing asterisk: `map[string][]*string`.

Apply this diff:

-| `Headers`                                                                                         | map[string][]*string*                                                                             | :heavy_check_mark:                                                                                | N/A                                                                                               |
+| `Headers`                                                                                         | `map[string][]*string`                                                                            | :heavy_check_mark:                                                                                | N/A                                                                                               |
📝 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
| `Headers` | map[string][]*string* | :heavy_check_mark: | N/A |
| `Headers` | `map[string][]*string` | :heavy_check_mark: | N/A |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

10-10: Reference links and images should use a label that is defined
Missing link or image reference definition: "string"

(MD052, reference-links-images)

🤖 Prompt for AI Agents
In pkg/client/docs/models/operations/v2reverttransactionresponse.md around line
10, the Headers type is incorrectly formatted as map[string][]*string* which
contains a stray trailing asterisk and breaks markdown; change it to a properly
backticked type `map[string][]*string` by removing the trailing asterisk and
wrapping the entire type in backticks so the markdown renders and lints
correctly.

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.

4 participants