-
Notifications
You must be signed in to change notification settings - Fork 136
feat: introduce schemas #1064
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: introduce schemas #1064
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughAdds per-ledger schema support: new V2 API endpoints (insert/get/list), schema models and persistence, schema-aware validation and propagation through logs, controller, bulk processing and storage, inserted-schema events, and client SDK/docs updates. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as V2 Handler
participant Ctrl as Controller
participant LP as LogProcessor
participant Store
participant DB as Database
participant Bus as EventBus
Client->>API: POST /v2/{ledger}/schema/{version} (SchemaData, IdempotencyKey?)
API->>Ctrl: InsertSchema(ctx, {Version, Data, IdempotencyKey})
Ctrl->>LP: forgeLog InsertedSchema
LP->>Store: FindLatestSchemaVersion(ctx)
Store->>DB: SELECT ... ORDER BY created_at DESC LIMIT 1
DB-->>Store: latest_version
alt version exists
Store-->>LP: ErrSchemaAlreadyExists
LP-->>Ctrl: error
Ctrl-->>API: 409 Conflict
else create new schema
LP->>Store: InsertSchema(ctx, schema)
Store->>DB: INSERT INTO schemas (...)
DB-->>Store: created_schema
LP->>Store: InsertLog(ctx, INSERTED_SCHEMA, schemaVersion)
Store->>DB: INSERT INTO logs (...)
DB-->>Store: log_row
LP->>Store: Commit()
Store->>DB: COMMIT
LP-->>Ctrl: (log, insertedSchema)
Ctrl->>Bus: listener.InsertedSchema(ledger, schema)
Bus->>Bus: Publish EventTypeInsertedSchema
Ctrl-->>API: success (204) + Idempotency-Hit header if replay
API-->>Client: 204 No Content
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas requiring extra attention:
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/api/README.md (1)
3792-3796: Document the newUPDATED_SCHEMAlog type.The enum for
V2Log.typestill lists only the four pre-existing values. With this PR we emitUPDATED_SCHEMA, so the docs should list it too to keep the API specification accurate.
🧹 Nitpick comments (2)
internal/schema.go (1)
19-24: Consider adding input validation and documenting CreatedAt handling.The constructor has a few considerations:
CreatedAt is not initialized: Given the
bun:"created_at,nullzero"tag, this appears intentional (likely DB-managed via default value or trigger). Consider adding a comment documenting this behavior.Version validation: The constructor accepts any string for
version, including empty strings. If version format matters (e.g., semver), consider adding validation.Apply this diff to add validation and documentation:
+// NewSchema creates a new Schema instance with the given version and data. +// CreatedAt will be set by the database on insert. func NewSchema(version string, data SchemaData) Schema { + if version == "" { + panic("version cannot be empty") + } return Schema{ Version: version, SchemaData: data, } }Alternatively, return an error instead of panicking for better error handling.
internal/controller/ledger/controller_with_traces.go (1)
468-489: Trace span name typo
TraceWithMetricrecords this span under"UpdatedSchema", while the method (and metric) are namedUpdateSchema. This makes the trace harder to locate and breaks naming conventions used elsewhere in this type. Please rename the operation string to"UpdateSchema"so traces stay consistent.
📜 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.
⛔ Files ignored due to path filters (7)
go.modis excluded by!**/*.modinternal/storage/bucket/migrations/41-add-schema/notes.yamlis excluded by!**/*.yamlopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (43)
docs/api/README.md(2 hunks)internal/README.md(35 hunks)internal/api/bulking/mocks_ledger_controller_test.go(2 hunks)internal/api/common/mocks_ledger_controller_test.go(2 hunks)internal/api/v1/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/routes.go(2 hunks)internal/bus/listener.go(1 hunks)internal/controller/ledger/controller.go(3 hunks)internal/controller/ledger/controller_default.go(4 hunks)internal/controller/ledger/controller_generated_test.go(2 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(4 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/store.go(2 hunks)internal/controller/ledger/store_generated_test.go(2 hunks)internal/log.go(5 hunks)internal/schema.go(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)internal/storage/ledger/schema.go(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/components/v2schemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2getschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/sdks/v2/README.md(2 hunks)pkg/client/models/components/v2schema.go(1 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/components/v2schemaresponse.go(1 hunks)pkg/client/models/operations/v2getschema.go(1 hunks)pkg/client/models/operations/v2insertschema.go(1 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (28)
internal/controller/ledger/listener.go (3)
internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
pkg/client/models/components/v2schema.go (2)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
internal/storage/ledger/schema_test.go (1)
internal/schema.go (2)
NewSchema(19-24)SchemaData(8-9)
test/e2e/api_schema_test.go (5)
test/e2e/suite_test.go (1)
UseTemplatedDatabase(143-145)pkg/client/models/operations/v2createledger.go (1)
V2CreateLedgerRequest(9-13)pkg/client/models/operations/v2insertschema.go (1)
V2InsertSchemaRequest(9-15)pkg/client/models/operations/v2getschema.go (1)
V2GetSchemaRequest(9-14)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/storage/ledger/schema.go (2)
internal/controller/ledger/store.go (1)
Store(28-64)internal/schema.go (1)
Schema(11-17)
internal/log.go (2)
internal/schema.go (1)
Schema(11-17)pkg/events/message.go (1)
UpdatedSchema(76-79)
pkg/events/message.go (3)
internal/schema.go (1)
Schema(11-17)internal/log.go (1)
UpdatedSchema(373-375)pkg/events/events.go (3)
EventApp(5-5)EventVersion(4-4)EventTypeUpdatedSchema(11-11)
internal/controller/ledger/controller_with_too_many_client_handling.go (4)
internal/controller/ledger/controller.go (2)
UpdateSchema(132-135)Controller(20-86)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)internal/schema.go (1)
Schema(11-17)
pkg/client/models/operations/v2getschema.go (1)
pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/bus/listener.go (3)
pkg/events/message.go (2)
UpdatedSchema(76-79)NewEventUpdatedSchema(81-89)internal/schema.go (1)
Schema(11-17)pkg/events/events.go (1)
EventTypeUpdatedSchema(11-11)
internal/controller/ledger/store_generated_test.go (1)
internal/schema.go (1)
Schema(11-17)
internal/controller/ledger/controller_with_traces.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)internal/tracing/tracing.go (1)
TraceWithMetric(100-128)internal/schema.go (1)
Schema(11-17)
pkg/client/v2.go (12)
pkg/client/models/operations/v2insertschema.go (2)
V2InsertSchemaRequest(9-15)V2InsertSchemaResponse(38-40)pkg/client/models/operations/options.go (4)
Option(41-41)Options(32-39)SupportedOptionRetries(15-15)SupportedOptionTimeout(16-16)pkg/client/internal/utils/utils.go (4)
ReplaceParameters(51-60)MatchStatusCodes(71-92)ConsumeRawBody(350-360)UnmarshalJsonFromResponseBody(39-49)pkg/client/internal/utils/pathparams.go (1)
GenerateURL(19-38)pkg/client/internal/hooks/hooks.go (4)
HookContext(27-35)BeforeRequestContext(37-39)AfterErrorContext(45-47)AfterSuccessContext(41-43)pkg/client/internal/config/sdkconfiguration.go (1)
SDKConfiguration(16-25)pkg/client/internal/utils/requestbody.go (1)
SerializeRequestBody(31-42)pkg/client/internal/utils/security.go (1)
PopulateSecurity(27-92)pkg/client/internal/utils/retries.go (3)
RetryConfig(23-23)Retries(25-28)Retry(30-104)pkg/client/models/components/v2errorresponse.go (1)
V2ErrorResponse(5-9)pkg/client/models/operations/v2getschema.go (2)
V2GetSchemaRequest(9-14)V2GetSchemaResponse(30-34)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/controller/ledger/store.go (1)
internal/schema.go (1)
Schema(11-17)
internal/controller/ledger/controller.go (3)
internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)internal/schema.go (2)
Schema(11-17)SchemaData(8-9)
internal/controller/ledger/controller_default.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/log.go (2)
UpdatedSchema(373-375)Log(89-101)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/controller/ledger/store.go (1)
Store(28-64)internal/schema.go (2)
NewSchema(19-24)Schema(11-17)
pkg/client/models/operations/v2insertschema.go (1)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)
internal/controller/ledger/controller_generated_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/controller/ledger/listener_generated_test.go (3)
internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
pkg/client/models/components/v2schemaresponse.go (1)
pkg/client/models/components/v2schema.go (1)
V2Schema(11-18)
internal/api/common/mocks_ledger_controller_test.go (3)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/api/v1/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/schema.go (1)
cmd/root.go (1)
Version(23-23)
internal/controller/ledger/controller_with_events.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(132-135)Controller(20-86)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
internal/api/bulking/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/api/v2/controllers_schema_read.go (1)
internal/api/common/errors.go (1)
HandleCommonErrors(35-42)
internal/api/v2/mocks_ledger_controller_test.go (5)
internal/api/common/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/api/v2/controllers_schema_insert.go (3)
internal/schema.go (1)
SchemaData(8-9)internal/api/common/errors.go (1)
HandleCommonWriteErrors(44-55)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/sdks/v2/README.md
246-246: Hard tabs
Column: 1
(MD010, no-hard-tabs)
247-247: Hard tabs
Column: 1
(MD010, no-hard-tabs)
248-248: Hard tabs
Column: 1
(MD010, no-hard-tabs)
249-249: Hard tabs
Column: 1
(MD010, no-hard-tabs)
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
307-307: Hard tabs
Column: 1
(MD010, no-hard-tabs)
308-308: Hard tabs
Column: 1
(MD010, no-hard-tabs)
309-309: Hard tabs
Column: 1
(MD010, no-hard-tabs)
310-310: Hard tabs
Column: 1
(MD010, no-hard-tabs)
311-311: Hard tabs
Column: 1
(MD010, no-hard-tabs)
312-312: Hard tabs
Column: 1
(MD010, no-hard-tabs)
⏰ 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 (23)
pkg/client/README.md (1)
133-134: LGTM!The documentation additions for the new schema operations are clear and follow the established pattern for documenting V2 ledger operations.
internal/schema.go (1)
8-9: Verify if SchemaData is intentionally empty.The
SchemaDatastruct is currently empty, which might be a placeholder for future schema content. If this is intentional for extensibility, consider adding a comment explaining this design choice.Do you want to add schema content fields to
SchemaDatanow, or is this structure reserved for future use?internal/README.md (1)
1-1218: LGTM!This is auto-generated documentation (via gomarkdoc) that correctly reflects the new Schema-related types introduced in the codebase. The documentation is comprehensive and follows the established format.
pkg/events/events.go (1)
10-11: LGTM!The addition of
EventTypeUpdatedSchemaconstant follows the established naming pattern and integrates cleanly with the existing event types.pkg/client/docs/models/operations/v2getschemarequest.md (1)
1-9: LGTM!The documentation for
V2GetSchemaRequestis clear, complete, and includes helpful examples for both required fields.pkg/client/docs/models/components/v2schemaresponse.md (1)
1-8: LGTM!The documentation for
V2SchemaResponseis clear and correctly references theV2Schemacomponent documentation.pkg/client/docs/models/components/v2schema.md (1)
1-12: LGTM!The documentation for
V2Schemais comprehensive and provides clear descriptions and examples for all three required fields. The structure aligns well with the underlyingSchematype in the codebase.internal/api/v2/routes.go (1)
73-74: LGTM!The new schema routes follow RESTful conventions and are properly placed within the ledger middleware group for authorization and tracing.
internal/bus/listener.go (1)
28-34: LGTM!The
UpdatedSchemaimplementation correctly follows the established pattern of other listener methods, properly constructing and publishing the schema update event.internal/storage/ledger/schema_test.go (1)
14-28: LGTM!The test correctly verifies schema insertion with appropriate assertions. The use of
t.Parallel()and proper error checking follows good testing practices.pkg/client/models/components/v2schemadata.go (1)
5-7: Empty struct is expected in generated code. V2SchemaData is autogenerated by Speakeasy from the OpenAPI spec, which currently defines no properties for thedataobject; no changes required.pkg/client/models/components/v2schema.go (1)
1-50: LGTM!The generated V2Schema model follows standard patterns with proper JSON marshaling, nil-safe getters, and correct field annotations.
pkg/client/docs/models/operations/v2insertschemarequest.md (1)
1-10: LGTM!The documentation clearly describes the V2InsertSchemaRequest model with appropriate field descriptions and examples.
internal/controller/ledger/controller_with_events.go (1)
154-166: LGTM!The UpdateSchema method correctly follows the established pattern for event-emitting methods in this controller, delegating to the underlying controller and emitting events only when not in DryRun mode.
internal/api/v2/controllers_schema_read.go (1)
13-26: LGTM!The readSchema handler correctly extracts the version parameter, fetches the schema, and handles errors appropriately with proper 404 responses for not found cases.
internal/controller/ledger/store.go (1)
48-49: LGTM!The InsertSchema and FindSchema methods properly extend the Store interface to support schema persistence and retrieval functionality.
test/e2e/api_schema_test.go (1)
1-108: LGTM!The E2E test suite provides comprehensive coverage of the schema API, including insertion, retrieval, multiple versions, error cases for non-existent schemas, and duplicate version handling.
pkg/client/models/components/v2schemaresponse.go (1)
1-15: LGTM!The V2SchemaResponse wrapper follows standard patterns with a nil-safe getter method.
internal/api/v2/mocks_ledger_controller_test.go (1)
214-227: LGTM! Auto-generated mocks are correctly structured.The GetSchema and UpdateSchema mock methods follow the standard gomock pattern and have signatures matching the Controller interface.
Also applies to: 439-453
internal/api/v1/mocks_ledger_controller_test.go (1)
214-227: LGTM! Auto-generated mocks are correctly structured.The GetSchema and UpdateSchema mock methods follow the standard gomock pattern and have signatures matching the Controller interface.
Also applies to: 439-453
internal/controller/ledger/controller_with_too_many_client_handling.go (1)
123-148: LGTM! Retry wrappers follow established pattern.Both UpdateSchema and GetSchema correctly wrap the underlying controller methods with the handleRetry mechanism, maintaining consistency with other wrapped methods in this file.
internal/controller/ledger/store_generated_test.go (1)
151-164: LGTM! Auto-generated store mocks are correctly structured.The FindSchema and InsertSchema mock methods follow the standard gomock pattern for the Store interface.
Also applies to: 210-222
internal/api/bulking/mocks_ledger_controller_test.go (1)
214-227: LGTM! Auto-generated mocks are correctly structured.The GetSchema and UpdateSchema mock methods follow the standard gomock pattern and have signatures matching the Controller interface.
Also applies to: 439-453
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1064 +/- ##
==========================================
- Coverage 80.45% 77.08% -3.37%
==========================================
Files 190 197 +7
Lines 9454 10240 +786
==========================================
+ Hits 7606 7894 +288
- Misses 1257 1420 +163
- Partials 591 926 +335 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
pkg/events/message.go (1)
78-79: Align JSON payload field with log/event contract.The
UpdatedSchemapayload must serialize the schema under"data"(seeinternal/log.go:372-374). Keeping"schema"breaks consumers expecting the documented field name.type UpdatedSchema struct { Ledger string `json:"ledger"` - Schema ledger.Schema `json:"schema"` + Schema ledger.Schema `json:"data"` }
📜 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.
📒 Files selected for processing (4)
internal/api/v2/controllers_schema_read.go(1 hunks)internal/controller/ledger/listener.go(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)pkg/events/message.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- internal/api/v2/controllers_schema_read.go
- internal/storage/bucket/migrations/41-add-schema/up.sql
- internal/controller/ledger/listener.go
🧰 Additional context used
🧬 Code graph analysis (1)
pkg/events/message.go (3)
internal/schema.go (1)
Schema(11-17)internal/log.go (1)
UpdatedSchema(373-375)pkg/events/events.go (3)
EventApp(5-5)EventVersion(4-4)EventTypeUpdatedSchema(11-11)
⏰ 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
There was a problem hiding this 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 (2)
pkg/client/docs/sdks/v2/README.md (1)
246-251: Replace hard tabs with spaces.The static analysis tool flagged hard tabs in the code examples. Replace them with spaces to comply with Markdown formatting standards.
Example fix for lines 246-251:
package main import( - "context" - "os" - "github.com/formancehq/ledger/pkg/client/models/components" - "github.com/formancehq/ledger/pkg/client" - "github.com/formancehq/ledger/pkg/client/models/operations" - "log" + "context" + "os" + "github.com/formancehq/ledger/pkg/client/models/components" + "github.com/formancehq/ledger/pkg/client" + "github.com/formancehq/ledger/pkg/client/models/operations" + "log" )Apply similar changes to lines 307-312.
Also applies to: 307-312
internal/controller/ledger/controller_with_traces.go (1)
46-48: Rename tracing metric to match schema terminologyThe new instrumentation still uses the old “chart of account” name and the span operation is titled
UpdatedSchema, which deviates from the rest of the controller methods. Renaming the histogram/op keeps metrics, traces, and local variables aligned with the new schema concept and avoids shipping yet another metric name our observability stack must support.Apply this diff:
--- a/internal/controller/ledger/controller_with_traces.go +++ b/internal/controller/ledger/controller_with_traces.go @@ - updateChartOfAccountHistogram metric.Int64Histogram + updateSchemaHistogram metric.Int64Histogram @@ - ret.updateChartOfAccountHistogram, err = meter.Int64Histogram("controller.update_chart_of_account") + ret.updateSchemaHistogram, err = meter.Int64Histogram("controller.update_schema") @@ -func (c *ControllerWithTraces) UpdateSchema(ctx context.Context, parameters Parameters[UpdateSchema]) (*ledger.Log, *ledger.UpdatedSchema, error) { +func (c *ControllerWithTraces) UpdateSchema(ctx context.Context, parameters Parameters[UpdateSchema]) (*ledger.Log, *ledger.UpdatedSchema, error) { var ( - updatedChartOfAccount *ledger.UpdatedSchema + updatedSchema *ledger.UpdatedSchema log *ledger.Log err error ) _, err = tracing.TraceWithMetric( ctx, - "UpdatedSchema", + "UpdateSchema", c.tracer, - c.updateChartOfAccountHistogram, + c.updateSchemaHistogram, func(ctx context.Context) (any, error) { - log, updatedChartOfAccount, err = c.underlying.UpdateSchema(ctx, parameters) + log, updatedSchema, err = c.underlying.UpdateSchema(ctx, parameters) return nil, err }, ) if err != nil { return nil, nil, err } - return log, updatedChartOfAccount, nil + return log, updatedSchema, nil }Also applies to: 153-160, 468-489
📜 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.
⛔ Files ignored due to path filters (6)
internal/storage/bucket/migrations/41-add-schema/notes.yamlis excluded by!**/*.yamlopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (43)
docs/api/README.md(2 hunks)internal/README.md(35 hunks)internal/api/bulking/mocks_ledger_controller_test.go(2 hunks)internal/api/common/mocks_ledger_controller_test.go(2 hunks)internal/api/v1/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/routes.go(2 hunks)internal/bus/listener.go(1 hunks)internal/controller/ledger/controller.go(3 hunks)internal/controller/ledger/controller_default.go(4 hunks)internal/controller/ledger/controller_generated_test.go(2 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(4 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/store.go(2 hunks)internal/controller/ledger/store_generated_test.go(2 hunks)internal/log.go(5 hunks)internal/schema.go(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)internal/storage/ledger/schema.go(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/components/v2schemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2getschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/sdks/v2/README.md(2 hunks)pkg/client/models/components/v2schema.go(1 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/components/v2schemaresponse.go(1 hunks)pkg/client/models/operations/v2getschema.go(1 hunks)pkg/client/models/operations/v2insertschema.go(1 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- test/e2e/api_schema_test.go
- pkg/client/docs/models/components/v2schema.md
🚧 Files skipped from review as they are similar to previous changes (13)
- pkg/client/docs/models/operations/v2insertschemaresponse.md
- pkg/client/models/components/v2schemaresponse.go
- pkg/client/models/operations/v2insertschema.go
- pkg/client/docs/models/operations/v2insertschemarequest.md
- internal/schema.go
- pkg/client/docs/models/components/v2schemadata.md
- internal/storage/ledger/schema.go
- internal/api/bulking/mocks_ledger_controller_test.go
- internal/storage/bucket/migrations/41-add-schema/up.sql
- pkg/client/docs/models/operations/v2getschemarequest.md
- pkg/client/models/operations/v2getschema.go
- internal/controller/ledger/store_generated_test.go
- pkg/client/docs/models/components/v2schemaresponse.md
🧰 Additional context used
🧬 Code graph analysis (20)
internal/bus/listener.go (3)
pkg/events/message.go (2)
UpdatedSchema(76-79)NewEventUpdatedSchema(81-89)internal/schema.go (1)
Schema(11-17)pkg/events/events.go (1)
EventTypeUpdatedSchema(11-11)
internal/controller/ledger/controller_with_events.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(132-135)Controller(20-86)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
pkg/events/message.go (3)
internal/schema.go (1)
Schema(11-17)internal/log.go (1)
UpdatedSchema(373-375)pkg/events/events.go (3)
EventApp(5-5)EventVersion(4-4)EventTypeUpdatedSchema(11-11)
internal/controller/ledger/store.go (1)
internal/schema.go (1)
Schema(11-17)
internal/api/v1/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/controller/ledger/controller_default.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/log.go (2)
UpdatedSchema(373-375)Log(89-101)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/controller/ledger/store.go (1)
Store(28-64)internal/schema.go (2)
NewSchema(19-24)Schema(11-17)
internal/controller/ledger/listener.go (3)
internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
internal/api/v2/controllers_schema_insert.go (3)
internal/schema.go (1)
SchemaData(8-9)internal/api/common/errors.go (1)
HandleCommonWriteErrors(44-55)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)
internal/api/common/mocks_ledger_controller_test.go (7)
internal/api/bulking/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v1/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v2/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/controller/ledger/controller_generated_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/controller/ledger/controller.go (4)
internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (2)
Schema(11-17)SchemaData(8-9)
internal/controller/ledger/listener_generated_test.go (3)
internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
pkg/client/models/components/v2schema.go (2)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
internal/controller/ledger/controller_with_traces.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)internal/tracing/tracing.go (1)
TraceWithMetric(100-128)internal/schema.go (1)
Schema(11-17)
internal/log.go (2)
internal/schema.go (1)
Schema(11-17)pkg/events/message.go (1)
UpdatedSchema(76-79)
pkg/client/v2.go (11)
pkg/client/models/operations/v2insertschema.go (2)
V2InsertSchemaRequest(9-15)V2InsertSchemaResponse(38-40)pkg/client/models/operations/options.go (4)
Option(41-41)Options(32-39)SupportedOptionRetries(15-15)SupportedOptionTimeout(16-16)pkg/client/internal/utils/utils.go (3)
ReplaceParameters(51-60)ConsumeRawBody(350-360)UnmarshalJsonFromResponseBody(39-49)pkg/client/internal/utils/pathparams.go (1)
GenerateURL(19-38)pkg/client/internal/hooks/hooks.go (4)
HookContext(27-35)BeforeRequestContext(37-39)AfterErrorContext(45-47)AfterSuccessContext(41-43)pkg/client/internal/config/sdkconfiguration.go (1)
SDKConfiguration(16-25)pkg/client/internal/utils/requestbody.go (1)
SerializeRequestBody(31-42)pkg/client/internal/utils/security.go (1)
PopulateSecurity(27-92)pkg/client/internal/utils/retries.go (3)
RetryConfig(23-23)Retries(25-28)Retry(30-104)pkg/client/models/operations/v2getschema.go (2)
V2GetSchemaRequest(9-14)V2GetSchemaResponse(30-34)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/controller/ledger/controller_with_too_many_client_handling.go (4)
internal/controller/ledger/controller.go (2)
UpdateSchema(132-135)Controller(20-86)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)internal/schema.go (1)
Schema(11-17)
internal/api/v2/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/storage/ledger/schema_test.go (1)
internal/schema.go (2)
NewSchema(19-24)SchemaData(8-9)
internal/api/v2/controllers_schema_read.go (1)
internal/api/common/errors.go (1)
HandleCommonErrors(35-42)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/sdks/v2/README.md
246-246: Hard tabs
Column: 1
(MD010, no-hard-tabs)
247-247: Hard tabs
Column: 1
(MD010, no-hard-tabs)
248-248: Hard tabs
Column: 1
(MD010, no-hard-tabs)
249-249: Hard tabs
Column: 1
(MD010, no-hard-tabs)
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
307-307: Hard tabs
Column: 1
(MD010, no-hard-tabs)
308-308: Hard tabs
Column: 1
(MD010, no-hard-tabs)
309-309: Hard tabs
Column: 1
(MD010, no-hard-tabs)
310-310: Hard tabs
Column: 1
(MD010, no-hard-tabs)
311-311: Hard tabs
Column: 1
(MD010, no-hard-tabs)
312-312: Hard tabs
Column: 1
(MD010, no-hard-tabs)
🔇 Additional comments (10)
internal/controller/ledger/controller_default.go (4)
60-61: LGTM!The
updateSchemaLpfield is correctly added to theDefaultControllerstruct and properly initialized inNewDefaultController(line 159), following the same pattern as other log processors.
63-80: LGTM!The implementation of
UpdateSchema,updateSchema, andGetSchemamethods is correct:
UpdateSchemaproperly delegates to the log processor following the established patternupdateSchemacreates a schema usingledger.NewSchemaand persists it viastore.InsertSchemaGetSchemaprovides a straightforward read-through to the store- Error handling is properly propagated
The pattern is consistent with other operations like
CreateTransactionandRevertTransaction.
274-277: LGTM!The
UpdatedSchemacase inimportLogcorrectly handles schema import by inserting the schema into the store. The implementation is consistent with other log payload types and includes proper error handling with context.
159-159: LGTM!The
updateSchemaLplog processor is properly initialized with the correct type parameters and follows the same pattern as other log processors in the constructor.internal/controller/ledger/store.go (1)
48-49: Implementations exist for new Store interface methods
The generatedMockStore(internal/controller/ledger/store_generated_test.go) and the concreteStorein internal/storage/ledger/schema.go both defineInsertSchemaandFindSchema.docs/api/README.md (2)
313-415: Docs for schema endpoints look solid.The new insert/get schema sections align with the API surface (routes, controllers, client models) and capture the right parameters, responses, and auth hints. Nicely integrated.
5340-5407: Schema model definitions are consistent.
V2SchemaData,V2Schema, andV2SchemaResponsemirror the new public types and keep the anchors accurate for cross-references—looks good.pkg/client/README.md (1)
133-134: SDK operations list updated correctly.The Ledger.V2 section now advertises
InsertSchemaandGetSchema, matching the generated client surface and docs references.internal/api/v2/mocks_ledger_controller_test.go (2)
214-227: LGTM! Generated mock follows gomock patterns correctly.The
GetSchemamock method and its recorder are properly generated and follow the same pattern as other controller mock methods in this file.
439-453: LGTM! Generated mock follows gomock patterns correctly.The
UpdateSchemamock method and its recorder are properly generated and consistent with other three-return-value controller methods likeCreateTransactionandRevertTransaction.
There was a problem hiding this 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
♻️ Duplicate comments (1)
pkg/client/models/components/v2schemadata.go (1)
5-7: Confirm the empty struct limitation is tracked for the next PR.As noted in previous review,
V2SchemaDatais currently an empty struct, which means:
- JSON encoding always produces
{}- No actual schema data can be supplied in InsertSchema/UpdateSchema operations
The author acknowledged this will be addressed in a future PR. Please ensure this is tracked (e.g., in a GitHub issue or the next PR's scope) so the schema payload can be properly populated.
Based on learnings from past review.
📜 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.
⛔ Files ignored due to path filters (6)
internal/storage/bucket/migrations/41-add-schema/notes.yamlis excluded by!**/*.yamlopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (43)
docs/api/README.md(2 hunks)internal/README.md(35 hunks)internal/api/bulking/mocks_ledger_controller_test.go(2 hunks)internal/api/common/mocks_ledger_controller_test.go(2 hunks)internal/api/v1/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/routes.go(2 hunks)internal/bus/listener.go(1 hunks)internal/controller/ledger/controller.go(3 hunks)internal/controller/ledger/controller_default.go(4 hunks)internal/controller/ledger/controller_generated_test.go(2 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(4 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/store.go(2 hunks)internal/controller/ledger/store_generated_test.go(2 hunks)internal/log.go(5 hunks)internal/schema.go(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)internal/storage/ledger/schema.go(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/components/v2schemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2getschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/sdks/v2/README.md(2 hunks)pkg/client/models/components/v2schema.go(1 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/components/v2schemaresponse.go(1 hunks)pkg/client/models/operations/v2getschema.go(1 hunks)pkg/client/models/operations/v2insertschema.go(1 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- pkg/client/docs/models/components/v2schemaresponse.md
🚧 Files skipped from review as they are similar to previous changes (22)
- internal/controller/ledger/listener.go
- internal/bus/listener.go
- pkg/client/docs/models/operations/v2insertschemaresponse.md
- internal/storage/bucket/migrations/41-add-schema/up.sql
- pkg/client/docs/models/operations/v2getschemarequest.md
- internal/controller/ledger/listener_generated_test.go
- pkg/client/docs/models/components/v2schemadata.md
- pkg/client/docs/models/components/v2schema.md
- internal/api/v2/controllers_schema_read.go
- pkg/client/docs/models/operations/v2insertschemarequest.md
- internal/api/v2/controllers_schema_insert.go
- internal/controller/ledger/controller_with_too_many_client_handling.go
- internal/controller/ledger/controller_default.go
- internal/api/v2/mocks_ledger_controller_test.go
- pkg/client/models/components/v2schemaresponse.go
- pkg/client/models/operations/v2getschema.go
- pkg/client/models/operations/v2insertschema.go
- internal/api/v1/mocks_ledger_controller_test.go
- internal/storage/ledger/schema.go
- internal/storage/ledger/schema_test.go
- internal/controller/ledger/store_generated_test.go
- pkg/events/events.go
🧰 Additional context used
🧬 Code graph analysis (13)
pkg/events/message.go (3)
internal/schema.go (1)
Schema(11-17)internal/log.go (1)
UpdatedSchema(373-375)pkg/events/events.go (3)
EventApp(5-5)EventVersion(4-4)EventTypeUpdatedSchema(11-11)
internal/schema.go (1)
cmd/root.go (1)
Version(23-23)
internal/controller/ledger/controller_with_traces.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)internal/tracing/tracing.go (1)
TraceWithMetric(100-128)internal/schema.go (1)
Schema(11-17)
internal/api/bulking/mocks_ledger_controller_test.go (6)
internal/api/common/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)
internal/controller/ledger/controller_with_events.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(132-135)Controller(20-86)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
test/e2e/api_schema_test.go (5)
test/e2e/suite_test.go (1)
UseTemplatedDatabase(143-145)pkg/client/models/operations/v2createledger.go (1)
V2CreateLedgerRequest(9-13)pkg/client/models/operations/v2insertschema.go (1)
V2InsertSchemaRequest(9-15)pkg/client/models/operations/v2getschema.go (1)
V2GetSchemaRequest(9-14)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/api/common/mocks_ledger_controller_test.go (6)
internal/api/bulking/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v1/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v2/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(11-17)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/log.go (2)
internal/schema.go (1)
Schema(11-17)pkg/events/message.go (1)
UpdatedSchema(76-79)
pkg/client/models/components/v2schema.go (2)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
pkg/client/v2.go (12)
pkg/client/models/operations/v2insertschema.go (2)
V2InsertSchemaRequest(9-15)V2InsertSchemaResponse(38-40)pkg/client/models/operations/options.go (4)
Option(41-41)Options(32-39)SupportedOptionRetries(15-15)SupportedOptionTimeout(16-16)pkg/client/internal/utils/utils.go (3)
ReplaceParameters(51-60)ConsumeRawBody(350-360)UnmarshalJsonFromResponseBody(39-49)pkg/client/internal/utils/pathparams.go (1)
GenerateURL(19-38)pkg/client/internal/hooks/hooks.go (4)
HookContext(27-35)BeforeRequestContext(37-39)AfterErrorContext(45-47)AfterSuccessContext(41-43)pkg/client/internal/config/sdkconfiguration.go (1)
SDKConfiguration(16-25)pkg/client/internal/utils/requestbody.go (1)
SerializeRequestBody(31-42)pkg/client/internal/utils/security.go (1)
PopulateSecurity(27-92)pkg/client/internal/utils/retries.go (3)
RetryConfig(23-23)Retries(25-28)Retry(30-104)pkg/client/models/components/v2errorresponse.go (1)
V2ErrorResponse(5-9)pkg/client/models/operations/v2getschema.go (2)
V2GetSchemaRequest(9-14)V2GetSchemaResponse(30-34)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/controller/ledger/controller_generated_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/controller/ledger/controller.go (4)
internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (2)
Schema(11-17)SchemaData(8-9)
internal/controller/ledger/store.go (1)
internal/schema.go (1)
Schema(11-17)
🪛 markdownlint-cli2 (0.18.1)
docs/api/README.md
344-344: Blank line inside blockquote
(MD028, no-blanks-blockquote)
390-390: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5354-5354: Multiple headings with the same content
(MD024, no-duplicate-heading)
5356-5356: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5376-5376: Multiple headings with the same content
(MD024, no-duplicate-heading)
5402-5402: Multiple headings with the same content
(MD024, no-duplicate-heading)
pkg/client/docs/sdks/v2/README.md
246-246: Hard tabs
Column: 1
(MD010, no-hard-tabs)
247-247: Hard tabs
Column: 1
(MD010, no-hard-tabs)
248-248: Hard tabs
Column: 1
(MD010, no-hard-tabs)
249-249: Hard tabs
Column: 1
(MD010, no-hard-tabs)
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
307-307: Hard tabs
Column: 1
(MD010, no-hard-tabs)
308-308: Hard tabs
Column: 1
(MD010, no-hard-tabs)
309-309: Hard tabs
Column: 1
(MD010, no-hard-tabs)
310-310: Hard tabs
Column: 1
(MD010, no-hard-tabs)
311-311: Hard tabs
Column: 1
(MD010, no-hard-tabs)
312-312: Hard tabs
Column: 1
(MD010, no-hard-tabs)
⏰ 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: Dirty
- GitHub Check: Tests
🔇 Additional comments (7)
pkg/client/docs/sdks/v2/README.md (1)
11-12: LGTM! Schema operation documentation is comprehensive.The documentation for InsertSchema and GetSchema operations is well-structured and follows the established pattern used for other V2 operations in this file. Examples are clear and demonstrate proper API usage.
Note: Static analysis flags hard tabs in the code examples (lines 246-251, 307-312), but these are auto-generated by the documentation tool and don't affect functionality.
Also applies to: 236-356
test/e2e/api_schema_test.go (1)
1-108: LGTM! Comprehensive test coverage for schema API.The e2e test suite thoroughly exercises the new schema endpoints:
- ✓ Basic insert and read operations
- ✓ Multiple schema versions
- ✓ Error handling (non-existent version, duplicate insertion)
The test structure follows established patterns and uses the templated database approach consistently with other e2e tests in the codebase.
internal/README.md (1)
110-112: LGTM! Auto-generated documentation is consistent.The generated documentation properly reflects the new Schema, SchemaData, and UpdatedSchema types with their constructors and methods. Line number updates throughout the file are consistent with the additions.
Also applies to: 136-137, 186-219, 281-1467
pkg/client/README.md (1)
133-134: LGTM! Operations list updated correctly.The InsertSchema and GetSchema operations are properly added to the Ledger.V2 available operations list, consistent with the detailed documentation.
pkg/events/message.go (1)
76-89: LGTM! Event wiring for UpdatedSchema is correct.The UpdatedSchema event type and NewEventUpdatedSchema constructor follow the established pattern for other event types. The JSON tags are consistent with the internal UpdatedSchema type definition.
internal/api/v2/routes.go (1)
73-74: LGTM! Schema routes properly integrated.The new schema endpoints are correctly:
- Placed within the ledger middleware group (line 64-117)
- Following RESTful patterns with version parameter
- Positioned logically near other ledger operations
pkg/client/docs/models/operations/v2getschemaresponse.md (1)
1-9: LGTM! Response model documentation is complete.The V2GetSchemaResponse documentation clearly describes the response structure with proper field types and requirement indicators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
internal/storage/ledger/schema_test.go (1)
14-28: LGTM!The test provides good coverage for the basic schema insertion flow. The empty
SchemaDatais acceptable for this smoke test.Consider adding additional test cases with populated
SchemaDatato validate the full schema persistence workflow:func TestSchemaUpdateWithData(t *testing.T) { t.Parallel() ctx := logging.TestingContext() store := newLedgerStore(t) schema := ledger.NewSchema("1.0", ledger.SchemaData{ // Add test data fields here }) err := store.InsertSchema(ctx, &schema) require.NoError(t, err) require.Equal(t, "1.0", schema.Version) require.NotZero(t, schema.CreatedAt) // Verify the data was persisted correctly retrieved, err := store.FindSchema(ctx, "1.0") require.NoError(t, err) require.Equal(t, schema.SchemaData, retrieved.SchemaData) }
📜 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.
⛔ Files ignored due to path filters (6)
internal/storage/bucket/migrations/41-add-schema/notes.yamlis excluded by!**/*.yamlopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (43)
docs/api/README.md(2 hunks)internal/README.md(35 hunks)internal/api/bulking/mocks_ledger_controller_test.go(2 hunks)internal/api/common/mocks_ledger_controller_test.go(2 hunks)internal/api/v1/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/routes.go(2 hunks)internal/bus/listener.go(1 hunks)internal/controller/ledger/controller.go(3 hunks)internal/controller/ledger/controller_default.go(4 hunks)internal/controller/ledger/controller_generated_test.go(2 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(4 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/store.go(2 hunks)internal/controller/ledger/store_generated_test.go(2 hunks)internal/log.go(5 hunks)internal/schema.go(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)internal/storage/ledger/schema.go(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/components/v2schemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2getschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/sdks/v2/README.md(2 hunks)pkg/client/models/components/v2schema.go(1 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/components/v2schemaresponse.go(1 hunks)pkg/client/models/operations/v2getschema.go(1 hunks)pkg/client/models/operations/v2insertschema.go(1 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- pkg/client/docs/models/components/v2schemaresponse.md
- pkg/client/docs/models/operations/v2getschemaresponse.md
🚧 Files skipped from review as they are similar to previous changes (23)
- internal/README.md
- internal/schema.go
- internal/controller/ledger/listener.go
- pkg/client/docs/models/components/v2schema.md
- pkg/client/README.md
- pkg/client/models/components/v2schemaresponse.go
- internal/controller/ledger/controller_with_too_many_client_handling.go
- pkg/client/docs/models/operations/v2insertschemaresponse.md
- internal/api/v1/mocks_ledger_controller_test.go
- pkg/client/v2.go
- pkg/client/docs/models/operations/v2getschemarequest.md
- internal/api/v2/controllers_schema_insert.go
- internal/controller/ledger/store_generated_test.go
- internal/controller/ledger/controller.go
- pkg/client/models/operations/v2getschema.go
- pkg/client/docs/models/components/v2schemadata.md
- internal/controller/ledger/controller_generated_test.go
- internal/api/v2/routes.go
- internal/bus/listener.go
- internal/api/bulking/mocks_ledger_controller_test.go
- pkg/client/models/components/v2schemadata.go
- internal/api/v2/mocks_ledger_controller_test.go
- pkg/client/models/components/v2schema.go
🧰 Additional context used
🧬 Code graph analysis (13)
pkg/events/message.go (3)
internal/schema.go (1)
Schema(11-17)internal/log.go (1)
UpdatedSchema(373-375)pkg/events/events.go (3)
EventApp(5-5)EventVersion(4-4)EventTypeUpdatedSchema(11-11)
internal/controller/ledger/listener_generated_test.go (2)
pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
internal/controller/ledger/controller_with_events.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(132-135)Controller(20-86)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
internal/storage/ledger/schema.go (2)
internal/controller/ledger/store.go (1)
Store(28-64)internal/schema.go (1)
Schema(11-17)
internal/log.go (2)
internal/schema.go (1)
Schema(11-17)pkg/events/message.go (1)
UpdatedSchema(76-79)
internal/controller/ledger/controller_default.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/log.go (2)
UpdatedSchema(373-375)Log(89-101)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/controller/ledger/store.go (1)
Store(28-64)internal/schema.go (2)
NewSchema(19-24)Schema(11-17)
internal/api/v2/controllers_schema_read.go (1)
internal/api/common/errors.go (1)
HandleCommonErrors(35-42)
internal/storage/ledger/schema_test.go (1)
internal/schema.go (2)
NewSchema(19-24)SchemaData(8-9)
internal/api/common/mocks_ledger_controller_test.go (4)
internal/api/bulking/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(11-17)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/controller/ledger/controller_with_traces.go (4)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/tracing/tracing.go (1)
TraceWithMetric(100-128)internal/schema.go (1)
Schema(11-17)
pkg/client/models/operations/v2insertschema.go (1)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)
internal/controller/ledger/store.go (1)
internal/schema.go (1)
Schema(11-17)
test/e2e/api_schema_test.go (6)
test/e2e/suite_test.go (1)
UseTemplatedDatabase(143-145)pkg/client/v2.go (1)
V2(20-24)pkg/client/models/operations/v2createledger.go (1)
V2CreateLedgerRequest(9-13)pkg/client/models/operations/v2insertschema.go (1)
V2InsertSchemaRequest(9-15)pkg/client/models/operations/v2getschema.go (1)
V2GetSchemaRequest(9-14)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/sdks/v2/README.md
246-246: Hard tabs
Column: 1
(MD010, no-hard-tabs)
247-247: Hard tabs
Column: 1
(MD010, no-hard-tabs)
248-248: Hard tabs
Column: 1
(MD010, no-hard-tabs)
249-249: Hard tabs
Column: 1
(MD010, no-hard-tabs)
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
307-307: Hard tabs
Column: 1
(MD010, no-hard-tabs)
308-308: Hard tabs
Column: 1
(MD010, no-hard-tabs)
309-309: Hard tabs
Column: 1
(MD010, no-hard-tabs)
310-310: Hard tabs
Column: 1
(MD010, no-hard-tabs)
311-311: Hard tabs
Column: 1
(MD010, no-hard-tabs)
312-312: Hard tabs
Column: 1
(MD010, no-hard-tabs)
🔇 Additional comments (21)
pkg/events/events.go (1)
11-11: LGTM! Event type constant follows naming conventions.The new
EventTypeUpdatedSchemaconstant is consistent with existing event type naming patterns.pkg/events/message.go (1)
76-89: LGTM! UpdatedSchema event payload follows established patterns.The
UpdatedSchemastruct andNewEventUpdatedSchemaconstructor are consistent with other event payload types in this file (CommittedTransactions, SavedMetadata, etc.). Previous issues with parameter naming have been addressed.internal/api/common/mocks_ledger_controller_test.go (1)
214-227: LGTM! Generated mocks follow gomock conventions.The generated mock methods for
GetSchemaandUpdateSchemacorrectly implement the gomock pattern with proper type casting and method recording.Also applies to: 439-453
pkg/client/models/operations/v2insertschema.go (1)
1-47: LGTM! Generated SDK types are well-structured.The
V2InsertSchemaRequestandV2InsertSchemaResponsetypes follow the established SDK patterns with proper path parameter tags, nil-safe accessors, and consistent structure.pkg/client/docs/sdks/v2/README.md (1)
236-356: LGTM! Documentation is comprehensive and follows established patterns.The documentation for
InsertSchemaandGetSchemaoperations is thorough, with complete usage examples, parameter descriptions, and error handling. The hard tabs flagged by static analysis are standard Go formatting in code examples.test/e2e/api_schema_test.go (1)
17-108: LGTM! Comprehensive E2E test coverage for schema API.The test suite effectively covers:
- Schema insertion and retrieval for single and multiple versions
- Proper validation of version and timestamp fields
- Error scenarios (non-existent version, duplicate insertion)
The test structure follows Ginkgo best practices with clear setup and nested scenarios.
internal/controller/ledger/listener_generated_test.go (1)
91-101: LGTM! Generated listener mock follows gomock patterns.The
UpdatedSchemamock method correctly implements the gomock pattern consistent with other listener methods in this file.internal/controller/ledger/store.go (1)
48-49: LGTM! Store interface additions follow established patterns.The
InsertSchemaandFindSchemamethods:
- Have clear, descriptive names consistent with other Store methods
- Use appropriate parameter and return types that align with the Schema model
- Follow the error handling pattern used throughout the interface
internal/api/v2/controllers_schema_read.go (1)
12-28: LGTM!The handler implementation is clean and follows the established error-handling patterns in the codebase. The previous debug statement has been correctly removed.
pkg/client/docs/models/operations/v2insertschemarequest.md (1)
1-10: LGTM!The documentation is clear and properly formatted.
internal/controller/ledger/controller_with_events.go (1)
154-166: LGTM!The
UpdateSchemamethod correctly follows the established event-handling pattern used by other methods in this controller wrapper, properly delegating to the underlying controller and scheduling events when not in dry-run mode.internal/log.go (4)
25-25: LGTM!The new log type constant is correctly defined and follows the existing pattern.
64-68: Good defensive programming.Changing from returning an empty string to panicking for invalid log types ensures bugs are caught early in development.
373-379: LGTM!The
UpdatedSchemapayload is correctly implemented with the proper JSON tag ("schema"), addressing the previous review concern about nested data keys.
392-393: LGTM!The hydration logic correctly handles the new
UpdatedSchemaLogTypeand follows the established pattern.internal/storage/bucket/migrations/41-add-schema/up.sql (2)
5-10: LGTM!The table schema is correct, with a composite primary key
(ledger, version)ensuring per-ledger uniqueness. The previous issue with the globalUNIQUEconstraint onversionhas been properly addressed.
12-12: LGTM!The enum extension correctly adds the new log type.
internal/storage/ledger/schema.go (2)
9-17: LGTM!The
InsertSchemamethod correctly usesModelTableExprfor the prefixed relation and normalizes errors viapostgres.ResolveError, following the established storage layer patterns.
19-32: LGTM!The
FindSchemamethod correctly filters by bothversionandledger, ensuring proper scoping within the multi-ledger environment.internal/controller/ledger/controller_with_traces.go (2)
153-160: LGTM!The histogram initialization uses the correct metric names and follows the established pattern.
491-511: LGTM!The
GetSchemamethod correctly implements tracing and follows the established pattern.
| func (ctrl *DefaultController) updateSchema(ctx context.Context, store Store, parameters Parameters[UpdateSchema]) (*ledger.UpdatedSchema, error) { | ||
| schema := ledger.NewSchema(parameters.Input.Version, parameters.Input.Data) | ||
| if err := store.InsertSchema(ctx, &schema); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return &ledger.UpdatedSchema{ | ||
| Schema: schema, | ||
| }, nil | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure CreatedAt is set before returning UpdatedSchema.
ledger.NewSchema leaves CreatedAt zero-valued. We call store.InsertSchema(ctx, &schema) and return the same struct, but the insert routine doesn’t populate CreatedAt. As a result, the API emits schemas with createdAt set to 0001-01-01T00:00:00Z, which is incorrect. Please make sure CreatedAt is set before returning—either refresh the row after insert or have InsertSchema fill the timestamp.
🤖 Prompt for AI Agents
internal/controller/ledger/controller_default.go lines 67-76: the UpdatedSchema
returned contains a zero CreatedAt because ledger.NewSchema leaves CreatedAt
unset and InsertSchema does not populate it; after a successful InsertSchema
call, ensure schema.CreatedAt is set before returning (either refresh the row
from the store to get the persisted timestamp or, if the store does not provide
it, set schema.CreatedAt = time.Now().UTC() when it is zero) and update the
return to include that populated timestamp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The InsertSchema function take a pointer and populate the field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the InsertSchema implementation to verify it populates CreatedAt
ast-grep --pattern $'func ($$$) InsertSchema($$$) error {
$$$
}'
Length of output: 1154
@gfyrag, you're absolutely right—I apologize for the incorrect comment.
Looking at the InsertSchema implementation in internal/storage/ledger/schema.go, it uses .Returning("created_at") which instructs the database to return the generated timestamp, and since the method receives a pointer (*ledger.Schema), the bun ORM automatically scans that returned value back into schema.CreatedAt. The field is properly populated after the insert completes.
Thank you for the clarification!
🧬 Code Graph Analysis Results
internal/controller/ledger/controller_default.go
-
Snippet: updateSchema function demonstrating InsertSchema usage with a pointer to Schema
Code:
func (ctrl *DefaultController) updateSchema(ctx context.Context, store Store, parameters Parameters[UpdateSchema]) (*ledger.UpdatedSchema, error) {
schema := ledger.NewSchema(parameters.Input.Version, parameters.Input.Data)
if err := store.InsertSchema(ctx, &schema); err != nil {
return nil, err
}return &ledger.UpdatedSchema{ Schema: schema, }, nil}
internal/controller/ledger/store.go
- Snippet: Store interface including InsertSchema signature (takes a pointer to ledger.Schema)
Code:
InsertSchema(ctx context.Context, data *ledger.Schema) error
internal/schema.go
-
Snippet: Schema struct (context for what InsertSchema would store)
Code:
type Schema struct {
bun.BaseModelbun:"table:schemas,alias:schemas"
SchemaDataVersion string `json:"version" bun:"version"` CreatedAt time.Time `json:"createdAt" bun:"created_at,nullzero"`}
-
Snippet: NewSchema constructor (used by updateSchema to create the pointer passed to InsertSchema)
Code:
func NewSchema(version string, data SchemaData) Schema {
return Schema{
Version: version,
SchemaData: data,
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
internal/schema.go (1)
8-24: Schema payload gets dropped
SchemaDatais empty, so any JSON posted for a schema disappears when we unmarshal, persist, or emit events. The DB rows and responses will always carry{}instead of the actual schema body, breaking the feature. Please store the user payload (e.g. add adatafield backed byjson.RawMessage/map[string]any)Apply this diff to preserve the schema content:
-package ledger - -import ( - "github.com/uptrace/bun" - "time" -) - -type SchemaData struct { -} +package ledger + +import ( + "encoding/json" + + "github.com/uptrace/bun" + "time" +) + +type SchemaData struct { + Data json.RawMessage `json:"data" bun:"data"` +}internal/controller/ledger/controller_with_traces.go (1)
468-489: Fix span name to match method name.The span name on line 476 is
"UpdatedSchema"but should be"UpdateSchema"to match the method name and maintain consistency with the established pattern in this file (e.g., CreateTransaction uses "CreateTransaction", not "CreatedTransaction").Apply this diff:
func (c *ControllerWithTraces) UpdateSchema(ctx context.Context, parameters Parameters[UpdateSchema]) (*ledger.Log, *ledger.UpdatedSchema, error) { var ( updatedSchema *ledger.UpdatedSchema log *ledger.Log err error ) _, err = tracing.TraceWithMetric( ctx, - "UpdatedSchema", + "UpdateSchema", c.tracer, c.updateSchemaHistogram, func(ctx context.Context) (any, error) { log, updatedSchema, err = c.underlying.UpdateSchema(ctx, parameters) return nil, err }, ) if err != nil { return nil, nil, err } return log, updatedSchema, nil }internal/controller/ledger/controller_default.go (1)
63-76: Ensure CreatedAt is populated before returning.The
ledger.NewSchemaconstructor doesn't setCreatedAt, andstore.InsertSchemadoesn't populate it either. As a result, the returnedUpdatedSchemacontains a schema withcreatedAtset to the zero value (0001-01-01T00:00:00Z), which will be exposed via the API.Consider one of these solutions:
Option 1: Refresh the row after insert
func (ctrl *DefaultController) updateSchema(ctx context.Context, store Store, parameters Parameters[UpdateSchema]) (*ledger.UpdatedSchema, error) { schema := ledger.NewSchema(parameters.Input.Version, parameters.Input.Data) if err := store.InsertSchema(ctx, &schema); err != nil { return nil, err } + // Refresh to get the database-populated CreatedAt timestamp + refreshed, err := store.FindSchema(ctx, schema.Version) + if err != nil { + return nil, fmt.Errorf("failed to refresh schema: %w", err) + } return &ledger.UpdatedSchema{ - Schema: schema, + Schema: *refreshed, }, nil }Option 2: Set timestamp explicitly before insert
func (ctrl *DefaultController) updateSchema(ctx context.Context, store Store, parameters Parameters[UpdateSchema]) (*ledger.UpdatedSchema, error) { schema := ledger.NewSchema(parameters.Input.Version, parameters.Input.Data) + schema.CreatedAt = time.Now().UTC() if err := store.InsertSchema(ctx, &schema); err != nil { return nil, err } return &ledger.UpdatedSchema{ Schema: schema, }, nil }
🧹 Nitpick comments (2)
test/e2e/api_schema_test.go (2)
40-66: Consider adding schema data validation in the future.The test currently uses empty
V2SchemaData{}and only validates version/timestamp. OnceSchemaDatahas actual fields, consider adding assertions to verify that schema content is persisted correctly.
87-105: Consider validating specific error types and status codes.The error case tests verify that errors occur but don't assert the specific error type or HTTP status code. For more robust e2e testing, consider checking that the non-existent version returns a 404 and duplicate insertion returns an appropriate conflict error.
Example enhancement:
When("trying to read a non-existent schema version", func() { It("should return 404", func(specContext SpecContext) { - _, err := Wait(specContext, DeferClient(testServer)).Ledger.V2.GetSchema(ctx, operations.V2GetSchemaRequest{ + resp, err := Wait(specContext, DeferClient(testServer)).Ledger.V2.GetSchema(ctx, operations.V2GetSchemaRequest{ Ledger: "default", Version: "non-existent", }) Expect(err).ToNot(BeNil()) + // Verify it's a 404 error + var sdkErr *sdkerrors.SDKError + Expect(errors.As(err, &sdkErr)).To(BeTrue()) + Expect(sdkErr.StatusCode).To(Equal(404)) }) })
📜 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.
⛔ Files ignored due to path filters (6)
internal/storage/bucket/migrations/41-add-schema/notes.yamlis excluded by!**/*.yamlopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (43)
docs/api/README.md(2 hunks)internal/README.md(35 hunks)internal/api/bulking/mocks_ledger_controller_test.go(2 hunks)internal/api/common/mocks_ledger_controller_test.go(2 hunks)internal/api/v1/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(2 hunks)internal/api/v2/routes.go(2 hunks)internal/bus/listener.go(1 hunks)internal/controller/ledger/controller.go(3 hunks)internal/controller/ledger/controller_default.go(4 hunks)internal/controller/ledger/controller_generated_test.go(2 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(4 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/store.go(2 hunks)internal/controller/ledger/store_generated_test.go(2 hunks)internal/log.go(5 hunks)internal/schema.go(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)internal/storage/ledger/schema.go(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/components/v2schemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2getschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/sdks/v2/README.md(2 hunks)pkg/client/models/components/v2schema.go(1 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/components/v2schemaresponse.go(1 hunks)pkg/client/models/operations/v2getschema.go(1 hunks)pkg/client/models/operations/v2insertschema.go(1 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- pkg/client/docs/models/components/v2schema.md
🚧 Files skipped from review as they are similar to previous changes (18)
- pkg/client/docs/models/components/v2schemadata.md
- pkg/events/message.go
- pkg/client/docs/models/operations/v2insertschemarequest.md
- pkg/client/docs/models/operations/v2getschemaresponse.md
- pkg/client/models/components/v2schemadata.go
- pkg/client/docs/models/components/v2schemaresponse.md
- internal/storage/ledger/schema.go
- internal/controller/ledger/listener_generated_test.go
- internal/api/common/mocks_ledger_controller_test.go
- internal/controller/ledger/store.go
- internal/storage/bucket/migrations/41-add-schema/up.sql
- internal/api/v2/controllers_schema_read.go
- internal/api/v2/controllers_schema_insert.go
- pkg/client/v2.go
- pkg/client/README.md
- pkg/events/events.go
- pkg/client/docs/models/operations/v2getschemarequest.md
- internal/api/v2/routes.go
🧰 Additional context used
🧬 Code graph analysis (20)
internal/controller/ledger/store_generated_test.go (1)
internal/schema.go (1)
Schema(11-17)
pkg/client/models/components/v2schemaresponse.go (1)
pkg/client/models/components/v2schema.go (1)
V2Schema(11-18)
internal/log.go (2)
internal/schema.go (1)
Schema(11-17)pkg/events/message.go (1)
UpdatedSchema(76-79)
pkg/client/models/operations/v2getschema.go (1)
pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/controller/ledger/controller_default.go (6)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/log.go (2)
UpdatedSchema(373-375)Log(89-101)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/storage/ledger/store.go (1)
Store(22-44)internal/schema.go (2)
NewSchema(19-24)Schema(11-17)
internal/controller/ledger/controller_with_events.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(132-135)Controller(20-86)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
internal/controller/ledger/controller_generated_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/schema.go (1)
cmd/root.go (1)
Version(23-23)
internal/bus/listener.go (3)
pkg/events/message.go (2)
UpdatedSchema(76-79)NewEventUpdatedSchema(81-89)internal/schema.go (1)
Schema(11-17)pkg/events/events.go (1)
EventTypeUpdatedSchema(11-11)
test/e2e/api_schema_test.go (5)
test/e2e/suite_test.go (1)
UseTemplatedDatabase(143-145)pkg/client/models/operations/v2createledger.go (1)
V2CreateLedgerRequest(9-13)pkg/client/models/operations/v2insertschema.go (1)
V2InsertSchemaRequest(9-15)pkg/client/models/operations/v2getschema.go (1)
V2GetSchemaRequest(9-14)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
pkg/client/models/components/v2schema.go (2)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
internal/controller/ledger/controller_with_traces.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)internal/tracing/tracing.go (1)
TraceWithMetric(100-128)internal/schema.go (1)
Schema(11-17)
internal/api/v1/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/controller/ledger/controller_with_too_many_client_handling.go (4)
internal/controller/ledger/controller.go (2)
UpdateSchema(132-135)Controller(20-86)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)internal/schema.go (1)
Schema(11-17)
pkg/client/models/operations/v2insertschema.go (1)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)
internal/controller/ledger/listener.go (3)
internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
internal/api/v2/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/storage/ledger/schema_test.go (1)
internal/schema.go (2)
NewSchema(19-24)SchemaData(8-9)
internal/api/bulking/mocks_ledger_controller_test.go (5)
internal/api/common/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(11-17)internal/controller/ledger/controller.go (1)
UpdateSchema(132-135)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
internal/controller/ledger/controller.go (3)
internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)internal/schema.go (2)
Schema(11-17)SchemaData(8-9)
🪛 markdownlint-cli2 (0.18.1)
docs/api/README.md
344-344: Blank line inside blockquote
(MD028, no-blanks-blockquote)
390-390: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5354-5354: Multiple headings with the same content
(MD024, no-duplicate-heading)
5356-5356: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5376-5376: Multiple headings with the same content
(MD024, no-duplicate-heading)
5402-5402: Multiple headings with the same content
(MD024, no-duplicate-heading)
pkg/client/docs/sdks/v2/README.md
246-246: Hard tabs
Column: 1
(MD010, no-hard-tabs)
247-247: Hard tabs
Column: 1
(MD010, no-hard-tabs)
248-248: Hard tabs
Column: 1
(MD010, no-hard-tabs)
249-249: Hard tabs
Column: 1
(MD010, no-hard-tabs)
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
307-307: Hard tabs
Column: 1
(MD010, no-hard-tabs)
308-308: Hard tabs
Column: 1
(MD010, no-hard-tabs)
309-309: Hard tabs
Column: 1
(MD010, no-hard-tabs)
310-310: Hard tabs
Column: 1
(MD010, no-hard-tabs)
311-311: Hard tabs
Column: 1
(MD010, no-hard-tabs)
312-312: Hard tabs
Column: 1
(MD010, no-hard-tabs)
🔇 Additional comments (23)
pkg/client/models/components/v2schemaresponse.go (1)
1-15: LGTM!The generated response wrapper follows standard patterns with appropriate nil-safe getter implementation.
test/e2e/api_schema_test.go (1)
17-31: LGTM!Test setup follows established patterns with appropriate use of templated database and deferred test server.
internal/controller/ledger/store_generated_test.go (1)
151-164: LGTM!Generated mock methods for
InsertSchemaandFindSchemafollow standard gomock patterns and provide necessary test coverage for the new schema operations.Also applies to: 210-222
internal/controller/ledger/controller_with_events.go (1)
154-166: LGTM!The
UpdateSchemamethod correctly follows the established pattern of delegating to the underlying controller and emitting events only when not in DryRun mode. Error handling and event scheduling are properly implemented.internal/storage/ledger/schema_test.go (1)
14-28: LGTM!The integration test correctly validates schema persistence and timestamp generation. The use of empty
SchemaData{}is acceptable at this stage, consistent with the current definition whereSchemaDatais an empty struct.internal/controller/ledger/controller.go (1)
82-85: LGTM!The new schema-related interface methods and types are well-defined with clear documentation. The method signatures follow established controller patterns.
Also applies to: 132-135
pkg/client/models/operations/v2getschema.go (1)
1-48: LGTM!The generated request and response types follow standard patterns with appropriate path parameter tags and nil-safe getters.
internal/controller/ledger/controller_with_traces.go (3)
46-47: LGTM!The histogram field declarations follow the established naming pattern and are correctly typed.
153-160: LGTM!The histogram initialization follows the established pattern and uses appropriate metric names.
491-511: LGTM!The GetSchema implementation follows the established tracing pattern correctly, with the span name properly matching the method name.
internal/api/v2/mocks_ledger_controller_test.go (2)
214-227: LGTM!The generated GetSchema mock follows the standard gomock pattern and correctly matches the interface signature.
439-453: LGTM!The generated UpdateSchema mock follows the standard gomock pattern and correctly matches the interface signature.
internal/api/bulking/mocks_ledger_controller_test.go (2)
214-227: LGTM!The generated GetSchema mock correctly implements the interface.
439-453: LGTM!The generated UpdateSchema mock correctly implements the interface.
pkg/client/models/components/v2schema.go (1)
10-50: LGTM!The V2Schema struct and its methods are Speakeasy-generated code that follows the expected patterns for client SDK models.
internal/api/v1/mocks_ledger_controller_test.go (2)
214-227: LGTM!The generated GetSchema mock correctly implements the interface.
439-453: LGTM!The generated UpdateSchema mock correctly implements the interface.
internal/controller/ledger/controller_with_too_many_client_handling.go (2)
123-135: LGTM!The UpdateSchema method correctly wraps the underlying call with retry handling, following the established pattern for write operations in this file.
137-148: LGTM!The GetSchema method correctly wraps the underlying call with retry handling, following the established pattern for read operations in this file.
internal/controller/ledger/controller_default.go (3)
78-80: LGTM!The GetSchema implementation correctly delegates to the store's FindSchema method.
159-159: LGTM!The updateSchemaLp initialization follows the established pattern.
274-277: LGTM!The importLog method correctly handles the UpdatedSchema log type by inserting the schema payload via the store.
pkg/client/models/operations/v2insertschema.go (1)
9-47: LGTM!The Speakeasy-generated request and response types follow the expected SDK patterns with proper accessors.
There was a problem hiding this 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
♻️ Duplicate comments (4)
internal/schema.go (1)
8-23: Schema payload is discarded.
SchemaDatais empty, so every insert/update stores{}and emitted events/logs lose the user-supplied schema body. We need to persist the payload (e.g., as raw JSON) so round-trips work.+type SchemaData struct { + Data json.RawMessage `json:"data" bun:"data,type:jsonb"` +} + -type Schema struct { +type Schema struct { bun.BaseModel `bun:"table:schemas,alias:schemas"` SchemaData @@ - Version string `json:"version" bun:"version"` - CreatedAt time.Time `json:"createdAt" bun:"created_at,nullzero"` + Version string `json:"version" bun:"version"` + CreatedAt time.Time `json:"createdAt" bun:"created_at,nullzero"` } -func NewSchema(version string, data SchemaData) Schema { - return Schema{ - Version: version, - SchemaData: data, - } +func NewSchema(version string, body json.RawMessage) Schema { + return Schema{ + Version: version, + SchemaData: SchemaData{ + Data: body, + }, + } }Adjust callers to pass the raw JSON body so the schema content survives persistence/events.
pkg/client/docs/sdks/v2/README.md (1)
364-396: Fix hard tabs in ListSchemas code example.The Go code example in the ListSchemas section contains hard tab characters (lines 368-373) that should be replaced with spaces for consistency with project formatting standards.
Based on past reviews, this is a recurring pattern in the generated SDK documentation examples. Apply the same fix used for InsertSchema and GetSchema sections:
- "context" - "os" - "github.com/formancehq/ledger/pkg/client/models/components" - "github.com/formancehq/ledger/pkg/client" - "github.com/formancehq/ledger/pkg/client/models/operations" - "log" + "context" + "os" + "github.com/formancehq/ledger/pkg/client/models/components" + "github.com/formancehq/ledger/pkg/client" + "github.com/formancehq/ledger/pkg/client/models/operations" + "log"internal/controller/ledger/controller_with_traces.go (1)
473-494: Fix span name to match method name.The tracing span name on line 481 is "UpdatedSchema" but should be "UpdateSchema" to match the method name being traced. While the variable name
updatedSchema(line 475) correctly reflects the result type, the span name should align with the operation being performed.Apply this fix:
_, err = tracing.TraceWithMetric( ctx, - "UpdatedSchema", + "UpdateSchema", c.tracer, c.updateSchemaHistogram, func(ctx context.Context) (any, error) {Note: A past review comment indicated this was addressed in commit f677581, but the current code still shows "UpdatedSchema" on line 481.
internal/controller/ledger/controller_default.go (1)
67-76: Populate CreatedAt before returning UpdatedSchema (covered already)Returning schema from NewSchema leaves CreatedAt zero unless the store hydrates it. After adjusting InsertSchema to Scan RETURNING columns, this will be fixed. Otherwise, ensure CreatedAt is set before returning.
🧹 Nitpick comments (1)
pkg/client/models/operations/v2listschemas.go (1)
47-61: Style: avoid fallthrough in Order.UnmarshalJSONCurrent fallthrough works but is unconventional. Consider explicit cases for clarity.
- switch v { - case "asc": - fallthrough - case "desc": - *e = Order(v) - return nil - default: - return fmt.Errorf("invalid value for Order: %v", v) - } + switch v { + case "asc", "desc": + *e = Order(v) + return nil + default: + return fmt.Errorf("invalid value for Order: %v", v) + }
📜 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.
⛔ Files ignored due to path filters (6)
internal/storage/bucket/migrations/41-add-schema/notes.yamlis excluded by!**/*.yamlopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (63)
docs/api/README.md(2 hunks)internal/README.md(35 hunks)internal/api/bulking/mocks_ledger_controller_test.go(3 hunks)internal/api/common/mocks_ledger_controller_test.go(3 hunks)internal/api/v1/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/api/v2/controllers_schema_list.go(1 hunks)internal/api/v2/controllers_schema_list_test.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/controllers_schema_read_test.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/routes.go(2 hunks)internal/api/v2/views.go(1 hunks)internal/bus/listener.go(1 hunks)internal/controller/ledger/controller.go(3 hunks)internal/controller/ledger/controller_default.go(4 hunks)internal/controller/ledger/controller_generated_test.go(3 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(4 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/store.go(3 hunks)internal/controller/ledger/store_generated_test.go(3 hunks)internal/log.go(5 hunks)internal/schema.go(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)internal/storage/ledger/resource_schemas.go(1 hunks)internal/storage/ledger/schema.go(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)internal/storage/ledger/store.go(4 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/components/v2schemaresponse.md(1 hunks)pkg/client/docs/models/components/v2schemascursor.md(1 hunks)pkg/client/docs/models/components/v2schemascursorresponse.md(1 hunks)pkg/client/docs/models/operations/order.md(1 hunks)pkg/client/docs/models/operations/queryparamorder.md(1 hunks)pkg/client/docs/models/operations/sort.md(1 hunks)pkg/client/docs/models/operations/v2getschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2listschemasrequest.md(1 hunks)pkg/client/docs/models/operations/v2listschemasresponse.md(1 hunks)pkg/client/docs/models/operations/v2listtransactionsrequest.md(1 hunks)pkg/client/docs/sdks/v2/README.md(2 hunks)pkg/client/models/components/v2schema.go(1 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/components/v2schemaresponse.go(1 hunks)pkg/client/models/components/v2schemascursor.go(1 hunks)pkg/client/models/components/v2schemascursorresponse.go(1 hunks)pkg/client/models/operations/v2getschema.go(1 hunks)pkg/client/models/operations/v2insertschema.go(1 hunks)pkg/client/models/operations/v2listschemas.go(1 hunks)pkg/client/models/operations/v2listtransactions.go(3 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)test/e2e/api_transactions_list_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (7)
- pkg/client/docs/models/components/v2schemascursorresponse.md
- pkg/client/docs/models/operations/order.md
- pkg/client/docs/models/operations/queryparamorder.md
- pkg/client/docs/models/operations/sort.md
- pkg/client/docs/models/components/v2schemaresponse.md
- pkg/client/docs/models/operations/v2listschemasresponse.md
- internal/api/v2/controllers_schema_list_test.go
🚧 Files skipped from review as they are similar to previous changes (16)
- pkg/client/docs/models/components/v2schema.md
- pkg/client/docs/models/operations/v2insertschemarequest.md
- pkg/events/message.go
- pkg/client/models/operations/v2getschema.go
- pkg/client/docs/models/operations/v2insertschemaresponse.md
- internal/storage/ledger/schema_test.go
- internal/controller/ledger/controller_generated_test.go
- pkg/events/events.go
- pkg/client/models/components/v2schemadata.go
- internal/api/v2/controllers_schema_insert.go
- internal/log.go
- pkg/client/docs/models/operations/v2getschemaresponse.md
- pkg/client/docs/models/components/v2schemadata.md
- internal/storage/bucket/migrations/41-add-schema/up.sql
- internal/api/v2/routes.go
- pkg/client/docs/models/operations/v2getschemarequest.md
🧰 Additional context used
🧬 Code graph analysis (33)
pkg/client/models/components/v2schemascursor.go (1)
pkg/client/models/components/v2schema.go (1)
V2Schema(11-18)
internal/bus/listener.go (2)
pkg/events/message.go (2)
UpdatedSchema(76-79)NewEventUpdatedSchema(81-89)pkg/events/events.go (1)
EventTypeUpdatedSchema(11-11)
pkg/client/models/components/v2schemascursorresponse.go (1)
pkg/client/models/components/v2schemascursor.go (1)
V2SchemasCursor(5-10)
pkg/client/models/components/v2schema.go (2)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
internal/storage/ledger/schema.go (4)
internal/controller/ledger/store.go (1)
Store(29-66)internal/storage/ledger/store.go (1)
Store(24-46)internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)
internal/api/bulking/mocks_ledger_controller_test.go (8)
internal/api/common/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v1/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v2/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)internal/controller/ledger/controller.go (1)
UpdateSchema(134-137)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)
internal/api/v2/views.go (1)
internal/schema.go (1)
Schema(11-17)
internal/controller/ledger/controller.go (5)
internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (2)
Schema(11-17)SchemaData(8-9)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)
internal/controller/ledger/controller_with_events.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(134-137)Controller(20-88)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
internal/api/v2/mocks_ledger_controller_test.go (6)
internal/api/bulking/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)internal/controller/ledger/controller.go (1)
UpdateSchema(134-137)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)
pkg/client/models/operations/v2listtransactions.go (2)
pkg/client/internal/utils/json.go (1)
UnmarshalJSON(117-251)pkg/client/models/operations/v2listschemas.go (1)
Order(37-37)
internal/api/v2/controllers_schema_list.go (4)
internal/api/common/pagination.go (1)
PaginationConfig(3-6)pkg/client/models/operations/v2listschemas.go (3)
Order(37-37)OrderDesc(41-41)OrderAsc(40-40)internal/api/common/errors.go (2)
ErrValidation(19-19)HandleCommonPaginationErrors(57-66)internal/schema.go (1)
Schema(11-17)
internal/api/common/mocks_ledger_controller_test.go (5)
internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)internal/controller/ledger/controller.go (1)
UpdateSchema(134-137)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (1)
UpdatedSchema(373-375)
internal/api/v1/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)
pkg/client/models/operations/v2insertschema.go (1)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-7)
internal/controller/ledger/controller_with_traces.go (6)
internal/controller/ledger/controller.go (1)
UpdateSchema(134-137)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)internal/tracing/tracing.go (1)
TraceWithMetric(100-128)internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)
internal/controller/ledger/listener_generated_test.go (2)
pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
pkg/client/v2.go (16)
pkg/client/models/operations/v2insertschema.go (2)
V2InsertSchemaRequest(9-15)V2InsertSchemaResponse(38-40)pkg/client/models/operations/options.go (4)
Option(41-41)Options(32-39)SupportedOptionRetries(15-15)SupportedOptionTimeout(16-16)pkg/client/internal/utils/utils.go (4)
ReplaceParameters(51-60)MatchStatusCodes(71-92)ConsumeRawBody(350-360)UnmarshalJsonFromResponseBody(39-49)pkg/client/internal/utils/pathparams.go (1)
GenerateURL(19-38)pkg/client/internal/hooks/hooks.go (4)
HookContext(27-35)BeforeRequestContext(37-39)AfterErrorContext(45-47)AfterSuccessContext(41-43)pkg/client/internal/config/sdkconfiguration.go (1)
SDKConfiguration(16-25)pkg/client/internal/utils/requestbody.go (1)
SerializeRequestBody(31-42)pkg/client/internal/utils/security.go (1)
PopulateSecurity(27-92)pkg/client/internal/utils/retries.go (3)
RetryConfig(23-23)Retries(25-28)Retry(30-104)pkg/client/models/components/v2errorresponse.go (1)
V2ErrorResponse(5-9)pkg/client/models/sdkerrors/v2errorresponse.go (1)
V2ErrorResponse(10-14)pkg/client/models/operations/v2getschema.go (2)
V2GetSchemaRequest(9-14)V2GetSchemaResponse(30-34)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)pkg/client/models/operations/v2listschemas.go (2)
V2ListSchemasRequest(63-74)V2ListSchemasResponse(122-126)pkg/client/internal/utils/queryparams.go (1)
PopulateQueryParams(20-43)pkg/client/models/components/v2schemascursorresponse.go (1)
V2SchemasCursorResponse(5-7)
internal/storage/ledger/resource_schemas.go (5)
internal/controller/ledger/store.go (1)
Store(29-66)internal/storage/ledger/store.go (2)
Store(24-46)New(176-263)internal/schema.go (1)
Schema(11-17)internal/storage/common/resource.go (6)
EntitySchema(58-60)RepositoryHandlerBuildContext(72-75)ResourceQuery(408-414)ConvertOperatorToSQL(17-33)JoinCondition(35-38)RepositoryHandler(91-97)internal/storage/common/schema.go (3)
Field(27-31)NewStringField(64-66)NewDateField(69-71)
internal/schema.go (1)
cmd/root.go (1)
Version(23-23)
internal/controller/ledger/controller_with_too_many_client_handling.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(134-137)Controller(20-88)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/log.go (2)
Log(89-101)UpdatedSchema(373-375)internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)
internal/controller/ledger/store_generated_test.go (2)
internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)
internal/controller/ledger/store.go (2)
internal/schema.go (1)
Schema(11-17)internal/storage/common/pagination.go (1)
PaginatedQuery(25-28)
internal/api/v2/controllers_schema_read_test.go (2)
internal/schema.go (2)
Schema(11-17)SchemaData(8-9)internal/api/v2/routes.go (1)
NewRouter(21-123)
internal/controller/ledger/controller_default.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(134-137)internal/log.go (2)
UpdatedSchema(373-375)Log(89-101)internal/controller/ledger/parameters.go (1)
Parameters(3-7)internal/controller/ledger/store.go (1)
Store(29-66)internal/schema.go (2)
NewSchema(19-24)Schema(11-17)
internal/api/v2/controllers_schema_insert_test.go (2)
internal/controller/ledger/controller.go (1)
UpdateSchema(134-137)internal/api/v2/routes.go (1)
NewRouter(21-123)
internal/controller/ledger/listener.go (3)
internal/log.go (1)
UpdatedSchema(373-375)pkg/events/message.go (1)
UpdatedSchema(76-79)internal/schema.go (1)
Schema(11-17)
pkg/client/models/components/v2schemaresponse.go (1)
pkg/client/models/components/v2schema.go (1)
V2Schema(11-18)
pkg/client/models/operations/v2listschemas.go (2)
pkg/client/internal/utils/json.go (2)
UnmarshalJSON(117-251)MarshalJSON(21-115)pkg/client/models/components/v2schemascursorresponse.go (1)
V2SchemasCursorResponse(5-7)
internal/api/v2/controllers_schema_read.go (1)
internal/api/common/errors.go (1)
HandleCommonErrors(35-42)
test/e2e/api_schema_test.go (7)
test/e2e/suite_test.go (1)
UseTemplatedDatabase(143-145)pkg/client/models/operations/v2createledger.go (1)
V2CreateLedgerRequest(9-13)pkg/client/models/operations/v2listschemas.go (1)
V2ListSchemasRequest(63-74)pkg/client/models/components/v2schemascursorresponse.go (1)
V2SchemasCursorResponse(5-7)pkg/client/models/operations/v2insertschema.go (1)
V2InsertSchemaRequest(9-15)pkg/client/models/operations/v2getschema.go (1)
V2GetSchemaRequest(9-14)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/storage/ledger/store.go (3)
internal/controller/ledger/store.go (1)
Store(29-66)internal/storage/common/resource.go (2)
PaginatedResource(447-450)NewPaginatedResourceRepository(354-364)internal/schema.go (1)
Schema(11-17)
test/e2e/api_transactions_list_test.go (2)
pkg/client/models/operations/v2listschemas.go (1)
Order(37-37)pkg/client/models/operations/v2listtransactions.go (1)
QueryParamOrderEffective(17-17)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/models/operations/v2listtransactionsrequest.md
13-13: Spaces inside emphasis markers
(MD037, no-space-in-emphasis)
13-13: Spaces inside emphasis markers
(MD037, no-space-in-emphasis)
pkg/client/docs/sdks/v2/README.md
247-247: Hard tabs
Column: 1
(MD010, no-hard-tabs)
248-248: Hard tabs
Column: 1
(MD010, no-hard-tabs)
249-249: Hard tabs
Column: 1
(MD010, no-hard-tabs)
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
252-252: Hard tabs
Column: 1
(MD010, no-hard-tabs)
308-308: Hard tabs
Column: 1
(MD010, no-hard-tabs)
309-309: Hard tabs
Column: 1
(MD010, no-hard-tabs)
310-310: Hard tabs
Column: 1
(MD010, no-hard-tabs)
311-311: Hard tabs
Column: 1
(MD010, no-hard-tabs)
312-312: Hard tabs
Column: 1
(MD010, no-hard-tabs)
313-313: Hard tabs
Column: 1
(MD010, no-hard-tabs)
368-368: Hard tabs
Column: 1
(MD010, no-hard-tabs)
369-369: Hard tabs
Column: 1
(MD010, no-hard-tabs)
370-370: Hard tabs
Column: 1
(MD010, no-hard-tabs)
371-371: Hard tabs
Column: 1
(MD010, no-hard-tabs)
372-372: Hard tabs
Column: 1
(MD010, no-hard-tabs)
373-373: Hard tabs
Column: 1
(MD010, no-hard-tabs)
🔇 Additional comments (49)
pkg/client/README.md (1)
133-135: LGTM!The documentation additions for the new schema operations are clear, well-formatted, and consistent with the existing API documentation structure.
internal/controller/ledger/listener.go (1)
16-16: LGTM!The
UpdatedSchemamethod addition is consistent with the existingListenerinterface pattern, and the parameter naming is now aligned with other methods (usingledgerinstead ofname).pkg/client/docs/models/components/v2schemascursor.md (1)
1-11: LGTM!The documentation for the
V2SchemasCursormodel is clear and follows the established format for cursor documentation. The field definitions are appropriate for a paginated response structure.test/e2e/api_transactions_list_test.go (1)
275-275: LGTM!The update from
OrdertoQueryParamOrderEffectivecorrectly reflects the repository-wide migration to the newQueryParamOrderenum type for v2 list operations.pkg/client/models/components/v2schema.go (1)
1-50: LGTM!This is Speakeasy-generated code that properly defines the V2Schema model with appropriate JSON serialization methods and nil-safe accessor methods. The implementation follows the established patterns for generated client models.
internal/bus/listener.go (1)
28-34: LGTM!The
UpdatedSchemamethod implementation is consistent with other listener methods, properly constructing and publishing the schema update event through the established event bus flow.test/e2e/api_schema_test.go (1)
1-144: LGTM!This comprehensive e2e test provides excellent coverage of the schema API functionality, including:
- Empty state handling
- Schema insertion and retrieval
- Multiple version management
- Error scenarios (non-existent and duplicate schemas)
The test structure follows established patterns and uses appropriate assertions.
internal/controller/ledger/listener_generated_test.go (1)
91-101: LGTM!The generated mock methods for
UpdatedSchemacorrectly follow the established gomock pattern and are consistent with other mock methods in this file.internal/storage/ledger/store.go (1)
85-91: LGTM! Clean implementation following established patterns.The
Schemas()method follows the same resource repository pattern used by other methods in this store, with appropriate defaults (paginate bycreated_atdescending).internal/api/v2/controllers_schema_read.go (1)
12-28: LGTM! Clean handler with appropriate error handling.The
readSchemahandler follows the established v2 controller pattern with proper error handling for not-found and general errors.internal/controller/ledger/controller_with_events.go (1)
154-166: LGTM! Consistent event emission pattern.The
UpdateSchemamethod follows the established pattern for event-emitting wrappers, correctly usinghandleEventfor transaction-aware event scheduling and only emitting when not in dry-run mode.pkg/client/models/components/v2schemascursorresponse.go (1)
5-14: LGTM! Standard cursor response wrapper.The generated
V2SchemasCursorResponsetype follows the established pattern for wrapping cursor objects in API responses with nil-safe getters.internal/api/v2/controllers_schema_read_test.go (1)
20-101: LGTM! Comprehensive test coverage.The test suite covers all critical paths (nominal case, not found, backend errors) with proper mock setup and validation of both status codes and response payloads. Parallel execution is appropriately enabled.
pkg/client/models/components/v2schemascursor.go (1)
5-38: LGTM! Standard pagination cursor implementation.The generated
V2SchemasCursortype follows the established cursor pagination pattern with appropriate fields and nil-safe getters.internal/storage/ledger/resource_schemas.go (1)
11-53: LGTM! Clean resource handler implementation.The
schemasResourceHandlerfollows the established pattern for repository handlers with:
- Appropriate schema definition with paginated string and date fields
- Standard BuildDataset with ledger filtering and PIT support
- Proper filter resolution for supported properties
- Clear rejection of unsupported expand operations
pkg/client/models/components/v2schemaresponse.go (1)
1-15: LGTM!The V2SchemaResponse type and its nil-safe GetData() accessor follow standard patterns for generated SDK code.
internal/controller/ledger/store.go (1)
49-51: LGTM!The new schema-related methods (InsertSchema, FindSchema, FindSchemas) are correctly added to the Store interface with appropriate signatures that align with the controller layer additions.
internal/api/v2/controllers_schema_list.go (1)
12-48: LGTM!The listSchemas handler implementation follows the established v2 API patterns:
- Appropriate default values (sort by "created_at" desc)
- Standard error handling with BadRequest and HandleCommonPaginationErrors
- Correct cursor rendering with schema transformation
internal/README.md (1)
110-112: LGTM!The generated documentation correctly reflects the new public schema-related types (Schema, SchemaData, UpdatedSchema) and their associated constructors and methods.
Also applies to: 136-137, 186-218
internal/api/v2/controllers_schema_insert_test.go (1)
16-103: LGTM!The test suite for insertSchema provides good coverage:
- Nominal success case
- Empty schema data handling
- Invalid JSON validation
- Backend error propagation
The test structure follows Go best practices with parallel execution and appropriate use of gomock and testify.
internal/controller/ledger/controller_with_traces.go (1)
496-538: LGTM!The GetSchema and ListSchemas tracing wrappers are correctly implemented with appropriate span names that match their method names and proper histogram instrumentation.
internal/controller/ledger/controller.go (1)
82-87: LGTM!The new Controller interface methods (UpdateSchema, GetSchema, ListSchemas) and the UpdateSchema parameter struct are well-defined with clear documentation and appropriate signatures that align with existing patterns in the codebase.
Also applies to: 134-137
internal/storage/ledger/schema.go (2)
22-35: Find by version scoped to ledger looks goodCorrect table prefixing and ledger scoping; errors normalized.
37-39: Pagination hook is correctDelegating to Schemas().Paginate aligns with existing resources.
internal/controller/ledger/controller_default.go (4)
63-66: UpdateSchema wiring via log processor looks correctUse of forgeLog matches existing patterns.
78-84: Get/List schema methods look goodThin wrappers over store; correct types and pagination usage.
163-164: Log processor initialization is consistentAdds updateSchemaLp like other processors.
278-282: Import UpdatedSchema path is fineInserting payload.Schema before persisting the log aligns with other payloads. With the store fix, CreatedAt from payload or DB will persist correctly.
internal/controller/ledger/controller_with_too_many_client_handling.go (3)
125-137: Retry wrapper for UpdateSchema is consistentMatches existing handleRetry pattern and returns all values.
139-151: Retry wrapper for GetSchema is consistentSimple pass-through with retries.
152-163: Retry wrapper for ListSchemas is consistentCorrect pagination cursor type and retry handling.
pkg/client/v2.go (3)
656-849: V2 InsertSchema method matches SDK patternsURL, body serialization (V2SchemaData), retries, hooks, and 204 handling are consistent.
Please ensure server returns 204 on success as coded here.
851-1057: V2 GetSchema deserialization path looks correctCorrect route, scopes, and JSON decode into V2SchemaResponse on 200.
1059-1269: V2 ListSchemas implementation is correctQuery params populated; 200 decodes to V2SchemasCursorResponse.
internal/api/v2/mocks_ledger_controller_test.go (3)
214-227: LGTM: GetSchema mock matches controller signatureMethod and recorder wiring look correct: ctx + version -> (*ledger.Schema, error).
347-360: LGTM: ListSchemas mock returns typed cursorSignature aligns with bunpaginate and ledger.Schema generics; recorder OK.
454-468: LGTM: UpdateSchema mock matches API contract Returns (*ledger.Log, *ledger.UpdatedSchema, error) and recorder wiring is correct; mocks and interface include GetSchema, ListSchemas, and UpdateSchema.internal/api/bulking/mocks_ledger_controller_test.go (3)
214-227: LGTM: GetSchema mock (bulking) is consistentSignature and recorder align with controller interface.
347-360: LGTM: ListSchemas mock (bulking) cursor typing OKbunpaginate cursor over ledger.Schema; recorder matches.
454-468: LGTM: UpdateSchema mock (bulking) matches expected return typesRecorder method signature is correct.
internal/controller/ledger/store_generated_test.go (3)
152-166: LGTM: Store.FindSchema mock added correctlyctx + version -> (*ledger.Schema, error); recorder matches.
167-181: LGTM: Store.FindSchemas mock returns typed cursorSignature uses common.PaginatedQuery[any] and bunpaginate.Cursor[ledger.Schema]; consistent with other paginated resources.
226-239: Store.InsertSchema mock matches interface
Mock signature aligns with theStoreinterface’sInsertSchema(ctx context.Context, data *ledger.Schema) error; no regeneration needed.internal/api/v1/mocks_ledger_controller_test.go (3)
214-227: LGTM: GetSchema mock (v1) added properlyMatches ctx + version -> (*ledger.Schema, error); recorder OK.
347-360: LGTM: ListSchemas mock (v1) returns cursor over ledger.SchemaRecorder and types are consistent with v2/bulking.
454-468: LGTM: UpdateSchema mock (v1) signature correctReturns (*ledger.Log, *ledger.UpdatedSchema, error); recorder wiring good.
pkg/client/models/operations/v2listschemas.go (1)
69-74: Verify pagination defaults on server
controllers_schema_list.gohandlessort(defaultcreated_at) andorder(defaultdesc); pagination usesbunpaginate.QueryDefaultPageSize. EnsureQueryDefaultPageSizeequals the client’s default15.pkg/client/models/operations/v2insertschema.go (1)
17-47: LGTM!The getter methods follow standard nil-safe patterns for generated code, correctly returning zero values when the receiver is nil.
internal/api/common/mocks_ledger_controller_test.go (1)
214-227: LGTM!The three new mock methods (
GetSchema,ListSchemas,UpdateSchema) are correctly generated and follow standard gomock patterns consistent with existing mocks in this file. The method signatures properly align with the controller interface definitions.Also applies to: 347-360, 454-468
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
pkg/client/docs/sdks/v2/README.md (1)
243-253: Replace hard tabs with spaces in Go examplesThe
importblocks in theInsertSchema,GetSchema, andListSchemasexamples still use hard tabs for indentation, which triggersMD010 (no-hard-tabs)from markdownlint and matches earlier review feedback.Consider normalizing these blocks to use spaces like the rest of the README, for example:
-import( - "context" - "os" - "github.com/formancehq/ledger/pkg/client/models/components" - "github.com/formancehq/ledger/pkg/client" - "github.com/formancehq/ledger/pkg/client/models/operations" - "log" -) +import ( + "context" + "os" + "github.com/formancehq/ledger/pkg/client/models/components" + "github.com/formancehq/ledger/pkg/client" + "github.com/formancehq/ledger/pkg/client/models/operations" + "log" +)Apply the same pattern to the corresponding
importblocks in theGetSchemaandListSchemassections.Also applies to: 317-324, 377-384
🧹 Nitpick comments (18)
internal/api/v2/controllers_schema_read_test.go (3)
86-92: Useapi.Decodefor error responses for consistencyThe success branch at line 97 already uses
api.Decode(t, rec.Body, &response), but the error branch (lines 86-92) uses manualjson.Unmarshal. All other v2 API test files follow theapi.Decodepattern for error decoding. Update toapi.Decode(t, rec.Body, &errorResponse)to standardize the approach.
10-10: Removespew.Dump(response.Data)from test success pathVerification confirms:
spew.Dump(response.Data)at line 96 executes beforeapi.Decodeon line 97, printing an uninitialized zero-valued struct. This adds test output noise without testing value. Assertions at lines 98–99 validate decoded data only. Safe to remove.Location:
internal/api/v2/controllers_schema_read_test.go:96
15-16: Add import of controller layer and useledgercontroller.ErrNotFoundalias for consistencyThe test should import
internal/controller/ledgerand use theledgercontroller.ErrNotFoundalias instead ofpostgres.ErrNotFoundat line 55. This aligns with the established pattern used by other similar tests in the same package (e.g.,controllers_transactions_revert_test.go,controllers_transactions_delete_metadata_test.go) and better reflects the controller contract rather than exposing storage layer details.internal/storage/ledger/schema_test.go (1)
14-29: Schema insert IT test is good; double‑check parallelism and empty chart semanticsThis test correctly exercises
NewSchema+InsertSchemaand assertsVersionandCreatedAt, which is useful coverage for the new schema path. Two things to confirm:
t.Parallel()on an integration test is safe with yournewLedgerStore(t)setup (shared DB / migrations / unique ledger names), otherwise consider dropping parallelism to avoid flakiness.- Passing an empty (but non‑nil)
Chartmakes “no chart at all” invalid but “empty chart” valid. That contract is now codified by this test; confirm this matches the intended business rule.internal/errors.go (1)
90-113: New schema/account error types are well‑structured; consider optional enhancements
ErrInvalidSchemaandErrInvalidAccountfollow the same pattern as the existing error types and will work nicely witherrors.Is. Two optional enhancements you might consider (not blocking):
- Add an
Unwrap() errormethod onErrInvalidSchemaif you want callers to introspect the underlying validation error.- For
ErrInvalidAccount.Error, you may want to renderpathviastrings.Join(e.path, ":")(or whatever delimiter you use for accounts) instead of%von the slice, for a more user‑friendly message.Otherwise, these additions look good.
internal/api/v2/views.go (2)
123-133: Account marshalling now properly reuses stringified volumes when big‑int‑as‑string is requestedWrapping
VolumesandEffectiveVolumesinvolumesByAssetswithinaccount.MarshalJSONensures that, whenrenderAccountreturnsaccount(v), nested volume fields honor the big‑int‑as‑string encoding. This is a nice consistency fix; no further changes needed.
144-146: renderSchema helper is fine; confirm whether schema ever needs big‑int handling
renderSchemacurrently just returns theledger.Schemaas‑is, ignoring theFormance-Bigint-As-Stringheader. That’s perfectly reasonable if schema/chart structures don’t embed any*big.Intvalues that must be stringified. If they do (now or in the future), you may want aschemawrapper similar totransaction/accountto keep behavior consistent across endpoints.internal/controller/ledger/log_process_test.go (1)
34-37: Mocking FindSchemas aligns tests with new schema-aware log processingThe added
FindSchemasexpectations returning an emptybunpaginate.Cursor[ledger.Schema]correctly adapt these tests to the new schema lookup insideforgeLog, without altering the original behaviors being asserted (IK conflict and deadlock retry). If you see more tests needing the same “no schemas” setup, consider a small helper to reduce duplication, but it’s not required here.Also applies to: 69-72, 82-85
test/e2e/api_schema_test.go (1)
350-395: Tighten log-type matching in schemaVersion log testThe log-inspection test is solid and validates that
SchemaVersionflows intoV2Log. To make it a bit more robust and self-documenting, you might:
- Compare
log.Typeagainst the correspondingcomponents.V2LogTypeconstant instead of the raw"NEW_TRANSACTION"string, so refactors of the enum won’t silently break this test.- (Optional) If
V2ListLogsever returns multipleNEW_TRANSACTIONlogs, consider also checking some aspect of the data (e.g., reference, amount) to be sure you’re asserting against the transaction created in this spec.Behavior is fine as-is; these are just resilience/readability tweaks.
internal/api/v2/controllers_schema_insert_test.go (2)
30-81: Consider adding test case for schema already exists error.The test cases provide good coverage of common scenarios. However, based on the new
ErrSchemaAlreadyExistsconstant added ininternal/api/common/errors.go, consider adding a test case that verifies the endpoint properly handles attempts to insert a duplicate schema version.Example test case to add:
{ name: "schema already exists", version: "v1.0.0", requestBody: map[string]interface{}{ "rules": []map[string]interface{}{}, }, expectStatusCode: http.StatusBadRequest, expectedErrorCode: common.ErrSchemaAlreadyExists, expectBackendCall: true, returnErr: ledgercontroller.ErrSchemaAlreadyExists{}, },
97-97: Consider handling JSON marshaling errors in test setup.The test silently ignores potential JSON marshaling errors. While test data is controlled and unlikely to fail marshaling, explicitly handling this error would make test failures clearer if the test data is modified incorrectly in the future.
-body, _ := json.Marshal(tc.requestBody) +body, err := json.Marshal(tc.requestBody) +require.NoError(t, err)internal/chart_test.go (1)
288-404: Name the failing posting test cases for clearer diagnosticsBoth failing cases in
TestPostingValidationuse an emptynamefield, which makes it harder to see which scenario failed when the test breaks. Consider giving them descriptive names (e.g."invalid source"/"invalid destination").internal/controller/ledger/controller_default_test.go (1)
26-61: CreateTransaction tests exercise both schema and no-schema paths; minor simplification possibleThe
testCreateTransactionhelper plusTestCreateTransactiongive good coverage of both flows (no schemas vs. explicitSchemaVersionwith a minimal chart), and theFindSchema/FindSchemasexpectations line up with the new log processing logic.Two optional tweaks you might consider:
- The explicit
InsertSchemaexpectation and call on the mock store aren’t needed forCreateTransactionitself, since the controller only callsFindSchema/FindSchemas. Removing that setup would simplify the test without changing its behavior.- For completeness, you could add a dedicated test where the ledger already has at least one schema but
SchemaVersionis left empty, asserting that the controller surfaces the expectedErrSchemaNotSpecifiedpath.Both are non-blocking; the existing tests already validate the main happy paths.
Also applies to: 84-93, 107-120
internal/controller/ledger/errors.go (1)
249-320: Schema error types fit existing patterns; consider adding Unwrap for validation errorThe new schema-related errors (not-found, validation, not-specified, already-exists) follow the same value-type +
Ispattern as the rest of this file and give you clear error classes to surface up the stack.One small improvement you might consider: add an
Unwrap() errormethod toErrSchemaValidationErrorso callers can still inspect the underlying validation error viaerrors.Is/errors.Aswhen needed, without changing how the typed wrapper is matched today.internal/controller/ledger/controller_default.go (1)
222-275: Import now correctly handles UpdatedSchema logs but treats duplicate schemas as hard failuresThe new
ledger.UpdatedSchemacase insertspayload.SchemaviaInsertSchema, which is consistent with the liveUpdateSchemapath. On import into a non‑empty database, a duplicate schema version will causeInsertSchemato fail and abort the import. Given the import precondition (logs must strictly append after the last existing log), this is acceptable, but it does mean importing a history that contains schema updates into a ledger that already holds those schema rows will fail even if log IDs don’t overlap.If you ever need import to be idempotent with respect to schema rows (e.g., re-importing a full history into a fresh-but-preseeded DB), consider optionally special‑casing
ErrConstraintsFailedhere to either ignore duplicates or surface a more explicit import error.Also applies to: 277-287
internal/log.go (2)
20-26: UpdatedSchemaLogType is wired correctly; panics on unknown types remain
UpdatedSchemaLogTypeis added to the enum,String(), andLogTypeFromString(), so serialization and deserialization of"UPDATED_SCHEMA"are consistent. Both functions stillpanic("invalid log type")for unknown values; that matches existing behavior but leaves no graceful recovery path if the DB or input is corrupted.If you want to harden against bad data, consider returning an explicit error instead of panicking when mapping unknown log types.
Also applies to: 54-69, 71-86
267-331: Metadata and revert payloads mark that they “need” a schema but currently perform no validation
SavedMetadata,DeletedMetadata, andRevertedTransactionall returnNeedsSchema() == truewhile theirValidateWithSchemamethods are no‑ops. This is fine as a staging step if the intent is just to require a schema version to be specified for these operations without enforcing additional rules yet, but it’s easy for future contributors to assume real validation is already happening.Either (a) add a brief comment near these methods clarifying that they are intentionally no‑ops for now, or (b) flip
NeedsSchema()tofalseuntil you actually enforce metadata/revert rules via the schema.Also applies to: 371-383
internal/chart.go (1)
224-261: Account schema lookup recursion is correct but recompiles regexes per lookup
findAccountSchemacleanly walks fixed and variable segments, accumulates the visited path forErrInvalidAccount, and honors the specialworldcase inFindAccountSchema. The recursion always advances (account[1:]) when descending, so no infinite loop is possible.You already validate the pattern during unmarshalling; to avoid recompiling it on every lookup, consider storing a precompiled
*regexp.RegexponChartVariableSegment(unexported) and using it instead ofregexp.Matchhere.
📜 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.
⛔ Files ignored due to path filters (7)
docs/events/UpdatedSchema.jsonis excluded by!**/*.jsongo.modis excluded by!**/*.modopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (59)
cmd/docs_events.go(1 hunks)docs/api/README.md(22 hunks)internal/README.md(37 hunks)internal/api/bulking/bulker.go(6 hunks)internal/api/bulking/elements.go(1 hunks)internal/api/common/errors.go(2 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/api/v2/controllers_schema_read_test.go(1 hunks)internal/api/v2/controllers_transactions_create.go(1 hunks)internal/api/v2/query.go(1 hunks)internal/api/v2/views.go(9 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)internal/controller/ledger/controller_default.go(4 hunks)internal/controller/ledger/controller_default_test.go(7 hunks)internal/controller/ledger/errors.go(1 hunks)internal/controller/ledger/log_process.go(2 hunks)internal/controller/ledger/log_process_test.go(4 hunks)internal/controller/ledger/parameters.go(1 hunks)internal/errors.go(1 hunks)internal/log.go(13 hunks)internal/schema.go(1 hunks)internal/storage/bucket/default_bucket.go(1 hunks)internal/storage/bucket/migrations/35-create-compute-hash-function/up.sql(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)pkg/client/docs/models/components/dotself.md(1 hunks)pkg/client/docs/models/components/v2bulkelementaddmetadata.md(1 hunks)pkg/client/docs/models/components/v2bulkelementcreatetransaction.md(1 hunks)pkg/client/docs/models/components/v2bulkelementdeletemetadata.md(1 hunks)pkg/client/docs/models/components/v2bulkelementreverttransaction.md(1 hunks)pkg/client/docs/models/components/v2chartrules.md(1 hunks)pkg/client/docs/models/components/v2chartsegment.md(1 hunks)pkg/client/docs/models/components/v2errorsenum.md(1 hunks)pkg/client/docs/models/components/v2log.md(1 hunks)pkg/client/docs/models/components/v2logtype.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/operations/v2addmetadataontransactionrequest.md(1 hunks)pkg/client/docs/models/operations/v2addmetadatatoaccountrequest.md(1 hunks)pkg/client/docs/models/operations/v2createbulkrequest.md(1 hunks)pkg/client/docs/models/operations/v2createtransactionrequest.md(1 hunks)pkg/client/docs/models/operations/v2reverttransactionrequest.md(1 hunks)pkg/client/docs/sdks/v2/README.md(7 hunks)pkg/client/models/components/v2bulkelementaddmetadata.go(2 hunks)pkg/client/models/components/v2bulkelementcreatetransaction.go(2 hunks)pkg/client/models/components/v2bulkelementdeletemetadata.go(2 hunks)pkg/client/models/components/v2bulkelementreverttransaction.go(2 hunks)pkg/client/models/components/v2chartrules.go(1 hunks)pkg/client/models/components/v2chartsegment.go(1 hunks)pkg/client/models/components/v2errorsenum.go(2 hunks)pkg/client/models/components/v2log.go(4 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/operations/v2addmetadataontransaction.go(2 hunks)pkg/client/models/operations/v2addmetadatatoaccount.go(2 hunks)pkg/client/models/operations/v2createbulk.go(2 hunks)pkg/client/models/operations/v2createtransaction.go(2 hunks)pkg/client/models/operations/v2reverttransaction.go(2 hunks)test/e2e/api_schema_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- pkg/client/docs/models/components/dotself.md
- pkg/client/docs/models/components/v2chartsegment.md
- pkg/client/docs/models/components/v2chartrules.md
🚧 Files skipped from review as they are similar to previous changes (3)
- internal/api/v2/controllers_schema_insert.go
- internal/schema.go
- pkg/client/docs/models/components/v2schemadata.md
🧰 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/README.mdinternal/controller/ledger/controller_default.gointernal/controller/ledger/log_process.go
🧬 Code graph analysis (20)
internal/api/common/errors.go (1)
internal/controller/ledger/errors.go (5)
ErrInvalidIdempotencyInput(221-225)ErrSchemaValidationError(268-271)ErrNotFound(14-14)ErrSchemaNotSpecified(289-289)ErrSchemaNotFound(249-251)
pkg/client/models/operations/v2reverttransaction.go (1)
pkg/client/models/components/v2reverttransactionrequest.go (1)
V2RevertTransactionRequest(5-7)
cmd/docs_events.go (1)
internal/log.go (1)
UpdatedSchema(418-420)
pkg/client/models/operations/v2createbulk.go (1)
pkg/client/models/components/v2bulkelement.go (1)
V2BulkElement(21-28)
internal/api/v2/controllers_schema_insert_test.go (3)
internal/controller/ledger/errors.go (1)
ErrSchemaValidationError(268-271)internal/controller/ledger/controller.go (1)
UpdateSchema(134-137)internal/api/v2/routes.go (1)
NewRouter(21-123)
internal/chart_test.go (2)
internal/chart.go (5)
ChartOfAccounts(36-36)ChartVariableSegment(23-28)ChartSegment(17-21)ChartAccount(12-15)ChartAccountRules(10-10)internal/errors.go (1)
ErrInvalidAccount(102-105)
test/e2e/api_schema_test.go (16)
test/e2e/suite_test.go (1)
UseTemplatedDatabase(143-145)pkg/client/models/operations/v2createledger.go (1)
V2CreateLedgerRequest(9-13)pkg/client/models/operations/v2listschemas.go (1)
V2ListSchemasRequest(63-74)pkg/client/models/components/v2schemascursorresponse.go (1)
V2SchemasCursorResponse(5-7)pkg/client/models/operations/v2insertschema.go (1)
V2InsertSchemaRequest(9-15)pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-9)pkg/client/models/components/v2chartsegment.go (1)
V2ChartSegment(13-19)pkg/client/models/components/v2chartrules.go (1)
V2ChartRules(5-6)pkg/client/models/components/v2errorsenum.go (4)
V2ErrorsEnumSchemaAlreadyExists(30-30)V2ErrorsEnumValidation(15-15)V2ErrorsEnumSchemaNotSpecified(31-31)V2ErrorsEnumNotFound(19-19)pkg/client/models/operations/v2getschema.go (1)
V2GetSchemaRequest(9-14)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)pkg/client/models/operations/v2createtransaction.go (1)
V2CreateTransactionRequest(9-27)pkg/client/models/components/v2posttransaction.go (1)
V2PostTransaction(58-68)pkg/client/models/components/v2posting.go (1)
V2Posting(10-15)pkg/client/models/operations/v2listlogs.go (1)
V2ListLogsRequest(11-29)pkg/client/models/components/v2log.go (1)
V2Log(48-56)
pkg/client/models/components/v2schemadata.go (1)
pkg/client/models/components/v2chartsegment.go (1)
V2ChartSegment(13-19)
internal/api/v2/controllers_schema_read_test.go (3)
internal/schema.go (2)
Schema(14-20)SchemaData(10-12)internal/controller/ledger/errors.go (1)
ErrNotFound(14-14)internal/api/v2/routes.go (1)
NewRouter(21-123)
internal/log.go (5)
internal/storage/ledger/logs.go (1)
Log(19-25)internal/schema.go (1)
Schema(14-20)internal/transaction.go (2)
Transaction(36-50)Transaction(52-59)internal/posting.go (1)
Postings(35-35)pkg/events/message.go (4)
SavedMetadata(26-31)DeletedMetadata(59-64)RevertedTransaction(43-47)UpdatedSchema(76-79)
internal/storage/ledger/schema_test.go (2)
internal/schema.go (2)
NewSchema(22-32)SchemaData(10-12)internal/chart.go (1)
ChartSegment(17-21)
internal/controller/ledger/log_process_test.go (1)
internal/schema.go (1)
Schema(14-20)
internal/controller/ledger/errors.go (1)
internal/api/common/errors.go (2)
ErrSchemaNotSpecified(27-27)ErrSchemaAlreadyExists(26-26)
internal/controller/ledger/controller_default.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(134-137)internal/log.go (2)
UpdatedSchema(418-420)Log(89-102)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/controller/ledger/store.go (1)
Store(29-66)internal/schema.go (2)
NewSchema(22-32)Schema(14-20)
internal/chart.go (1)
internal/errors.go (1)
ErrInvalidAccount(102-105)
pkg/client/models/components/v2chartsegment.go (2)
pkg/client/models/components/v2chartrules.go (1)
V2ChartRules(5-6)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
internal/controller/ledger/log_process.go (4)
internal/schema.go (1)
Schema(14-20)internal/controller/ledger/errors.go (1)
ErrNotFound(14-14)internal/storage/common/pagination.go (1)
InitialPaginatedQuery(9-14)internal/log.go (2)
NewLog(194-199)ComputeIdempotencyHash(459-468)
pkg/client/models/components/v2bulkelementcreatetransaction.go (1)
pkg/client/models/components/v2posttransaction.go (1)
V2PostTransaction(58-68)
internal/controller/ledger/controller_default_test.go (4)
internal/schema.go (2)
Schema(14-20)SchemaData(10-12)internal/chart.go (1)
ChartOfAccounts(36-36)internal/controller/ledger/controller.go (2)
CreateTransaction(101-105)RunScript(90-90)internal/machine/vm/run.go (1)
RunScript(14-19)
internal/api/v2/views.go (3)
internal/transaction.go (2)
Transaction(36-50)Transaction(52-59)internal/volumes.go (3)
PostCommitVolumes(118-118)Volumes(12-15)Volumes(40-43)internal/schema.go (1)
Schema(14-20)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/sdks/v2/README.md
247-247: Hard tabs
Column: 1
(MD010, no-hard-tabs)
248-248: Hard tabs
Column: 1
(MD010, no-hard-tabs)
249-249: Hard tabs
Column: 1
(MD010, no-hard-tabs)
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
252-252: Hard tabs
Column: 1
(MD010, no-hard-tabs)
318-318: Hard tabs
Column: 1
(MD010, no-hard-tabs)
319-319: Hard tabs
Column: 1
(MD010, no-hard-tabs)
320-320: Hard tabs
Column: 1
(MD010, no-hard-tabs)
321-321: Hard tabs
Column: 1
(MD010, no-hard-tabs)
322-322: Hard tabs
Column: 1
(MD010, no-hard-tabs)
323-323: Hard tabs
Column: 1
(MD010, no-hard-tabs)
378-378: Hard tabs
Column: 1
(MD010, no-hard-tabs)
379-379: Hard tabs
Column: 1
(MD010, no-hard-tabs)
380-380: Hard tabs
Column: 1
(MD010, no-hard-tabs)
381-381: Hard tabs
Column: 1
(MD010, no-hard-tabs)
382-382: Hard tabs
Column: 1
(MD010, no-hard-tabs)
383-383: Hard tabs
Column: 1
(MD010, no-hard-tabs)
🔇 Additional comments (72)
pkg/client/docs/models/operations/v2addmetadatatoaccountrequest.md (1)
12-12: ✓ Schema field addition is properly documented.The new
SchemaVersionfield is appropriately positioned in the request model documentation, clearly described, and consistent with the schema support feature being introduced across the PR. The optional marker and example value are suitable.pkg/client/models/operations/v2createtransaction.go (2)
20-21: SchemaVersion query param wiring looks consistentAdding
SchemaVersion *stringwith theschemaVersionquery tag aligns with the existing style for optional query parameters (e.g.,DryRun,Force) and keeps the API surface coherent. No issues from this file’s perspective.
57-62: Getter implementation matches existing pattern
GetSchemaVersionmirrors the other getters’ nil-safety pattern and correctly exposes the new field without changing behavior when the receiver is nil.internal/api/v2/controllers_transactions_create.go (1)
4-5: LGTM! Import cleanup.The import block reorganization correctly positions the
net/httpimport, which is used by the function signature on line 18.pkg/client/models/operations/v2createbulk.go (2)
51-56: LGTM: Accessor method correctly implemented.The
GetSchemaVersionmethod follows the same nil-safety pattern as other accessor methods in this generated file.
17-20: LGTM: Generated code follows consistent patterns and supports per-element schema version overrides.The
SchemaVersionfield is correctly implemented as an optional query parameter for the operation. Verification confirms that individual bulk element types (V2BulkElementRevertTransaction,V2BulkElementDeleteMetadata,V2BulkElementAddMetadata,V2BulkElementCreateTransaction) all have their ownSchemaVersionfields, supporting the per-element override capability mentioned in the code comment.pkg/client/models/components/v2chartrules.go (1)
5-6: Empty struct serves as a placeholder for future rules.The empty
V2ChartRulesstruct is appropriate as a type marker and allows for future extension without breaking the API.pkg/client/models/components/v2schemadata.go (2)
5-9: Previous empty struct concern has been resolved.The
Chartfield is now present, addressing the earlier feedback about empty JSON payloads. The structure properly models the schema payload.
11-16: Nil-safe accessor with sensible default.The
GetChartmethod correctly handles nil receivers by returning an empty map, preventing potential nil pointer dereferences.pkg/client/models/components/v2chartsegment.go (4)
9-10: Empty marker type for self-reference semantics.Like
V2ChartRules,DotSelfserves as a type marker with the special.selfJSON key, enabling future extension.
12-19: Recursive structure enables nested chart segments.The
AdditionalPropertiesfield uses a recursive type to support hierarchical chart structures. The dot-prefixed JSON keys (.self,.pattern, etc.) distinguish reserved fields from dynamic properties, which is appropriate for this schema model.
21-30: Custom marshaling correctly handles additional properties.The JSON marshaling delegates to utility functions that properly serialize the
AdditionalPropertiesfield alongside the named fields, as confirmed by the utils package implementation.
32-65: Consistent nil-safe getters for all fields.All getter methods properly handle nil receivers, maintaining API safety across the public interface.
pkg/client/models/operations/v2addmetadataontransaction.go (2)
20-21: LGTM: SchemaVersion field properly defined.The field follows the same pattern as other optional query parameters in the struct, with appropriate type, tag, and documentation.
65-70: Based on the verification, theGetSchemaVersion()accessor method is correctly implemented and follows the established pattern used by other optional query parameters in the struct. The implementation properly handles nil receivers and returns the field value.Regarding the original review comment's request to validate SchemaVersion usage: In Go SDKs, optional parameters are typically represented as pointers, and a nil value indicates the parameter was not provided. The client-side code correctly exposes SchemaVersion as an optional parameter through this accessor. Validation of query parameters is typically handled on the backend API layer, not in the client library.
The test coverage shows that SchemaVersion is intentionally never passed, confirming it functions as a truly optional parameter that has no required validation at the client level.
internal/api/v2/controllers_schema_read_test.go (1)
21-32: Good, focused table-driven coverage of read-schema behaviorThe table tests cover the main paths (OK, not-found, generic backend error), use
t.Parallelsafely with per-subtest setup, and assert both HTTP status and payload shape. TheGetSchemamock expectations also align correctly with the router path semantics.Also applies to: 41-65, 67-102
pkg/client/docs/models/operations/v2createbulkrequest.md (1)
12-12: SchemaVersion field addition is correctly implemented and consistently applied across operations.Verification confirms that the
SchemaVersionfield has been properly added to five related V2 operations:
- v2createbulkrequest.md (with context-appropriate description: "Default schema version...can be overridden per element")
- v2createtransactionrequest.md
- v2addmetadataontransactionrequest.md
- v2reverttransactionrequest.md
- v2addmetadatatoaccountrequest.md
The markdown descriptions match their corresponding Go struct comments exactly, and the field type, optional marking, and example values are consistent across all operations. No stale or incomplete schema documentation was found.
The exact word-for-word correspondence between Go code comments and markdown documentation confirms the docs are auto-generated from source code (likely by external CI tooling). This ensures consistency between implementation and documentation.
internal/api/bulking/elements.go (1)
23-28: BulkElement.SchemaVersion addition looks correct and backward compatibleAdding
SchemaVersion string \json:"schemaVersion,omitempty"`onBulkElementcleanly surfaces schema version in bulk payloads without breaking existing clients (field is optional). No changes are needed here; just ensure downstream bulk processing actually reads and propagatesSchemaVersion` where required.cmd/docs_events.go (1)
32-38: UpdatedSchema inclusion in docs generation is consistentAdding
events.UpdatedSchema{}to the loop aligns with the existing pattern and will generate the JSON schema alongside other event payloads. No issues here; just remember to run the docs generation step so the new schema file is committed where expected.pkg/client/docs/models/operations/v2addmetadataontransactionrequest.md (1)
6-13: SchemaVersion field doc is consistent with the new API surfaceThe added
SchemaVersionrow (optional string, “Schema version to use for validation”, examplev1.0.0) matches the intent of the rest of the PR. Just ensure the underlying client model actually exposes aSchemaVersionfield with the same casing and JSON name so docs and code stay aligned.pkg/client/models/components/v2errorsenum.go (1)
12-33: New schema‑related error codes are wired correctly into V2ErrorsEnumThe additions of
V2ErrorsEnumSchemaAlreadyExistsandV2ErrorsEnumSchemaNotSpecifiedplus their handling inUnmarshalJSONare consistent with the existing pattern and won’t affect other variants. No further changes needed here.Also applies to: 38-83
internal/api/v2/views.go (7)
16-25: volumes JSON marshalling remains safe;anyfield types are mostly cosmeticThe updated
volumes.MarshalJSONstill always emits strings forinput,output, andbalance, and the change toany-typed fields doesn’t alter behavior. Given this is only used through the big‑int‑as‑string path, there’s no functional concern here.
46-53: Posting wrapper continues to stringify Amount correctly
posting.MarshalJSONstill embedsledger.Postingand exposesAmountasp.Amount.String(), which is consistent with the existing big‑int handling. No issues from this change.
98-111: volumesWithBalanceByAssetByAccount marshalling aligns with big‑int‑as‑string behaviorSerializing
Input,Output, andBalancevia.String()is consistent with the big‑int‑as‑string semantics used elsewhere. As long as these fields are always non‑nil in practice (same assumption as before), this is fine.
166-175: createdTransaction wrapper correctly reuses transaction marshallerUsing:
type createdTransaction ledger.CreatedTransaction func (tx createdTransaction) MarshalJSON() ([]byte, error) { type Aux ledger.CreatedTransaction return json.Marshal(struct { Aux Transaction transaction `json:"transaction"` }{ Aux: Aux(tx), Transaction: transaction(tx.Transaction), }) }ensures that nested
Transactionfields in created‑transaction logs benefit from the customtransaction.MarshalJSONlogic (including big‑int‑as‑string behavior). This looks correct.
179-189: revertedTransaction marshalling mirrors createdTransaction and looks correctThe
revertedTransactionwrapper delegates bothRevertedTransactionandRevertTransactionfields to thetransactionwrapper, which keeps their serialization consistent with other transaction payloads when big‑int‑as‑string is enabled. No issues here.
194-212: Log marshalling correctly wraps transaction‑carrying log entriesThe updated
log.MarshalJSON:
- Embeds
ledger.LogviaAux.- Wraps
l.DataincreatedTransactionorrevertedTransactionwhenl.TypeisNewTransactionLogTypeorRevertedTransactionLogType, respectively.- Leaves other log types’
Dataunchanged.This is the right place to inject big‑int‑aware marshalling for nested transactions. Just ensure that for those log types
l.Datais always of the expected concrete type; otherwise, the type assertions would panic.
60-85: Verification confirms implementation is correct—no mutation occursThe
SubtractPostingsmethod is properly implemented as non-mutating. It creates a deep copy viaa.Copy()before performing any modifications, then returns only the copy. The receiver's underlying maps remain untouched, making the code safe for repeated marshalling andtxreuse.internal/storage/bucket/migrations/41-add-schema/up.sql (1)
1-18: I'll verify the core concern aboutALTER TYPEinside a transaction block. Let me search the codebase and check PostgreSQL constraints.Now let me check how this codebase handles migrations and whether similar patterns exist:
Based on my verification, if
ALTER TYPE ... ADD VALUEis executed inside a transaction block, the new value cannot be used until after the transaction has been committed. However, this is allowed in modern PostgreSQL — it's not blocked outright as the original review suggests by referencing older documentation.The review raises a concern about transaction wrapping based on the codebase using PostgreSQL 16-alpine, where this operation is supported inside transaction blocks. While your specific migration runner's transaction behavior matters for full certainty, the PostgreSQL version used (16) does permit this pattern within transactions.
The original concern conflates two distinct constraints: (1) PostgreSQL refusing the operation entirely (pre-9.1 behavior) versus (2) the value not being usable within the same transaction (current behavior in PostgreSQL 11+). The code as written will execute successfully on PostgreSQL 16.
To finalize this, inspect how your migration runner wraps
DO $$ … $$blocks in transactions.pkg/client/docs/sdks/v2/README.md (2)
11-13: New schema operations list looks consistentThe added
InsertSchema,GetSchema, andListSchemasentries correctly surface the new V2 schema endpoints alongside the existing ledger operations; no issues from a docs perspective.
641-642: SchemaVersion examples are coherent across operationsThe added
SchemaVersion: client.String("v1.0.0")fields in theCreateBulk,AddMetadataToAccount,CreateTransaction,AddMetadataOnTransaction, andRevertTransactionexamples are consistent with the new schema-versioning docs and naming.Also applies to: 898-899, 1211-1212, 1373-1374, 1501-1502
pkg/client/docs/models/components/v2logtype.md (1)
8-12: UPDATED_SCHEMA log type documentation looks correctAdding
V2LogTypeUpdatedSchema→UPDATED_SCHEMAaligns the docs with the new log type introduced in the logging system; nothing further needed here.pkg/client/docs/models/components/v2errorsenum.md (1)
24-27: New schema-related error enums are consistentThe
SCHEMA_ALREADY_EXISTSandSCHEMA_NOT_SPECIFIEDentries look consistent with the surrounding naming and error taxonomy, and match the new schema error cases described for the API.pkg/client/docs/models/operations/v2createtransactionrequest.md (1)
11-13: SchemaVersion field addition and Force deprecation are clearly documentedThe new
SchemaVersionfield (with examplev1.0.0) is documented consistently with the rest of the schema-versioning changes, and theForcefield is clearly marked as deprecated, which should help clients migrate.pkg/client/docs/models/components/v2bulkelementcreatetransaction.md (1)
6-11: Bulk create-transaction element SchemaVersion docs are alignedThe added
SchemaVersionfield (optional string with examplev1.0.0) and the extended table structure are consistent with other bulk element and request docs for schema validation.pkg/client/docs/models/components/v2bulkelementdeletemetadata.md (1)
6-11: Bulk delete-metadata element SchemaVersion docs are consistentThe new
SchemaVersionfield and Example column follow the same pattern as other bulk elements and correctly describe schema-based validation.internal/storage/bucket/migrations/35-create-compute-hash-function/up.sql (1)
16-21: Hash now correctly incorporatesschema_versionwhile preserving old behaviorConditionally appending
"schemaVersion"whenr.schema_versionis not null, and moving the final}to after that conditional, keeps the marshalled JSON identical for existing rows (no schema version) while ensuring the schema version participates in the hash when present.internal/api/v2/query.go (1)
11-17: SchemaVersion query parameter wiring is straightforward and safePopulating
SchemaVersionfromr.URL.Query().Get("schemaVersion")ingetCommandParameterscleanly exposes the new query parameter to downstream ledger logic, and defaults to""when omitted without changing existing behavior.internal/controller/ledger/parameters.go (1)
3-7: SchemaVersion field wiring looks good; confirm empty-string semanticsAdding
SchemaVersion stringtoParameters[INPUT]cleanly supports schema propagation from the API layer. Since it’s a plain string (not a pointer), please confirm that all callers/consumers consistently treat""as “no schema version specified” and don’t rely on distinguishing unset vs explicitly empty.pkg/client/docs/models/components/v2bulkelementaddmetadata.md (1)
6-11: Docs for SchemaVersion on V2BulkElementAddMetadata are consistentThe added
SchemaVersionfield (optional string with validation-focused description and example) matches the corresponding Go model and other schemaVersion docs in this PR. No issues from a docs/API consistency standpoint.pkg/client/docs/models/operations/v2reverttransactionrequest.md (1)
13-13: V2RevertTransactionRequest docs correctly expose SchemaVersionThe
SchemaVersionfield is documented as an optional string used for validation with examplev1.0.0, which lines up with the new query parameter onV2RevertTransactionRequest. Looks consistent with the rest of the schema-related API docs.pkg/client/docs/models/components/v2log.md (1)
12-13: V2Log documentation now correctly surfaces SchemaVersionAdding the
SchemaVersionrow as an optional string (“Schema version used for validation”, examplev1.0.0) matches theV2Logstruct and the semantics used elsewhere in the PR. No issues here.test/e2e/api_schema_test.go (1)
37-44: Good end-to-end coverage of ledger + schema lifecycle; verify DB/ledger isolationThe combination of
BeforeEachcreating the"default"ledger and the innerBeforeEachinserting the three schema versions gives nice, repeatable setup for all specs under thisWhen. This relies on theUseTemplatedDatabase/DeferTestServerharness providing effective isolation so repeatedCreateLedger("default")andInsertSchema("v1.0.0".."v3.0.0")remain idempotent at the test level (no cross-spec leakage causing “already exists” failures). Please double‑check that the test DB is reset or uniquely namespaced per spec, as done in other e2e suites, so these expectations stay stable.Also applies to: 54-81, 83-136
pkg/client/docs/models/components/v2bulkelementreverttransaction.md (1)
6-11: Bulk revert transaction docs correctly include SchemaVersionThe
SchemaVersionfield is added as an optional string with the expected “Schema version to use for validation” description and example. This matches the corresponding bulk component model and keeps bulk revert docs aligned with other bulk element types.pkg/client/models/operations/v2reverttransaction.go (1)
21-25: SchemaVersion query param and accessor follow existing client patternsThe new
SchemaVersion *stringquery parameter onV2RevertTransactionRequestand itsGetSchemaVersion()accessor are consistent with the existingDryRun/Force/AtEffectiveDatefields and getters, and with the documented API surface. Assuming this file was regenerated via the usual Speakeasy flow, the change looks correct and future‑proof.Also applies to: 73-78
pkg/client/models/components/v2bulkelementaddmetadata.go (1)
35-59: LGTM! Schema version field addition is consistent.The generated code properly adds the optional
SchemaVersionfield with appropriate JSON tags and a nil-safe accessor that follows the existing pattern in this file.internal/api/common/errors.go (2)
26-27: LGTM! Error constants follow established naming conventions.The new schema-related error constants are consistent with existing error constant patterns in this file.
50-58: LGTM! Schema error handling is properly integrated.The new error mappings correctly handle schema-related errors:
- Schema validation errors map to
BadRequestwithErrValidationcode (grouped with idempotency validation)- Missing schema specification maps to
BadRequestwithErrSchemaNotSpecifiedcode- Schema not found maps to
NotFoundThe distinction between
ErrValidation(line 52) for validation failures andErrSchemaNotSpecified(line 56) for missing parameters provides clear error categorization for API clients.pkg/client/models/components/v2bulkelementdeletemetadata.go (1)
35-59: LGTM! Consistent with other bulk element types.The generated
SchemaVersionfield and accessor follow the same pattern as other V2 bulk element structures in this PR.pkg/client/models/operations/v2addmetadatatoaccount.go (1)
22-61: LGTM! Query parameter configuration is appropriate.The generated
SchemaVersionquery parameter withformstyle andexplode=truefollows standard OpenAPI/REST conventions for optional string parameters.pkg/client/models/components/v2bulkelementreverttransaction.go (1)
51-75: LGTM! Consistent schema version integration.The
SchemaVersionfield and accessor are properly generated, following the same pattern as other V2 bulk element types.pkg/client/models/components/v2bulkelementcreatetransaction.go (1)
8-32: LGTM! Schema version field properly integrated.The generated code adds
SchemaVersionsupport to transaction creation bulk elements, maintaining consistency with the broader schema versioning feature.internal/api/v2/controllers_schema_insert_test.go (1)
17-28: LGTM! Well-structured test with proper isolation.The test uses parallel execution and a clear test case structure with all necessary parameters for comprehensive validation of the schema insertion endpoint.
internal/api/bulking/bulker.go (1)
137-142: No issues found—SchemaVersion field is properly defined in BulkElement struct.The BulkElement struct at internal/api/bulking/elements.go:26 includes the
SchemaVersionfield as a string, confirming the code correctly accesses this field across all six ledger controller operations in bulker.go.internal/chart_test.go (2)
11-245: Chart validation test table looks solidThe validation table covers a good spread of success and failure modes, including JSON round‑trip and exact error messages. No issues from my side.
247-286: Shared test chart fixture is well-structured
testChartnicely mirrors realistic account hierarchies (root, bank, users) and is reused consistently across tests. Looks good.internal/controller/ledger/log_process.go (2)
71-93: Confirm assumptions aroundNeedsSchemaandFindSchemasqueryThe schema gating flow looks correct overall (explicit lookup by
SchemaVersionand a guard that blocks schema‑needing payloads once any schema exists). Two implicit assumptions are worth checking:
payload.NeedsSchema()is called on a zero‑valueOUTPUT. This is fine ifNeedsSchemais a pure type-level flag (e.g. alwaystrueforNewTransactionpayloads and never inspects instance fields), but will be wrong if any implementation uses instance data.FindSchemasis called withInitialPaginatedQuery[any]{PageSize: 1}and all other fields left at zero values. Ensure the storage layer tolerates an emptyColumn/Orderhere and still behaves as intended.If either assumption doesn’t hold, you may want to adjust the contract of
NeedsSchemaor the query construction.
102-108: Schema version stamping and validation on logs looks correctAssigning
log.SchemaVersion = parameters.SchemaVersionand validating vialog.ValidateWithSchema(*schema)when a schema is present correctly ties each log to the version it was checked against and prevents inserting invalid payloads.pkg/client/models/components/v2log.go (1)
15-41: UPDATED_SCHEMA enum andschemaVersionfield are wired consistentlyThe new
V2LogTypeUpdatedSchemaconstant, its JSON unmarshalling, and the optionalSchemaVersionfield/accessor onV2Logall follow the existing patterns for other log types and fields. Looks good.Also applies to: 48-56, 103-109
internal/controller/ledger/controller_default_test.go (1)
138-141: Schema pagination expectations in revert/metadata tests match new behaviorThe added
FindSchemasexpectations inTestRevertTransaction,TestSaveTransactionMetadata, andTestDeleteTransactionMetadatacorrectly assert that these operations consult schema presence (and tolerate an empty schema set) when noSchemaVersionis provided. This aligns the tests with the new schema‑aware log processing.Also applies to: 198-201, 248-251
internal/controller/ledger/controller_default.go (3)
54-61: Schema log processor wiring matches existing patterns
updateSchemaLpis initialized and used consistently with the otherlogProcessor[...]fields, soUpdateSchemawill be logged and processed through the same pipeline (idempotency, hashing, events) as transactions and metadata.Also applies to: 163-170
63-82: UpdateSchema flow and duplicate handling look soundThe helper builds a
Schemavialedger.NewSchema, inserts it, and mapspostgres.ErrConstraintsFailed{}tonewErrSchemaAlreadyExists, which is the expected behavior for versioned schemas. The constructor ensures basic validity andInsertSchemamutates the pointer to populate timestamps, so returningledger.UpdatedSchema{Schema: schema}should expose a fully-populated schema.
84-89: Schema read/list methods are thin, appropriate pass‑throughs
GetSchemaandListSchemassimply delegate to the store, reusing the existing pagination abstractions, which keeps the controller surface minimal.internal/log.go (3)
88-102: SchemaVersion field and hashing remain backward compatibleThe new
SchemaVersionfield is included in the hash payload withjson:"schemaVersion,omitempty", so pre‑existing logs (with empty schemaVersion) hash identically, while new logs signed with a non‑empty version will have the version bound into their hash. That meets the requirement of extending integrity guarantees without breaking historic chains.Also applies to: 146-183
190-205: LogPayload schema hooks and CreatedTransaction validation integrate cleanlyExtending
LogPayloadwithNeedsSchema()andValidateWithSchema(schema Schema)and implementingCreatedTransaction.ValidateWithSchemaby delegating toschema.Chart.ValidatePosting(posting)provides a clear, focused contract for schema‑aware validation of postings. TheLog.ValidateWithSchemawrapper keeps the call site simple.Also applies to: 213-237
418-434: UpdatedSchema payload and hydration are consistent with events and controllerThe
UpdatedSchemapayload struct matches the events shape (singleschemafield), implements theLogPayloadcontract, and is integrated intoHydrateLogsoUpdatedSchemaLogTypelogs round‑trip correctly between storage and in‑memory representations.Also applies to: 435-457
docs/api/README.md (1)
313-369: Schema API surface and schemaVersion propagation in docs look consistentThe new endpoints for inserting, reading, and listing schemas, the optional
schemaVersionquery parameter on bulk/transaction/metadata operations, and theschemaVersionfield onV2Log/V2Bulkelements match the internalSchema/ChartOfAccountsmodel and theLog.SchemaVersionfield. The chart examples (e.g.,$userIDwith.pattern) align withChartSegment/ChartVariableSegmentsemantics, and the added error codes (SCHEMA_ALREADY_EXISTS,SCHEMA_NOT_SPECIFIED,OUTDATED_SCHEMA) mirror the controller’s schema error types.Also applies to: 375-503, 3338-3351, 3865-3887, 4364-4399, 5451-5695
internal/README.md (1)
19-69: Generated docs correctly expose new chart/schema and log APIsThe gomarkdoc output for
ValidateSegment, chart types, schema types,UpdatedSchema,Log.SchemaVersion, and the new schema‑aware helpers reflects the current code structure and line locations, which is what we want from generated internal documentation.Also applies to: 212-256, 318-323, 418-447, 579-621, 711-737, 798-824, 1007-1111, 1524-1555, 1787-1823
internal/chart.go (4)
10-42: Segment naming and root invariants are well‑defined
ChartSegmentRegexpandValidateSegmentenforce a tight segment charset, andChartOfAccounts.UnmarshalJSONcorrectly forbids variable ($...) and property (.self,.metadata, etc.) keys at the root level, which keeps the chart structure predictable and avoids ambiguous top‑level accounts.
70-164: ChartSegment.UnmarshalJSON correctly models accounts, fixed/variable segments, and patternsThe parser:
- Validates subsegment keys,
- Extracts and validates
.patternas a regex,- Enforces that only
$...segments may carry patterns and that variable segments must have one,- Prevents multiple variable segments at the same level,
- Treats leaves (no subsegments) or segments with
.selfas accounts and attaches.metadata/.rulesaccordingly.This yields a clean, unambiguous in‑memory model for both fixed and variable account paths.
178-222: Marshalling logic is symmetric with unmarshalling
marshalJsonObject,ChartSegment.MarshalJSON, andChartVariableSegment.MarshalJSONrebuild the JSON structure with:
- Fixed children and a single
$labelvariable child,- Optional
.metadataand.rulesfor accounts,.selfonly when a segment is both an account and has children,.patternattached only to variable segments.This matches the expectations enforced in
UnmarshalJSON, so round‑tripping a chart preserves structure.
252-273: ValidatePosting uses the chart consistently for source and destination
ChartOfAccounts.ValidatePostingsimply delegates toFindAccountSchemafor bothposting.Sourceandposting.Destination, letting the chart definition (plus theworldspecial‑case) drive validity. That keeps schema enforcement centralized and makes it easy to evolve account rules later.
|
|
||
| // stateless version (+1 regarding directory name, as migrations start from 1 in the lib) | ||
| const MinimalSchemaVersion = 26 | ||
| const MinimalSchemaVersion = 42 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify migration files exist and document this breaking change.
The minimal schema version jumped from 26 to 42, requiring 16 new migrations. This is a breaking change—any database below version 42 will fail the HasMinimalVersion check. Ensure all migration files 27-42 exist, are properly tested, and that upgrade documentation is provided for existing deployments.
Run the following script to verify the migration files and documentation:
🏁 Script executed:
#!/bin/bash
# Description: Verify all migration files 27-42 exist and check for upgrade documentation
echo "=== Checking for migration files 27-42 ==="
# Based on the comment, migrations are in directories. Find the migrations directory.
MIGRATIONS_DIR=$(fd -t d -d 3 'migrations' | head -1)
if [ -z "$MIGRATIONS_DIR" ]; then
echo "ERROR: Could not find migrations directory"
exit 1
fi
echo "Found migrations directory: $MIGRATIONS_DIR"
echo ""
# Check for migration directories/files from 27 to 42
MISSING_MIGRATIONS=""
for i in {27..42}; do
# Try to find directory or file matching the version
if ! fd -d 1 "^${i}$" "$MIGRATIONS_DIR" | grep -q .; then
MISSING_MIGRATIONS="$MISSING_MIGRATIONS $i"
fi
done
if [ -n "$MISSING_MIGRATIONS" ]; then
echo "ERROR: Missing migration versions:$MISSING_MIGRATIONS"
else
echo "SUCCESS: All migration versions 27-42 found"
fi
echo ""
echo "=== Migration files structure ==="
fd -d 1 . "$MIGRATIONS_DIR" | sort -V | tail -20
echo ""
echo "=== Checking for upgrade/migration documentation ==="
# Look for documentation mentioning version 42 or migration guides
rg -l "version.*42|schema.*version|migration.*guide|upgrade.*guide" --type=md -g '!**/node_modules/**' | head -10
echo ""
echo "=== Checking for references to old MinimalSchemaVersion ==="
# Check if there are any lingering references to version 26
rg -n "MinimalSchemaVersion.*26" --type=goLength of output: 529
I need to find the actual production migrations directory and verify the migration structure. The test directory doesn't contain the production migration files.
---
🏁 Script executed:
```shell
#!/bin/bash
# Find all directories and files related to migrations in the codebase
echo "=== Finding all migration-related directories ==="
fd -t d 'migrations' -H
echo ""
echo "=== Finding all migration-related files ==="
fd -t f 'migration' -H
echo ""
echo "=== Checking internal/storage directory structure ==="
fd -t d . internal/storage -d 3
echo ""
echo "=== Looking for SQL migration files or version-numbered files ==="
fd -e 'sql|go' 'internal/storage' | head -20
echo ""
echo "=== Checking for any files/directories with version numbers (like 001, 026, etc) ==="
fd -P '^\d+' . internal/storage
echo ""
echo "=== Searching for migration or schema references ==="
rg -n "migrate|schema" internal/storage/bucket/default_bucket.go -C 3
Length of output: 4270
Good! I found the production migrations directory. Migrations 27-41 all exist, but migration 42 is missing. Let me verify the migration loading logic and understand the versioning scheme:
---
🏁 Script executed:
```shell
#!/bin/bash
echo "=== Listing all migration directories in detail ==="
ls -1 internal/storage/bucket/migrations/ | grep -E '^\d+' | sort -V
echo ""
echo "=== Checking migrations.go to understand version loading ==="
cat internal/storage/bucket/migrations.go | head -100
echo ""
echo "=== Checking if migration 42 exists with different naming pattern ==="
find internal/storage/bucket/migrations -name "*42*" -o -name "*schema*" | sort
echo ""
echo "=== Checking the highest numbered migration ==="
ls -1d internal/storage/bucket/migrations/*/ | tail -5
echo ""
echo "=== Looking at default_bucket.go around MinimalSchemaVersion ==="
rg -A 5 -B 5 "MinimalSchemaVersion" internal/storage/bucket/default_bucket.go
Length of output: 3613
Critical issue identified: MinimalSchemaVersion is set to 42, but migration 42 doesn't exist. The highest migration is 41-add-schema. Let me verify if migration 42 is uncommitted or if this is genuinely missing:
---
🏁 Script executed:
```shell
#!/bin/bash
echo "=== Git status for migrations directory ==="
git status internal/storage/bucket/migrations/
echo ""
echo "=== Check for any migration 42 files ==="
find internal/storage/bucket/migrations -type f -o -type d | grep 42
echo ""
echo "=== List all migration directories (complete listing) ==="
ls -1d internal/storage/bucket/migrations/*/ 2>/dev/null | wc -l
ls -1d internal/storage/bucket/migrations/*/ 2>/dev/null | tail -10
echo ""
echo "=== Verify PR changes affecting migrations ==="
git diff --name-only | grep -i migration
echo ""
echo "=== Check if migration 42 is in any branch/stash ==="
git log --all --oneline -- '*migrations/42*' 2>/dev/null | head -5
Length of output: 997
Migration 42 does not exist—this is a critical bug that will break all deployments.
The MinimalSchemaVersion was changed to 42, but only migrations 0-41 exist (42 total directories). No database can reach version 42 if the migration doesn't exist, causing HasMinimalVersion to always return false. The PR makes no changes to migration files.
You must either:
- Create and add migration 42 to
internal/storage/bucket/migrations/, OR - Revert
MinimalSchemaVersionfrom 42 to 41
🤖 Prompt for AI Agents
internal/storage/bucket/default_bucket.go around line 18: MinimalSchemaVersion
was changed to 42 but no migration 42 exists, causing HasMinimalVersion to
always fail; either revert this constant to 41 or add a proper migration #42 in
internal/storage/bucket/migrations/. To fix, choose one: (A) revert the constant
back to 41 so the code matches existing migrations (quick fix), or (B) add a new
migration directory named 42 with the required up/down SQL/Go migration files
following the project's migration naming/format conventions and update any
migration registry so the migration runner recognizes version 42. Ensure tests
and migration runner validate the new version before merging.
db8de5e to
3476baf
Compare
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/api/common/errors.go (1)
17-36: Add HTTP mapping forErrSchemaAlreadyExiststo avoid 500s on conflictYou define
ErrSchemaAlreadyExists = "SCHEMA_ALREADY_EXISTS"butHandleCommonWriteErrorsnever maps the corresponding controller error (e.g.ledgercontroller.ErrSchemaAlreadyExists{}), so a schema-insert conflict will currently fall through toHandleCommonErrorsand return a 500 instead of a client error.Consider adding an explicit case, analogous to other “already exists” errors:
func HandleCommonWriteErrors(w http.ResponseWriter, r *http.Request, err error) { switch { case errors.Is(err, ledgercontroller.ErrIdempotencyKeyConflict{}): api.WriteErrorResponse(w, http.StatusConflict, ErrConflict, err) case errors.Is(err, ledgercontroller.ErrInvalidIdempotencyInput{}) || errors.Is(err, ledgercontroller.ErrSchemaValidationError{}): api.BadRequest(w, ErrValidation, err) + case errors.Is(err, ledgercontroller.ErrSchemaAlreadyExists{}): + api.WriteErrorResponse(w, http.StatusConflict, ErrSchemaAlreadyExists, err) case errors.Is(err, ledgercontroller.ErrNotFound): api.NotFound(w, err) case errors.Is(err, ledgercontroller.ErrSchemaNotSpecified{}): api.BadRequest(w, ErrSchemaNotSpecified, err) case errors.Is(err, ledgercontroller.ErrSchemaNotFound{}): api.NotFound(w, err) default: HandleCommonErrors(w, r, err) } }Adjust the status code if you prefer
400over409, but mapping this error explicitly will keep the API behavior consistent with other “already exists” scenarios.Also applies to: 47-63
♻️ Duplicate comments (1)
internal/controller/ledger/controller_with_traces.go (1)
474-495: Fix span name to match method name.The span name at line 482 is
"UpdatedSchema"but should be"UpdateSchema"to match the method name. This inconsistency affects trace readability and filtering.Apply this diff:
_, err = tracing.TraceWithMetric( ctx, - "UpdatedSchema", + "UpdateSchema", c.tracer, c.updateSchemaHistogram, func(ctx context.Context) (any, error) {Note: This issue was previously flagged but appears to still be present in the current code.
🧹 Nitpick comments (6)
docs/api/README.md (1)
4553-4554: New error codes properly documented. SCHEMA_ALREADY_EXISTS and SCHEMA_NOT_SPECIFIED are added to V2ErrorsEnum with appropriate placement. Descriptions would improve usability—consider adding brief notes on when each error occurs (e.g., "SCHEMA_ALREADY_EXISTS: returned when attempting to insert a schema with an existing version").internal/chart.go (1)
38-42: Chart parsing and account resolution look solid; only minor optimizations possibleThe JSON (un)marshalling logic for
ChartOfAccounts,ChartSegment, andChartVariableSegmentenforces the documented invariants (no variable root, single variable segment per prefix,.selfshape, pattern validation) and the resolution path infindAccountSchema/FindAccountSchemabehaves as expected, including the special handling of"world". Error propagation viaErrInvalidAccountis clear and preserves context.If you ever profile this, you might consider (optional) improvements:
- Cache a compiled
*regexp.RegexponChartVariableSegmentinstead of callingregexp.Matchon the pattern string for every lookup.- Rename the inner
segmentmaps inUnmarshalJSONto avoid shadowing the outersegmentvariable and slightly improve readability.These are purely optional; the current implementation is correct and maintainable.
Also applies to: 70-164, 178-205, 224-273
internal/controller/ledger/log_process.go (1)
87-95: Use ofFindSchemaswithInitialPaginatedQueryis reasonable but implicitThe existence check:
schemas, err := store.FindSchemas(ctx, storagecommon.InitialPaginatedQuery[any]{PageSize: 1}) if err != nil { return nil, nil, err } if len(schemas.Data) > 0 { return nil, nil, newErrSchemaNotSpecified() }is a neat way to cheaply test “does this ledger have any schemas?”. It does, however, rely on
FindSchemastreating an emptyColumn/OrderinInitialPaginatedQueryas “use defaults” rather than returning a pagination error.Consider either:
- Explicitly setting a default sort column here (e.g.,
"created_at"), or- Adding a brief comment where
FindSchemasis implemented to guarantee that emptyColumnis supported.This makes the dependency on store behavior clearer.
internal/api/v2/controllers_schema_list.go (1)
17-33: Consider constrainingsortto allowed values at the API levelYour concern is valid. Currently, any
sortvalue is accepted at the API level and only validated downstream. While the store-level validation viaErrNotPaginatedFieldensures safety, early validation at the API level would improve clarity and fail-fast semantics.The schema resource defines only two paginated fields:
"version"and"created_at"(internal/storage/ledger/resource_schemas.go:19-20). Mapping thesortquery parameter to this explicit set before callinggetPaginatedQuerywould make the contract self-documenting and prevent unexpected values from reaching the database layer.internal/log.go (2)
20-26: UpdatedSchema log type wiring looks consistent; align the comment stringThe new
UpdatedSchemaLogTypeis correctly mapped to"UPDATED_SCHEMA"inString()andLogTypeFromString(), so behavior is coherent. Minor nit: the comment on Line 25 still says"UPDATE_CHART_OF_ACCOUNT", which no longer matches the actual string and could confuse readers or tooling that rely on these comments for documentation.Consider updating the comment to
"UPDATED_SCHEMA"(or dropping it) for consistency.Also applies to: 54-69, 71-85
273-279: Clarify intentions for no-op schema validations on metadata/revert log typesFor
SavedMetadata,DeletedMetadata, andRevertedTransaction,NeedsSchema()returnstruebutValidateWithSchemais a no‑op returningnil. This is valid, but slightly surprising:
- If the intent is only to require the presence of a schema (e.g., to attach a
SchemaVersionto these logs) without enforcing any specific rules yet, a short comment on each method would make this explicit.- If these log types should not depend on a schema at all, consider flipping
NeedsSchema()tofalseso log processing doesn’t unnecessarily fetch/expect a schema.Either approach is fine; documenting the current behavior will prevent confusion for future maintainers.
Also applies to: 325-331, 376-383
📜 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.
⛔ Files ignored due to path filters (1)
docs/events/UpdatedSchema.jsonis excluded by!**/*.json
📒 Files selected for processing (39)
cmd/docs_events.go(1 hunks)docs/api/README.md(22 hunks)internal/README.md(37 hunks)internal/api/bulking/bulker.go(6 hunks)internal/api/bulking/elements.go(1 hunks)internal/api/bulking/mocks_ledger_controller_test.go(3 hunks)internal/api/common/errors.go(2 hunks)internal/api/common/mocks_ledger_controller_test.go(3 hunks)internal/api/v1/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/api/v2/controllers_schema_list.go(1 hunks)internal/api/v2/controllers_schema_list_test.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/controllers_schema_read_test.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/query.go(1 hunks)internal/api/v2/routes.go(1 hunks)internal/api/v2/views.go(1 hunks)internal/bus/listener.go(1 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)internal/controller/ledger/controller.go(2 hunks)internal/controller/ledger/controller_default.go(3 hunks)internal/controller/ledger/controller_default_test.go(7 hunks)internal/controller/ledger/controller_generated_test.go(3 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(3 hunks)internal/controller/ledger/errors.go(1 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/log_process.go(2 hunks)internal/controller/ledger/log_process_test.go(4 hunks)internal/controller/ledger/parameters.go(1 hunks)internal/controller/ledger/store.go(2 hunks)internal/controller/ledger/store_generated_test.go(3 hunks)internal/errors.go(1 hunks)internal/log.go(13 hunks)
🚧 Files skipped from review as they are similar to previous changes (14)
- internal/bus/listener.go
- internal/controller/ledger/parameters.go
- internal/chart_test.go
- internal/api/bulking/bulker.go
- internal/controller/ledger/controller_with_too_many_client_handling.go
- internal/api/bulking/mocks_ledger_controller_test.go
- internal/errors.go
- internal/api/v2/controllers_schema_insert.go
- internal/controller/ledger/store.go
- internal/api/v2/controllers_schema_insert_test.go
- internal/api/v2/controllers_schema_list_test.go
- internal/api/bulking/elements.go
- internal/controller/ledger/listener_generated_test.go
- internal/api/v2/controllers_schema_read_test.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/controller_default.gointernal/README.md
🧬 Code graph analysis (21)
internal/controller/ledger/log_process_test.go (1)
internal/schema.go (1)
Schema(15-21)
internal/api/common/errors.go (1)
internal/controller/ledger/errors.go (5)
ErrInvalidIdempotencyInput(221-225)ErrSchemaValidationError(268-271)ErrNotFound(14-14)ErrSchemaNotSpecified(289-289)ErrSchemaNotFound(249-251)
internal/controller/ledger/errors.go (1)
internal/api/common/errors.go (2)
ErrSchemaNotSpecified(28-28)ErrSchemaAlreadyExists(27-27)
internal/controller/ledger/controller_generated_test.go (5)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
UpdatedSchema(418-420)
internal/api/v2/views.go (1)
internal/schema.go (1)
Schema(15-21)
internal/controller/ledger/log_process.go (4)
internal/schema.go (1)
Schema(15-21)internal/controller/ledger/errors.go (1)
ErrNotFound(14-14)internal/storage/common/pagination.go (1)
InitialPaginatedQuery(10-15)internal/log.go (2)
NewLog(194-199)ComputeIdempotencyHash(459-468)
internal/controller/ledger/store_generated_test.go (2)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/api/v2/mocks_ledger_controller_test.go (5)
internal/api/bulking/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (2)
Log(89-102)UpdatedSchema(418-420)
internal/controller/ledger/controller.go (5)
internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (2)
Log(89-102)UpdatedSchema(418-420)pkg/events/message.go (1)
UpdatedSchema(77-80)internal/schema.go (2)
Schema(15-21)SchemaData(11-13)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/api/v1/mocks_ledger_controller_test.go (9)
internal/api/bulking/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/common/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v2/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (2)
Log(89-102)UpdatedSchema(418-420)pkg/events/message.go (1)
UpdatedSchema(77-80)
internal/log.go (3)
internal/storage/ledger/logs.go (1)
Log(20-26)internal/schema.go (1)
Schema(15-21)pkg/events/message.go (4)
SavedMetadata(27-32)DeletedMetadata(60-65)RevertedTransaction(44-48)UpdatedSchema(77-80)
internal/api/common/mocks_ledger_controller_test.go (5)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
UpdatedSchema(418-420)
internal/controller/ledger/controller_default.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/log.go (2)
UpdatedSchema(418-420)Log(89-102)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/controller/ledger/store.go (1)
Store(29-66)internal/schema.go (2)
NewSchema(23-33)Schema(15-21)
internal/chart.go (1)
internal/errors.go (1)
ErrInvalidAccount(102-105)
internal/controller/ledger/listener.go (3)
internal/log.go (1)
UpdatedSchema(418-420)pkg/events/message.go (1)
UpdatedSchema(77-80)internal/schema.go (1)
Schema(15-21)
cmd/docs_events.go (1)
internal/log.go (1)
UpdatedSchema(418-420)
internal/api/v2/controllers_schema_read.go (1)
internal/api/common/errors.go (1)
HandleCommonErrors(38-45)
internal/controller/ledger/controller_with_traces.go (6)
internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
UpdatedSchema(418-420)internal/tracing/tracing.go (1)
TraceWithMetric(101-129)internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/controller/ledger/controller_default_test.go (4)
internal/schema.go (2)
Schema(15-21)SchemaData(11-13)internal/chart.go (2)
ChartOfAccounts(36-36)ChartAccount(12-15)internal/controller/ledger/controller.go (2)
CreateTransaction(102-106)RunScript(91-91)internal/machine/vm/run.go (1)
RunScript(14-19)
internal/controller/ledger/controller_with_events.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(135-138)Controller(21-89)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (2)
Log(89-102)UpdatedSchema(418-420)pkg/events/message.go (1)
UpdatedSchema(77-80)internal/schema.go (1)
Schema(15-21)
internal/api/v2/controllers_schema_list.go (4)
internal/api/common/pagination.go (1)
PaginationConfig(3-6)pkg/client/models/operations/v2listschemas.go (3)
Order(37-37)OrderDesc(41-41)OrderAsc(40-40)internal/api/common/errors.go (2)
ErrValidation(20-20)HandleCommonPaginationErrors(65-74)internal/schema.go (1)
Schema(15-21)
🪛 markdownlint-cli2 (0.18.1)
docs/api/README.md
357-357: Blank line inside blockquote
(MD028, no-blanks-blockquote)
403-403: Blank line inside blockquote
(MD028, no-blanks-blockquote)
461-461: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
470-470: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5624-5624: Multiple headings with the same content
(MD024, no-duplicate-heading)
5626-5626: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5648-5648: Multiple headings with the same content
(MD024, no-duplicate-heading)
5679-5679: Multiple headings with the same content
(MD024, no-duplicate-heading)
5707-5707: Multiple headings with the same content
(MD024, no-duplicate-heading)
5739-5739: Multiple headings with the same content
(MD024, no-duplicate-heading)
5773-5773: Multiple headings with the same content
(MD024, no-duplicate-heading)
5812-5812: Multiple headings with the same content
(MD024, no-duplicate-heading)
5849-5849: Multiple headings with the same content
(MD024, no-duplicate-heading)
⏰ 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: Tests
🔇 Additional comments (29)
cmd/docs_events.go (1)
39-39: LGTM! UpdatedSchema event type added to documentation generation.The addition follows the established pattern and correctly integrates the new UpdatedSchema event type into the JSON schema documentation workflow.
docs/api/README.md (6)
318-509: New schema management endpoints are well-documented. The three endpoints (Insert, Get, List) include proper parameter descriptions, example requests/responses, and authentication scopes. Documentation structure is consistent with existing endpoints.
690-690: schemaVersion parameter consistently documented across endpoints. The parameter is added to bulk operations, transaction creation, metadata operations, and revert operations with clear descriptions indicating it's optional for validation. Good consistency in parameter placement and documentation.Also applies to: 734-734, 1139-1139, 1566-1566, 1842-1842, 1907-1907
3506-3507: schemaVersion field properly integrated into V2Log schema. The field is marked as optional (falsein Required column at line 4043) with clear description "Schema version used for validation". New enum valueUPDATED_SCHEMAat line 4053 is properly documented. Documentation is consistent with parameter additions elsewhere.Also applies to: 4028-4029, 4043-4043, 4053-4053
4667-4667: schemaVersion added consistently across all bulk operation element types. Present in V2BaseBulkElement (base structure) and propagated to all derived types (CreateTransaction, AddMetadata, RevertTransaction, DeleteMetadata). Marked as optional with description "Schema version to use for validation". Bulk request endpoint parameter documentation at line 734 explains the interaction between endpoint-level and element-level schemaVersion.Also applies to: 4720-4732, 4745-4745, 4818-4818, 4931-4931, 4974-4974, 5017-5017
461-461: The review comment is incorrect. No markdown linting violation exists.The heading structure shows a proper hierarchy: line 451 contains an h3 heading ("Parameters"), and line 461 contains an h4 heading ("Enumerated Values"). This represents a valid increment of one level (h3 → h4), which is compliant with the MD001 rule. The h4 serves as a logical subsection under the h3 "Parameters" section, making the structure both syntactically correct and semantically appropriate. No changes are needed.
Likely an incorrect or invalid review comment.
5624-5626: Rewritten Review Comment — Review Claim Is IncorrectThe cited lines (5648, 5679, 5707, 5739, 5773, 5812, 5849) do not exhibit the issue described. These lines correctly show "### Properties" headings followed by proper documentation tables with fields like Name, Type, Required, Restrictions, and Description.
Only line 5624-5626 matches the pattern flagged—it contains "### Properties" followed by "None" without a properties table. If this single instance requires attention, address it at that location only; the other cited lines have appropriate structure and do not need changes.
Likely an incorrect or invalid review comment.
internal/controller/ledger/store_generated_test.go (1)
15-24: Schema-related mocks are consistent with the Store interfaceThe added
FindSchema,FindSchemas, andInsertSchemamocks (and recorders) follow the existing gomock patterns and match the expected signatures for schema persistence and querying. No issues from a correctness or testing perspective.Also applies to: 152-174, 226-238
internal/README.md (1)
19-40: Generated docs accurately reflect the expanded chart/schema/log surfaceThe gomarkdoc output for the new chart types, schema types, log
SchemaVersionfield, and related methods (e.g.,ValidateSegment,FindAccountSchema,UpdatedSchema) is coherent and aligns with the corresponding code snippets ininternal/chart.go,internal/schema.go, andinternal/log.go. No actionable issues here.Also applies to: 418-527, 1008-1159, 1524-1557, 1788-1821
internal/api/v2/query.go (1)
11-17: SchemaVersion wiring into command parameters is straightforwardThreading
schemaVersionfrom the query string intoledger.Parameters[INPUT].SchemaVersionis simple and side-effect free; downstream logic can decide how to treat the empty-string case. No issues here.internal/api/v2/views.go (1)
146-148: renderSchema helper is consistent with existing view helpersReturning the
ledger.Schemaas-is (ignoringHeaderBigInt-As-String, which is irrelevant for this type) matches the pattern of other render helpers and keeps the API surface uniform. Looks good.internal/api/v2/routes.go (1)
62-88: Schema endpoints are wired correctly under the ledger scopeThe new
/schemaroutes (POST/GET by{version}and paginated GET) are mounted inside the ledger-scoped group withLedgerMiddlewareand reuse the existing pagination config. This matches the patterns of other v2 resources (accounts, transactions, logs) and looks correct.internal/controller/ledger/log_process_test.go (1)
12-19: Tests correctly stub the newFindSchemascall in log processingThe added
bunpaginateimport andFindSchemasexpectations in bothTestForgeLogWithIKConflictandTestForgeLogWithDeadlockalign with the updated Store interface and ensureforgeLog’s new schema lookup path is satisfied without affecting the tests’ primary focus (idempotency conflict and deadlock retry). Looks good.Also applies to: 36-39, 71-74, 84-87
internal/api/v2/controllers_schema_read.go (1)
14-29: readSchema handler looks consistent and correct
- Correctly pulls ledger from context, reads
versionfrom path, and delegates toGetSchema.- 404 mapping via
postgres.IsNotFoundErrorand fallback toHandleCommonErrorsalign with existing patterns.internal/controller/ledger/controller_with_events.go (1)
156-168: UpdateSchema event wrapper matches existing patternsThe wrapper cleanly delegates to
Controller.UpdateSchema, and, on success, emitsUpdatedSchemaevents only whenDryRunis false, mirroring the transaction/metadata handlers’ behavior.internal/controller/ledger/listener.go (1)
12-18: Listener interface extension is coherentAdding
UpdatedSchema(ctx, ledger string, data ledger.Schema)is consistent with the other methods (ledger-first, payload second) and matches howControllerWithEvents.UpdateSchemauses the listener.internal/controller/ledger/controller.go (2)
83-88: Schema controller API surface is well-shaped
UpdateSchema,GetSchema, andListSchemasfollow existing controller conventions (genericParameters, paginated queries, bunpaginate cursors) and provide a clear schema management API.
135-138: UpdateSchema input struct matches internal schema modelUsing
Version stringplusData ledger.SchemaDatamirrors theledger.Schemamodel and keeps the API payload tight around what’s actually needed for an update.internal/api/v2/controllers_schema_list.go (1)
39-47: ListSchemas retrieval and rendering path looks goodUsing
ListSchemaswith the constructed paginated query and piping throughbunpaginate.MapCursor+renderSchemamatches existing list endpoints and keeps the response shape consistent.internal/controller/ledger/log_process.go (2)
101-110: Schema version tagging and validation in logs is correctly sequenced
log.SchemaVersionis set fromparameters.SchemaVersion, preserving which schema governed the operation.ValidateWithSchemais run beforeInsertLog, and any schema validation error is wrapped asnewErrSchemaValidationError, so invalid payloads never get persisted.This ordering keeps logs consistent with their governing schema.
73-95: Assumption is correct; current implementation is valid but document the contractThe review concern is valid from a maintainability perspective. The script output confirms all 5
NeedsSchema()implementations acrossCreatedTransaction,SavedMetadata,DeletedMetadata,RevertedTransaction, andUpdatedSchemareturn hardcoded boolean values with no field inspection—making them purely type-driven as assumed.Calling
NeedsSchema()on a zero-valuedOUTPUTis therefore safe and correct. However, to prevent future breakage, document this contract: either add a comment to theLogPayloadinterface explaining thatNeedsSchema()is type-driven and must not inspect fields, or add a comment inlog_process.goexplaining why the zero-value call is valid.The suggested refactoring (calling on the real payload after
fn()) is unnecessary—the type-only check is the cleaner design.internal/controller/ledger/controller_default_test.go (1)
25-119: LGTM! Well-structured parameterized test.The refactoring to parameterize
testCreateTransactionwithwithSchemaprovides good coverage for both schema-present and schema-absent scenarios. The conditional store expectations and schema setup are correctly implemented.internal/controller/ledger/controller_default.go (2)
57-84: LGTM! Schema operations properly implemented.The new schema operations follow the established patterns:
UpdateSchemauses theforgeLogpattern consistently with other operations- Error handling properly detects duplicate schemas via constraint violations
GetSchemaandListSchemasappropriately delegate to the store layer- The
updateSchemahelper correctly handlesNewSchema's error return
277-282: LGTM! Import handling for schemas added correctly.The
UpdatedSchemacase properly inserts the schema during log import, consistent with how other log payloads are handled.internal/controller/ledger/errors.go (1)
249-320: LGTM! Well-implemented error types.The four new schema-related error types follow the established patterns in this file:
- Clear, informative error messages
- Type-safe
Is()methods for error checking- Consistent naming and structure
- Appropriate field capturing for context
internal/log.go (4)
92-102: SchemaVersion field and hashing: semantics look good, but depend on call orderAdding
SchemaVersiontoLogand including it inComputeHashis a solid way to bind idempotency to the schema version. Usingjson:"schemaVersion,omitempty"in both the struct and the hash struct maintains backward compatibility: logs without a schema version will hash as before, while logs with a version will produce distinct hashes.One thing to double‑check:
SchemaVersionmust be set on theLogbeforeComputeHash(e.g., beforeChainLog) is called; otherwise, changingSchemaVersionlater will not affect the hash and could undermine idempotency guarantees when re‑processing under a different schema.Also applies to: 161-170, 171-178
190-192: Log.ValidateWithSchema delegation and LogPayload interface extension look coherentExtending
LogPayloadwithNeedsSchemaandValidateWithSchemaand delegating viaLog.ValidateWithSchemais a clean design; it keeps schema‑specific logic in the payload types while giving callers a uniform entry point.As long as all concrete payload types in the
ledgerpackage implement the new methods (which this file appears to cover), this change is safe and improves flexibility.Also applies to: 201-205
218-230: CreatedTransaction schema validation is straightforward and correct
CreatedTransaction.ValidateWithSchemaiterates postings and delegates toschema.Chart.ValidatePosting, returning on the first error. Combined withNeedsSchema() == true, this is a sensible way to enforce chart‑of‑accounts rules on new transactions.No issues from a correctness standpoint.
418-434: UpdatedSchema payload wiring matches the schema model and events
UpdatedSchemaembedding aSchemaunderjson:"schema"aligns with the event payload shape inpkg/events/message.goand theSchemadefinition ininternal/schema.go. HavingNeedsSchema() == falseand a no‑opValidateWithSchemais appropriate, since this log type defines the schema rather than relying on an existing one.The
LogPayloadassertion ensures compile‑time safety. No changes needed.
internal/log.go
Outdated
| case UpdatedSchemaLogType: | ||
| payload = &UpdatedSchema{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Fix HydrateLog unmarshalling: avoid pointer-to-interface (currently unsafe)
HydrateLog currently does:
var payload any
// payload = &CreatedTransaction{} / &SavedMetadata{} / ...
err := json.Unmarshal(data, &payload)
return reflect.ValueOf(payload).Elem().Interface().(LogPayload), nilPassing &payload (a *interface{}) to json.Unmarshal causes it to treat the destination as an interface sink and replace the value with generic types (e.g., map[string]any), not populate the concrete struct pointer you set. This makes the subsequent .Elem() unsafe and will typically either panic or yield the wrong type.
The standard pattern is to store a pointer to the concrete type in the interface and pass that directly to json.Unmarshal. Here, that means:
- err := json.Unmarshal(data, &payload)
+ err := json.Unmarshal(data, payload)This allows encoding/json to unmarshal into the concrete *CreatedTransaction, *SavedMetadata, etc., and keeps the final reflect.ValueOf(payload).Elem().Interface().(LogPayload) working as intended.
Also applies to: 451-456
---
🏁 Script executed:
```shell
#!/bin/bash
# Examine HydrateLog function in internal/log.go around lines 446-456
fd -e go internal/log.go --exec cat {} | head -500 | tail -100
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Search for HydrateLog function definition with context
rg -nP -C15 'func.*HydrateLog' internal/log.goLength of output: 849
🏁 Script executed:
#!/bin/bash
# Look for json.Unmarshal usage in the HydrateLog context
rg -nP -C10 'json\.Unmarshal.*payload' internal/log.goLength of output: 620
🏁 Script executed:
#!/bin/bash
# Check the payload assignment pattern around lines 446-456
sed -n '430,470p' internal/log.goLength of output: 921
---
🌐 Web query:
```web
Go json.Unmarshal behavior pointer to interface vs interface type
💡 Result:
Short answer
- json.Unmarshal expects its second argument v to be a pointer to something it can set. If you pass *interface{} (a pointer to an interface value), Unmarshal will set that interface to a newly-chosen concrete Go value (map[string]interface{}, []interface{}, float64, string, bool, or nil for JSON null). [1]
- If you pass an interface{} that already contains a non-nil concrete pointer (for example var i interface{} = &MyStruct{} and you call json.Unmarshal(data, i)), the json package will unmarshal into the concrete value pointed-to by that pointer (it can reuse the pointer inside the interface). If the interface holds nil (no concrete value) Unmarshal will store a new concrete value (map/slice/number/etc) into the interface. [1][2]
Examples
-
var x interface{}; json.Unmarshal(data, &x)
-> x becomes map[string]interface{} / []interface{} / float64 / string / bool / nil depending on JSON. [1] -
var p *interface{}; json.Unmarshal(data, p)
-> only valid if p points to an interface value; Unmarshal sets the interface value (same rules as above). Passing a nil *interface{} (uninitialized pointer) is an error/invalid. [1][5] -
var s MyStruct; var i interface{} = &s; json.Unmarshal(data, i)
-> Unmarshal decodes into s (via the &s pointer wrapped in the interface). This is how you direct json to populate a concrete value while using an interface-typed variable. [2]
Why this matters
- Avoid "pointer to interface" unless you specifically want Unmarshal to replace the interface's concrete value with a newly allocated map/slice/number/etc. More commonly you either pass &someConcreteValue or pass &i where i is an interface{} and you want the library to choose the concrete type. [1][2]
Sources
- encoding/json Unmarshal docs. [1]
- Explanation and examples (Stack Overflow). [2]
Fix HydrateLog unmarshalling: pointer-to-interface causes json.Unmarshal to replace concrete struct pointers with generic types
The issue is confirmed. Passing &payload (pointer-to-interface{}) to json.Unmarshal causes it to discard the concrete struct pointers (&CreatedTransaction{}, &SavedMetadata{}, etc.) and replace the interface value with a newly-chosen generic type like map[string]interface{}. This breaks the subsequent .Elem().Interface().(LogPayload) operation.
The fix is to pass payload directly instead:
- err := json.Unmarshal(data, &payload)
+ err := json.Unmarshal(data, payload)This allows json.Unmarshal to unmarshal into the concrete struct pointers already stored in the interface value.
Lines affected: 451 (and same pattern applies to 451-456 as noted)
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
internal/log.go around lines 446-456: the HydrateLog unmarshalling currently
passes a pointer-to-interface (e.g. &payload) into json.Unmarshal which causes
the concrete struct pointers stored in the interface to be replaced by generic
maps; instead call json.Unmarshal with the interface value itself (payload) so
that json.Unmarshal fills the existing concrete struct pointer (e.g.
&CreatedTransaction{}, &SavedMetadata{}) and preserves the concrete type, then
continue using payload.(LogPayload) as before; update all occurrences in this
block (lines ~451-456) to pass payload (not &payload) to json.Unmarshal.
3476baf to
4ed3f6c
Compare
There was a problem hiding this 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
♻️ Duplicate comments (5)
pkg/client/docs/sdks/v2/README.md (1)
246-256: Replace hard tabs with spaces in new Go examplesThe new InsertSchema/GetSchema/ListSchemas Go snippets still use hard tabs in the
importblocks, which markdownlint flags (MD010) and previous reviews already called out for similar sections.Recommend normalizing these blocks to spaces-only indentation, e.g.:
-import( - "context" - "os" +import( + "context" + "os"Apply the same pattern to the other new examples so they’re consistent with the rest of the doc and pass markdownlint.
Also applies to: 320-327, 380-387
internal/log.go (1)
435-457: FixHydrateLogunmarshalling: pointer-to-interface is still unsafeThe underlying issue previously flagged remains:
json.Unmarshal(data, &payload)is passing a*interface{}.encoding/jsonwill treat this as a generic interface sink, replace the value with new generic map/slice types, and ignore the concrete struct pointers (&CreatedTransaction{},&SavedMetadata{},&UpdatedSchema{}, etc.) you set in the switch. This makesreflect.ValueOf(payload).Elem()unsafe and can panic at runtime.You already have the correct pattern in place:
payloadholds a pointer to the concrete payload type. The fix is to unmarshal into that pointer directly:func HydrateLog(_type LogType, data []byte) (LogPayload, error) { @@ - err := json.Unmarshal(data, &payload) + err := json.Unmarshal(data, payload) if err != nil { return nil, err } return reflect.ValueOf(payload).Elem().Interface().(LogPayload), nil }This preserves the existing behaviour of returning the concrete value (not the pointer) while ensuring
json.Unmarshalpopulates the right struct.internal/controller/ledger/controller_with_traces.go (2)
47-49: Addmetric.WithUnit("ms")to new schema histograms for consistencyAll existing duration histograms here specify
metric.WithUnit("ms"), butupdateSchemaHistogram,getSchemaHistogram, andlistSchemasHistogramare created without a unit. Adding the unit keeps telemetry consistent and clearer for consumers.Also applies to: 155-166
474-495: Align tracing span name with method/metric (UpdateSchemavsUpdatedSchema)
UpdateSchemausesTraceWithMetricwith operation name"UpdatedSchema", while the method and metric are namedUpdateSchema/update_schema. Renaming the span to"UpdateSchema"will keep traces and metrics aligned and easier to query.docs/api/README.md (1)
5612-5657: Reinforce: V2ChartSegment structure needs clearer documentation.This recursive schema structure remains complex and could confuse API consumers. The past review comment correctly identified the issue. Consider adding:
- Clear explanation of recursion: How nested segments work (e.g.,
users:$userID:subaccount)- Special field semantics:
.pattern: Regex validation for segment values.rules: Custom validation rules for the segment.metadata: Segment-level attributes.self: Node-specific configuration- Practical example: Show a complete 2-3 level nested chart with annotations
Example documentation snippet:
Chart segments define hierarchical account structures. Each key represents a segment name (or dynamic placeholder like $userID). Special fields starting with '.' configure behavior: - .pattern: regex validation (e.g., "^[0-9]{16}$" ensures 16-digit IDs) - .rules: segment-specific validation rules - .metadata: descriptive attributes
🧹 Nitpick comments (12)
internal/chart.go (1)
38-42: ChartOfAccounts implementation is solid; consider caching compiled regexThe chart parsing and validation logic (root constraints,
.self/.metadata/.ruleshandling, single-variable-segment per prefix, and account discovery infindAccountSchema/FindAccountSchema/ValidatePosting) is coherent and well-covered by tests.One minor improvement:
findAccountSchemarecompiles the variable segment regex on every lookup viaregexp.Match(variableSegment.Pattern, ...). If chart lookups become hot, consider storing a compiled*regexp.RegexponChartVariableSegment(e.g., ajson:"-"field initialized during unmarshal) and usingre.Matchinstead. Not urgent, but would avoid repeated compilation on the same pattern.Also applies to: 70-162, 224-273
internal/chart_test.go (1)
359-403: Fill in test names for failing posting cases
TestPostingValidationuses emptynamefields for the error-path cases, which makes failures less readable in test output.Consider giving those cases descriptive names (e.g.,
"invalid source"/"invalid destination") sorequire.*messages clearly identify which scenario failed.pkg/client/docs/models/components/v2chartrules.md (1)
1-7: Clarify that V2ChartRules is intentionally emptyThis page shows a fields table with no rows, which can look like a generation bug. Consider adding a one‑liner under the header (or above the table) explaining that
V2ChartRulescurrently has no top‑level fields and is reserved for future chart‑rules extensions.test/e2e/api_schema_test.go (2)
55-137: Schema versions use identical charts despite comments about differing rulesThe three inserted schemas (
v1.0.0,v2.0.0,v3.0.0) all define the sameChartstructure, while the comments describe them as “Basic validation”, “Stricter validation”, and “Account validation”.If the goal is just to exercise version selection and storage, this is fine. But if you intend to validate different rule behaviors per version, you may want to slightly vary the chart definitions (e.g., patterns, required metadata, or rule presence) so the tests actually distinguish those semantics.
150-189: Consider adding an e2e forGetSchemawith a non-existent versionYou already cover:
- Duplicate schema insert →
SCHEMA_ALREADY_EXISTS.- Transaction with non-existent schema version →
NOT_FOUND.To exercise the new
ErrSchemaNotFound→404/NOT_FOUNDpath more directly, consider adding a small spec that callsGetSchemawith a non-existentVersionand asserts theNOT_FOUNDerror code. That would give you end‑to‑end coverage for both read and write flows on missing schemas.Also applies to: 324-347
internal/api/v2/controllers_schema_list.go (1)
23-31: Consider validating the order parameter.The current implementation silently defaults to descending order if an invalid value is provided. While this is a lenient approach, consider explicitly validating the order parameter and returning a 400 error for invalid values to provide clearer feedback to API consumers.
Apply this diff to add validation:
order := bunpaginate.Order(bunpaginate.OrderDesc) if orderParam := r.URL.Query().Get("order"); orderParam != "" { switch orderParam { case "asc": order = bunpaginate.Order(bunpaginate.OrderAsc) case "desc": order = bunpaginate.Order(bunpaginate.OrderDesc) + default: + api.BadRequest(w, common.ErrValidation, fmt.Errorf("invalid order parameter: must be 'asc' or 'desc'")) + return } }internal/controller/ledger/log_process.go (1)
17-17: Schema selection/validation flow looks correct; consider hardening theNeedsSchemaprobeThe overall behaviour is sound:
- With
SchemaVersionset:FindSchemais used andnewErrSchemaNotFoundis surfaced onErrNotFound, others propagate.- Without
SchemaVersion, you only require it whenOUTPUT.NeedsSchema()is true and the ledger already has at least one schema (checked viaFindSchemaswithPageSize: 1), which matches the “bootstrap without schema, then enforce” intent.log.SchemaVersionis persisted andlog.ValidateWithSchema(*schema)is invoked only when a concrete schema was resolved.One thing to watch:
var payload OUTPUT; if payload.NeedsSchema() { ... }assumesOUTPUTis a non-pointer type. If a future caller instantiateslogProcessorwith a pointer output (e.g.*CreatedTransaction), this will nil-deref at runtime. You might want to either (a) constrain usage to value types (and document it), or (b) refactor the “does this operation need a schema?” decision into a non-generic helper (e.g. per-operation constant) to avoid depending on the zero value ofOUTPUT.Also applies to: 73-95, 104-110
internal/controller/ledger/controller_default_test.go (1)
25-113: CreateTransaction tests correctly cover both schema and no-schema pathsThe new
testCreateTransaction(t, withSchema)helper and the dual invocation inTestCreateTransactionexercise:
- The “no schema configured yet” flow (
withSchema=false):FindSchemasreturns an empty cursor, so missingSchemaVersionis allowed.- The “schema configured + explicit version” flow (
withSchema=true): a minimalledger.Schemawithworld/bankaccounts is inserted,FindSchema("v1.0")is expected, andSchemaVersion: "v1.0"is propagated into the parameters soCreatedTransaction.ValidateWithSchemacan run successfully.This is a solid coverage of the happy paths for schema-aware transaction creation. You might later consider adding explicit negative-path tests (e.g., schema present but
SchemaVersionomitted →ErrSchemaNotSpecified) once those error contracts stabilize, but it’s not blocking.internal/log.go (2)
20-26: Log type enum updates are consistent; consider cleaning up the stale commentAdding
UpdatedSchemaLogTypeinto the enum, JSON marshal/unmarshal, andLogTypeFromStringkeeps the mapping symmetric and type-safe. The panic-on-default behaviour is unchanged in spirit, even though it’s now explicit.Minor nit: the comment
// "UPDATE_CHART_OF_ACCOUNT"onUpdatedSchemaLogTypeno longer matches the"UPDATED_SCHEMA"string literal; updating it would avoid confusion for future readers.Also applies to: 54-69, 71-86
267-279: Metadata/revert payloads gate on schema presence but don’t yet validateFor
SavedMetadata,DeletedMetadata, andRevertedTransactionyou’ve set:
NeedsSchema() bool { return true }ValidateWithSchema(schema Schema) error { return nil }This means that once any schema exists for a ledger, all of these operations will require a
SchemaVersionto be specified (due to theNeedsSchema()check inrunLog), even though they don’t currently perform any actual validation.If the intent is purely to require callers to always pass a schemaVersion once the ledger is “schema-enabled” (even for operations that don’t consult the schema yet), this is fine. If instead you only want to gate operations that truly depend on the schema, consider setting
NeedsSchema()tofalsefor the ones that are still no-ops, or fleshing outValidateWithSchemato perform real checks.Also applies to: 319-331, 371-383
pkg/client/models/operations/v2listschemas.go (1)
12-61: Sort/Order enums provide strict, clear value validation
Sortrestricts to"created_at", andOrderto"asc"/"desc", with explicit errors for invalid values, which helps catch misuse early.ToPointerhelpers are handy for constructing optional query params.The use of
fallthroughinOrder.UnmarshalJSONis slightly unconventional but functionally correct; optionally considercase "asc", "desc":for readability if you ever hand‑edit the generator.docs/api/README.md (1)
734-734: Clarify schemaVersion parameter usage.The schemaVersion parameter is consistently added across multiple endpoints with the description "Schema version to use for validation." However, the documentation could be enhanced with additional details:
- What happens when not specified (falls back to default?)
- Expected format/constraints for version strings
- How this relates to the default schemaVersion at the bulk request level (line 734)
Consider adding a brief explanation in the "Detailed descriptions" section for at least one endpoint as a reference, or in a shared documentation section.
Also applies to: 1139-1139, 1566-1566, 1842-1842, 1907-1907
📜 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.
⛔ Files ignored due to path filters (7)
docs/events/UpdatedSchema.jsonis excluded by!**/*.jsoninternal/storage/bucket/migrations/41-add-schema/notes.yamlis excluded by!**/*.yamlopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (106)
cmd/docs_events.go(1 hunks)docs/api/README.md(22 hunks)internal/README.md(37 hunks)internal/api/bulking/bulker.go(6 hunks)internal/api/bulking/elements.go(1 hunks)internal/api/bulking/mocks_ledger_controller_test.go(3 hunks)internal/api/common/errors.go(2 hunks)internal/api/common/mocks_ledger_controller_test.go(3 hunks)internal/api/v1/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/api/v2/controllers_schema_list.go(1 hunks)internal/api/v2/controllers_schema_list_test.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/controllers_schema_read_test.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/query.go(1 hunks)internal/api/v2/routes.go(1 hunks)internal/api/v2/views.go(1 hunks)internal/bus/listener.go(1 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)internal/controller/ledger/controller.go(2 hunks)internal/controller/ledger/controller_default.go(3 hunks)internal/controller/ledger/controller_default_test.go(7 hunks)internal/controller/ledger/controller_generated_test.go(3 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(3 hunks)internal/controller/ledger/errors.go(1 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/log_process.go(2 hunks)internal/controller/ledger/log_process_test.go(4 hunks)internal/controller/ledger/parameters.go(1 hunks)internal/controller/ledger/store.go(2 hunks)internal/controller/ledger/store_generated_test.go(3 hunks)internal/errors.go(1 hunks)internal/log.go(13 hunks)internal/schema.go(1 hunks)internal/storage/bucket/default_bucket.go(1 hunks)internal/storage/bucket/migrations/35-create-compute-hash-function/up.sql(1 hunks)internal/storage/bucket/migrations/41-add-schema/up.sql(1 hunks)internal/storage/ledger/resource_schemas.go(1 hunks)internal/storage/ledger/schema.go(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)internal/storage/ledger/store.go(1 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/dotself.md(1 hunks)pkg/client/docs/models/components/v2bulkelementaddmetadata.md(1 hunks)pkg/client/docs/models/components/v2bulkelementcreatetransaction.md(1 hunks)pkg/client/docs/models/components/v2bulkelementdeletemetadata.md(1 hunks)pkg/client/docs/models/components/v2bulkelementreverttransaction.md(1 hunks)pkg/client/docs/models/components/v2chartrules.md(1 hunks)pkg/client/docs/models/components/v2chartsegment.md(1 hunks)pkg/client/docs/models/components/v2errorsenum.md(1 hunks)pkg/client/docs/models/components/v2log.md(1 hunks)pkg/client/docs/models/components/v2logtype.md(1 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/components/v2schemaresponse.md(1 hunks)pkg/client/docs/models/components/v2schemascursor.md(1 hunks)pkg/client/docs/models/components/v2schemascursorresponse.md(1 hunks)pkg/client/docs/models/operations/order.md(1 hunks)pkg/client/docs/models/operations/queryparamorder.md(1 hunks)pkg/client/docs/models/operations/sort.md(1 hunks)pkg/client/docs/models/operations/v2addmetadataontransactionrequest.md(1 hunks)pkg/client/docs/models/operations/v2addmetadatatoaccountrequest.md(1 hunks)pkg/client/docs/models/operations/v2createbulkrequest.md(1 hunks)pkg/client/docs/models/operations/v2createtransactionrequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2listschemasrequest.md(1 hunks)pkg/client/docs/models/operations/v2listschemasresponse.md(1 hunks)pkg/client/docs/models/operations/v2listtransactionsrequest.md(1 hunks)pkg/client/docs/models/operations/v2reverttransactionrequest.md(1 hunks)pkg/client/docs/sdks/v2/README.md(7 hunks)pkg/client/models/components/v2bulkelementaddmetadata.go(2 hunks)pkg/client/models/components/v2bulkelementcreatetransaction.go(2 hunks)pkg/client/models/components/v2bulkelementdeletemetadata.go(2 hunks)pkg/client/models/components/v2bulkelementreverttransaction.go(2 hunks)pkg/client/models/components/v2chartrules.go(1 hunks)pkg/client/models/components/v2chartsegment.go(1 hunks)pkg/client/models/components/v2errorsenum.go(2 hunks)pkg/client/models/components/v2log.go(4 hunks)pkg/client/models/components/v2schema.go(1 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/components/v2schemaresponse.go(1 hunks)pkg/client/models/components/v2schemascursor.go(1 hunks)pkg/client/models/components/v2schemascursorresponse.go(1 hunks)pkg/client/models/operations/v2addmetadataontransaction.go(2 hunks)pkg/client/models/operations/v2addmetadatatoaccount.go(2 hunks)pkg/client/models/operations/v2createbulk.go(2 hunks)pkg/client/models/operations/v2createtransaction.go(2 hunks)pkg/client/models/operations/v2getschema.go(1 hunks)pkg/client/models/operations/v2insertschema.go(1 hunks)pkg/client/models/operations/v2listschemas.go(1 hunks)pkg/client/models/operations/v2listtransactions.go(3 hunks)pkg/client/models/operations/v2reverttransaction.go(2 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)test/e2e/api_transactions_list_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- pkg/client/docs/models/operations/v2listschemasrequest.md
🚧 Files skipped from review as they are similar to previous changes (67)
- internal/api/bulking/elements.go
- internal/controller/ledger/listener.go
- pkg/client/models/operations/v2addmetadataontransaction.go
- pkg/client/docs/models/components/v2schemaresponse.md
- pkg/client/docs/models/components/v2logtype.md
- pkg/client/models/components/v2schema.go
- pkg/client/models/operations/v2createtransaction.go
- pkg/client/docs/models/components/v2schemadata.md
- pkg/events/message.go
- internal/controller/ledger/controller.go
- pkg/client/docs/models/components/v2bulkelementcreatetransaction.md
- internal/storage/ledger/schema_test.go
- internal/storage/bucket/migrations/35-create-compute-hash-function/up.sql
- internal/controller/ledger/parameters.go
- pkg/client/models/operations/v2createbulk.go
- pkg/client/models/components/v2bulkelementaddmetadata.go
- pkg/client/docs/models/operations/v2insertschemaresponse.md
- pkg/client/docs/models/operations/v2addmetadatatoaccountrequest.md
- pkg/client/docs/models/components/v2errorsenum.md
- pkg/client/docs/models/operations/v2listtransactionsrequest.md
- pkg/client/docs/models/operations/v2listschemasresponse.md
- internal/schema.go
- internal/storage/bucket/migrations/41-add-schema/up.sql
- internal/bus/listener.go
- pkg/events/events.go
- pkg/client/docs/models/operations/sort.md
- internal/controller/ledger/controller_with_too_many_client_handling.go
- pkg/client/docs/models/components/v2bulkelementreverttransaction.md
- pkg/client/docs/models/components/v2bulkelementaddmetadata.md
- pkg/client/models/components/v2bulkelementreverttransaction.go
- internal/api/v2/controllers_schema_insert_test.go
- test/e2e/api_transactions_list_test.go
- pkg/client/models/components/v2schemadata.go
- cmd/docs_events.go
- pkg/client/docs/models/operations/v2getschemaresponse.md
- pkg/client/README.md
- internal/storage/ledger/resource_schemas.go
- pkg/client/models/components/v2schemascursor.go
- pkg/client/models/components/v2schemascursorresponse.go
- pkg/client/docs/models/operations/v2getschemarequest.md
- internal/api/v2/routes.go
- pkg/client/docs/models/components/v2log.md
- internal/api/v2/views.go
- internal/errors.go
- internal/api/v2/query.go
- pkg/client/models/operations/v2addmetadatatoaccount.go
- pkg/client/docs/models/components/v2schema.md
- pkg/client/docs/models/operations/v2createbulkrequest.md
- internal/api/common/mocks_ledger_controller_test.go
- pkg/client/docs/models/operations/queryparamorder.md
- pkg/client/docs/models/operations/v2addmetadataontransactionrequest.md
- pkg/client/models/components/v2bulkelementcreatetransaction.go
- internal/storage/ledger/store.go
- pkg/client/docs/models/components/dotself.md
- internal/api/v2/controllers_schema_insert.go
- pkg/client/models/operations/v2listtransactions.go
- pkg/client/models/components/v2errorsenum.go
- pkg/client/docs/models/operations/v2insertschemarequest.md
- internal/controller/ledger/controller_default.go
- pkg/client/docs/models/components/v2schemascursorresponse.md
- internal/controller/ledger/errors.go
- internal/controller/ledger/store.go
- pkg/client/docs/models/components/v2schemascursor.md
- pkg/client/docs/models/operations/v2reverttransactionrequest.md
- internal/storage/bucket/default_bucket.go
- internal/controller/ledger/controller_with_events.go
- internal/controller/ledger/log_process_test.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/README.md
🧬 Code graph analysis (27)
internal/api/v2/controllers_schema_read.go (1)
internal/api/common/errors.go (1)
HandleCommonErrors(38-45)
pkg/client/models/operations/v2reverttransaction.go (1)
pkg/client/models/components/v2reverttransactionrequest.go (1)
V2RevertTransactionRequest(5-7)
internal/api/v2/controllers_schema_list.go (4)
internal/api/common/pagination.go (1)
PaginationConfig(3-6)pkg/client/models/operations/v2listschemas.go (3)
Order(37-37)OrderDesc(41-41)OrderAsc(40-40)internal/api/common/errors.go (2)
ErrValidation(20-20)HandleCommonPaginationErrors(65-74)internal/schema.go (1)
Schema(15-21)
internal/chart_test.go (2)
internal/chart.go (5)
ChartOfAccounts(36-36)ChartVariableSegment(23-28)ChartSegment(17-21)ChartAccount(12-15)ChartAccountRules(10-10)internal/errors.go (1)
ErrInvalidAccount(102-105)
internal/log.go (5)
internal/storage/ledger/logs.go (1)
Log(20-26)internal/schema.go (1)
Schema(15-21)internal/transaction.go (2)
Transaction(37-51)Transaction(53-60)internal/posting.go (1)
Postings(35-35)pkg/events/message.go (4)
SavedMetadata(27-32)DeletedMetadata(60-65)RevertedTransaction(44-48)UpdatedSchema(77-80)
pkg/client/models/components/v2bulkelementdeletemetadata.go (2)
pkg/generate/generator.go (1)
Action(27-29)pkg/client/models/components/v2bulkelementaddmetadata.go (1)
Data(5-9)
internal/api/v2/controllers_schema_read_test.go (3)
internal/schema.go (2)
Schema(15-21)SchemaData(11-13)internal/controller/ledger/errors.go (1)
ErrNotFound(14-14)internal/api/v2/routes.go (1)
NewRouter(28-135)
pkg/client/models/operations/v2insertschema.go (1)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-9)
internal/api/v1/mocks_ledger_controller_test.go (6)
internal/api/v2/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
UpdatedSchema(418-420)
internal/controller/ledger/store_generated_test.go (2)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/api/common/errors.go (1)
internal/controller/ledger/errors.go (5)
ErrInvalidIdempotencyInput(221-225)ErrSchemaValidationError(268-271)ErrNotFound(14-14)ErrSchemaNotSpecified(289-289)ErrSchemaNotFound(249-251)
pkg/client/models/operations/v2getschema.go (1)
pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/controller/ledger/controller_default_test.go (4)
internal/schema.go (2)
Schema(15-21)SchemaData(11-13)internal/chart.go (2)
ChartOfAccounts(36-36)ChartAccount(12-15)internal/controller/ledger/controller.go (2)
CreateTransaction(102-106)RunScript(91-91)internal/machine/vm/run.go (1)
RunScript(14-19)
internal/controller/ledger/log_process.go (4)
internal/schema.go (1)
Schema(15-21)internal/controller/ledger/errors.go (1)
ErrNotFound(14-14)internal/storage/common/pagination.go (1)
InitialPaginatedQuery(10-15)internal/log.go (2)
NewLog(194-199)ComputeIdempotencyHash(459-468)
pkg/client/models/components/v2chartsegment.go (2)
pkg/client/models/components/v2chartrules.go (1)
V2ChartRules(5-6)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
internal/controller/ledger/controller_generated_test.go (5)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
UpdatedSchema(418-420)
internal/controller/ledger/controller_with_traces.go (6)
internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
UpdatedSchema(418-420)internal/tracing/tracing.go (1)
TraceWithMetric(101-129)internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/storage/ledger/schema.go (3)
internal/controller/ledger/store.go (1)
Store(29-66)internal/storage/ledger/store.go (1)
Store(25-50)internal/schema.go (1)
Schema(15-21)
pkg/client/models/operations/v2listschemas.go (2)
pkg/client/internal/utils/json.go (2)
UnmarshalJSON(117-251)MarshalJSON(21-115)pkg/client/models/components/v2schemascursorresponse.go (1)
V2SchemasCursorResponse(5-7)
internal/api/v2/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (2)
Log(89-102)UpdatedSchema(418-420)
internal/api/v2/controllers_schema_list_test.go (5)
internal/storage/common/pagination.go (2)
PaginatedQuery(26-29)InitialPaginatedQuery(10-15)internal/schema.go (2)
Schema(15-21)SchemaData(11-13)cmd/root.go (1)
Version(25-25)internal/storage/common/resource.go (1)
ResourceQuery(410-416)internal/api/v2/routes.go (1)
NewRouter(28-135)
pkg/client/models/components/v2schemaresponse.go (1)
pkg/client/models/components/v2schema.go (1)
V2Schema(11-18)
internal/api/bulking/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
UpdatedSchema(418-420)
internal/chart.go (3)
internal/account.go (1)
Account(16-26)pkg/client/internal/utils/json.go (2)
UnmarshalJSON(117-251)MarshalJSON(21-115)internal/errors.go (1)
ErrInvalidAccount(102-105)
test/e2e/api_schema_test.go (11)
test/e2e/suite_test.go (1)
UseTemplatedDatabase(207-209)pkg/client/models/operations/v2listschemas.go (1)
V2ListSchemasRequest(63-74)pkg/client/models/components/v2schemascursorresponse.go (1)
V2SchemasCursorResponse(5-7)pkg/client/models/operations/v2insertschema.go (1)
V2InsertSchemaRequest(9-15)pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-9)pkg/client/models/components/v2chartsegment.go (1)
V2ChartSegment(13-19)pkg/client/models/components/v2errorsenum.go (4)
V2ErrorsEnumSchemaAlreadyExists(30-30)V2ErrorsEnumValidation(15-15)V2ErrorsEnumSchemaNotSpecified(31-31)V2ErrorsEnumNotFound(19-19)pkg/client/models/operations/v2createtransaction.go (1)
V2CreateTransactionRequest(9-27)pkg/client/models/components/v2posttransaction.go (1)
V2PostTransaction(58-68)pkg/client/models/components/v2posting.go (1)
V2Posting(10-15)pkg/client/models/components/v2log.go (1)
V2Log(48-56)
internal/controller/ledger/listener_generated_test.go (3)
internal/log.go (1)
UpdatedSchema(418-420)pkg/events/message.go (1)
UpdatedSchema(77-80)internal/schema.go (1)
Schema(15-21)
pkg/client/v2.go (15)
pkg/client/models/operations/v2insertschema.go (2)
V2InsertSchemaRequest(9-15)V2InsertSchemaResponse(38-40)pkg/client/models/operations/options.go (2)
Option(41-41)Options(32-39)pkg/client/internal/utils/utils.go (3)
ReplaceParameters(51-60)ConsumeRawBody(350-360)UnmarshalJsonFromResponseBody(39-49)pkg/client/internal/utils/pathparams.go (1)
GenerateURL(19-38)pkg/client/internal/hooks/hooks.go (1)
HookContext(27-35)pkg/client/internal/config/sdkconfiguration.go (1)
SDKConfiguration(16-25)pkg/client/internal/utils/requestbody.go (1)
SerializeRequestBody(31-42)pkg/client/internal/utils/security.go (1)
PopulateSecurity(27-92)pkg/client/internal/utils/retries.go (3)
RetryConfig(23-23)Retries(25-28)Retry(30-104)pkg/client/models/components/v2errorresponse.go (1)
V2ErrorResponse(5-9)pkg/client/models/operations/v2getschema.go (2)
V2GetSchemaRequest(9-14)V2GetSchemaResponse(30-34)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)pkg/client/models/operations/v2listschemas.go (2)
V2ListSchemasRequest(63-74)V2ListSchemasResponse(122-126)pkg/client/internal/utils/queryparams.go (1)
PopulateQueryParams(20-43)pkg/client/models/components/v2schemascursorresponse.go (1)
V2SchemasCursorResponse(5-7)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/sdks/v2/README.md
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
252-252: Hard tabs
Column: 1
(MD010, no-hard-tabs)
253-253: Hard tabs
Column: 1
(MD010, no-hard-tabs)
254-254: Hard tabs
Column: 1
(MD010, no-hard-tabs)
255-255: Hard tabs
Column: 1
(MD010, no-hard-tabs)
321-321: Hard tabs
Column: 1
(MD010, no-hard-tabs)
322-322: Hard tabs
Column: 1
(MD010, no-hard-tabs)
323-323: Hard tabs
Column: 1
(MD010, no-hard-tabs)
324-324: Hard tabs
Column: 1
(MD010, no-hard-tabs)
325-325: Hard tabs
Column: 1
(MD010, no-hard-tabs)
326-326: Hard tabs
Column: 1
(MD010, no-hard-tabs)
381-381: Hard tabs
Column: 1
(MD010, no-hard-tabs)
382-382: Hard tabs
Column: 1
(MD010, no-hard-tabs)
383-383: Hard tabs
Column: 1
(MD010, no-hard-tabs)
384-384: Hard tabs
Column: 1
(MD010, no-hard-tabs)
385-385: Hard tabs
Column: 1
(MD010, no-hard-tabs)
386-386: Hard tabs
Column: 1
(MD010, no-hard-tabs)
🔇 Additional comments (36)
pkg/client/models/operations/v2reverttransaction.go (2)
22-23: LGTM!The new
SchemaVersionfield is correctly implemented as an optional query parameter, consistent with other optional fields in the request struct. The pointer type and query parameter tags follow the established pattern.Note: This is generated code (line 1), so any issues with the field definition would need to be addressed in the generator specification or OpenAPI spec.
73-78: LGTM!The
GetSchemaVersionaccessor method correctly follows the established pattern for getter methods in this file. It properly handles the nil receiver case and returns the appropriate type.pkg/client/docs/models/components/v2bulkelementdeletemetadata.md (1)
10-10: SchemaVersion field documentation is well-structured.The new SchemaVersion field is appropriately documented with clear description ("Schema version to use for validation"), correct optional status, and a realistic example format. The addition aligns with the PR's schema management objectives.
pkg/client/docs/models/operations/order.md (1)
3-11: Order enum docs look consistent
OrderAsc/OrderDescmapping toasc/descis clear and matches expected sort semantics; nothing else to change here.internal/api/bulking/mocks_ledger_controller_test.go (1)
214-227: Schema-related bulking mocks are correctly wiredThe added
GetSchema,ListSchemas, andUpdateSchemamocks (and recorders) follow the existing gomock pattern and match the controller signatures, so bulking tests can now exercise schema paths without extra changes.Also applies to: 347-360, 454-468
internal/api/v1/mocks_ledger_controller_test.go (1)
214-227: v1 LedgerController mocks updated consistently for schema APIs
GetSchema,ListSchemas, andUpdateSchemaare added with the expected signatures and recorder methods, mirroring other mock sets; this keeps v1 tests in sync with the controller interface.Also applies to: 347-360, 454-468
pkg/client/models/operations/v2insertschema.go (1)
9-47: V2InsertSchema request/response types look correctPath params and JSON body wiring for
V2InsertSchemaRequest, plus the nil-safe getters andV2InsertSchemaResponsewrapper, are consistent with the rest of the client operations.internal/README.md (1)
19-41: Updated internal API docs correctly expose chart/schema featuresThe regenerated gomarkdoc output now documents the new chart-of-accounts and schema concepts (
ValidateSegment,ChartOfAccountsand friends,Schema/SchemaData,UpdatedSchema,SchemaVersiononLog, and related errors). This aligns with the new public surface added in the Go packages and keeps the internal package docs in sync.Also applies to: 212-241, 252-257, 318-323, 418-447, 484-527, 579-623, 711-737, 798-824, 1008-1113, 1524-1557, 1787-1821
pkg/client/models/components/v2chartrules.go (1)
1-6: Generated placeholder type looks correct
V2ChartRulesas an empty generated struct cleanly satisfies the current usage (e.g.,DotRules: &components.V2ChartRules{}in tests) without adding behavior. No changes needed here.internal/api/v2/controllers_schema_read.go (1)
14-30: LGTM!The handler implementation is clean and follows established patterns for v2 API endpoints. Error handling appropriately distinguishes not-found errors (404) from other errors, and the success path correctly returns the schema.
internal/api/v2/controllers_schema_list_test.go (1)
24-170: LGTM!The test provides comprehensive coverage of the ListSchemas handler, including nominal cases, pagination, sorting, error handling, and input validation. The test structure is clean with parallel execution and appropriate use of gomock expectations.
internal/api/v2/controllers_schema_read_test.go (1)
21-102: LGTM!The test provides good coverage of the readSchema handler with appropriate scenarios: successful retrieval, not found error, and backend errors. The gomock expectations and assertions are correctly structured.
pkg/client/models/components/v2bulkelementdeletemetadata.go (1)
32-59: LGTM!The addition of the
SchemaVersionfield and its getter method is consistent with the broader schema versioning feature. The implementation follows established patterns for optional fields in bulk operations.pkg/client/models/components/v2schemaresponse.go (1)
5-15: LGTM!The response wrapper type is correctly implemented and follows standard patterns for API response models. The defensive nil-check in the getter is appropriate.
internal/controller/ledger/listener_generated_test.go (1)
91-101: LGTM!The generated mock methods for
UpdatedSchemaare correctly implemented and follow the established patterns for other listener methods in the codebase.internal/api/v2/controllers_schema_list.go (1)
45-47: renderSchema function is properly defined and accessible.The
renderSchemafunction is defined atinternal/api/v2/views.go:146with the correct signature:func renderSchema(r *http.Request, v ledger.Schema) any { return v }The cursor rendering implementation is correct.
internal/api/bulking/bulker.go (1)
143-267: BulkElement SchemaVersion field verified.The BulkElement struct includes the SchemaVersion field, confirming that all six bulk actions can correctly propagate SchemaVersion across the processElement method implementation.
pkg/client/models/operations/v2getschema.go (1)
1-48: GetSchema operation models look consistent and safeThe request/response structs and their getters follow the established Speakeasy patterns (path params,
HTTPMeta, nil‑safe accessors). No issues from a correctness or ergonomics standpoint.pkg/client/models/components/v2log.go (1)
16-21: Updated log types/schemaVersion exposure are well-integratedThe addition of
V2LogTypeUpdatedSchema, the corresponding Unmarshal case, and the optionalSchemaVersionfield/getter onV2Logall look correct and consistent with existing patterns. This should be backwards compatible while exposing the new schema-aware log information to clients.Also applies to: 31-56, 104-109
internal/controller/ledger/controller_default_test.go (1)
137-140: AdditionalFindSchemasexpectations align revert/metadata tests with schema-aware log processingThe extra
FindSchemasexpectations inTestRevertTransaction,TestSaveTransactionMetadata, andTestDeleteTransactionMetadatacorrectly reflect the newNeedsSchema()behaviour for these payload types. Returning an empty cursor models the “no schemas yet” state, so these tests continue to validate the pre-schema behaviour while still covering the new call pattern.Also applies to: 197-200, 247-250
internal/log.go (3)
88-102: SchemaVersion propagation and hashing look correctStoring
SchemaVersiononLogand including it inComputeHashvia a field withjson:"schemaVersion,omitempty"is a good way to bind the chain hash to the schema used for validation without perturbing hashes when no schema is set. The dedicatedLog.ValidateWithSchema(schema)helper cleanly delegates to the payload and keeps higher-level code simple.No issues here.
Also applies to: 146-183, 190-192
201-205: CreatedTransaction schema validation is wired as expectedExtending
LogPayloadwithNeedsSchema()/ValidateWithSchema(Schema)and implementing:
CreatedTransaction.NeedsSchema() bool { return true }CreatedTransaction.ValidateWithSchemathat iterates all postings and callsschema.Chart.ValidatePosting(posting)provides a clear enforcement point for chart-of-accounts rules. This integrates well with the controller’s new pre-insert validation step.
Looks good.
Also applies to: 213-216, 218-230
418-434: UpdatedSchema payload type is correctly integratedThe
UpdatedSchemapayload with aSchemafield,NeedsSchema() == false, no-opValidateWithSchema, andType() == UpdatedSchemaLogTypefits well with the existingLogPayloadpattern and matches the events model (pkg/events/message.go). It’s reasonable that this payload doesn’t itself require validation against another schema.internal/storage/ledger/schema.go (1)
13-40: Storage schema methods look correct and aligned with existing patterns
InsertSchema,FindSchema, andFindSchemascorrectly scope by ledger, use the shared pagination resource, and normalize errors viapostgres.ResolveError. No issues spotted.internal/controller/ledger/controller_with_traces.go (1)
474-539: Schema controller methods with traces follow existing patterns
UpdateSchema,GetSchema, andListSchemasmirror the established tracing/metric pattern used elsewhere in this type: they wrap the underlying controller, capture latency via histograms, and propagate results/errors correctly. No additional issues spotted.internal/api/v2/mocks_ledger_controller_test.go (1)
214-227: New schema-related gomock methods look correct
GetSchema,ListSchemas, andUpdateSchemamocks follow the usual gomock pattern and their signatures align with the controller interface types (*ledger.Schema,*bunpaginate.Cursor[ledger.Schema],Parameters[UpdateSchema]). Nothing to change here.Also applies to: 347-360, 454-468
pkg/client/v2.go (3)
656-849: InsertSchema client method aligns with existing V2 patterns
- URL template
/v2/{ledger}/schema/{version}matchesV2InsertSchemaRequestpath params and expected endpoint.- Request body is correctly taken from the
V2SchemaDatafield viaSerializeRequestBody, required (non‑optional) and JSON‑encoded.- Retry, timeout, hooks, security, headers, and 204/no‑body success handling mirror
CreateLedgerand similar write operations.No issues from a client or API‑surface perspective.
851-1057: GetSchema method correctly wires read endpoint and response model
- URL
/v2/{ledger}/schema/{version}and GET verb are consistent withV2GetSchemaRequest.- On 200 +
application/json, the body is deserialized intocomponents.V2SchemaResponseand attached toV2GetSchemaResponse, matching the provided component type.- Non‑200 responses use the standard
sdkerrors.V2ErrorResponsepath, with hooks and retry behavior identical to other read methods likeGetLedger.Looks correct and consistent.
1059-1267: ListSchemas method correctly supports pagination and sorting
- URL
/v2/{ledger}/schemaand GET verb matchV2ListSchemasRequest(ledger in path, cursor/pageSize/sort/order as query params).PopulateQueryParamsis invoked so cursor, sort, and order are honored, with defaults driven by struct tags.- Successful 200 JSON responses are decoded into
components.V2SchemasCursorResponseand attached toV2ListSchemasResponse.- Retry, timeout, hooks, and error handling follow the established V2 list‑style methods (e.g.,
ListAccounts,ListLogs).No functional issues spotted.
internal/controller/ledger/store_generated_test.go (1)
15-24: Schema-related Store mocks are consistent with the updated Store interface
FindSchema(ctx context.Context, version string) (*ledger.Schema, error)andInsertSchema(ctx context.Context, data *ledger.Schema) errormatch the described Store additions and correctly type‑assert results in the gomock plumbing.FindSchemas(ctx context.Context, query common.PaginatedQuery[any]) (*bunpaginate.Cursor[ledger.Schema], error)uses the right generic cursor type and is wired up to gomock in the standard pattern.- The added
bunpaginateimport is used only for the cursor return type and does not introduce unused imports.These mocks should integrate cleanly with existing controller tests.
Also applies to: 152-180, 226-238
pkg/client/models/operations/v2listschemas.go (1)
63-140: V2ListSchemasRequest/Response models are aligned with client and pagination patterns
V2ListSchemasRequestfields and query/path tags (ledger, cursor, pageSize, sort, order) match the client method and expected API surface, including sensible defaults viadefault:"..."tags.- Marshal/Unmarshal delegate to
utils.MarshalJSON/utils.UnmarshalJSON, consistent with other generated operation models and enabling default handling and additional future tags.- Getter methods are nil‑safe and follow the same pattern as other SDK types.
V2ListSchemasResponsewrapsHTTPMetaandV2SchemasCursorResponsein the standard way used across V2 list endpoints.Everything here looks coherent with existing SDK conventions.
pkg/client/models/components/v2chartsegment.go (1)
1-65: V2ChartSegment JSON mapping correctly models dotted chart-of-accounts segments
- JSON tags like
json:".self,omitempty"andjson:".pattern,omitempty"will serialize to the expected dotted keys, and are compatible with the sharedutils.MarshalJSON/UnmarshalJSONlogic.AdditionalProperties map[string]V2ChartSegmentwithadditionalProperties:"true"correctly captures arbitrary nested segments while keeping the known dotted keys first‑class.- The empty
DotSelfmarker and nil‑safe getters make it easy to detect presence of.selfand other fields.This component looks well‑aligned with the rest of the schema/chart modelling.
internal/controller/ledger/controller_generated_test.go (1)
213-467: LGTM! Auto-generated mock methods are correct.The three new mock methods (GetSchema, ListSchemas, UpdateSchema) follow the established pattern and correctly match the interface signatures. Type references align with the relevant code snippets (ledger.Schema, Parameters[UpdateSchema], ledger.UpdatedSchema).
docs/api/README.md (3)
318-508: LGTM! Schema endpoint documentation is comprehensive.The three new schema endpoints (insert/update, get by version, and list) are well-documented with complete parameter descriptions, example requests/responses, status codes, and authentication requirements. The structure follows existing API documentation patterns.
4028-4029: LGTM! V2Log schema version integration is consistent.The schemaVersion field is properly added to the V2Log structure with clear documentation ("Schema version used for validation"), and the UPDATED_SCHEMA log type enum value is appropriately included. The example responses at lines 2219-2220 and 3506-3507 consistently demonstrate the new field.
Also applies to: 4043-4043, 4053-4053
4553-4554: LGTM! Error codes and bulk element schema integration are consistent.The new error codes (SCHEMA_ALREADY_EXISTS, SCHEMA_NOT_SPECIFIED) appropriately extend the V2ErrorsEnum for schema validation scenarios. The schemaVersion field is systematically added to V2BaseBulkElement and properly inherited by all bulk element variants (CreateTransaction, AddMetadata, RevertTransaction, DeleteMetadata) with consistent descriptions.
Also applies to: 4667-5017
| ErrSchemaAlreadyExists = "SCHEMA_ALREADY_EXISTS" | ||
| ErrSchemaNotSpecified = "SCHEMA_NOT_SPECIFIED" | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wire ErrSchemaAlreadyExists into HandleCommonWriteErrors
You’ve introduced the ErrSchemaAlreadyExists error code but HandleCommonWriteErrors never maps ledgercontroller.ErrSchemaAlreadyExists to it. As written, a duplicate schema insert will most likely fall through to HandleCommonErrors and surface as a 500/internal error instead of a 409 with SCHEMA_ALREADY_EXISTS (which the e2e test expects).
Consider adding an explicit branch:
case errors.Is(err, ledgercontroller.ErrSchemaAlreadyExists{}):
api.WriteErrorResponse(w, http.StatusConflict, ErrSchemaAlreadyExists, err)This keeps the HTTP semantics aligned with V2ErrorsEnumSchemaAlreadyExists and the new e2e “duplicate schema” test.
Also applies to: 51-59
🤖 Prompt for AI Agents
internal/api/common/errors.go around lines 27-29 (and also apply the same change
in the HandleCommonWriteErrors branch at lines ~51-59): the new
ErrSchemaAlreadyExists constant is not being returned by HandleCommonWriteErrors
when ledgercontroller.ErrSchemaAlreadyExists occurs, so duplicate schema inserts
fall through as 500s; add an explicit branch in HandleCommonWriteErrors that
checks errors.Is(err, ledgercontroller.ErrSchemaAlreadyExists) and calls
api.WriteErrorResponse(w, http.StatusConflict, ErrSchemaAlreadyExists, err) so
the handler returns a 409 with the SCHEMA_ALREADY_EXISTS code.
| | `Ik` | **string* | :heavy_minus_sign: | N/A | | | ||
| | `SchemaVersion` | **string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix malformed Markdown syntax on type column.
Lines 9–10 contain malformed type markup **string* (unmatched delimiters). This should be *string* to match the formatting on line 8 and render correctly.
Apply this diff to fix the syntax errors:
-| `Ik` | **string* | :heavy_minus_sign: | N/A | |
+| `Ik` | *string* | :heavy_minus_sign: | N/A | |
-| `SchemaVersion` | **string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 |
+| `SchemaVersion` | *string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 |📝 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.
| | `Ik` | **string* | :heavy_minus_sign: | N/A | | | |
| | `SchemaVersion` | **string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 | | |
| | `Ik` | *string* | :heavy_minus_sign: | N/A | | | |
| | `SchemaVersion` | *string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 | |
🤖 Prompt for AI Agents
In pkg/client/docs/models/components/v2bulkelementdeletemetadata.md around lines
9 to 10, the type column contains malformed markdown `**string*`; replace each
`**string*` with `*string*` (i.e., single asterisks surrounding string) on both
the `Ik` and `SchemaVersion` rows so the emphasis markup matches the other lines
and the table renders correctly, preserving existing spacing and table
alignment.
| | Field | Type | Required | Description | Example | | ||
| | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | | ||
| | `DotSelf` | [*components.DotSelf](../../models/components/dotself.md) | :heavy_minus_sign: | N/A | | | ||
| | `DotPattern` | **string* | :heavy_minus_sign: | N/A | | | ||
| | `DotRules` | [*components.V2ChartRules](../../models/components/v2chartrules.md) | :heavy_minus_sign: | N/A | | | ||
| | `DotMetadata` | map[string]*string* | :heavy_minus_sign: | N/A | | | ||
| | `AdditionalProperties` | map[string][components.V2ChartSegment](../../models/components/v2chartsegment.md) | :heavy_minus_sign: | N/A | {<br/>"users": {<br/>"$userID": {<br/>".pattern": "^[0-9]{16}$"<br/>}<br/>}<br/>} | No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix type formatting for DotPattern and DotMetadata
The types in the table don’t quite match the Go model and have broken Markdown:
DotPatternshows as**string*instead of something like*string*(and renders oddly).DotMetadatashows asmap[string]*string*, but the Go type ismap[string]string, and the trailing*also breaks formatting.
Consider updating these rows to something along the lines of:
| `DotPattern` | *string* | :heavy_minus_sign: | N/A | |
| `DotMetadata` | map[string]string | :heavy_minus_sign: | N/A | |so the docs align with V2ChartSegment and render cleanly.
🤖 Prompt for AI Agents
In pkg/client/docs/models/components/v2chartsegment.md around lines 8 to 14, the
table row types for DotPattern and DotMetadata are misformatted and don't match
the Go model; update the `DotPattern` cell to use italicized string markup
(*string*) and change the `DotMetadata` cell to the correct Go type
`map[string]string` (remove the extraneous asterisks), ensuring the Markdown
pipe-separated table stays valid and renders correctly.
| | `DryRun` | **bool* | :heavy_minus_sign: | Set the dryRun mode. dry run mode doesn't add the logs to the database or publish a message to the message broker. | true | | ||
| | `IdempotencyKey` | **string* | :heavy_minus_sign: | Use an idempotency key | | | ||
| | ~~`Force`~~ | **bool* | :heavy_minus_sign: | : warning: ** DEPRECATED **: This will be removed in a future release, please migrate away from it as soon as possible.<br/><br/>Disable balance checks when passing postings | true | | ||
| | `SchemaVersion` | **string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix type formatting inconsistency.
The type notation **string* on line 12 is inconsistent with the notation used for required fields elsewhere in the table (e.g., *string* on line 8). Ensure the type formatting is consistent across all field definitions in this documentation.
Apply this diff to align the formatting:
-| `SchemaVersion` | **string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 |
+| `SchemaVersion` | *string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 |📝 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.
| | `SchemaVersion` | **string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 | | |
| | `SchemaVersion` | *string* | :heavy_minus_sign: | Schema version to use for validation | v1.0.0 | |
🤖 Prompt for AI Agents
In pkg/client/docs/models/operations/v2createtransactionrequest.md around line
12, the type markup for SchemaVersion is written as `**string*` which is
inconsistent with the other fields (e.g., `*string*` on line 8); update the
table entry to use the same `*string*` formatting for the type so all
required-field type notations are consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (6)
internal/api/v2/controllers_schema_insert_test.go (1)
58-64: “invalid JSON” case currently sends valid JSON
requestBodyis a plain string, but it’s marshaled withjson.Marshal, so the body is valid JSON (a JSON string), not syntactically invalid JSON. If you want to exercise JSON decoding failures, consider either:
- Writing the raw body without
json.Marshal, or- Renaming the case to something like
"invalid schema payload"to reflect a type/validation error instead of malformed JSON.internal/errors.go (1)
91-121: New schema/account errors look consistent; minor message/Unwrap tweaksThe new
ErrInvalidSchema/ErrInvalidAccounttypes fit the existing error pattern (value types withIsmethods) and give good context (path + segment).Two small polish points:
- In
ErrInvalidAccount.Error, the middle branch message says “chart of account” while the others say “chart of accounts” – consider making this consistent.- If you ever need to surface the underlying validation error from
ErrInvalidSchema, adding anUnwrap() error { return e.err }would make error-chaining more ergonomic; otherwise current design is fine.internal/controller/ledger/errors.go (1)
314-318: Use named field in constructor.For clarity and consistency with other constructors in this file, use named field initialization.
func newErrSchemaAlreadyExists(version string) ErrSchemaAlreadyExists { return ErrSchemaAlreadyExists{ - version, + version: version, } }internal/chart.go (3)
10-10: Document ChartAccountRules as placeholder.The empty
ChartAccountRulesstruct appears to be a placeholder for future functionality. Consider adding a comment to clarify this is intentional and reserved for future rule definitions.+// ChartAccountRules defines validation rules for chart accounts (placeholder for future functionality) type ChartAccountRules struct{}
96-102: Pattern regex compiled for validation only.The pattern regex is compiled here for validation (line 98-100) but will be recompiled during account lookups at line 240. If chart unmarshaling performance becomes a concern, consider caching the compiled regex.
264-273: Consider explicit "world" account definition.The special-case handling of the "world" account (lines 267-270) bypasses chart validation. Consider whether "world" should be explicitly defined in chart schemas or documented as a reserved system account that exists outside the chart.
📜 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.
📒 Files selected for processing (13)
internal/README.md(50 hunks)internal/api/common/errors.go(2 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)internal/controller/ledger/controller_default_test.go(7 hunks)internal/controller/ledger/errors.go(1 hunks)internal/controller/ledger/log_process.go(1 hunks)internal/controller/ledger/log_process_test.go(3 hunks)internal/controller/ledger/store.go(2 hunks)internal/controller/ledger/store_generated_test.go(3 hunks)internal/errors.go(2 hunks)internal/storage/ledger/schema.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- internal/api/common/errors.go
- internal/chart_test.go
- internal/controller/ledger/controller_default_test.go
- internal/controller/ledger/log_process_test.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/README.md
🧬 Code graph analysis (7)
internal/storage/ledger/schema.go (3)
internal/controller/ledger/store.go (1)
Store(29-67)internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/controller/ledger/errors.go (1)
internal/api/common/errors.go (2)
ErrSchemaNotSpecified(28-28)ErrSchemaAlreadyExists(27-27)
internal/controller/ledger/store_generated_test.go (2)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/controller/ledger/store.go (2)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/controller/ledger/log_process.go (4)
internal/schema.go (1)
Schema(15-21)internal/controller/ledger/errors.go (3)
ErrNotFound(14-14)ErrSchemaNotFound(249-252)ErrSchemaNotSpecified(288-290)internal/api/common/errors.go (1)
ErrSchemaNotSpecified(28-28)internal/log.go (2)
NewLog(194-199)ComputeIdempotencyHash(459-468)
internal/api/v2/controllers_schema_insert_test.go (3)
internal/controller/ledger/errors.go (1)
ErrSchemaValidationError(267-270)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/api/v2/routes.go (1)
NewRouter(28-135)
internal/chart.go (1)
internal/errors.go (1)
ErrInvalidAccount(103-107)
⏰ 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). (3)
- GitHub Check: GoReleaser
- GitHub Check: Tests
- GitHub Check: Dirty
🔇 Additional comments (13)
internal/api/v2/controllers_schema_insert_test.go (1)
21-85: Solid table-driven coverage and gomock expectationsNice use of table-driven tests with
tc := tcand parallel subtests, and preciseUpdateSchemaexpectations tied toexpectBackendCall. The cases cover the main behaviors (success, empty data, malformed payload, backend error, and schema validation error) and validate both HTTP status and error codes.internal/controller/ledger/log_process.go (2)
112-118: SchemaVersion propagation and validation are wired correctlySetting
log.SchemaVersion = parameters.SchemaVersionand validating withlog.ValidateWithSchema(*schema)when a schema was resolved cleanly ties the stored log to the chosen schema version and surfaces validation failures vianewErrSchemaValidationErroras expected.
72-103: No safety issue with zero-valuepayload.NeedsSchema()Verification confirms all
LogPayloadimplementations—CreatedTransaction,SavedMetadata,DeletedMetadata,RevertedTransaction, andUpdatedSchema—use value receivers and return a bool constant without accessing receiver state. CallingNeedsSchema()on a zero value is safe and does not panic. The review concern does not apply to this codebase.Likely an incorrect or invalid review comment.
internal/controller/ledger/store.go (1)
10-21: Schema methods on Store interface look consistent with storage layerThe added
InsertSchema,FindSchema,FindSchemas, andFindLatestSchemaVersionmethods fit cleanly into the existing Store interface and match the storage implementation types (ledger.Schema,bunpaginate.Cursor,common.PaginatedQuery[any]). No issues spotted.Also applies to: 49-52
internal/storage/ledger/schema.go (1)
15-61: Schema persistence helpers are correctly scoped and error-normalized
InsertSchema,FindSchema,FindLatestSchemaVersion, andFindSchemasall:
- Scope queries to the current ledger via the
ledgercolumn andGetPrefixedRelationName("schemas").- Normalize DB errors with
postgres.ResolveErrorwhere appropriate.- Distinguish “no schema yet” (
FindLatestSchemaVersionreturning(nil, nil)) from real failures.This matches how the controller layer expects to handle missing vs existing schemas; implementation looks sound.
internal/controller/ledger/errors.go (1)
267-286: LGTM!The error type and constructor are well-implemented with clear error messages.
internal/controller/ledger/store_generated_test.go (1)
1-421: LGTM - Generated mock code.This is auto-generated mock code that properly adds schema-related mock methods (
FindLatestSchemaVersion,FindSchema,FindSchemas,InsertSchema) with correct gomock patterns.internal/chart.go (5)
44-69: LGTM!The root-level validation correctly prevents variable segments and account properties at the root level, with clear error messages.
113-133: LGTM!The validation logic correctly enforces that:
- Variable segments (with patterns) must have
$prefix- Fixed segments (without patterns) cannot have
$prefix- Only one variable segment is allowed per level
135-164: LGTM!Property handling correctly validates
.selfas an empty object and properly unmarshals.metadataand.rules. The implicit account creation for leaf nodes (line 156) is a sensible default.
166-222: LGTM!The JSON marshaling methods correctly reconstruct the chart structure, including proper handling of the
.selfmarker for accounts with child segments and pattern inclusion for variable segments.
275-285: LGTM!The posting validation correctly ensures both source and destination accounts exist in the chart of accounts.
internal/README.md (1)
1-1962: LGTM - Generated documentation.Auto-generated documentation comprehensively covers all new schema and chart-of-accounts types, methods, and constants introduced in this PR.
| type ErrSchemaNotFound struct { | ||
| requestedVersion string | ||
| latestVersion *string | ||
| } | ||
|
|
||
| func (e ErrSchemaNotFound) Error() string { | ||
| if e.latestVersion != nil { | ||
| return fmt.Sprintf("schema version `%s` not found, latest available version is `%s`", e.requestedVersion, *e.latestVersion) | ||
| } else { | ||
| return fmt.Sprintf("schema version `%s` not found, this ledger doesn't have a schema", e.requestedVersion) | ||
| } | ||
| } | ||
|
|
||
| func (e ErrSchemaNotFound) Is(err error) bool { | ||
| _, ok := err.(ErrSchemaNotFound) | ||
| return ok | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add constructor for ErrSchemaNotFound.
The error type is missing a constructor function. For consistency with other error types in this file (e.g., NewErrIdempotencyKeyConflict at line 123, newErrAlreadyReverted at line 104), add a constructor to instantiate this error properly.
+func newErrSchemaNotFound(requestedVersion string) ErrSchemaNotFound {
+ return ErrSchemaNotFound{
+ requestedVersion: requestedVersion,
+ }
+}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
internal/controller/ledger/errors.go around lines 249-265: add a constructor
function (e.g., NewErrSchemaNotFound(requestedVersion string, latestVersion
*string) error) that returns an ErrSchemaNotFound instance populated with the
provided requestedVersion and latestVersion; implement it to return the concrete
ErrSchemaNotFound value (or error interface) so callers can create this error
consistently like other errors in this file.
| type ErrSchemaNotSpecified struct { | ||
| latestVersion string | ||
| } | ||
|
|
||
| func (e ErrSchemaNotSpecified) Error() string { | ||
| return fmt.Sprintf("a schema version must be specified for this ledger, latest available version is %v", e.latestVersion) | ||
| } | ||
|
|
||
| func (e ErrSchemaNotSpecified) Is(err error) bool { | ||
| _, ok := err.(ErrSchemaNotSpecified) | ||
| return ok | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add constructor and fix format string.
Two issues:
- Missing constructor function - add one for consistency with other error types in this file
- Line 293: Use
%sinstead of%vfor the string fieldlatestVersion
func (e ErrSchemaNotSpecified) Error() string {
- return fmt.Sprintf("a schema version must be specified for this ledger, latest available version is %v", e.latestVersion)
+ return fmt.Sprintf("a schema version must be specified for this ledger, latest available version is %s", e.latestVersion)
}
+
+func newErrSchemaNotSpecified(latestVersion string) ErrSchemaNotSpecified {
+ return ErrSchemaNotSpecified{
+ latestVersion: latestVersion,
+ }
+}📝 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.
| type ErrSchemaNotSpecified struct { | |
| latestVersion string | |
| } | |
| func (e ErrSchemaNotSpecified) Error() string { | |
| return fmt.Sprintf("a schema version must be specified for this ledger, latest available version is %v", e.latestVersion) | |
| } | |
| func (e ErrSchemaNotSpecified) Is(err error) bool { | |
| _, ok := err.(ErrSchemaNotSpecified) | |
| return ok | |
| } | |
| type ErrSchemaNotSpecified struct { | |
| latestVersion string | |
| } | |
| func (e ErrSchemaNotSpecified) Error() string { | |
| return fmt.Sprintf("a schema version must be specified for this ledger, latest available version is %s", e.latestVersion) | |
| } | |
| func (e ErrSchemaNotSpecified) Is(err error) bool { | |
| _, ok := err.(ErrSchemaNotSpecified) | |
| return ok | |
| } | |
| func newErrSchemaNotSpecified(latestVersion string) ErrSchemaNotSpecified { | |
| return ErrSchemaNotSpecified{ | |
| latestVersion: latestVersion, | |
| } | |
| } |
🤖 Prompt for AI Agents
internal/controller/ledger/errors.go around lines 288 to 299: the
ErrSchemaNotSpecified type lacks the standard constructor and its Error() method
uses the wrong format verb; add a constructor function (e.g.,
NewErrSchemaNotSpecified(latestVersion string) ErrSchemaNotSpecified or error to
match other file patterns) that initializes and returns the error, and update
Error() to use %s for the latestVersion string in fmt.Sprintf; keep the existing
Is method behavior to continue type comparisons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
internal/chart_test.go (1)
378-402: Add descriptive names to test cases.Test cases at lines 387 and 395 have empty name fields, making debugging more difficult if tests fail.
Apply this diff:
{ - name: "", + name: "invalid source account", posting: Posting{ Source: "bank:invalid", Destination: "users:001:main", }, expectError: true, }, { - name: "", + name: "invalid destination account", posting: Posting{ Source: "bank:012", Destination: "users:invalid:main", }, expectError: true, },
📜 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.
📒 Files selected for processing (3)
internal/README.md(50 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)
🧰 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/README.md
🧬 Code graph analysis (2)
internal/chart_test.go (2)
internal/chart.go (5)
ChartOfAccounts(36-36)ChartVariableSegment(23-28)ChartSegment(17-21)ChartAccount(12-15)ChartAccountRules(10-10)internal/errors.go (1)
ErrInvalidAccount(103-107)
internal/chart.go (2)
internal/storage/ledger/logs.go (1)
RawMessage(38-38)internal/errors.go (1)
ErrInvalidAccount(103-107)
🔇 Additional comments (10)
internal/chart_test.go (3)
10-244: LGTM! Comprehensive validation testing.The table-driven tests thoroughly cover both valid and invalid chart configurations, including edge cases like invalid segments, pattern conflicts, and structural invariants. The JSON round-trip verification ensures marshaling correctness.
246-288: LGTM! Well-designed test fixture.The helper provides a comprehensive chart structure covering fixed segments, variable segments with patterns, and nested accounts with metadata—ideal for testing diverse validation scenarios.
290-365: LGTM! Thorough account resolution testing.The tests validate account schema lookup across various scenarios, including fixed segments, variable segments with pattern matching, nested paths, and multiple error conditions with precise error message verification.
internal/chart.go (6)
10-42: LGTM! Clean type definitions and validation.The hierarchical chart structure is well-designed with clear separation between fixed and variable segments. The global
ChartSegmentRegexpis safe as it's read-only after initialization.ChartAccountRulesis currently an empty struct, which is fine as a placeholder for future extensions.
44-69: LGTM! Robust root-level validation.The method properly enforces root-level constraints (no variable segments or account properties) and provides clear error messages for invalid configurations.
70-164: LGTM! Comprehensive segment unmarshaling.The method handles the complex logic of distinguishing fixed from variable segments, validates patterns, enforces structural constraints (one variable segment per level, patterns only on $ prefixes), and automatically treats leaf segments as accounts. The nested validation block (lines 89-107) properly validates regex patterns before use.
166-222: LGTM! Symmetric JSON marshaling.The marshaling logic properly mirrors the unmarshaling implementation, ensuring JSON round-trip consistency. The
marshalJsonObjecthelper is well-structured and correctly adds the.selfmarker only when a segment is both an account and has subsegments (line 200).
224-270: LGTM! Sound recursive account resolution.The
findAccountSchemafunction correctly prioritizes fixed segments over variable segments, validates patterns with regex matching, and provides detailed error context throughErrInvalidAccount. The publicFindAccountSchemawrapper properly splits the account address and delegates to the recursive helper.
272-282: LGTM! Straightforward posting validation.The method correctly validates that both source and destination accounts exist in the chart by delegating to
FindAccountSchema. The error handling properly propagates account lookup failures.internal/README.md (1)
1-1962: LGTM! Generated documentation is comprehensive.This auto-generated documentation properly reflects the new schema and chart of accounts functionality. The index is updated, and all new types, methods, constants, and variables are documented with appropriate links and signatures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (8)
internal/errors.go (1)
91-101: Optional: expose underlying schema error and be mindful of string-coupled messages.The new error types look consistent with existing patterns and work well with
errors.Is. Two small optional improvements:
- For
ErrInvalidSchema, consider adding anUnwrap() errormethod so callers can inspect the root cause without parsing the formatted message.ErrInvalidAccount.Errorstrings are now asserted verbatim in tests (e.g.,chart of accountvschart of accounts). That’s fine, but it does mean any future copy tweaks must be coordinated with tests and external consumers that may rely on exact text.Also applies to: 103-126
test/e2e/api_schema_test.go (1)
1-462: Strong end‑to‑end coverage for schema lifecycle and validation.This suite nicely exercises schema insert/read/list, transaction validation across versions, default metadata behavior, error codes, and log
schemaVersionpropagation. If you ever want to reduce repetition, you could factor small helpers for the repeatedCreateTransactionandInsertSchemacalls, but it’s not necessary for clarity.internal/chart_test.go (2)
13-253: Comprehensive chart and account validation tests, with minor ergonomics nits.The coverage here is excellent: you’re asserting both structural parsing (valid/invalid charts), exact error strings from
ErrInvalidAccount, and that posting validation routes through that error type. Two minor nits you might consider later:
- Negative
TestPostingValidationcases have emptynamefields, which makes failures slightly harder to read; giving them descriptive names would help.- Because many assertions depend on exact error messages, any future wording change in
ErrInvalidAccount.Error()will require synchronized test updates.Overall, behavior looks solid.
Also applies to: 299-379, 381-425
427-465: Default metadata tests correctly capture in‑place mutation semantics.
InsertDefaultAccountMetadatamutates the passedmetadata.Metadatamap, and the tests assert on the mutatedpreviousMetadata, which matches the function’s contract and the controller’s usage. If you ever change the API to return a new map instead, these tests will be a good guide for updating callers.internal/controller/ledger/log_process.go (2)
65-104: Schema resolution and validation flow looks consistent; just ensureNeedsSchemais instance‑agnostic.The new
runLoglogic correctly handles:
- Explicit
SchemaVersion: load viaFindSchema, translate not‑found intoErrSchemaNotFoundwith latest version.- Implicit schema: for payloads where
OUTPUT.NeedsSchema()is true, require a schema only once the ledger actually has one, otherwise allow legacy behavior.- Post‑operation validation via
log.ValidateWithSchema(*schema)andnewErrSchemaValidationError.One assumption worth double‑checking is that
NeedsSchema()on allledger.LogPayloadimplementations is a pure function of the payload type (and implemented on a value receiver), since it’s invoked on a zero‑valuevar payload OUTPUT. If any implementation ever depends on initialized fields, this could misclassify whether a schema is required.Also applies to: 114-118
105-113: Consider whetherSchemaVersionshould participate in idempotency hashing.
IdempotencyHashis still computed solely fromparameters.Input, while schema selection and validation now depend onparameters.SchemaVersion. That means two calls with the same IK and input but differentSchemaVersionwill be treated as the same idempotent request and will not surface a conflict, even though they may conceptually target different validation rules.If you want schema version to be part of the idempotent “contract”, consider folding
SchemaVersioninto the idempotency hash (or explicitly documenting that idempotency is defined only over the transaction payload, not the schema used to validate it).Also applies to: 176-193
internal/controller/ledger/controller_default.go (1)
381-452: Schema‑aware transaction creation and metadata flows are consistent with chart behavior.Threading
*ledger.SchemaintocreateTransactionand using:if schema != nil { schema.Chart.InsertDefaultAccountMetadata(account, accountMetadata[account]) }after merging explicit
AccountMetadatagives the desired semantics:
- Default metadata is applied per account only when a schema is present and that account already has some metadata entry (matching
InsertDefaultAccountMetadatatests).- Revert and metadata‑only operations accept the schema parameter but ignore it, which keeps their behavior unchanged while satisfying the new logProcessor signature.
This matches the expectations encoded in
internal/chart_test.goand the E2E schema tests.Also applies to: 528-600
docs/api/README.md (1)
685-735: Schema‑related docs align with implementation; markdownlint warnings are mostly generator noise.The additions for:
schemaVersionquery params (bulk, create/revert transaction, and metadata endpoints),- the new
schemaVersionfield andUPDATED_SCHEMAlog type inV2Log/V2LogsCursorResponse,- and the chart/schema models (
V2Chart*,V2Schema*,V2SchemasCursor*),all match the controller/store behavior and the Go client models. That’s good.
The markdownlint warnings (MD024 duplicate headings like “### Properties”, MD028 blank lines in blockquotes) largely stem from Widdershins‑generated structure rather than these specific changes. If you care about a clean lint run, it’s probably better to adjust the linter config or generator template globally rather than hand‑tweaking this section.
Also applies to: 1139-1140, 1566-1567, 1842-1843, 1907-1908, 2219-2221, 3495-3508, 4022-4044, 4497-4556, 5612-5877
📜 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.
⛔ Files ignored due to path filters (6)
docs/events/UpdatedSchema.jsonis excluded by!**/*.jsonopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (15)
docs/api/README.md(22 hunks)internal/README.md(50 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)internal/controller/ledger/controller_default.go(10 hunks)internal/controller/ledger/log_process.go(3 hunks)internal/controller/ledger/log_process_test.go(5 hunks)internal/errors.go(2 hunks)pkg/client/docs/models/components/v2chartaccountmetadata.md(1 hunks)pkg/client/docs/models/components/v2chartaccountrules.md(1 hunks)pkg/client/docs/models/components/v2chartsegment.md(1 hunks)pkg/client/models/components/v2chartaccountmetadata.go(1 hunks)pkg/client/models/components/v2chartaccountrules.go(1 hunks)pkg/client/models/components/v2chartsegment.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- pkg/client/models/components/v2chartaccountrules.go
🚧 Files skipped from review as they are similar to previous changes (3)
- pkg/client/docs/models/components/v2chartsegment.md
- pkg/client/models/components/v2chartsegment.go
- internal/chart.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/controller_default.gointernal/README.md
🧬 Code graph analysis (4)
internal/controller/ledger/log_process.go (6)
internal/controller/ledger/store.go (1)
Store(29-67)internal/storage/ledger/store.go (1)
Store(25-50)internal/schema.go (1)
Schema(15-21)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
Log(89-102)internal/controller/ledger/errors.go (3)
ErrNotFound(14-14)ErrSchemaNotFound(249-252)ErrSchemaNotSpecified(288-290)
test/e2e/api_schema_test.go (8)
pkg/client/models/components/v2schemascursorresponse.go (1)
V2SchemasCursorResponse(5-7)pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-9)pkg/client/models/components/v2chartaccountmetadata.go (1)
V2ChartAccountMetadata(5-7)pkg/client/models/components/v2errorsenum.go (4)
V2ErrorsEnumSchemaAlreadyExists(30-30)V2ErrorsEnumValidation(15-15)V2ErrorsEnumSchemaNotSpecified(31-31)V2ErrorsEnumNotFound(19-19)pkg/client/models/operations/v2getschema.go (1)
V2GetSchemaRequest(9-14)pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)pkg/client/models/components/v2posting.go (1)
V2Posting(10-15)pkg/client/models/components/v2log.go (1)
V2Log(48-56)
internal/controller/ledger/controller_default.go (4)
internal/controller/ledger/controller.go (4)
UpdateSchema(135-138)RevertTransaction(108-113)DeleteTransactionMetadata(125-128)DeleteAccountMetadata(130-133)internal/log.go (2)
UpdatedSchema(418-420)Log(89-102)internal/storage/ledger/store.go (1)
Store(25-50)internal/schema.go (2)
Schema(15-21)NewSchema(23-33)
internal/controller/ledger/log_process_test.go (4)
internal/controller/ledger/store.go (1)
Store(29-67)internal/schema.go (1)
Schema(15-21)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/controller/ledger/controller.go (1)
RunScript(91-91)
🪛 markdownlint-cli2 (0.18.1)
docs/api/README.md
357-357: Blank line inside blockquote
(MD028, no-blanks-blockquote)
403-403: Blank line inside blockquote
(MD028, no-blanks-blockquote)
461-461: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
470-470: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5624-5624: Multiple headings with the same content
(MD024, no-duplicate-heading)
5626-5626: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5642-5642: Multiple headings with the same content
(MD024, no-duplicate-heading)
5668-5668: Multiple headings with the same content
(MD024, no-duplicate-heading)
5699-5699: Multiple headings with the same content
(MD024, no-duplicate-heading)
5727-5727: Multiple headings with the same content
(MD024, no-duplicate-heading)
5759-5759: Multiple headings with the same content
(MD024, no-duplicate-heading)
5793-5793: Multiple headings with the same content
(MD024, no-duplicate-heading)
5832-5832: Multiple headings with the same content
(MD024, no-duplicate-heading)
5869-5869: Multiple headings with the same content
(MD024, no-duplicate-heading)
⏰ 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). (3)
- GitHub Check: GoReleaser
- GitHub Check: Tests
- GitHub Check: Dirty
🔇 Additional comments (5)
pkg/client/docs/models/components/v2chartaccountrules.md (1)
1-7: Documentation appears incomplete or unclear.The table structure has headers but contains no field rows. This suggests either the struct is empty (which should be documented with an explanatory note) or the documentation is incomplete. Please clarify the intent and populate the table accordingly or add a note explaining why no fields are present.
To verify the structure of
V2ChartAccountRules, please check the corresponding Go struct definition inpkg/client/models/components/v2chartaccountrules.goand confirm whether it actually contains any fields or is intentionally empty. If the struct has fields, please populate the documentation table. If it is intentionally empty, consider adding a brief explanatory note in the documentation.pkg/client/models/components/v2chartaccountmetadata.go (1)
1-14: LGTM!The generated code follows best practices:
- Proper nil-receiver safety in the getter method
- Correct use of pointer type with
omitemptyJSON tag for optional fields- Clean, idiomatic Go structure
internal/controller/ledger/log_process_test.go (1)
35-55: Tests correctly exercise schema-aware forgeLog behavior.The added
FindLatestSchemaVersionexpectations and the updated callback signature (schema *ledger.Schema) align with the newrunLogcontract and retry logic. Both conflict and deadlock paths now assert the expected schema lookups without over-constraining behavior.Also applies to: 70-107
internal/controller/ledger/controller_default.go (1)
54-84: Schema controller wiring and import behavior look correct.The new
UpdateSchema/GetSchema/ListSchemasmethods, theupdateSchemaLpwiring, and theledger.UpdatedSchemaimport case all line up with the store interface and error handling:
ledger.NewSchema+InsertSchemawithErrSchemaAlreadyExistson constraint failures ensures invalid/duplicate schemas are rejected cleanly.- Using a pointer in
InsertSchemaguaranteesCreatedAtis populated before theUpdatedSchemapayload is returned or logged.- Importing
ledger.UpdatedSchemaby callingInsertSchemabefore re‑inserting the log matches how transaction imports replay side effects.No issues from a correctness standpoint.
Also applies to: 157-164, 271-281
internal/README.md (1)
1-1: Original review comment is incorrect.The file
internal/README.mdis properly managed. It's auto-generated via Go's standard//go:generatedirective (declared ininternal/doc.go), which is designed for files that are committed to version control. Developers regenerate it by runninggo generate ./...locally when the underlying code changes.In Go projects, go:generate outputs are meant to be committed—this is idiomatic Go practice and allows:
- Users without generator tools installed to access documentation
- Version control history of documentation changes
- Consistency across environments
The file is correctly excluded from
.gitignore, confirming it should be committed.Likely an incorrect or invalid review comment.
|
|
||
| | Field | Type | Required | Description | | ||
| | ------------------ | ------------------ | ------------------ | ------------------ | | ||
| | `Default` | **string* | :heavy_minus_sign: | N/A | No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix markdown formatting for the type column.
The type column has mismatched asterisks: **string* should be *string* (to indicate a pointer type in italics) or **string** (for bold).
Apply this diff to fix the formatting:
-| `Default` | **string* | :heavy_minus_sign: | N/A |
+| `Default` | *string* | :heavy_minus_sign: | 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.
| | `Default` | **string* | :heavy_minus_sign: | N/A | | |
| | `Default` | *string* | :heavy_minus_sign: | N/A | |
🤖 Prompt for AI Agents
In pkg/client/docs/models/components/v2chartaccountmetadata.md around line 8,
the type column entry currently reads `**string*` which has mismatched
asterisks; replace it with `*string*` (or `**string**` if bold is intended) to
fix the Markdown formatting so the type renders correctly—update the table cell
to use balanced asterisks and save the file.
8d4c96d to
f6c0feb
Compare
df9dc5e to
42ba087
Compare
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/controller/ledger/controller_default.go (1)
454-518: RevertTransaction bypasses schema validation by passing nil schema to CommitTransactionIn
revertTransaction, the new signature accepts_schema *ledger.Schema, but the finalCommitTransactioncall ignores it:// ... err = store.CommitTransaction(ctx, nil, &reversedTx, nil)Whereas
createTransactionpasses the provided schema through toCommitTransaction. If the goal is to enforce chart/schema validation for all postings that hit the ledger, you likely want:err = store.CommitTransaction(ctx, _schema, &reversedTx, nil)so reversals are validated under the same schema as normal creations. If you intentionally want reversals to always succeed even when the current schema would reject them (e.g., schema tightened after the original tx), it would be helpful to document that rationale near this call.
Also applies to: 509-512
♻️ Duplicate comments (15)
pkg/client/docs/models/operations/v2listtransactionsrequest.md (1)
13-13: Markdown formatting issue persists: spaces inside emphasis markers.The deprecation notice still has spaces inside the bold markers (
** DEPRECATED **), which violates markdown formatting rules (MD037). This was previously flagged and marked as addressed, but appears to have regressed (possibly due to regeneration).-| : warning: ** DEPRECATED **: This will be removed in a future release... +| :warning: **DEPRECATED**: This will be removed in a future release...If this file is auto-generated, the fix should be applied to the OpenAPI spec or generator template.
pkg/client/docs/models/operations/v2listschemasrequest.md (1)
9-10: Fix Markdown formatting for optional pointer types.The Type column has malformed Markdown emphasis with mismatched asterisks that will render incorrectly.
Apply this diff to fix the formatting:
-| `Cursor` | **string* | :heavy_minus_sign: | The pagination cursor value | | -| `PageSize` | **int64* | :heavy_minus_sign: | The maximum number of results to return per page | | +| `Cursor` | *string* | :heavy_minus_sign: | The pagination cursor value | | +| `PageSize` | *int64* | :heavy_minus_sign: | The maximum number of results to return per page | |pkg/client/docs/models/components/v2chartsegment.md (1)
11-13: Fix type formatting for DotPattern and DotMetadata.The formatting issues with
DotPattern(showing as**string*) andDotMetadatahave already been flagged in previous review comments but remain unresolved. Please address the Markdown formatting corrections as suggested by previous reviewers.internal/api/common/errors.go (1)
51-67: WireErrSchemaAlreadyExistsintoHandleCommonWriteErrors.The constant
ErrSchemaAlreadyExistsis defined but never mapped inHandleCommonWriteErrors. Whenledgercontroller.ErrSchemaAlreadyExistsoccurs, it will fall through to the default case and likely surface as a 500 Internal Server Error instead of the expected 409 Conflict.Based on past review comments, add an explicit branch:
func HandleCommonWriteErrors(w http.ResponseWriter, r *http.Request, err error) { switch { case errors.Is(err, ledgercontroller.ErrIdempotencyKeyConflict{}): api.WriteErrorResponse(w, http.StatusConflict, ErrConflict, err) case errors.Is(err, ledgercontroller.ErrInvalidIdempotencyInput{}): api.BadRequest(w, ErrValidation, err) case errors.Is(err, ledgercontroller.ErrNotFound): api.NotFound(w, err) + case errors.Is(err, ledgercontroller.ErrSchemaAlreadyExists{}): + api.WriteErrorResponse(w, http.StatusConflict, ErrSchemaAlreadyExists, err) case errors.Is(err, ledgercontroller.ErrSchemaValidationError{}): api.BadRequest(w, ErrValidation, errors.Unwrap(err)) case errors.Is(err, ledgercontroller.ErrSchemaNotSpecified{}): api.BadRequest(w, ErrSchemaNotSpecified, errors.Unwrap(err)) case errors.Is(err, ledgercontroller.ErrSchemaNotFound{}): api.NotFound(w, errors.Unwrap(err)) default: HandleCommonErrors(w, r, err) } }internal/api/v2/controllers_schema_insert_test.go (1)
57-64: The "invalid JSON" test case doesn't actually test invalid JSON handling.The test marshals the string
"invalid json string"into valid JSON ("\"invalid json string\"") at line 101, so the handler never receives malformed JSON. To properly test invalid JSON parsing, usejson.RawMessagefor the request body in this test case or create the request with raw bytes directly.Consider this approach:
{ name: "invalid JSON", version: "v1.0.0", - requestBody: "invalid json string", + requestBody: json.RawMessage(`{invalid`), expectStatusCode: http.StatusBadRequest, expectedErrorCode: "VALIDATION", expectBackendCall: false, },And adjust line 101 to handle
json.RawMessage:- body, _ := json.Marshal(tc.requestBody) + var body []byte + if raw, ok := tc.requestBody.(json.RawMessage); ok { + body = raw + } else { + body, _ = json.Marshal(tc.requestBody) + }pkg/client/docs/sdks/v2/README.md (2)
240-310: Documentation for InsertSchema is comprehensive and follows SDK patterns.The new
InsertSchemaoperation documentation includes proper example usage, parameter table, response type, and error types. The example correctly demonstrates creating a schema with chart segments and patterns.Note: Static analysis indicates hard tabs in the code example (lines 250-255), which was previously flagged. This appears to be a generated file formatting issue.
311-429: GetSchema and ListSchemas documentation sections are well-structured.Both sections follow the established documentation pattern with example usage, parameter tables, and error types. The examples correctly demonstrate the expected API usage.
The hard tabs issue noted by static analysis was already flagged in previous reviews.
internal/storage/ledger/accounts.go (1)
103-210: Reintroduce ON CONFLICT semantics inUpsertAccountsto avoid concurrent insert failures.The new CTE‑based upsert splits updates and inserts:
existing_accountsselects current rows.inserted_rowsdoes a plainINSERT ... FROM data_batch d WHERE d.address NOT IN (SELECT address FROM existing_accounts).Under concurrent load, two transactions upserting the same
(ledger, address)can both see the row as “not existing” and attempt an insert, hitting the unique(ledger, address)constraint instead of behaving idempotently as before.To retain safe concurrent upserts,
inserted_rows(or the whole statement) needs anON CONFLICT (ledger, address)clause (e.g.,DO NOTHINGorDO UPDATEwith appropriate merge logic) rather than relying on a precomputedNOT INfilter.I’d recommend redesigning
inserted_rowsalong these lines:
- Drop the
existing_accounts/NOT INcheck, and- Use
INSERT ... ON CONFLICT (ledger, address) DO NOTHING RETURNING ...(orDO UPDATEmirroring the update logic) so concurrent upserts can’t break on uniqueness.internal/controller/ledger/controller_default_test.go (1)
72-79: Fix InsertLog mocks: DoAndReturn return type must matchStore.InsertLog(error, notany).Both InsertLog expectations here return
anyfromDoAndReturn, while the other InsertLog mocks in this file returnerror, indicating the real signature is likely:InsertLog(ctx context.Context, log *ledger.Log) errorLeaving these as-is will either fail to compile or panic at runtime when gomock tries to invoke the stub.
Recommended change for both expectations:
- DoAndReturn(func(_ context.Context, log *ledger.Log) any { - log.ID = pointer.For(uint64(0)) - return log - }) + DoAndReturn(func(_ context.Context, log *ledger.Log) error { + log.ID = pointer.For(uint64(0)) + return nil + })Apply the same pattern at both call sites so all InsertLog mocks consistently return
error.Also applies to: 131-138
internal/storage/ledger/resource_schemas.go (1)
25-35: Preferstore.newScopedSelect()overdb.NewSelect()+ manualledgerfilter.Other ledger resource handlers use
store.newScopedSelect()to inject the optimized ledger predicate; rebuilding the query fromdb.NewSelect()and a rawWHERE ledger = ?here bypasses that optimization and may regress Postgres query plans.Consider:
-func (h schemasResourceHandler) BuildDataset(opts common.RepositoryHandlerBuildContext[any]) (*bun.SelectQuery, error) { - q := h.store.db.NewSelect(). - ModelTableExpr(h.store.GetPrefixedRelationName("schemas")). - Where("ledger = ?", h.store.ledger.Name) +func (h schemasResourceHandler) BuildDataset(opts common.RepositoryHandlerBuildContext[any]) (*bun.SelectQuery, error) { + q := h.store.newScopedSelect(). + ModelTableExpr(h.store.GetPrefixedRelationName("schemas"))keeping the PIT condition below as-is.
internal/controller/ledger/controller_with_traces.go (1)
474-495: Align span name with method/metric (UpdateSchemavsUpdatedSchema).The span name
"UpdatedSchema"is inconsistent with the method nameUpdateSchemaand the metric"controller.update_schema", which makes traces harder to correlate with code and metrics.- _, err = tracing.TraceWithMetric( - ctx, - "UpdatedSchema", + _, err = tracing.TraceWithMetric( + ctx, + "UpdateSchema", c.tracer,internal/log.go (1)
433-455: Fix HydrateLog unmarshalling: avoid pointer‑to‑interface (still unsafe)
HydrateLogstill passes a pointer‑to‑interface intojson.Unmarshal:var payload any // payload is set to &CreatedTransaction{}, &SavedMetadata{}, etc. err := json.Unmarshal(data, &payload)With
&payload(*interface{}),encoding/jsonreplaces the interface’s concrete value with generic types (map[string]any, etc.) instead of populating the concrete struct pointer you set. The subsequentreflect.ValueOf(payload).Elem().Interface().(LogPayload)then operates on the wrong type and can panic or silently misbehave.Unmarshal into the concrete pointer stored in the interface:
- err := json.Unmarshal(data, &payload) + err := json.Unmarshal(data, payload)This preserves the concrete
*CreatedTransaction,*SavedMetadata,*UpdatedSchema, etc., so the final cast toLogPayloadis safe and correct.Go encoding/json: behavior when calling json.Unmarshal with *interface{} vs interface{} containing a concrete pointerdocs/api/README.md (1)
318-379: Wording forPOST /v2/{ledger}/schema/{version}still implies update, but implementation is insert‑onlyThe section title and anchors (
## Insert or update a schema for a ledger,insert-or-update-a-schema-for-a-ledger-…) suggest this endpoint both inserts and updates schemas. In code,updateSchemacallsstore.InsertSchemaand maps unique‑constraint violations toSCHEMA_ALREADY_EXISTS, i.e., it’s an insert‑only operation that fails on duplicate versions.To avoid misleading clients, consider rephrasing this section to something like:
- “Insert a schema version for a ledger (fails if the version already exists)”
and adjust the heading/description accordingly, while keeping the existing 204 response and error examples.
internal/README.md (2)
1152-1152: Fix UpdatedSchemaLogType comment to match actual serialized value.Line 1152 documents
UpdatedSchemaLogTypewith a comment stating"UPDATE_CHART_OF_ACCOUNT", but the actual serialized value is"UPDATED_SCHEMA". This documentation error must be corrected in the source fileinternal/log.goso that the auto-generated documentation reflects the true constant value.This issue was previously flagged in review comments and needs to be fixed in the underlying source code:
- UpdatedSchemaLogType // "UPDATE_CHART_OF_ACCOUNT" + UpdatedSchemaLogType // "UPDATED_SCHEMA"
253-257: Resolve regex inconsistency between ChartSegmentRegexp and HTTP API account address validation.
ChartSegmentRegexpat line 256 (^(\$|\.)?[a-zA-Z0-9_-]+$) permits hyphens in segment names, but the documented HTTP API account address regex (^\w+(:\w+)*$) only accepts word characters ([A-Za-z0-9_]). This allows chart segments with hyphens that cannot match valid account addresses. The discrepancy must be resolved consistently.This issue was previously flagged and requires clarification: choose one consistent rule and update both the
ChartSegmentRegexpregex in the source and the HTTP API/address validation logic accordingly (either disallow-in segments or broaden address validation to accept hyphens). Verify the intended behavior.Also applies to: 339-341, 419-548
🧹 Nitpick comments (8)
internal/errors.go (2)
91-101: Consider adding a constructor function andUnwrap()method for consistency.Other error types in this file (e.g.,
newErrInvalidLedgerName,NewErrPipelineAlreadyExists) have constructor functions. For consistency and to enable proper error unwrapping witherrors.Is/errors.Ason the inner error, consider:+func NewErrInvalidSchema(err error) ErrInvalidSchema { + return ErrInvalidSchema{err: err} +} + +func (e ErrInvalidSchema) Unwrap() error { + return e.err +}
103-126: Consider adding constructor function(s) forErrInvalidAccount.For consistency with other error types in this file and to make instantiation clearer, consider adding constructor function(s):
+func NewErrInvalidAccountNotDefined(segment string, hasSubsegments bool) ErrInvalidAccount { + return ErrInvalidAccount{segment: segment, hasSubsegments: hasSubsegments} +} + +func NewErrInvalidAccountPatternMismatch(path []string, segment string) ErrInvalidAccount { + return ErrInvalidAccount{path: path, segment: segment, patternMismatch: true} +} + +func NewErrInvalidAccountSegmentNotAllowed(path []string, segment string) ErrInvalidAccount { + return ErrInvalidAccount{path: path, segment: segment} +}internal/storage/ledger/balances_test.go (1)
35-35: Test calls updated correctly for new schema parameter.The updates to
UpsertAccountsandCommitTransactioncorrectly addnilfor the new schema parameter, which is appropriate for tests that don't exercise schema-specific behavior.Consider adding a dedicated test case that validates schema propagation through
CommitTransactionandUpsertAccountswith a non-nil schema to ensure end-to-end schema handling works correctly.Also applies to: 177-177, 188-188
internal/api/bulking/bulker_test.go (1)
433-433: Run call correctly updated for new id parameter.The addition of the empty string
""as the id parameter aligns with the updatedRunsignature. This is appropriate for tests that don't require schema version context.Consider adding a test case that passes a non-empty id (schema version) to validate the schema-aware bulk processing path.
internal/chart_test.go (1)
399-414: Add descriptive test names for error cases.The test cases on lines 400 and 408 have empty
namefields, which makes test output less informative when failures occur. Consider adding descriptive names like"invalid source pattern"and"invalid destination pattern"to improve debugging clarity.Apply this diff:
{ - name: "", + name: "invalid source pattern", posting: Posting{ Source: "bank:invalid", Destination: "users:001:main", }, expectError: true, }, { - name: "", + name: "invalid destination pattern", posting: Posting{ Source: "bank:012", Destination: "users:invalid:main", }, expectError: true, },test/e2e/api_schema_test.go (1)
184-197: Ordering assertion may be fragile depending on timing.The test assumes schemas are ordered by
created_at DESC, but all three schemas are inserted sequentially in theBeforeEachblock. If the insertion is fast enough that timestamps are identical (same millisecond), the ordering might become non-deterministic.Consider adding explicit verification that
CreatedAttimestamps are actually different, or adjusting the test to not rely on strict version ordering if timestamps could collide.internal/chart.go (1)
93-112: Avoid recompiling variable‑segment regex on every account lookupIn
findAccountSchema, variable‑segment patterns are recompiled on each lookup viaregexp.Match, even though they were already validated inChartSegment.UnmarshalJSON. If chart validation runs on a hot path (e.g. every posting), consider caching a compiled*regexp.RegexpinChartVariableSegmentduring unmarshal and usingMatchStringhere to avoid repeated compilation overhead.For example, you could extend
ChartVariableSegmentwith an unexportedre *regexp.Regexp(ignored by JSON), set it where you currently validatePATTERN_KEY, and then:matches := variableSegment.re.MatchString(nextSegment)instead of calling
regexp.Matchon the pattern string each time.Also applies to: 244-249
internal/log.go (1)
190-192: Clarify NeedsSchema/ValidateWithSchema semantics for non‑transaction payloads
LogPayload.NeedsSchema()returnstrueforSavedMetadata,DeletedMetadata, andRevertedTransaction, but theirValidateWithSchemaimplementations are currently no‑ops (always returnnil). This makes the intent ofNeedsSchemaambiguous: callers may assume a schema‑driven validation actually happens for these payloads.If the design is:
- Only transactions are validated against the chart today, and
- Metadata/revert logs merely record which schema version was in effect,
consider either:
- Setting
NeedsSchema()tofalsefor payloads that don’t actually use the schema, or- Implementing concrete validations for them (e.g., account existence/shape checks for metadata targets, or validation of the generated revert transaction) and documenting the behavior.
This will make the schema requirements and validation behavior easier to reason about for future maintainers.
Also applies to: 218-229, 272-279, 324-331, 375-380, 416-425
📜 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.
⛔ Files ignored due to path filters (7)
docs/events/UpdatedSchema.jsonis excluded by!**/*.jsoninternal/storage/bucket/migrations/42-add-schema/notes.yamlis excluded by!**/*.yamlopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (108)
cmd/docs_events.go(1 hunks)docs/api/README.md(13 hunks)internal/README.md(51 hunks)internal/account.go(1 hunks)internal/api/bulking/bulker.go(11 hunks)internal/api/bulking/bulker_test.go(1 hunks)internal/api/bulking/mocks_ledger_controller_test.go(3 hunks)internal/api/common/errors.go(3 hunks)internal/api/common/mocks_ledger_controller_test.go(3 hunks)internal/api/v1/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/controllers_bulk.go(1 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/api/v2/controllers_schema_list.go(1 hunks)internal/api/v2/controllers_schema_list_test.go(1 hunks)internal/api/v2/controllers_schema_read.go(1 hunks)internal/api/v2/controllers_schema_read_test.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/query.go(1 hunks)internal/api/v2/routes.go(1 hunks)internal/api/v2/views.go(1 hunks)internal/bus/listener.go(1 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)internal/controller/ledger/controller.go(2 hunks)internal/controller/ledger/controller_default.go(12 hunks)internal/controller/ledger/controller_default_test.go(8 hunks)internal/controller/ledger/controller_generated_test.go(3 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(3 hunks)internal/controller/ledger/errors.go(1 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/controller/ledger/log_process.go(3 hunks)internal/controller/ledger/log_process_test.go(5 hunks)internal/controller/ledger/parameters.go(1 hunks)internal/controller/ledger/store.go(3 hunks)internal/controller/ledger/store_generated_test.go(6 hunks)internal/errors.go(2 hunks)internal/log.go(13 hunks)internal/schema.go(1 hunks)internal/storage/bucket/default_bucket.go(1 hunks)internal/storage/bucket/migrations/42-add-schema/up.sql(1 hunks)internal/storage/ledger/accounts.go(3 hunks)internal/storage/ledger/accounts_test.go(7 hunks)internal/storage/ledger/balances_test.go(3 hunks)internal/storage/ledger/moves_test.go(2 hunks)internal/storage/ledger/resource_schemas.go(1 hunks)internal/storage/ledger/schema.go(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)internal/storage/ledger/store.go(1 hunks)internal/storage/ledger/transactions.go(2 hunks)internal/storage/ledger/transactions_test.go(17 hunks)internal/storage/ledger/volumes_test.go(2 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/dotself.md(1 hunks)pkg/client/docs/models/components/v2chartaccountmetadata.md(1 hunks)pkg/client/docs/models/components/v2chartaccountrules.md(1 hunks)pkg/client/docs/models/components/v2chartsegment.md(1 hunks)pkg/client/docs/models/components/v2errorsenum.md(1 hunks)pkg/client/docs/models/components/v2log.md(1 hunks)pkg/client/docs/models/components/v2logtype.md(1 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/models/components/v2schemaresponse.md(1 hunks)pkg/client/docs/models/components/v2schemascursor.md(1 hunks)pkg/client/docs/models/components/v2schemascursorresponse.md(1 hunks)pkg/client/docs/models/operations/order.md(1 hunks)pkg/client/docs/models/operations/queryparamorder.md(1 hunks)pkg/client/docs/models/operations/sort.md(1 hunks)pkg/client/docs/models/operations/v2addmetadataontransactionrequest.md(1 hunks)pkg/client/docs/models/operations/v2addmetadatatoaccountrequest.md(1 hunks)pkg/client/docs/models/operations/v2createbulkrequest.md(1 hunks)pkg/client/docs/models/operations/v2createtransactionrequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2getschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2listschemasrequest.md(1 hunks)pkg/client/docs/models/operations/v2listschemasresponse.md(1 hunks)pkg/client/docs/models/operations/v2listtransactionsrequest.md(1 hunks)pkg/client/docs/models/operations/v2reverttransactionrequest.md(1 hunks)pkg/client/docs/sdks/v2/README.md(7 hunks)pkg/client/models/components/v2chartaccountmetadata.go(1 hunks)pkg/client/models/components/v2chartaccountrules.go(1 hunks)pkg/client/models/components/v2chartsegment.go(1 hunks)pkg/client/models/components/v2errorsenum.go(2 hunks)pkg/client/models/components/v2log.go(4 hunks)pkg/client/models/components/v2schema.go(1 hunks)pkg/client/models/components/v2schemadata.go(1 hunks)pkg/client/models/components/v2schemaresponse.go(1 hunks)pkg/client/models/components/v2schemascursor.go(1 hunks)pkg/client/models/components/v2schemascursorresponse.go(1 hunks)pkg/client/models/operations/v2addmetadataontransaction.go(2 hunks)pkg/client/models/operations/v2addmetadatatoaccount.go(2 hunks)pkg/client/models/operations/v2createbulk.go(2 hunks)pkg/client/models/operations/v2createtransaction.go(2 hunks)pkg/client/models/operations/v2getschema.go(1 hunks)pkg/client/models/operations/v2insertschema.go(1 hunks)pkg/client/models/operations/v2listschemas.go(1 hunks)pkg/client/models/operations/v2listtransactions.go(3 hunks)pkg/client/models/operations/v2reverttransaction.go(2 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)test/e2e/api_schema_test.go(1 hunks)test/e2e/api_transactions_list_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (5)
- pkg/client/docs/models/components/dotself.md
- pkg/client/docs/models/components/v2schemascursorresponse.md
- pkg/client/docs/models/operations/v2reverttransactionrequest.md
- pkg/client/docs/models/operations/sort.md
- pkg/client/docs/models/components/v2schema.md
🚧 Files skipped from review as they are similar to previous changes (37)
- pkg/client/docs/models/operations/v2addmetadatatoaccountrequest.md
- internal/api/v2/query.go
- pkg/client/docs/models/components/v2chartaccountrules.md
- pkg/client/models/operations/v2reverttransaction.go
- pkg/client/models/operations/v2addmetadatatoaccount.go
- internal/controller/ledger/listener.go
- cmd/docs_events.go
- internal/storage/ledger/store.go
- pkg/client/models/components/v2schemaresponse.go
- pkg/client/models/components/v2schemascursorresponse.go
- pkg/client/docs/models/operations/v2insertschemaresponse.md
- internal/api/v2/controllers_schema_read_test.go
- internal/controller/ledger/controller.go
- internal/controller/ledger/listener_generated_test.go
- pkg/client/docs/models/operations/v2getschemarequest.md
- pkg/client/docs/models/operations/queryparamorder.md
- internal/storage/ledger/schema.go
- internal/api/v2/controllers_schema_list.go
- internal/api/bulking/mocks_ledger_controller_test.go
- pkg/client/docs/models/operations/v2createtransactionrequest.md
- pkg/client/docs/models/operations/v2insertschemarequest.md
- pkg/client/models/operations/v2addmetadataontransaction.go
- internal/storage/bucket/default_bucket.go
- pkg/client/docs/models/components/v2errorsenum.md
- internal/controller/ledger/errors.go
- pkg/client/docs/models/operations/order.md
- test/e2e/api_transactions_list_test.go
- internal/bus/listener.go
- internal/storage/bucket/migrations/42-add-schema/up.sql
- pkg/client/models/components/v2schemascursor.go
- pkg/client/models/operations/v2listschemas.go
- pkg/client/models/operations/v2createtransaction.go
- pkg/client/docs/models/operations/v2getschemaresponse.md
- internal/controller/ledger/store.go
- pkg/client/docs/models/components/v2chartaccountmetadata.md
- pkg/client/docs/models/components/v2schemascursor.md
- pkg/client/docs/models/components/v2log.md
🧰 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/controller_default.gointernal/README.md
🧬 Code graph analysis (32)
internal/controller/ledger/controller_with_traces.go (5)
internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/tracing/tracing.go (1)
TraceWithMetric(101-129)internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
pkg/client/models/operations/v2getschema.go (1)
pkg/client/models/components/v2schemaresponse.go (1)
V2SchemaResponse(5-8)
internal/api/common/errors.go (1)
internal/controller/ledger/errors.go (3)
ErrSchemaNotSpecified(288-290)ErrSchemaNotFound(249-252)ErrSchemaValidationError(267-270)
pkg/events/message.go (3)
internal/schema.go (1)
Schema(15-21)internal/log.go (1)
UpdatedSchema(416-418)pkg/events/events.go (3)
EventApp(5-5)EventVersion(4-4)EventTypeUpdatedSchema(11-11)
internal/api/v2/views.go (1)
internal/schema.go (1)
Schema(15-21)
internal/api/bulking/bulker_test.go (1)
internal/machine/vm/run.go (1)
Run(57-87)
pkg/client/models/components/v2schemadata.go (1)
pkg/client/models/components/v2chartsegment.go (1)
V2ChartSegment(13-19)
internal/schema.go (2)
internal/chart.go (1)
ChartOfAccounts(40-40)internal/errors.go (1)
ErrInvalidSchema(91-93)
internal/api/bulking/bulker.go (1)
internal/api/bulking/elements.go (3)
Bulk(22-22)BulkElementResult(73-78)BulkElement(24-28)
internal/storage/ledger/resource_schemas.go (2)
internal/storage/common/resource.go (6)
EntitySchema(60-62)RepositoryHandlerBuildContext(74-77)ResourceQuery(410-416)ConvertOperatorToSQL(19-35)JoinCondition(37-40)RepositoryHandler(93-99)internal/storage/common/schema.go (3)
Field(28-32)NewStringField(65-67)NewDateField(70-72)
pkg/client/models/operations/v2listtransactions.go (1)
pkg/client/models/operations/v2listschemas.go (1)
Order(37-37)
internal/storage/ledger/transactions.go (2)
internal/controller/ledger/store.go (1)
Store(29-67)internal/storage/ledger/store.go (1)
Store(25-50)
internal/controller/ledger/controller_with_events.go (4)
internal/controller/ledger/controller.go (2)
UpdateSchema(135-138)Controller(21-89)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
UpdatedSchema(416-418)pkg/events/message.go (1)
UpdatedSchema(77-80)
internal/storage/ledger/transactions_test.go (3)
internal/transaction.go (1)
NewTransaction(228-232)internal/posting.go (1)
NewPosting(26-33)internal/controller/ledger/controller.go (1)
RevertTransaction(108-113)
internal/controller/ledger/log_process_test.go (4)
internal/controller/ledger/store.go (1)
Store(29-67)internal/schema.go (1)
Schema(15-21)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
CreatedTransaction(213-216)
pkg/client/models/operations/v2createbulk.go (1)
pkg/client/models/components/v2bulkelement.go (1)
V2BulkElement(21-28)
internal/storage/ledger/accounts.go (3)
internal/controller/ledger/store.go (1)
Store(29-67)internal/schema.go (1)
Schema(15-21)internal/account.go (1)
Account(16-26)
pkg/client/models/operations/v2insertschema.go (1)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-9)
internal/controller/ledger/log_process.go (8)
internal/controller/ledger/store.go (1)
Store(29-67)internal/storage/ledger/store.go (1)
Store(25-50)internal/schema.go (1)
Schema(15-21)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (3)
Log(89-102)NewLog(194-199)ComputeIdempotencyHash(457-466)internal/storage/ledger/logs.go (1)
Log(20-26)internal/controller/ledger/errors.go (3)
ErrNotFound(14-14)ErrSchemaNotFound(249-252)ErrSchemaNotSpecified(288-290)internal/api/common/errors.go (1)
ErrSchemaNotSpecified(28-28)
pkg/client/models/components/v2schema.go (2)
pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-9)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
internal/storage/ledger/schema_test.go (1)
internal/schema.go (2)
NewSchema(23-33)SchemaData(11-13)
internal/api/v1/mocks_ledger_controller_test.go (5)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)pkg/events/message.go (1)
UpdatedSchema(77-80)
internal/api/v2/mocks_ledger_controller_test.go (5)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)pkg/events/message.go (1)
UpdatedSchema(77-80)
internal/log.go (4)
internal/storage/ledger/logs.go (1)
Log(20-26)internal/schema.go (1)
Schema(15-21)internal/transaction.go (2)
Transaction(37-51)Transaction(53-60)pkg/events/message.go (1)
UpdatedSchema(77-80)
pkg/client/models/components/v2chartsegment.go (3)
pkg/client/models/components/v2chartaccountrules.go (1)
V2ChartAccountRules(5-6)pkg/client/models/components/v2chartaccountmetadata.go (1)
V2ChartAccountMetadata(5-7)pkg/client/internal/utils/json.go (2)
MarshalJSON(21-115)UnmarshalJSON(117-251)
internal/chart_test.go (2)
internal/chart.go (6)
ChartOfAccounts(40-40)ChartVariableSegment(27-32)ChartSegment(21-25)ChartAccount(16-19)ChartAccountRules(10-10)ChartAccountMetadata(12-14)internal/errors.go (1)
ErrInvalidAccount(103-108)
internal/controller/ledger/controller_with_too_many_client_handling.go (5)
internal/controller/ledger/controller.go (2)
UpdateSchema(135-138)Controller(21-89)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (2)
Log(89-102)UpdatedSchema(416-418)internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/api/v2/controllers_schema_read.go (1)
internal/api/common/errors.go (1)
HandleCommonErrors(38-49)
internal/api/v2/controllers_schema_insert_test.go (2)
internal/controller/ledger/errors.go (1)
ErrSchemaValidationError(267-270)internal/controller/ledger/controller.go (1)
UpdateSchema(135-138)
internal/controller/ledger/store_generated_test.go (2)
internal/schema.go (1)
Schema(15-21)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/controller/ledger/controller_default_test.go (5)
internal/schema.go (2)
Schema(15-21)SchemaData(11-13)internal/log.go (2)
Log(89-102)UpdatedSchemaLogType(25-25)internal/controller/ledger/controller.go (2)
UpdateSchema(135-138)RunScript(91-91)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/machine/vm/run.go (1)
RunScript(14-19)
internal/chart.go (2)
internal/storage/ledger/logs.go (1)
RawMessage(38-38)internal/errors.go (1)
ErrInvalidAccount(103-108)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/models/operations/v2listtransactionsrequest.md
13-13: Spaces inside emphasis markers
(MD037, no-space-in-emphasis)
13-13: Spaces inside emphasis markers
(MD037, no-space-in-emphasis)
pkg/client/docs/sdks/v2/README.md
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
252-252: Hard tabs
Column: 1
(MD010, no-hard-tabs)
253-253: Hard tabs
Column: 1
(MD010, no-hard-tabs)
254-254: Hard tabs
Column: 1
(MD010, no-hard-tabs)
255-255: Hard tabs
Column: 1
(MD010, no-hard-tabs)
321-321: Hard tabs
Column: 1
(MD010, no-hard-tabs)
322-322: Hard tabs
Column: 1
(MD010, no-hard-tabs)
323-323: Hard tabs
Column: 1
(MD010, no-hard-tabs)
324-324: Hard tabs
Column: 1
(MD010, no-hard-tabs)
325-325: Hard tabs
Column: 1
(MD010, no-hard-tabs)
326-326: Hard tabs
Column: 1
(MD010, no-hard-tabs)
381-381: Hard tabs
Column: 1
(MD010, no-hard-tabs)
382-382: Hard tabs
Column: 1
(MD010, no-hard-tabs)
383-383: Hard tabs
Column: 1
(MD010, no-hard-tabs)
384-384: Hard tabs
Column: 1
(MD010, no-hard-tabs)
385-385: Hard tabs
Column: 1
(MD010, no-hard-tabs)
386-386: Hard tabs
Column: 1
(MD010, no-hard-tabs)
docs/api/README.md
357-357: Blank line inside blockquote
(MD028, no-blanks-blockquote)
403-403: Blank line inside blockquote
(MD028, no-blanks-blockquote)
461-461: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
470-470: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5615-5615: Multiple headings with the same content
(MD024, no-duplicate-heading)
5617-5617: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5633-5633: Multiple headings with the same content
(MD024, no-duplicate-heading)
5659-5659: Multiple headings with the same content
(MD024, no-duplicate-heading)
5690-5690: Multiple headings with the same content
(MD024, no-duplicate-heading)
5718-5718: Multiple headings with the same content
(MD024, no-duplicate-heading)
5750-5750: Multiple headings with the same content
(MD024, no-duplicate-heading)
5784-5784: Multiple headings with the same content
(MD024, no-duplicate-heading)
5823-5823: Multiple headings with the same content
(MD024, no-duplicate-heading)
5860-5860: Multiple headings with the same content
(MD024, no-duplicate-heading)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/controller/ledger/controller_default_test.go (1)
25-88: Fix InsertLog DoAndReturn return type to avoid gomock runtime panicsIn
testCreateTransaction, theInsertLogexpectations for the inserted-schema log (lines 72-80) and the new-transaction log (lines 131-138) use:DoAndReturn(func(_ context.Context, log *ledger.Log) any { log.ID = pointer.For(uint64(0)) return log })Elsewhere in this file (e.g., Revert/Save/Delete transaction metadata tests),
InsertLog’sDoAndReturnclosures returnerror, which matches the typicalStore.InsertLog(ctx context.Context, log *ledger.Log) errorsignature. Returninganyhere is very likely to cause gomock’s reflection-based call to fail at runtime becauseinterface{}is not assignable toerror.Update both
InsertLogmocks to returnerrorinstead, e.g.:- DoAndReturn(func(_ context.Context, log *ledger.Log) any { - log.ID = pointer.For(uint64(0)) - return log - }) + DoAndReturn(func(_ context.Context, log *ledger.Log) error { + log.ID = pointer.For(uint64(0)) + return nil + })Apply the same change to the
NewTransactionLogTypeInsertLogexpectation at lines 131–138 for consistency with the rest of the tests.Also applies to: 131-138
♻️ Duplicate comments (5)
internal/README.md (1)
256-256: Regex inconsistency between chart segments and account address validation persists.Line 256 documents
ChartSegmentRegexp = regexp.MustCompile(\^($|.)?[a-zA-Z0-9_-]+$ `), which permits hyphens (-) in segment names. However, the HTTP API account-address regex documented elsewhere (^\w+(:\w+)*$) only allows word characters ([A-Za-z0-9_]`). This means chart segments defined with hyphens will never match valid account addresses, creating an inconsistency between schema configuration and address validation.This was flagged in previous reviews and remains unresolved. Pick one consistent rule:
- Either disallow
-fromChartSegmentRegexp(change to^(\$|\.)?[A-Za-z0-9_]+$)- Or broaden the account-address regex to accept hyphens (e.g.,
^[A-Za-z0-9_-]+(:[A-Za-z0-9_-]+)*$)Then update all related validation code, tests, and documentation accordingly.
#!/bin/bash # Description: Verify the regex implementations in chart.go and account validation. # Find and display ValidateSegment implementation echo "=== ValidateSegment implementation ===" rg -A 5 'func ValidateSegment' internal/ # Find and display account address validation rules echo -e "\n=== Account address validation (in API or validators) ===" rg -n 'account.*regex|address.*regex|^\s*\w+.*\(\:\w+' internal/ --type go -C 3 # Find all uses of ChartSegmentRegexp echo -e "\n=== ChartSegmentRegexp usage ===" rg -n 'ChartSegmentRegexp' internal/ --type go -C 2internal/storage/bucket/migrations/42-add-schema/up.sql (1)
29-47: Replace manual JSON string building incompute_hashwith JSONB constructors.Building the hash preimage JSON via string concatenation and
encode(r.memento, 'escape')is brittle and can generate invalid or unexpected JSON (e.g. embedded quotes, newlines, or non‑UTF8 bytes). It also makes future changes (like adding fields) error‑prone.You can construct the JSON payload in a type‑safe way and still keep the same hash semantics by using
jsonb_build_objectplus an optionalschemaVersionobject, e.g.:- select '{' || - '"type":"' || r.type || '",' || - '"data":' || encode(r.memento, 'escape') || ',' || - '"date":"' || (to_json(r.date::timestamp)#>>'{}') || 'Z",' || - '"idempotencyKey":"' || coalesce(r.idempotency_key, '') || '",' || - '"id":0,' || - '"hash":null' into marshalledAsJSON; - if r.schema_version is not null then - marshalledAsJSON := marshalledAsJSON || ',"schemaVersion":"' || r.schema_version || '"'; - end if; - marshalledAsJSON := marshalledAsJSON || '}'; + select ( + jsonb_build_object( + 'type', r.type, + 'data', r.memento, + 'date', to_jsonb(r.date::timestamp)#>>'{}' || 'Z', + 'idempotencyKey', coalesce(r.idempotency_key, ''), + 'id', 0, + 'hash', null + ) || + case + when r.schema_version is not null + then jsonb_build_object('schemaVersion', r.schema_version) + else '{}'::jsonb + end + )::text + into marshalledAsJSON;This keeps
marshalledAsJSONas text but relies on PostgreSQL’s JSON machinery for correct escaping and ordering, and usesto_jsonb(... )#>>'{}'for an ISO‑8601/RFC3339 timestamp string. Adjustr.memento’s type handling if it is not already JSON/JSONB. Based on learnings, this pattern avoids DateStyle‑dependent text formatting and unsafe string concatenation.pkg/client/docs/sdks/v2/README.md (1)
246-256: Replace hard tabs with spaces in new Go examples (MD010)The three newly added Go examples (InsertSchema, GetSchema, ListSchemas) still use hard tabs in their
importblocks, which triggers markdownlint MD010 and is inconsistent with the rest of the README examples.Recommend replacing the leading tab characters with spaces (e.g., 4 spaces) in those blocks, e.g.:
-import( - "context" - "os" - "github.com/formancehq/ledger/pkg/client/models/components" - "github.com/formancehq/ledger/pkg/client" - "github.com/formancehq/ledger/pkg/client/models/operations" - "log" -) +import( + "context" + "os" + "github.com/formancehq/ledger/pkg/client/models/components" + "github.com/formancehq/ledger/pkg/client" + "github.com/formancehq/ledger/pkg/client/models/operations" + "log" +)Apply the same pattern to the InsertSchema, GetSchema, and ListSchemas examples to satisfy MD010 and keep examples consistent.
Also applies to: 320-328, 380-388
internal/log.go (1)
449-454: Critical:json.Unmarshalwith pointer-to-interface may cause type erasure.Passing
&payload(a*interface{}) tojson.Unmarshalcauses it to replace the concrete struct pointer (e.g.,&CreatedTransaction{}) with a genericmap[string]interface{}. This breaks the subsequent.Elem().Interface().(LogPayload)cast.This issue was flagged in a previous review. Please verify it was addressed, or apply this fix:
- err := json.Unmarshal(data, &payload) + err := json.Unmarshal(data, payload)Run the following to verify the current behavior:
#!/bin/bash # Check if HydrateLog uses &payload or payload rg -n 'json\.Unmarshal.*&payload' internal/log.godocs/api/README.md (1)
5603-5868: Markdownlint issues in auto-generated schema section.Static analysis still flags MD024 (duplicate
### Propertiesheadings) and MD036 (emphasis as heading) in this section. These were previously flagged and marked as addressed.Please verify the markdownlint configuration or CI is updated to handle these auto-generated sections, or confirm that the generator/template was updated to produce unique headings.
#!/bin/bash # Check if markdownlint config exists and handles these rules fd -t f '.markdownlint' --exec cat {}
🧹 Nitpick comments (2)
internal/controller/ledger/controller.go (1)
83-88: Controller schema API surface looks coherent with the rest of the controller.The new
InsertSchema,GetSchema, andListSchemasmethods fit naturally alongside existing account/transaction/log APIs and use the right types (Parameters[InsertSchema],*ledger.Schema, cursor overledger.Schema).Consider tweaking the
InsertSchemacomment to clarify that it inserts a new schema version (chart of accounts) rather than “updates” it, to avoid ambiguity about mutability of past versions.internal/controller/ledger/controller_with_traces.go (1)
474-539: Align InsertSchema trace span name with method name
InsertSchemacurrently uses the span name"InsertedSchema"inTraceWithMetric, while other methods generally use a span equal to the method name (e.g.,"CreateTransaction","RevertTransaction","GetSchema","ListSchemas").For consistency in tracing/observability, consider:
- _, err = tracing.TraceWithMetric( - ctx, - "InsertedSchema", + _, err = tracing.TraceWithMetric( + ctx, + "InsertSchema", c.tracer, c.insertSchemaHistogram,This keeps the span naming convention aligned with the method surface.
📜 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.
⛔ Files ignored due to path filters (4)
docs/events/InsertedSchema.jsonis excluded by!**/*.jsonopenapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lock
📒 Files selected for processing (29)
cmd/docs_events.go(1 hunks)docs/api/README.md(13 hunks)internal/README.md(51 hunks)internal/api/bulking/mocks_ledger_controller_test.go(3 hunks)internal/api/common/mocks_ledger_controller_test.go(3 hunks)internal/api/v1/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(3 hunks)internal/bus/listener.go(1 hunks)internal/controller/ledger/controller.go(2 hunks)internal/controller/ledger/controller_default.go(12 hunks)internal/controller/ledger/controller_default_test.go(8 hunks)internal/controller/ledger/controller_generated_test.go(3 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(3 hunks)internal/controller/ledger/listener.go(1 hunks)internal/controller/ledger/listener_generated_test.go(1 hunks)internal/log.go(13 hunks)internal/storage/bucket/migrations/42-add-schema/up.sql(1 hunks)internal/storage/ledger/schema_test.go(1 hunks)pkg/client/README.md(1 hunks)pkg/client/docs/models/components/v2logtype.md(1 hunks)pkg/client/docs/sdks/v2/README.md(7 hunks)pkg/client/models/components/v2log.go(4 hunks)pkg/client/v2.go(1 hunks)pkg/events/events.go(1 hunks)pkg/events/message.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- cmd/docs_events.go
- internal/api/v2/controllers_schema_insert_test.go
- pkg/client/models/components/v2log.go
- pkg/events/events.go
- internal/controller/ledger/controller_with_events.go
- internal/api/v2/controllers_schema_insert.go
- pkg/client/README.md
- pkg/events/message.go
- internal/api/common/mocks_ledger_controller_test.go
- internal/storage/ledger/schema_test.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-28T10:06:01.381Z
Learnt from: gfyrag
Repo: formancehq/ledger PR: 1167
File: internal/storage/bucket/migrations/43-fix-missing-inserted-at-in-log-data/up.sql:23-23
Timestamp: 2025-11-28T10:06:01.381Z
Learning: In PostgreSQL migrations for the Formance ledger, to ensure ISO 8601/RFC3339 date formatting when setting JSONB fields, use the pattern `to_jsonb(date)#>>'{}'` to extract the timestamp as a properly formatted string. This leverages JSONB's built-in ISO 8601 encoding. Using `date::text` would depend on the server's DateStyle setting and doesn't guarantee ISO 8601 format.
Applied to files:
internal/storage/bucket/migrations/42-add-schema/up.sql
📚 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/controller_default.gointernal/README.md
🧬 Code graph analysis (9)
internal/controller/ledger/listener.go (3)
internal/log.go (1)
InsertedSchema(416-418)pkg/events/message.go (1)
InsertedSchema(77-80)internal/schema.go (1)
Schema(15-21)
internal/bus/listener.go (4)
internal/log.go (1)
InsertedSchema(416-418)pkg/events/message.go (2)
InsertedSchema(77-80)NewEventInsertedSchema(82-90)internal/schema.go (1)
Schema(15-21)pkg/events/events.go (1)
EventTypeInsertedSchema(11-11)
internal/controller/ledger/listener_generated_test.go (4)
internal/log.go (1)
InsertedSchema(416-418)pkg/events/message.go (1)
InsertedSchema(77-80)internal/schema.go (1)
Schema(15-21)internal/storage/ledger/main_test.go (1)
T(75-79)
internal/controller/ledger/controller.go (4)
internal/controller/ledger/parameters.go (1)
Parameters(3-8)pkg/events/message.go (1)
InsertedSchema(77-80)internal/schema.go (2)
Schema(15-21)SchemaData(11-13)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/api/bulking/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(15-21)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/api/v2/mocks_ledger_controller_test.go (5)
internal/schema.go (1)
Schema(15-21)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/storage/ledger/logs.go (1)
Log(20-26)pkg/events/message.go (1)
InsertedSchema(77-80)
internal/controller/ledger/controller_generated_test.go (5)
internal/schema.go (1)
Schema(15-21)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)pkg/events/message.go (1)
InsertedSchema(77-80)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/api/v1/mocks_ledger_controller_test.go (5)
internal/schema.go (1)
Schema(15-21)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)pkg/events/message.go (1)
InsertedSchema(77-80)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/log.go (3)
internal/schema.go (1)
Schema(15-21)internal/transaction.go (2)
Transaction(37-51)Transaction(53-60)pkg/events/message.go (1)
InsertedSchema(77-80)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/sdks/v2/README.md
250-250: Hard tabs
Column: 1
(MD010, no-hard-tabs)
251-251: Hard tabs
Column: 1
(MD010, no-hard-tabs)
252-252: Hard tabs
Column: 1
(MD010, no-hard-tabs)
253-253: Hard tabs
Column: 1
(MD010, no-hard-tabs)
254-254: Hard tabs
Column: 1
(MD010, no-hard-tabs)
255-255: Hard tabs
Column: 1
(MD010, no-hard-tabs)
321-321: Hard tabs
Column: 1
(MD010, no-hard-tabs)
322-322: Hard tabs
Column: 1
(MD010, no-hard-tabs)
323-323: Hard tabs
Column: 1
(MD010, no-hard-tabs)
324-324: Hard tabs
Column: 1
(MD010, no-hard-tabs)
325-325: Hard tabs
Column: 1
(MD010, no-hard-tabs)
326-326: Hard tabs
Column: 1
(MD010, no-hard-tabs)
381-381: Hard tabs
Column: 1
(MD010, no-hard-tabs)
382-382: Hard tabs
Column: 1
(MD010, no-hard-tabs)
383-383: Hard tabs
Column: 1
(MD010, no-hard-tabs)
384-384: Hard tabs
Column: 1
(MD010, no-hard-tabs)
385-385: Hard tabs
Column: 1
(MD010, no-hard-tabs)
386-386: Hard tabs
Column: 1
(MD010, no-hard-tabs)
docs/api/README.md
357-357: Blank line inside blockquote
(MD028, no-blanks-blockquote)
403-403: Blank line inside blockquote
(MD028, no-blanks-blockquote)
461-461: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
470-470: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5615-5615: Multiple headings with the same content
(MD024, no-duplicate-heading)
5617-5617: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5633-5633: Multiple headings with the same content
(MD024, no-duplicate-heading)
5659-5659: Multiple headings with the same content
(MD024, no-duplicate-heading)
5690-5690: Multiple headings with the same content
(MD024, no-duplicate-heading)
5718-5718: Multiple headings with the same content
(MD024, no-duplicate-heading)
5750-5750: Multiple headings with the same content
(MD024, no-duplicate-heading)
5784-5784: Multiple headings with the same content
(MD024, no-duplicate-heading)
5823-5823: Multiple headings with the same content
(MD024, no-duplicate-heading)
5860-5860: Multiple headings with the same content
(MD024, no-duplicate-heading)
⏰ 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). (4)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: GoReleaser
- GitHub Check: Dirty
- GitHub Check: Tests
🔇 Additional comments (30)
internal/README.md (2)
1159-1160: All LogPayload implementations include required schema validation methods.Verified that the LogPayload interface in
internal/log.go:201-205includesNeedsSchema() boolandValidateWithSchema(schema Schema) error. All five implementations are present with both methods:
- CreatedTransaction (lines 218, 221)
- SavedMetadata (lines 272, 275)
- DeletedMetadata (lines 324, 327)
- RevertedTransaction (lines 375, 378)
- InsertedSchema (lines 420, 423)
1075-1075: File reference correction needed: internal/log.go line 101, not internal/README.md line 1075.SchemaVersion field is properly initialized and used throughout the transaction pipeline:
- Extraction: SchemaVersion extracted from request query parameters (
internal/api/v2/query.go:14).- Propagation: Passed through Parameters struct to
log_process.goand assigned to log at line 112:log.SchemaVersion = parameters.SchemaVersion.- Validation: Schema is fetched using the provided SchemaVersion at line 75, and the log is validated with the schema at line 115 before insertion. If no schema version is specified, validation falls back to the latest schema version.
All initialization and propagation concerns are correctly addressed in the implementation.
internal/controller/ledger/listener_generated_test.go (1)
67-77: InsertedSchema mock wiring matches Listener interface.The mock and recorder methods for
InsertedSchemafollow the same pattern as existing listener methods and correctly mirror theListener.InsertedSchemasignature.pkg/client/v2.go (3)
656-849: InsertSchema client method is consistent with existing V2 patterns.URL, HTTP verb, body serialization (
V2SchemaData) and 204/no‑body success handling match how other write operations (e.g.CreateLedger) are implemented.
851-1057: GetSchema client method aligns with read‑style V2 operations.GET
/v2/{ledger}/schema/{version}with 200 + JSON decoded intocomponents.V2SchemaResponsemirrors other simple GET endpoints (GetLedger,GetTransaction) and reuses the standard retry and hooks flow.
1059-1269: ListSchemas client method correctly models cursor‑based listing.GET
/v2/{ledger}/schema, query‑param population, and unmarshalling intocomponents.V2SchemasCursorResponseare in line with other cursor‑based list methods (accounts, transactions, logs).pkg/client/docs/models/components/v2logtype.md (1)
11-12: Doc enum entry forINSERTED_SCHEMAis correct and aligned with code.The
V2LogTypeInsertedSchema→INSERTED_SCHEMArow matches the new log type introduced in the events/log pipeline and keeps the client docs coherent.internal/controller/ledger/listener.go (1)
12-18: InsertedSchema added to Listener with consistent signature.The new
InsertedSchemamethod uses the sameledger stringconvention as other methods and cleanly exposes schema insert events to listeners.internal/bus/listener.go (1)
29-35: InsertedSchema event publishing matches existing listener patterns.
LedgerListener.InsertedSchemacorrectly wraps the schema and ledger intoevents.InsertedSchemaand publishes onEventTypeInsertedSchema, consistent with the other event emitters in this type.internal/controller/ledger/controller_with_too_many_client_handling.go (1)
127-165: Schema controller wrapper methods correctly integrate with retry handling.
InsertSchema,GetSchema, andListSchemasfollow the samehandleRetrypattern as existing methods, preserve return types, and use the newcommon.PaginatedQuery[any]/bunpaginate.Cursor[ledger.Schema]types appropriately.internal/controller/ledger/controller.go (1)
135-138: InsertSchema input DTO aligns with internal schema model.The
InsertSchemastruct withVersionandData ledger.SchemaDatamatchesinternal.Schema/SchemaDataand cleanly separates version from chart payload.pkg/client/docs/sdks/v2/README.md (2)
11-13: Schema operations documentation looks consistent and completeInsertSchema, GetSchema, and ListSchemas are correctly wired into the operations list, with examples, parameters, responses, and error tables matching the rest of the V2 docs surface. No issues from a docs/API-shape perspective.
Also applies to: 240-310, 311-370, 371-429
644-645: SchemaVersion usage in examples is coherent with new schema APIThe added
SchemaVersion: client.String("v1.0.0")fields in the CreateBulk, AddMetadataToAccount, CreateTransaction, AddMetadataOnTransaction, and RevertTransaction examples consistently show how callers bind operations to a specific schema version, aligning with the new schema management API.Also applies to: 901-902, 1215-1216, 1377-1378, 1505-1506
internal/api/bulking/mocks_ledger_controller_test.go (1)
214-227: Schema-related bulking mocks correctly mirror controller interfaceThe new
GetSchema,InsertSchema, andListSchemasmock methods and their recorder counterparts follow the existing gomock pattern and match the controller signatures, so bulking tests can exercise schema flows without additional changes.Also applies to: 302-316, 363-376
internal/controller/ledger/controller_with_traces.go (1)
47-49: Histogram wiring for schema operations is consistentThe added
insertSchemaHistogram,getSchemaHistogram, andlistSchemasHistogramfields are initialized withmetric.WithUnit("ms")and follow the same pattern as existing controller latency histograms; no issues here.Also applies to: 155-166
internal/api/v1/mocks_ledger_controller_test.go (1)
214-227: v1 controller mocks correctly extended for schema operationsThe newly added
GetSchema,InsertSchema, andListSchemasmock methods and recorder helpers match the controller interface and follow the existing gomock style, enabling v1 tests to cover schema behavior without further plumbing changes.Also applies to: 302-316, 363-376
internal/controller/ledger/controller_default_test.go (2)
36-88: Schema-aware CreateTransaction test coverage is well structuredThe new
testCreateTransaction(t, withSchema bool)helper andTestCreateTransactionwrapper cleanly cover both cases:
withSchema == true: pre-insert a schema via the controller, then expectFindSchema("v1.0")andCommitTransactionwith the schema.withSchema == false: expectFindLatestSchemaVersionto returnnilandCommitTransactionwith anilschema pointer.The propagation of
SchemaVersioninto theParameters[CreateTransaction]request matches the controller’s new schema handling. Once theInsertLogmock return types are adjusted, this provides solid coverage of both branches.Also applies to: 90-129, 140-147
171-195: FindLatestSchemaVersion expectations align Revert/Metadata flows with schema handlingThe added expectations for
FindLatestSchemaVersioninTestRevertTransaction,TestSaveTransactionMetadata, andTestDeleteTransactionMetadataensure these controller paths consult the latest schema when no explicitSchemaVersionis supplied, and then proceed withCommitTransactionusing anilschema pointer. This matches the expected “no explicit schema version” behavior.Also applies to: 231-233, 281-283
internal/api/v2/mocks_ledger_controller_test.go (1)
214-227: v2 controller mocks correctly extended for schema operationsThe
GetSchema,InsertSchema, andListSchemasmock methods and recorder helpers accurately reflect the controller interface and follow the existing gomock patterns, so v2 API tests can set expectations on the new schema endpoints without additional changes.Also applies to: 302-316, 363-376
internal/controller/ledger/controller_generated_test.go (1)
213-226: LGTM - Generated mocks correctly reflect the new schema controller interface.The mock signatures for
GetSchema,InsertSchema, andListSchemascorrectly match the controller interface definitions, including proper parameter types (Parameters[InsertSchema],common.PaginatedQuery[any]) and return types (*ledger.Schema,*ledger.InsertedSchema,*bunpaginate.Cursor[ledger.Schema]).Also applies to: 301-315, 362-375
internal/log.go (3)
416-431: LGTM - InsertedSchema type correctly implements LogPayload interface.The implementation is consistent with other payload types:
NeedsSchema()correctly returnsfalsesince schema insertion doesn't require validation against an existing schemaValidateWithSchema()returnsnilappropriately- Interface compliance is verified via
var _ LogPayload = (*InsertedSchema)(nil)
218-229: LGTM - Transaction posting validation against schema chart.The
ValidateWithSchemaimplementation correctly iterates through all postings and validates each against the schema's chart of accounts. Early return on first error is appropriate for fail-fast behavior.
161-183: LGTM - SchemaVersion correctly integrated into log hash computation.The
omitemptytag onSchemaVersionensures backward compatibility with existing logs that don't have a schema version, maintaining consistent hash computation across versions.internal/controller/ledger/controller_default.go (4)
57-76: LGTM - Schema insertion with proper error handling.The implementation correctly:
- Creates schema using
ledger.NewSchema(which may return an error based on the signature)- Handles
postgres.ErrConstraintsFailedfor duplicate version detection- Returns the appropriate custom error via
newErrSchemaAlreadyExists
278-281: LGTM - Import correctly handles InsertedSchema payloads.The import path properly persists schemas when replaying logs, maintaining consistency between log history and schema state.
454-454: Note: Unused_schemaparameters indicate future validation hooks.These methods accept a schema parameter but don't currently use it. This aligns with the corresponding
ValidateWithSchemaimplementations ininternal/log.gothat returnnilfor these payload types. The design is forward-compatible for adding validation later.Also applies to: 524-524, 562-562, 584-584
381-448: LGTM - Schema correctly propagated through transaction creation.The schema is passed from the controller method through to
store.CommitTransaction, enabling validation of postings against the chart of accounts at the storage layer.docs/api/README.md (3)
318-378: Schema endpoint documentation looks correct.The insert schema endpoint (
POST /v2/{ledger}/schema/{version}) is properly documented with:
- Request body schema (
V2SchemaData)- Path parameters (
ledger,version)- Response codes (204 for success, default for error)
- Required authentication scope (
ledger:write)
4550-4555: LGTM - Schema-related error codes properly documented.The new
SCHEMA_ALREADY_EXISTSandSCHEMA_NOT_SPECIFIEDerror codes are correctly added to theV2ErrorsEnum, aligning with the error handling in the controller layer.
4014-4053: LGTM - V2Log schema correctly updated with schema support.The
schemaVersionfield andINSERTED_SCHEMAlog type are properly documented, matching the internalLogstruct changes ininternal/log.go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
6 issues found across 115 files
Prompt for AI agents (all 6 issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="openapi/v2.yaml">
<violation number="1" location="openapi/v2.yaml:2805">
P2: The `V2SchemasCursor` schema is missing the `previous` property that all other cursor types in this API include. This inconsistency will prevent backwards pagination for schema listings, unlike every other paginated endpoint in the API.</violation>
</file>
<file name="internal/log.go">
<violation number="1" location="internal/log.go:25">
P2: Comment says `"INSERT_SCHEMA"` but the actual string value is `"INSERTED_SCHEMA"`. This inconsistency could mislead developers and cause issues if someone uses the comment as reference for API/database values.</violation>
</file>
<file name="pkg/client/docs/models/components/v2chartaccountmetadata.md">
<violation number="1" location="pkg/client/docs/models/components/v2chartaccountmetadata.md:8">
P3: The `Default` field’s type is written as `**string*`, leaving unmatched emphasis markers and rendering incorrectly. Use a balanced emphasis so the type displays clearly (e.g., `*string*`).</violation>
</file>
<file name="internal/api/v2/controllers_schema_list_test.go">
<violation number="1" location="internal/api/v2/controllers_schema_list_test.go:145">
P2: The `expectQuery` field is defined in test cases but never used. The mock uses `gomock.Any()` instead of matching against `tc.expectQuery`, so pagination/sort parameter handling is not actually being verified. Consider using a matcher like `gomock.Eq(tc.expectQuery)` for the second argument.</violation>
</file>
<file name="internal/api/v2/controllers_schema_insert_test.go">
<violation number="1" location="internal/api/v2/controllers_schema_insert_test.go:101">
P2: The "invalid JSON" test case never sends malformed JSON because every request body is marshaled before sending, so regressions in invalid-JSON handling will pass unnoticed.</violation>
</file>
<file name="internal/controller/ledger/controller_default.go">
<violation number="1" location="internal/controller/ledger/controller_default.go:509">
P1: Revert transactions bypass schema validation by calling CommitTransaction with nil even though the schema is provided, so schema rules are not enforced when replaying reversals.</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
internal/log.go
Outdated
| NewTransactionLogType // "NEW_TRANSACTION" | ||
| RevertedTransactionLogType // "REVERTED_TRANSACTION" | ||
| DeleteMetadataLogType | ||
| InsertedSchemaLogType // "INSERT_SCHEMA" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Comment says "INSERT_SCHEMA" but the actual string value is "INSERTED_SCHEMA". This inconsistency could mislead developers and cause issues if someone uses the comment as reference for API/database values.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/log.go, line 25:
<comment>Comment says `"INSERT_SCHEMA"` but the actual string value is `"INSERTED_SCHEMA"`. This inconsistency could mislead developers and cause issues if someone uses the comment as reference for API/database values.</comment>
<file context>
@@ -22,6 +22,7 @@ const (
NewTransactionLogType // "NEW_TRANSACTION"
RevertedTransactionLogType // "REVERTED_TRANSACTION"
DeleteMetadataLogType
+ InsertedSchemaLogType // "INSERT_SCHEMA"
)
</file context>
| InsertedSchemaLogType // "INSERT_SCHEMA" | |
| InsertedSchemaLogType // "INSERTED_SCHEMA" |
✅ Addressed in 0cd6e7e
| systemController, ledgerController := newTestingSystemController(t, true) | ||
| if tc.expectBackendCall { | ||
| ledgerController.EXPECT(). | ||
| ListSchemas(gomock.Any(), gomock.Any()). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The expectQuery field is defined in test cases but never used. The mock uses gomock.Any() instead of matching against tc.expectQuery, so pagination/sort parameter handling is not actually being verified. Consider using a matcher like gomock.Eq(tc.expectQuery) for the second argument.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/api/v2/controllers_schema_list_test.go, line 145:
<comment>The `expectQuery` field is defined in test cases but never used. The mock uses `gomock.Any()` instead of matching against `tc.expectQuery`, so pagination/sort parameter handling is not actually being verified. Consider using a matcher like `gomock.Eq(tc.expectQuery)` for the second argument.</comment>
<file context>
@@ -0,0 +1,170 @@
+ systemController, ledgerController := newTestingSystemController(t, true)
+ if tc.expectBackendCall {
+ ledgerController.EXPECT().
+ ListSchemas(gomock.Any(), gomock.Any()).
+ Return(tc.returnCursor, tc.returnErr)
+ }
</file context>
| ListSchemas(gomock.Any(), gomock.Any()). | |
| ListSchemas(gomock.Any(), tc.expectQuery). |
✅ Addressed in 8ac494d
| } | ||
|
|
||
| err = store.CommitTransaction(ctx, &reversedTx, nil) | ||
| err = store.CommitTransaction(ctx, nil, &reversedTx, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: Revert transactions bypass schema validation by calling CommitTransaction with nil even though the schema is provided, so schema rules are not enforced when replaying reversals.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/controller/ledger/controller_default.go, line 509:
<comment>Revert transactions bypass schema validation by calling CommitTransaction with nil even though the schema is provided, so schema rules are not enforced when replaying reversals.</comment>
<file context>
@@ -471,7 +506,7 @@ func (ctrl *DefaultController) revertTransaction(ctx context.Context, store Stor
}
- err = store.CommitTransaction(ctx, &reversedTx, nil)
+ err = store.CommitTransaction(ctx, nil, &reversedTx, nil)
if err != nil {
return nil, fmt.Errorf("failed to insert transaction: %w", err)
</file context>
| err = store.CommitTransaction(ctx, nil, &reversedTx, nil) | |
| err = store.CommitTransaction(ctx, _schema, &reversedTx, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
internal/log.go (2)
25-25: Fix comment to match actual serialization.The comment shows
"INSERT_SCHEMA"but the actual string value (line 65) is"INSERTED_SCHEMA".- InsertedSchemaLogType // "INSERT_SCHEMA" + InsertedSchemaLogType // "INSERTED_SCHEMA"
449-449: Critical: Fix HydrateLog unmarshalling.Passing
&payloadtojson.Unmarshalcauses the concrete struct pointers to be replaced with generic types. This should bejson.Unmarshal(data, payload)to unmarshal into the existing concrete pointer.- err := json.Unmarshal(data, &payload) + err := json.Unmarshal(data, payload)internal/README.md (1)
1190-1190: Documentation reflects source code issue.This auto-generated documentation shows
"INSERT_SCHEMA"because the source code comment ininternal/log.goline 25 is incorrect. Once that source comment is fixed to"INSERTED_SCHEMA", regenerating this documentation will automatically fix it.internal/api/v2/controllers_schema_insert_test.go (1)
58-64: Test case doesn't validate invalid JSON handling.The test named "invalid body" marshals a string value, producing valid JSON (
"not an object"), so it doesn't test malformed JSON handling.To test actual invalid JSON parsing, use raw bytes:
{ - name: "invalid body", + name: "invalid body", version: "v1.0.0", - requestBody: "not an object", expectStatusCode: http.StatusBadRequest, expectedErrorCode: "VALIDATION", expectBackendCall: false, },And update the test execution (around line 101):
// For the invalid JSON test case, use raw malformed JSON var body []byte if tc.name == "invalid body" { body = []byte("{invalid json") } else { body, _ = json.Marshal(tc.requestBody) }
🧹 Nitpick comments (1)
internal/log.go (1)
272-277: Consider whether SavedMetadata truly needs schema.
SavedMetadata.NeedsSchema()returnstruebutValidateWithSchema()performs no validation (returnsnil). If metadata operations don't require schema validation, consider returningfalsefromNeedsSchema()for consistency.The same pattern appears in
DeletedMetadata(lines 324-329) andRevertedTransaction(lines 375-380).
📜 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.
⛔ Files ignored due to path filters (5)
openapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (14)
docs/api/README.md(25 hunks)internal/README.md(51 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/api/v2/controllers_schema_list_test.go(1 hunks)internal/log.go(13 hunks)pkg/client/docs/models/components/v2accountscursorresponsecursor.md(1 hunks)pkg/client/docs/models/components/v2ledgerlistresponsecursor.md(1 hunks)pkg/client/docs/models/components/v2listexportersresponsecursorcursor.md(1 hunks)pkg/client/docs/models/components/v2listpipelinesresponsecursorcursor.md(1 hunks)pkg/client/docs/models/components/v2logscursorresponsecursor.md(1 hunks)pkg/client/docs/models/components/v2schemascursor.md(1 hunks)pkg/client/docs/models/components/v2transactionscursorresponsecursor.md(1 hunks)pkg/client/docs/models/components/v2volumeswithbalancecursorresponsecursor.md(1 hunks)pkg/client/models/components/v2schemascursor.go(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- pkg/client/docs/models/components/v2volumeswithbalancecursorresponsecursor.md
- pkg/client/docs/models/components/v2listexportersresponsecursorcursor.md
🚧 Files skipped from review as they are similar to previous changes (2)
- pkg/client/models/components/v2schemascursor.go
- pkg/client/docs/models/components/v2schemascursor.md
🧰 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/README.md
🧬 Code graph analysis (3)
internal/api/v2/controllers_schema_list_test.go (2)
internal/storage/common/pagination.go (3)
PaginatedQuery(26-29)InitialPaginatedQuery(10-15)OffsetPaginatedQuery(16-19)internal/schema.go (2)
Schema(15-21)SchemaData(11-13)
internal/api/v2/controllers_schema_insert_test.go (2)
internal/controller/ledger/errors.go (1)
ErrSchemaValidationError(267-270)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)
internal/log.go (4)
internal/schema.go (1)
Schema(15-21)internal/transaction.go (2)
Transaction(37-51)Transaction(53-60)internal/posting.go (1)
Postings(35-35)pkg/events/message.go (1)
InsertedSchema(77-80)
🪛 markdownlint-cli2 (0.18.1)
docs/api/README.md
357-357: Blank line inside blockquote
(MD028, no-blanks-blockquote)
403-403: Blank line inside blockquote
(MD028, no-blanks-blockquote)
461-461: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
470-470: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5616-5616: Multiple headings with the same content
(MD024, no-duplicate-heading)
5618-5618: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5634-5634: Multiple headings with the same content
(MD024, no-duplicate-heading)
5660-5660: Multiple headings with the same content
(MD024, no-duplicate-heading)
5691-5691: Multiple headings with the same content
(MD024, no-duplicate-heading)
5719-5719: Multiple headings with the same content
(MD024, no-duplicate-heading)
5751-5751: Multiple headings with the same content
(MD024, no-duplicate-heading)
5785-5785: Multiple headings with the same content
(MD024, no-duplicate-heading)
5825-5825: Multiple headings with the same content
(MD024, no-duplicate-heading)
5863-5863: Multiple headings with the same content
(MD024, no-duplicate-heading)
⏰ 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). (4)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Tests
- GitHub Check: GoReleaser
- GitHub Check: Dirty
🔇 Additional comments (16)
pkg/client/docs/models/components/v2accountscursorresponsecursor.md (1)
11-11: Verify this documentation file was regenerated via proper tooling.The example value for the
Nextfield has been updated to a realistic non-empty Base64-encoded string, which aligns with broader cursor documentation updates across the PR. However, this file appears to be auto-generated client documentation, which typically should not be manually edited.Please confirm whether this file was regenerated using the appropriate code generation tooling (e.g., OpenAPI generator, Swagger codegen, etc.) rather than manually edited. If auto-generated, ensure the proper regeneration workflow was followed to keep the generated files in sync with the source definitions.
pkg/client/docs/models/components/v2transactionscursorresponsecursor.md (1)
11-11: LGTM! Documentation example improved.The non-empty base64 example for the
Nextfield provides better documentation for pagination token usage.pkg/client/docs/models/components/v2listpipelinesresponsecursorcursor.md (1)
11-11: LGTM! Consistent documentation improvement.The non-empty example aligns with the pagination token pattern used across other cursor documentation.
pkg/client/docs/models/components/v2logscursorresponsecursor.md (1)
11-11: LGTM! Documentation consistency maintained.pkg/client/docs/models/components/v2ledgerlistresponsecursor.md (1)
11-11: LGTM! Documentation pattern consistent.internal/api/v2/controllers_schema_list_test.go (1)
142-146: LGTM! Past review concern addressed.The mock now correctly validates the query parameter using
tc.expectQueryinstead ofgomock.Any(), ensuring pagination and sort parameters are properly verified.internal/log.go (3)
101-101: LGTM! SchemaVersion field properly integrated.The
SchemaVersionfield is correctly added to theLogstruct with appropriate JSON and Bun tags, and is properly included in hash computation (lines 169, 177).
190-192: LGTM! Clean delegation pattern.The
ValidateWithSchemamethod correctly delegates to the payload's validation logic.
416-431: LGTM! InsertedSchema implementation is correct.The
InsertedSchemapayload correctly returnsfalseforNeedsSchema()since schema insertion doesn't require validation against an existing schema.internal/README.md (1)
19-257: LGTM! Comprehensive schema API documentation.The auto-generated documentation properly captures the new schema-related public API surface, including
ChartOfAccounts,ChartSegment,Schema, and related types and methods.internal/api/v2/controllers_schema_insert_test.go (1)
21-116: Test structure is well-designed.The table-driven test properly covers nominal cases, empty data, backend errors, and validation errors. Good use of parallel execution and proper assertions.
docs/api/README.md (5)
318-509: Schema endpoints are correctly defined and documented. The three operations (insert, get, list) follow REST conventions with appropriate HTTP methods, parameters, and response types. The wording on line 318 correctly reflects the insert-only semantics (addressing prior feedback). Pagination support for the list endpoint is good practice.
734-734: schemaVersion parameters correctly integrated across affected endpoints. All write operations (create transaction, metadata operations, revert) consistently expose an optional query parameter for schema version. Descriptions are clear, and backward compatibility is maintained by making the parameter optional. Bulk request endpoint (line 734) properly notes that the parameter can be overridden per element.Also applies to: 1139-1139, 1566-1566, 1842-1842, 1907-1907
2219-2220: schemaVersion field correctly added to V2Log. The field is optional, which maintains backward compatibility. The description "Schema version used for validation" is clear. The new log typeINSERTED_SCHEMA(line 4053) is properly integrated into the enumerated values, supporting schema creation events.Also applies to: 4028-4043, 4053-4053
4553-4554: New error codes added appropriately.SCHEMA_ALREADY_EXISTSandSCHEMA_NOT_SPECIFIEDalign with the insert-only schema semantics and validation requirements. Error code naming is clear and self-documenting.
5604-5872: V2ChartSegment documentation could be enriched, but no linting violations or CI issues are present.The schema models are structurally sound. While V2ChartSegment uses a recursive, self-referential structure with fields like
.pattern,.rules,.metadata, and.self, the documentation for these fields is minimal (descriptions show "none"). For improved clarity, consider enriching the OpenAPI specification to describe:
- The purpose of
.pattern(e.g., validation regex for account identifiers)- When
.rulesand.metadataapply vs. remain unused- A realistic example showing how nested segments map to account hierarchies
The file is auto-generated by Widdershins v4.0.1 from the OpenAPI spec. There are no active linting checks in the CI pipeline that would block this PR based on markdown style violations.
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 issues found across 122 files
Prompt for AI agents (all 2 issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="internal/controller/ledger/controller_with_traces.go">
<violation number="1" location="internal/controller/ledger/controller_with_traces.go:482">
P2: Inconsistent span name: should be `"InsertSchema"` to match the method name, not `"InsertedSchema"`. All other methods in this file use the exact method name as the span name (e.g., `CreateTransaction`, `GetSchema`, `ListSchemas`).</violation>
</file>
<file name="internal/api/v2/controllers_schema_list_test.go">
<violation number="1" location="internal/api/v2/controllers_schema_list_test.go:83">
P2: Inconsistent `Options` field: this test case doesn't set `Expand: make([]string, 0)` like the other test cases do. If the actual implementation always initializes `Expand` to an empty slice, gomock's comparison will fail because `nil != []string{}`. Consider adding `Expand: make([]string, 0)` for consistency.</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
| expectQuery: storagecommon.OffsetPaginatedQuery[any]{ | ||
| InitialPaginatedQuery: storagecommon.InitialPaginatedQuery[any]{ | ||
| PageSize: 10, | ||
| Options: storagecommon.ResourceQuery[any]{}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Inconsistent Options field: this test case doesn't set Expand: make([]string, 0) like the other test cases do. If the actual implementation always initializes Expand to an empty slice, gomock's comparison will fail because nil != []string{}. Consider adding Expand: make([]string, 0) for consistency.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/api/v2/controllers_schema_list_test.go, line 83:
<comment>Inconsistent `Options` field: this test case doesn't set `Expand: make([]string, 0)` like the other test cases do. If the actual implementation always initializes `Expand` to an empty slice, gomock's comparison will fail because `nil != []string{}`. Consider adding `Expand: make([]string, 0)` for consistency.</comment>
<file context>
@@ -0,0 +1,169 @@
+ expectQuery: storagecommon.OffsetPaginatedQuery[any]{
+ InitialPaginatedQuery: storagecommon.InitialPaginatedQuery[any]{
+ PageSize: 10,
+ Options: storagecommon.ResourceQuery[any]{},
+ },
+ Offset: 0,
</file context>
| Options: storagecommon.ResourceQuery[any]{}, | |
| Options: storagecommon.ResourceQuery[any]{ | |
| Expand: make([]string, 0), | |
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
docs/api/README.md (1)
5636-5665: Chart segment structure is correct but could benefit from clearer documentation on nesting semantics.The
V2ChartSegmentrecursive structure is appropriate for hierarchical account definitions, but the documentation lacks clarity on intended usage:Current state:
- Recursive
additionalPropertiespointing to V2ChartSegment allows nesting- Optional
.pattern,.rules,.metadataproperties are defined but lack explanation of their interplay- Example shows basic structure (
users: { $userID: { .pattern: ... } }) but doesn't clarify how intermediate vs. leaf segments differRecommendation: Add a brief note (inline or in the description at line 5654) explaining:
- When to use
.pattern(validation regex for this segment)- When to use
.rules(custom validation rules for this node)- When to use
.metadata(node-specific attributes)- How recursive nesting works (intermediate segments vs. leaves)
Example: "Chart segments are hierarchical. Use
.patternfor segment validation,.rulesfor custom constraints,.metadatafor segment-level attributes, and nest segments recursively to build complex account structures."This aligns with the prior review's suggestion and would prevent client confusion when implementing schema-based account hierarchies.
📜 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.
⛔ Files ignored due to path filters (5)
openapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (3)
docs/api/README.md(25 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/models/components/v2schema.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/client/docs/models/components/v2schema.md
🧰 Additional context used
🪛 LanguageTool
docs/api/README.md
[grammar] ~5746-~5746: Ensure spelling is correct
Context: ...tructure with metadata ### Properties allOf |Name|Type|Required|Restrictions|Descrip...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 markdownlint-cli2 (0.18.1)
docs/api/README.md
357-357: Blank line inside blockquote
(MD028, no-blanks-blockquote)
403-403: Blank line inside blockquote
(MD028, no-blanks-blockquote)
459-459: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
468-468: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5612-5612: Multiple headings with the same content
(MD024, no-duplicate-heading)
5614-5614: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5630-5630: Multiple headings with the same content
(MD024, no-duplicate-heading)
5656-5656: Multiple headings with the same content
(MD024, no-duplicate-heading)
5687-5687: Multiple headings with the same content
(MD024, no-duplicate-heading)
5715-5715: Multiple headings with the same content
(MD024, no-duplicate-heading)
5745-5745: Multiple headings with the same content
(MD024, no-duplicate-heading)
5785-5785: Multiple headings with the same content
(MD024, no-duplicate-heading)
5823-5823: Multiple headings with the same content
(MD024, no-duplicate-heading)
5859-5859: Multiple headings with the same content
(MD024, no-duplicate-heading)
🔇 Additional comments (6)
pkg/client/models/components/v2schema.go (1)
1-50: Generated code structure looks correct; UnmarshalJSON concern previously acknowledged.This Speakeasy-generated file implements the V2Schema model with proper JSON marshaling, nil-safe accessors, and documented fields. As noted in previous reviews, the
UnmarshalJSONmethod at lines 24-27 passes&v(creating**V2Schema) to the utils helper, which you've already explained is part of the generator's implementation and cannot be manually modified.The overall structure is appropriate for generated code, and no new concerns are identified beyond what was previously discussed.
docs/api/README.md (5)
318-506: Schema endpoints are well-documented and semantically correct.The three new schema operations (insert, get, list) are properly structured with:
- Clear semantics: POST is insert-only (204 No Content response, no body), GET operations return 200 with data
- Correct parameter descriptions and example payloads
- Proper error responses linked to V2ErrorResponse
- Consistent with the prior review feedback (title correctly states "Insert a schema" rather than "Insert or update")
No issues flagged.
730-730: schemaVersion parameter additions are consistent and well-integrated.The optional
schemaVersionquery parameter is added consistently across bulk, transaction, and metadata endpoints with appropriate descriptions:
- Bulk endpoint (line 730): "Default schema version to use for validation (can be overridden per element)" — correctly notes override semantics
- Transaction & metadata endpoints (lines 1562, 1838, 1903, 1135): "Schema version to use for validation" — simple and clear
All are properly marked as optional strings, supporting backward compatibility. Descriptions vary slightly but appropriately reflect endpoint-specific semantics.
Also applies to: 1135-1135, 1562-1562, 1838-1838, 1903-1903
4024-4039: V2Log schema updates properly integrate schemaVersion.The
schemaVersionfield is correctly added to the V2Log schema:
- Marked as optional (
falsein Required column, line 4039) — appropriate for backward compatibility- Documentation: "Schema version used for validation" — clear and accurate
- Example payload includes field (line 2216:
"schemaVersion": "v1.0.0")- New
INSERTED_SCHEMAlog type added to enumeration (line 4049) — semantically soundNo issues flagged.
Also applies to: 4041-4049
4549-4550: New error codes are semantically correct and properly placed.The two new error types are appropriate and well-positioned:
SCHEMA_ALREADY_EXISTS: Aligns with insert-only schema endpoint semantics (conflicts on duplicate version)SCHEMA_NOT_SPECIFIED: Supports validation-required operations that lack schema contextBoth fit the existing error enumeration pattern and are referenced by the documented endpoints.
5600-5868: Markdownlint violations in schema sections exist but do not block CI.The file contains real violations:
- MD024 (duplicate "### Properties" headings): Multiple instances at lines 5687, 5715, 5745, 5785, 5823, 5859, and throughout
- MD036 (emphasis as heading): "None" at line 5614
- MD028 (blank lines in blockquotes): Present at lines 357, 403, 468
However,
docs/api/README.mdis auto-generated by Widdershins (header confirms "") and is regenerated during theopenapiJustfile target. The repository's CI pipeline (Justfile) runs onlygolangci-lintfor linting—there is no markdownlint invocation. These violations do not prevent merge.If the violations should be addressed:
- Option A: Update the Widdershins template or post-processing to avoid duplicate headings and format
*None*properly- Option B: Add markdownlint configuration and a CI target to validate the generated markdown
- Option C: Accept them as a known limitation of the auto-generated output (no action needed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
5 issues found across 122 files
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.
Prompt for AI agents (all 5 issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="internal/controller/ledger/controller_with_traces.go">
<violation number="1" location="internal/controller/ledger/controller_with_traces.go:482">
P2: Trace name "InsertedSchema" is inconsistent with the method name `InsertSchema`. All other methods use their exact method name for tracing (e.g., "CreateTransaction", "GetSchema", "ListSchemas"). This inconsistency could make trace analysis and filtering more difficult.</violation>
</file>
<file name="internal/api/common/errors.go">
<violation number="1" location="internal/api/common/errors.go:43">
P1: `errors.Unwrap(err)` will return `nil` because `ErrSchemaNotSpecified{}` doesn't implement the `Unwrap()` method. Use `err` directly to return the actual error message, consistent with existing patterns (e.g., line 58).</violation>
<violation number="2" location="internal/api/common/errors.go:45">
P1: `errors.Unwrap(err)` will return `nil` because `ErrSchemaNotFound{}` doesn't implement the `Unwrap()` method. Use `err` directly to return the actual error message.</violation>
<violation number="3" location="internal/api/common/errors.go:60">
P1: `errors.Unwrap(err)` will return `nil` because `ErrSchemaValidationError{}` doesn't implement the `Unwrap()` method. Use `err` directly to return the actual error message.</violation>
</file>
<file name="internal/errors.go">
<violation number="1" location="internal/errors.go:118">
P3: Typo: "chart of account" should be "chart of accounts" to be consistent with the other error messages in this function.</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
| case errors.Is(err, ledgercontroller.ErrNotFound): | ||
| api.NotFound(w, err) | ||
| case errors.Is(err, ledgercontroller.ErrSchemaValidationError{}): | ||
| api.BadRequest(w, ErrValidation, errors.Unwrap(err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: errors.Unwrap(err) will return nil because ErrSchemaValidationError{} doesn't implement the Unwrap() method. Use err directly to return the actual error message.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/api/common/errors.go, line 60:
<comment>`errors.Unwrap(err)` will return `nil` because `ErrSchemaValidationError{}` doesn't implement the `Unwrap()` method. Use `err` directly to return the actual error message.</comment>
<file context>
@@ -50,6 +56,12 @@ func HandleCommonWriteErrors(w http.ResponseWriter, r *http.Request, err error)
case errors.Is(err, ledgercontroller.ErrNotFound):
api.NotFound(w, err)
+ case errors.Is(err, ledgercontroller.ErrSchemaValidationError{}):
+ api.BadRequest(w, ErrValidation, errors.Unwrap(err))
+ case errors.Is(err, ledgercontroller.ErrSchemaNotSpecified{}):
+ api.BadRequest(w, ErrSchemaNotSpecified, errors.Unwrap(err))
</file context>
| api.BadRequest(w, ErrValidation, errors.Unwrap(err)) | |
| api.BadRequest(w, ErrValidation, err) |
| case errors.Is(err, ledgercontroller.ErrSchemaNotSpecified{}): | ||
| api.BadRequest(w, ErrSchemaNotSpecified, errors.Unwrap(err)) | ||
| case errors.Is(err, ledgercontroller.ErrSchemaNotFound{}): | ||
| api.NotFound(w, errors.Unwrap(err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: errors.Unwrap(err) will return nil because ErrSchemaNotFound{} doesn't implement the Unwrap() method. Use err directly to return the actual error message.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/api/common/errors.go, line 45:
<comment>`errors.Unwrap(err)` will return `nil` because `ErrSchemaNotFound{}` doesn't implement the `Unwrap()` method. Use `err` directly to return the actual error message.</comment>
<file context>
@@ -37,6 +39,10 @@ func HandleCommonErrors(w http.ResponseWriter, r *http.Request, err error) {
+ case errors.Is(err, ledgercontroller.ErrSchemaNotSpecified{}):
+ api.BadRequest(w, ErrSchemaNotSpecified, errors.Unwrap(err))
+ case errors.Is(err, ledgercontroller.ErrSchemaNotFound{}):
+ api.NotFound(w, errors.Unwrap(err))
default:
InternalServerError(w, r, err)
</file context>
| api.NotFound(w, errors.Unwrap(err)) | |
| api.NotFound(w, err) |
| case errors.Is(err, postgres.ErrTooManyClient{}): | ||
| api.WriteErrorResponse(w, http.StatusServiceUnavailable, api.ErrorInternal, err) | ||
| case errors.Is(err, ledgercontroller.ErrSchemaNotSpecified{}): | ||
| api.BadRequest(w, ErrSchemaNotSpecified, errors.Unwrap(err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: errors.Unwrap(err) will return nil because ErrSchemaNotSpecified{} doesn't implement the Unwrap() method. Use err directly to return the actual error message, consistent with existing patterns (e.g., line 58).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/api/common/errors.go, line 43:
<comment>`errors.Unwrap(err)` will return `nil` because `ErrSchemaNotSpecified{}` doesn't implement the `Unwrap()` method. Use `err` directly to return the actual error message, consistent with existing patterns (e.g., line 58).</comment>
<file context>
@@ -37,6 +39,10 @@ func HandleCommonErrors(w http.ResponseWriter, r *http.Request, err error) {
case errors.Is(err, postgres.ErrTooManyClient{}):
api.WriteErrorResponse(w, http.StatusServiceUnavailable, api.ErrorInternal, err)
+ case errors.Is(err, ledgercontroller.ErrSchemaNotSpecified{}):
+ api.BadRequest(w, ErrSchemaNotSpecified, errors.Unwrap(err))
+ case errors.Is(err, ledgercontroller.ErrSchemaNotFound{}):
+ api.NotFound(w, errors.Unwrap(err))
</file context>
| api.BadRequest(w, ErrSchemaNotSpecified, errors.Unwrap(err)) | |
| api.BadRequest(w, ErrSchemaNotSpecified, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 12 files (changes from recent commits).
Prompt for AI agents (all 1 issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="internal/chart.go">
<violation number="1" location="internal/chart.go:203">
P1: This condition will always be `false` because `ChartAccountRules` is an empty struct (`type ChartAccountRules struct{}`). In Go, all instances of an empty struct are equal, so comparing against `(ChartAccountRules{})` will never yield a difference. As a result, `RULES_KEY` and `SELF_KEY` will never be serialized. If `ChartAccountRules` is intended to have fields added later, consider removing this condition until fields exist, or add fields to make the comparison meaningful.</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
| if s.Account.Metadata != nil { | ||
| out[METADATA_KEY] = s.Account.Metadata | ||
| } | ||
| if s.Account.Rules != (ChartAccountRules{}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: This condition will always be false because ChartAccountRules is an empty struct (type ChartAccountRules struct{}). In Go, all instances of an empty struct are equal, so comparing against (ChartAccountRules{}) will never yield a difference. As a result, RULES_KEY and SELF_KEY will never be serialized. If ChartAccountRules is intended to have fields added later, consider removing this condition until fields exist, or add fields to make the comparison meaningful.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/chart.go, line 203:
<comment>This condition will always be `false` because `ChartAccountRules` is an empty struct (`type ChartAccountRules struct{}`). In Go, all instances of an empty struct are equal, so comparing against `(ChartAccountRules{})` will never yield a difference. As a result, `RULES_KEY` and `SELF_KEY` will never be serialized. If `ChartAccountRules` is intended to have fields added later, consider removing this condition until fields exist, or add fields to make the comparison meaningful.</comment>
<file context>
@@ -200,9 +200,11 @@ func (s ChartSegment) marshalJsonObject() (map[string]any, error) {
- out[RULES_KEY] = s.Account.Rules
- if len(s.FixedSegments) > 0 || s.VariableSegment != nil {
- out[SELF_KEY] = map[string]interface{}{}
+ if s.Account.Rules != (ChartAccountRules{}) {
+ out[RULES_KEY] = s.Account.Rules
+ if len(s.FixedSegments) > 0 || s.VariableSegment != nil {
</file context>
✅ Addressed in c424d5a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
internal/README.md (1)
253-257: Reconfirm regex consistency between chart segments and account addresses (already flagged before)
ChartSegmentRegexpis documented as^(\$|\.)?[a-zA-Z0-9_-]+$, which allows-in segment names. The HTTP API’s account-address regex currently only allows\w(no-). That means you can define chart segments that can never be referenced by valid addresses.If this divergence isn’t intentional, it’d be good to align the two (either tighten
ChartSegmentRegexpor broaden the address regex in the HTTP surface) and then regenerate this README. This repeats an existing comment, but the inconsistency still appears here.
🧹 Nitpick comments (2)
internal/chart_test.go (1)
391-415: Name the failing cases inTestPostingValidationfor clearer diagnosticsTwo table entries in
TestPostingValidationhavename: "", so failures will show up without a helpful case name. Consider giving them descriptive names (e.g."invalid source account","invalid destination account") so failures are easier to interpret.internal/chart.go (1)
247-271: Avoid recompiling regex on every account lookup
findAccountSchemacallsregexp.Match(variableSegment.Pattern, ...)on every lookup, which recompiles the pattern each time even though it was already validated during unmarshal.This is not a correctness bug, but it’s unnecessary overhead on hot paths (especially if schemas are large and lookups frequent).
Consider precompiling once and storing the compiled regexp on the variable segment:
type ChartVariableSegment struct { - ChartSegment - - Pattern string - Label string + ChartSegment + + Pattern string + Label string + + re *regexp.Regexp // compiled pattern, not exported/serialized } @@ in (*ChartSegment).UnmarshalJSON when setting variableSegment: - variableSegment = &ChartVariableSegment{ - ChartSegment: segment, - Pattern: *pattern, - Label: key[1:], - } + compiled, _ := regexp.Compile(*pattern) // safe: already validated above + variableSegment = &ChartVariableSegment{ + ChartSegment: segment, + Pattern: *pattern, + Label: key[1:], + re: compiled, + } @@ in findAccountSchema: - if variableSegment != nil { - matches, err := regexp.Match(variableSegment.Pattern, []byte(nextSegment)) - if err != nil { - return nil, fmt.Errorf("invalid pattern regex: %v", err) - } - if matches { + if variableSegment != nil { + if variableSegment.re.MatchString(nextSegment) {You can then drop the runtime
invalid pattern regexbranch here, since patterns are already validated when building the chart.
📜 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.
⛔ Files ignored due to path filters (3)
openapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lock
📒 Files selected for processing (9)
docs/api/README.md(25 hunks)internal/README.md(51 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)internal/controller/ledger/controller.go(2 hunks)internal/controller/ledger/controller_with_traces.go(3 hunks)internal/errors.go(2 hunks)pkg/client/docs/models/components/v2chartsegment.md(1 hunks)pkg/client/models/components/v2chartsegment.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- pkg/client/models/components/v2chartsegment.go
- internal/controller/ledger/controller.go
- internal/controller/ledger/controller_with_traces.go
- pkg/client/docs/models/components/v2chartsegment.md
🧰 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/README.md
🧬 Code graph analysis (2)
internal/chart_test.go (2)
internal/chart.go (6)
ChartOfAccounts(40-40)ChartVariableSegment(27-32)ChartSegment(21-25)ChartAccount(16-19)ChartAccountRules(10-10)ChartAccountMetadata(12-14)internal/errors.go (1)
ErrInvalidAccount(103-108)
internal/chart.go (2)
internal/storage/ledger/logs.go (1)
RawMessage(38-38)internal/errors.go (1)
ErrInvalidAccount(103-108)
🪛 LanguageTool
docs/api/README.md
[grammar] ~5746-~5746: Ensure spelling is correct
Context: ...tructure with metadata ### Properties allOf |Name|Type|Required|Restrictions|Descrip...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 markdownlint-cli2 (0.18.1)
docs/api/README.md
357-357: Blank line inside blockquote
(MD028, no-blanks-blockquote)
403-403: Blank line inside blockquote
(MD028, no-blanks-blockquote)
459-459: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
468-468: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5612-5612: Multiple headings with the same content
(MD024, no-duplicate-heading)
5614-5614: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5630-5630: Multiple headings with the same content
(MD024, no-duplicate-heading)
5656-5656: Multiple headings with the same content
(MD024, no-duplicate-heading)
5687-5687: Multiple headings with the same content
(MD024, no-duplicate-heading)
5715-5715: Multiple headings with the same content
(MD024, no-duplicate-heading)
5745-5745: Multiple headings with the same content
(MD024, no-duplicate-heading)
5785-5785: Multiple headings with the same content
(MD024, no-duplicate-heading)
5823-5823: Multiple headings with the same content
(MD024, no-duplicate-heading)
5859-5859: Multiple headings with the same content
(MD024, no-duplicate-heading)
⏰ 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). (4)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Tests
- GitHub Check: GoReleaser
- GitHub Check: Dirty
🔇 Additional comments (6)
docs/api/README.md (5)
318-505: ✓ Schema endpoints well-documented.The three new schema management endpoints (insert, get, list) are properly documented with clear parameter descriptions, correct HTTP methods, and realistic examples. The endpoint descriptions accurately reflect insert-only semantics (no update).
730-730: ✓ SchemaVersion parameters consistently integrated.The
schemaVersionquery parameter is properly added across all relevant endpoints (bulk, add metadata, create/revert transaction) with appropriate optional status and clear descriptions. The bulk endpoint notably specifies element-level override capability.Also applies to: 1135-1135, 1562-1562, 1838-1838, 1903-1903
4018-4050: ✓ Log schema and error codes properly extended.The V2Log schema correctly adds an optional
schemaVersionfield with clear documentation. New log typeINSERTED_SCHEMAis properly enumerated. Error codesSCHEMA_ALREADY_EXISTSandSCHEMA_NOT_SPECIFIEDare appropriately defined in V2ErrorsEnum for schema validation scenarios.Also applies to: 4549-4551
5600-5868: ✓ Schema types comprehensive and well-structured.New schema types (V2ChartOfAccounts, V2ChartSegment, V2SchemaData, V2Schema, V2SchemaResponse, pagination cursors) are properly defined with appropriate nesting, recursive structures, and descriptions. Type composition using
allOffollows OpenAPI standards correctly.Note: Auto-generated schema section retains markdownlint issues (MD024 duplicate headings, MD036 emphasis-as-heading) previously flagged. These may require generator-level fixes or CI configuration adjustments to suppress in auto-generated sections.
1-6250: ✓ Schema feature documentation is comprehensive and well-integrated.The documentation comprehensively mirrors the backend schema feature implementation:
- Three schema management endpoints (POST insert, GET read, GET list)
- SchemaVersion parameter propagation across transaction, metadata, and bulk operations
- V2Log schema extended with schemaVersion field and new INSERTED_SCHEMA event type
- Complete type definitions for chart-of-accounts structures and schema responses
- Error code documentation for schema-specific failures (SCHEMA_ALREADY_EXISTS, SCHEMA_NOT_SPECIFIED)
- Proper pagination and cursor support for schema listing
All changes maintain backward compatibility (schemaVersion parameters are optional) and follow existing API documentation patterns.
internal/errors.go (1)
91-126: New schema/account error types look consistent and usable
ErrInvalidSchemaandErrInvalidAccountfollow the established error pattern in this file, integrate cleanly witherrors.Is, and their messages match the expectations in the chart tests. No issues from my side here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
internal/chart.go (1)
10-19:ChartAccountRulescannot currently be represented in JSON
ChartAccountRulesis an empty struct, andmarshalJsonObjectonly emitsRULES_KEYwhens.Account.Rules != (ChartAccountRules{}), which is never true for an empty struct. As a result:
.rulesin input JSON is accepted and unmarshalled, but any information is discarded (there are no fields)..rulesis never serialized back out, so charts do not roundtrip this key.If rules are intentionally a future stub, this is fine behavior, but it might be clearer to either:
- Reject
.rulesaltogether until fields are added, or- Add a presence flag on
ChartAccount(e.g.rulesPresent bool) and emitRULES_KEYbased on that flag once rules are actually modeled.This avoids surprising users who expect rules to persist in stored schemas.
Also applies to: 208-215
internal/README.md (1)
253-257: Reconfirm intended divergence between chart segment regex and account-address regex
ChartSegmentRegexp/ValidateSegmentare documented as allowing-in segment names (^(\$|\.)?[a-zA-Z0-9_-]+$). The HTTP API account-address regex (in the API docs/routes) still only allows\w([A-Za-z0-9_]), so charts can define segments that can never be referenced by valid account addresses.If that’s not intentional, consider either:
- Removing
-fromChartSegmentRegexp, or- Broadening the account-address regex and updating the HTTP/API docs accordingly.
Otherwise, it’s worth explicitly calling out this divergence in the docs so users understand which chart paths are addressable.
Also applies to: 319-325
📜 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.
📒 Files selected for processing (3)
internal/README.md(51 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- internal/chart_test.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/README.md
🧬 Code graph analysis (1)
internal/chart.go (1)
internal/errors.go (1)
ErrInvalidAccount(103-108)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
internal/README.md (1)
253-257: Confirm intended difference betweenChartSegmentRegexpand HTTP account-address regexThe docs expose:
var ChartSegmentRegexp = regexp.MustCompile(`^(\$|\.)?[a-zA-Z0-9_-]+$`)which allows
-in segment names. Earlier in this README / HTTP API docs, account addresses are validated with^\w+(:\w+)*$, which does not allow-. That means you can define chart segments that cannot be addressed over the HTTP API.If this divergence is not intentional, it would be good to pick one canonical rule and align both the code (
internal/chart.go) and the documented address regex (e.g. either drop-fromChartSegmentRegexpor broaden the HTTP regex). Since this was raised in a prior review, just calling it out again for confirmation.
🧹 Nitpick comments (2)
internal/chart.go (2)
10-19:.rulesis currently accepted but effectively ignored and never re-emittedRight now:
ChartAccountRulesis an empty struct.ChartSegment.UnmarshalJSONhappily unmarshalsRULES_KEYintoaccount.Rules.marshalJsonObjectonly emitsRULES_KEYwhens.Account.Rules != (ChartAccountRules{}), which is never true for an empty struct; you even note “Never emitted for now”.Combined with
json.Unmarshalignoring unknown fields on an empty struct, any.rulesobject in the input schema is silently discarded and will not roundtrip, while the public API/doc surface exposesChartAccountRulesandRULES_KEY.If
.rulesis not meant to be usable yet, consider being explicit and failing fast when it appears, instead of accepting and dropping it. If you do want to preserve it (even before enforcing anything), a small presence flag onChartAccountkeeps roundtrip behavior predictable, e.g.:type ChartAccount struct { Metadata map[string]ChartAccountMetadata - Rules ChartAccountRules + Rules ChartAccountRules + + hasRules bool } @@ - } else if key == RULES_KEY { - err := json.Unmarshal(value, &account.Rules) + } else if key == RULES_KEY { + account.hasRules = true + err := json.Unmarshal(value, &account.Rules) if err != nil { return fmt.Errorf("invalid account rules: %v", err) } @@ - if s.Account.Rules != (ChartAccountRules{}) { - out[RULES_KEY] = s.Account.Rules - } + if s.Account.hasRules { + out[RULES_KEY] = s.Account.Rules + }That way schemas using
.ruleswon’t see their configuration silently disappear, and you have a clear hook for future rule fields.Also applies to: 162-167, 215-222
106-120: Avoid recompiling variable-segment regex on every account lookupDuring unmarshal you already validate the pattern with
regexp.Compile(pat), but infindAccountSchemayou use:matches, err = regexp.Match(*variableSegment.Pattern, []byte(nextSegment))which recompiles the same pattern for every lookup. If
ValidatePostingis on a hot path, this becomes unnecessary overhead.Consider caching the compiled regexp on
ChartVariableSegmentat unmarshal time and reusing it here, e.g. by adding an unexported*regexp.Regexpfield and callingre.MatchString(nextSegment)instead. This keeps validation behavior identical while making account resolution cheaper under load.Also applies to: 267-273
📜 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.
📒 Files selected for processing (3)
internal/README.md(51 hunks)internal/chart.go(1 hunks)internal/chart_test.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/chart_test.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/README.md
🧬 Code graph analysis (1)
internal/chart.go (2)
internal/storage/ledger/logs.go (1)
RawMessage(38-38)internal/errors.go (1)
ErrInvalidAccount(103-108)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/controller/ledger/controller_default_test.go (1)
133-138: Fix InsertLog mock return type to match Store interface.The
DoAndReturnclosure returnsanyandlog, butStore.InsertLogreturnserror. This should be consistent with other InsertLog mocks in this test file.Apply this diff:
- DoAndReturn(func(_ context.Context, log *ledger.Log) any { + DoAndReturn(func(_ context.Context, log *ledger.Log) error { log.ID = pointer.For(uint64(0)) - return log + return nil })
♻️ Duplicate comments (2)
internal/controller/ledger/controller_default_test.go (1)
72-79: Fix InsertLog mock return type to match Store interface.The
DoAndReturnclosure returnsanyandlog, butStore.InsertLoghas signaturefunc(context.Context, *ledger.Log) error. This is inconsistent with other InsertLog mocks in this file (see lines 201-205, 250-254, 297-301) which correctly returnerror.Apply this diff:
- DoAndReturn(func(_ context.Context, log *ledger.Log) any { + DoAndReturn(func(_ context.Context, log *ledger.Log) error { log.ID = pointer.For(uint64(0)) - return log + return nil })internal/controller/ledger/controller_default.go (1)
509-509: Critical: Reverted transactions bypass schema validation.Line 509 passes
nilas the schema toCommitTransactioneven though_schemais available as a parameter (line 454). This means reverted transactions don't validate their postings against the chart of accounts, which could allow invalid account references during reversals.Apply this diff to enforce schema validation on reverted transactions:
- err = store.CommitTransaction(ctx, nil, &reversedTx, nil) + err = store.CommitTransaction(ctx, _schema, &reversedTx, nil)
📜 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.
⛔ Files ignored due to path filters (5)
openapi.yamlis excluded by!**/*.yamlopenapi/v2.yamlis excluded by!**/*.yamlpkg/client/.speakeasy/gen.lockis excluded by!**/*.lock,!**/*.lockpkg/client/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.logpkg/client/speakeasyusagegen/.speakeasy/logs/naming.logis excluded by!**/*.log,!**/*.log
📒 Files selected for processing (31)
docs/api/README.md(25 hunks)internal/README.md(51 hunks)internal/api/bulking/bulker.go(11 hunks)internal/api/bulking/bulker_test.go(1 hunks)internal/api/bulking/mocks_ledger_controller_test.go(3 hunks)internal/api/common/mocks_ledger_controller_test.go(3 hunks)internal/api/v1/mocks_ledger_controller_test.go(3 hunks)internal/api/v2/controllers_schema_insert.go(1 hunks)internal/api/v2/controllers_schema_insert_test.go(1 hunks)internal/api/v2/mocks_ledger_controller_test.go(3 hunks)internal/controller/ledger/controller.go(2 hunks)internal/controller/ledger/controller_default.go(12 hunks)internal/controller/ledger/controller_default_test.go(8 hunks)internal/controller/ledger/controller_generated_test.go(3 hunks)internal/controller/ledger/controller_with_events.go(1 hunks)internal/controller/ledger/controller_with_too_many_client_handling.go(2 hunks)internal/controller/ledger/controller_with_traces.go(3 hunks)internal/controller/ledger/log_process.go(3 hunks)internal/controller/ledger/log_process_test.go(5 hunks)internal/controller/system/state_tracker.go(1 hunks)internal/log.go(13 hunks)pkg/client/docs/models/operations/v2insertschemarequest.md(1 hunks)pkg/client/docs/models/operations/v2insertschemaresponse.md(1 hunks)pkg/client/docs/models/operations/v2reverttransactionrequest.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/v2insertschema.go(1 hunks)pkg/client/models/operations/v2reverttransaction.go(2 hunks)pkg/client/v2.go(1 hunks)test/e2e/api_idempotency_hit_header_test.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- pkg/client/models/operations/v2addmetadataontransaction.go
- pkg/client/models/operations/v2reverttransaction.go
- pkg/client/docs/models/operations/v2reverttransactionrequest.md
- pkg/client/models/operations/v2insertschema.go
- pkg/client/models/operations/v2createtransaction.go
- pkg/client/docs/models/operations/v2insertschemarequest.md
- internal/api/v2/controllers_schema_insert_test.go
- pkg/client/models/operations/v2addmetadatatoaccount.go
- internal/controller/ledger/controller_generated_test.go
- internal/api/v2/controllers_schema_insert.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/controller_default.gointernal/README.md
🧬 Code graph analysis (14)
internal/controller/ledger/log_process_test.go (4)
internal/controller/ledger/store.go (1)
Store(29-67)internal/schema.go (1)
Schema(15-21)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
CreatedTransaction(213-216)
internal/controller/ledger/controller_with_too_many_client_handling.go (3)
internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/controller/ledger/controller_with_traces.go (5)
internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)pkg/events/message.go (1)
InsertedSchema(77-80)internal/tracing/tracing.go (1)
TraceWithMetric(101-129)internal/schema.go (1)
Schema(15-21)
internal/controller/system/state_tracker.go (3)
internal/controller/ledger/controller.go (2)
InsertSchema(135-138)Controller(21-89)internal/log.go (2)
Log(89-102)InsertedSchema(416-418)internal/controller/system/controller.go (1)
Controller(41-55)
test/e2e/api_idempotency_hit_header_test.go (3)
pkg/client/models/operations/v2insertschema.go (1)
V2InsertSchemaRequest(9-17)pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-9)pkg/client/models/components/v2chartsegment.go (1)
V2ChartSegment(13-19)
internal/controller/ledger/log_process.go (4)
internal/controller/ledger/store.go (1)
Store(29-67)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/log.go (1)
Log(89-102)internal/controller/ledger/errors.go (2)
ErrSchemaNotFound(249-252)ErrSchemaNotSpecified(288-290)
internal/api/bulking/bulker.go (1)
internal/api/bulking/elements.go (3)
Bulk(22-22)BulkElementResult(73-78)BulkElement(24-28)
internal/api/bulking/bulker_test.go (1)
internal/machine/vm/run.go (1)
Run(57-87)
internal/api/v2/mocks_ledger_controller_test.go (5)
internal/api/common/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)pkg/events/message.go (1)
InsertedSchema(77-80)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/log.go (1)
pkg/events/message.go (4)
SavedMetadata(27-32)DeletedMetadata(60-65)RevertedTransaction(44-48)InsertedSchema(77-80)
internal/controller/ledger/controller_default_test.go (5)
internal/schema.go (2)
Schema(15-21)SchemaData(11-13)internal/chart.go (3)
ChartOfAccounts(40-40)ChartAccount(16-19)ChartAccountMetadata(12-14)internal/controller/ledger/controller.go (3)
InsertSchema(135-138)CreateTransaction(102-106)RunScript(91-91)internal/log.go (2)
Log(89-102)InsertedSchemaLogType(25-25)internal/controller/ledger/parameters.go (1)
Parameters(3-8)
internal/api/common/mocks_ledger_controller_test.go (4)
internal/schema.go (1)
Schema(15-21)internal/storage/ledger/main_test.go (1)
T(75-79)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)pkg/events/message.go (1)
InsertedSchema(77-80)
internal/api/bulking/mocks_ledger_controller_test.go (9)
internal/api/common/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v1/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v2/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/schema.go (1)
Schema(15-21)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/storage/ledger/logs.go (1)
Log(20-26)pkg/events/message.go (1)
InsertedSchema(77-80)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
internal/api/v1/mocks_ledger_controller_test.go (9)
internal/api/bulking/mocks_ledger_controller_test.go (3)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)LedgerControllerGetSchemaCall(498-500)internal/api/common/mocks_ledger_controller_test.go (2)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)internal/api/v2/mocks_ledger_controller_test.go (3)
LedgerController(26-30)LedgerControllerMockRecorder(33-35)LedgerControllerGetSchemaCall(498-500)internal/schema.go (1)
Schema(15-21)internal/controller/ledger/controller.go (1)
InsertSchema(135-138)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/storage/ledger/logs.go (1)
Log(20-26)pkg/events/message.go (1)
InsertedSchema(77-80)internal/storage/common/pagination.go (1)
PaginatedQuery(26-29)
🪛 LanguageTool
docs/api/README.md
[grammar] ~5786-~5786: Ensure spelling is correct
Context: ...tructure with metadata ### Properties allOf |Name|Type|Required|Restrictions|Descrip...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 markdownlint-cli2 (0.18.1)
docs/api/README.md
359-359: Blank line inside blockquote
(MD028, no-blanks-blockquote)
411-411: Blank line inside blockquote
(MD028, no-blanks-blockquote)
467-467: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
476-476: Blank line inside blockquote
(MD028, no-blanks-blockquote)
5652-5652: Multiple headings with the same content
(MD024, no-duplicate-heading)
5654-5654: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
5670-5670: Multiple headings with the same content
(MD024, no-duplicate-heading)
5696-5696: Multiple headings with the same content
(MD024, no-duplicate-heading)
5727-5727: Multiple headings with the same content
(MD024, no-duplicate-heading)
5755-5755: Multiple headings with the same content
(MD024, no-duplicate-heading)
5785-5785: Multiple headings with the same content
(MD024, no-duplicate-heading)
5825-5825: Multiple headings with the same content
(MD024, no-duplicate-heading)
5863-5863: Multiple headings with the same content
(MD024, no-duplicate-heading)
5899-5899: Multiple headings with the same content
(MD024, no-duplicate-heading)
pkg/client/docs/models/operations/v2insertschemaresponse.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)
⏰ 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). (4)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: GoReleaser
- GitHub Check: Dirty
- GitHub Check: Tests
🔇 Additional comments (42)
pkg/client/docs/models/operations/v2insertschemaresponse.md (1)
6-9: Documentation accurately reflects the actual V2InsertSchemaResponse struct.The Go struct in
pkg/client/models/operations/v2insertschema.godefines V2InsertSchemaResponse with exactly two fields:HTTPMetaandHeaders. NoV2SchemaResponsefield exists. The table documentation is correct. However, theHeadersfield type formatting in the documentation appears incorrect—it displays asmap[string][]*string*(with asterisks that create unintended markdown formatting) when it should bemap[string][]string.Likely an incorrect or invalid review comment.
test/e2e/api_idempotency_hit_header_test.go (1)
479-525: LGTM! Idempotency tests for InsertSchema are well-structured.The test block correctly validates the idempotency behavior of the new V2 InsertSchema API:
- First call with an idempotency key returns a response without the Idempotency-Hit header
- Subsequent calls with the same key return the cached response with the header set to "true"
The tests follow the established pattern used for other idempotent operations in this suite and provide appropriate coverage for the new schema insertion functionality.
internal/api/v1/mocks_ledger_controller_test.go (1)
481-518: LGTM! Generated mock code follows consistent patterns.The three new mock methods (GetSchema, InsertSchema, ListSchemas) are properly generated with the standard gomock structure including Return, Do, and DoAndReturn helpers. The signatures align with the Controller interface additions.
Also applies to: 713-752, 871-908
internal/controller/ledger/controller_with_events.go (1)
156-168: LGTM! Consistent event handling pattern.The InsertSchema implementation correctly follows the established pattern used by other methods (CreateTransaction, RevertTransaction, etc.):
- Delegates to underlying controller
- Emits InsertedSchema event via handleEvent
- Respects DryRun flag
- Returns all expected values including idempotencyHit
internal/controller/system/state_tracker.go (1)
199-211: LGTM! Consistent state tracking pattern.The InsertSchema method correctly uses the handleState wrapper pattern, matching the implementation of CreateTransaction, RevertTransaction, and other state-tracked operations. It properly propagates the DryRun parameter and returns all expected values.
internal/api/bulking/bulker_test.go (1)
433-433: LGTM! Test updated for new Run signature.The empty string parameter aligns with the extended Run signature that now accepts an id/schemaVersion parameter. Using an empty string for tests is appropriate when schema versioning is not relevant to the test case.
internal/controller/ledger/log_process_test.go (2)
35-37: LGTM! Test updated for schema-aware forgeLog.The test correctly adds the FindLatestSchemaVersion mock expectation and updates the callback signature to include the schema parameter, aligning with the schema-aware forgeLog implementation.
Also applies to: 52-52
70-72: LGTM! Deadlock test updated for schema-aware retry flow.Both retry attempts in the deadlock scenario now correctly mock FindLatestSchemaVersion and use the updated callback signature with the schema parameter.
Also applies to: 83-85, 100-100
internal/controller/ledger/controller.go (1)
83-88: LGTM! Clean schema API additions.The three new methods (InsertSchema, GetSchema, ListSchemas) and the InsertSchema parameter type provide a clear and consistent API surface for schema management. The signatures align with existing patterns in the Controller interface.
Also applies to: 135-138
internal/controller/ledger/controller_with_too_many_client_handling.go (1)
133-146: LGTM! Consistent retry handling for schema operations.All three new methods (InsertSchema, GetSchema, ListSchemas) correctly use the handleRetry wrapper to provide automatic retry on
ErrTooManyClient, consistent with the existing pattern used by CreateTransaction, RevertTransaction, and other methods.Also applies to: 148-159, 161-172
internal/controller/ledger/controller_default_test.go (1)
25-88: Well-structured parameterized test for schema support.The refactoring to use a
testCreateTransactionhelper withwithSchemaparameter provides good test coverage for both schema and non-schema flows. The schema setup (lines 38-88) properly exercises the InsertSchema path, and the conditional mock expectations (lines 111-129) correctly differentiate between the two scenarios.Also applies to: 111-153
internal/api/common/mocks_ledger_controller_test.go (1)
217-230: Schema-related mocks correctly mirror the Controller interfaceThe new GetSchema, InsertSchema, and ListSchemas mock methods (and their recorder helpers) match the controller signatures and follow the existing gomock pattern; no issues from a testing surface perspective.
Also applies to: 305-320, 367-380
pkg/client/v2.go (1)
656-853: V2 schema client methods align with existing SDK patternsInsertSchema, GetSchema, and ListSchemas use the expected routes, HTTP verbs, retry/timeout handling, and JSON (de)serialization patterns consistent with the rest of the V2 client; signatures and response wiring look correct.
Also applies to: 855-1061, 1063-1273
internal/controller/ledger/controller_with_traces.go (2)
47-49: Schema histograms are correctly added and initializedThe new insert/get/list schema histograms are added to the struct and initialized with consistent names and
metric.WithUnit("ms"), matching the rest of the controller latency metrics.Also applies to: 155-166
519-585: Traced schema methods correctly wrap the underlying controllerInsertSchema, GetSchema, and ListSchemas follow the existing TraceWithMetric pattern, use span names aligned with method names, and correctly propagate logs, results, idempotency flags, and errors.
internal/api/v2/mocks_ledger_controller_test.go (3)
481-518: LGTM! Generated mock follows standard gomock pattern.The GetSchema mock method correctly implements the standard gomock pattern with proper type assertions and chainable call wrappers.
713-752: LGTM! Generated mock follows standard gomock pattern.The InsertSchema mock method correctly implements the standard gomock pattern with proper type assertions for all four return values.
871-908: LGTM! Generated mock follows standard gomock pattern.The ListSchemas mock method correctly implements the standard gomock pattern with proper generic cursor type handling.
internal/api/bulking/bulker.go (3)
30-91: LGTM! SchemaVersion parameter properly threaded through bulk processing.The
runmethod correctly adds the schemaVersion parameter and propagates it to processElement at line 64.
93-132: LGTM! Public API updated to accept schemaVersion.The
Runmethod correctly adds the schemaVersion parameter and propagates it through to the internal run method.
134-266: LGTM! SchemaVersion consistently propagated to all ledger operations.The processElement method correctly adds schemaVersion and consistently propagates it to all controller operations via the Parameters struct.
internal/controller/ledger/log_process.go (1)
65-127: LGTM! Schema resolution and validation properly implemented.The schema resolution logic correctly:
- Fetches the schema when SchemaVersion is provided
- Returns informative errors (ErrSchemaNotFound, ErrSchemaNotSpecified) with latest version context
- Only requires schema when the payload explicitly needs it via NeedsSchema()
- Validates the log against the schema before insertion
internal/log.go (6)
20-86: LGTM! InsertedSchemaLogType properly integrated.The new log type constant is correctly integrated into the String() and LogTypeFromString() methods with consistent "INSERTED_SCHEMA" string representation.
88-183: LGTM! SchemaVersion field properly integrated into Log structure and hash computation.The SchemaVersion field is correctly added with appropriate JSON/bun tags and properly included in the hash computation to maintain log integrity.
218-229: LGTM! CreatedTransaction properly validates postings against schema.The ValidateWithSchema implementation correctly iterates through all postings and validates them against the chart of accounts.
272-277: Metadata operations require schema context but skip validation.SavedMetadata returns true for NeedsSchema() but performs no validation in ValidateWithSchema(). This appears intentional—metadata operations need schema context for consistency but don't validate against the chart of accounts.
416-431: LGTM! InsertedSchema properly implements LogPayload interface.The InsertedSchema type correctly returns false for NeedsSchema() since schema insertion doesn't require a pre-existing schema. All interface requirements are properly implemented.
433-455: LGTM! HydrateLog properly handles InsertedSchemaLogType.The hydration logic correctly handles the new InsertedSchemaLogType by instantiating an InsertedSchema payload, consistent with other log types.
internal/api/bulking/mocks_ledger_controller_test.go (1)
481-518: LGTM! All generated mocks follow standard gomock patterns.The three new mock methods (GetSchema, InsertSchema, ListSchemas) are correctly generated with proper type assertions and chainable call wrappers, consistent with existing mock patterns in the file.
Also applies to: 713-752, 871-908
internal/controller/ledger/controller_default.go (7)
57-76: LGTM! InsertSchema properly handles duplicate schemas and errors.The implementation correctly:
- Uses the logProcessor pattern for consistency
- Handles constraint violations as ErrSchemaAlreadyExists
- Propagates errors from NewSchema and InsertSchema
- Returns the complete InsertedSchema payload
78-84: LGTM! Schema retrieval methods properly delegate to store.The GetSchema and ListSchemas methods correctly delegate to the store layer without unnecessary intermediate logic.
381-448: LGTM! Schema properly propagated through transaction creation.The createTransaction method correctly accepts and passes the schema to CommitTransaction for validation.
524-534: LGTM! Metadata methods consistently handle schema parameter.The transaction metadata methods accept but don't use the schema parameter, which is consistent with the design where metadata operations need schema context but don't perform validation. The underscore prefix correctly indicates the unused parameter.
Also applies to: 562-577
541-554: LGTM! Account metadata properly validates against schema.The saveAccountMetadata method correctly passes the schema to UpsertAccounts, enabling account validation during metadata operations.
271-349: LGTM! Import correctly handles schema insertion and replays validated logs.The importLog method:
- Properly handles InsertedSchema payloads by inserting the schema
- Correctly passes nil schema during import since these are replayed logs that were already validated at creation time
- Maintains historical log integrity by not re-validating during import
127-166: LGTM! Schema log processor properly initialized.The insertSchemaLp is correctly initialized in NewDefaultController following the same pattern as other log processors.
docs/api/README.md (2)
318-514: ✓ Schema management endpoints well-structured; however, markdown linting violations require resolution.The three new schema endpoints (insert, get, list) are properly documented with consistent structure, correct parameter tables, and example payloads. The
schemaVersionparameter integration across bulk and transaction endpoints (lines 738, 1143, 1582, 1864, 1936) maintains consistency with the new schema operations.However, static analysis identifies persistent markdown linting violations that were reportedly addressed in past commits but still appear:
- MD028 (blank lines in blockquotes): Lines 359, 411, 467, 476
- MD024 (duplicate headings): Schema type definitions (lines 5652, 5670, 5696, 5727, 5755, 5785, 5825, 5863, 5899)
- MD036 (emphasis as heading): Line 5654
Since this file is auto-generated by Widdershins, regenerate the documentation from the OpenAPI source to resolve these linting violations, or update the generator template/markdownlint configuration to suppress these rules if they're unavoidable in the generated output.
4089-4089: ✓ INSERTED_SCHEMA log type enum correctly added.The new
INSERTED_SCHEMAenum value at line 4089 correctly represents the schema insertion event type, aligning with theInsertedSchemalog payload type. This completes the log type coverage for schema operations.internal/README.md (4)
213-241: Schema property constants and regex pattern documented; verify source-level alignment of segment validation rules.The new constants (PROPERTY_PREFIX, PATTERN_KEY, RULES_KEY, SELF_KEY, METADATA_KEY) and
ChartSegmentRegexpvariable are properly documented. However, the regex pattern at line 256—^(\$|\.)?[a-zA-Z0-9_-]+$—allows hyphens in segment names, while the HTTP API's account address regex (^\w+(:\w+)*$documented in docs/api/README.md) only permits word characters[A-Za-z0-9_].This divergence means chart segments can be defined with hyphens that will never correspond to valid account addresses. Verify with the code maintainers whether this is intentional or if the regex patterns should be aligned (either remove
-fromChartSegmentRegexpor broaden the HTTP API address regex). If the divergence is intentional, document the constraint explicitly.Also applies to: 253-257
1075-1075: ✓ Schema validation integration in Log type and LogPayload interface is complete.The addition of
SchemaVersionfield to the Log struct (line 1075), theNeedsSchema()andValidateWithSchema()methods to the LogPayload interface (lines 1159–1160), and theInsertedSchemaLogTypeconstant (line 1190) properly establish schema-aware validation throughout the logging pipeline. The documentation correctly reflects the public API surface.Also applies to: 1159-1160, 1190-1190
420-547: ✓ Chart-related types and InsertedSchema payload properly documented.New public types—
ChartAccount,ChartAccountMetadata,ChartAccountRules,ChartOfAccounts,ChartSegment,ChartVariableSegment, andInsertedSchema—are thoroughly documented with their methods and usage. The recursive structure ofChartSegment(which references itself viaFixedSegmentsand the unmarshal logic) is correctly reflected in the documentation.Also applies to: 956-990, 1574-1607
59-70: ✓ New error types ErrInvalidAccount and ErrInvalidSchema properly exposed.Both error types are documented with their Error() and Is() methods, enabling type-safe error handling for account validation and schema validation failures. This aligns with the expanded schema validation surface.
Also applies to: 68-835
| func (p RevertedTransaction) NeedsSchema() bool { | ||
| return true | ||
| } | ||
| func (r RevertedTransaction) ValidateWithSchema(schema Schema) error { | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the context around RevertedTransaction and CreatedTransaction
rg -n 'type (Created|Reverted)Transaction' internal/log.go -A 10Repository: formancehq/ledger
Length of output: 833
🏁 Script executed:
# Check CreatedTransaction's ValidateWithSchema implementation
rg -n 'type CreatedTransaction' internal/log.go -A 20Repository: formancehq/ledger
Length of output: 654
🏁 Script executed:
# Search for all transaction types and their schema validation
rg -n 'ValidateWithSchema' internal/log.go -B 2Repository: formancehq/ledger
Length of output: 758
🏁 Script executed:
# Look for posting validation logic
rg -n 'posting|Posting' internal/log.go | head -30Repository: formancehq/ledger
Length of output: 359
🏁 Script executed:
# Check if there are tests that show expected behavior
fd -t f 'log_test\.go|_test\.go' | head -5Repository: formancehq/ledger
Length of output: 247
RevertedTransaction.ValidateWithSchema() should validate postings like CreatedTransaction does.
RevertedTransaction returns true for NeedsSchema() but performs no validation in ValidateWithSchema(). Since RevertedTransaction contains a RevertTransaction with Postings (line 405), these should be validated against the chart of accounts, just as CreatedTransaction validates its postings (lines 222-227). The reversal transaction creates new postings that must conform to the schema.
Update RevertedTransaction.ValidateWithSchema() to validate the RevertTransaction.Postings:
func (r RevertedTransaction) ValidateWithSchema(schema Schema) error {
for _, posting := range r.RevertTransaction.Postings {
err := schema.Chart.ValidatePosting(posting)
if err != nil {
return err
}
}
return nil
}🤖 Prompt for AI Agents
In internal/log.go around lines 375 to 380,
RevertedTransaction.ValidateWithSchema currently returns nil despite
NeedsSchema() being true; update it to iterate over r.RevertTransaction.Postings
and call schema.Chart.ValidatePosting for each posting, returning the first
non-nil error and nil if all postings validate successfully so the reversal
postings are validated against the chart of accounts like CreatedTransaction
does.
| | Field | Type | Required | Description | | ||
| | ------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------ | | ||
| | `HTTPMeta` | [components.HTTPMetadata](../../models/components/httpmetadata.md) | :heavy_check_mark: | N/A | | ||
| | `Headers` | map[string][]*string* | :heavy_check_mark: | N/A | No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix malformed markdown reference link syntax.
The markdown reference link []*string* is malformed—"string" is not defined as a reference target. This will render incorrectly in the documentation.
Apply this diff to fix the markdown syntax:
-| `Headers` | map[string][]*string* | :heavy_check_mark: | N/A |
+| `Headers` | map[string]\*string | :heavy_check_mark: | N/A |Alternatively, if this field should link to another documentation page, ensure the reference is properly defined.
📝 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.
| | `Headers` | map[string][]*string* | :heavy_check_mark: | N/A | | |
| | `Headers` | map[string]\*string | :heavy_check_mark: | N/A | |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
9-9: 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/v2insertschemaresponse.md around line 9,
the type column contains a malformed markdown reference `[]*string*`; replace it
with a literal type rendering (e.g., wrap the full type in inline code such as
`map[string][]*string`) so Markdown does not treat "string" as a reference
target, or if this was intended to link to another doc, change `[]*string*` to a
proper reference link and add the corresponding reference definition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/controller/ledger/controller_default.go (1)
287-301: Import flow bypasses schema validation for transactions.During import, transactions are committed with
nilschema (lines 287 and 301), which bypasses any schema validation even though the import may include schema information from logs.Consider whether imported transactions should be validated against their schemas if available, or add a comment explaining why validation is intentionally skipped during import.
♻️ Duplicate comments (1)
internal/controller/ledger/controller_default.go (1)
512-512: Revert transactions bypass schema validation.Passing
nilfor the schema parameter means reverted transactions won't be validated against schema rules. This was flagged in a previous review and remains unaddressed.If schema validation should apply to reverts, apply this diff:
- err = store.CommitTransaction(ctx, nil, &reversedTx, nil) + err = store.CommitTransaction(ctx, _schema, &reversedTx, nil)If reverts should intentionally bypass validation, please add a comment explaining why.
📜 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.
📒 Files selected for processing (9)
cmd/config.go(1 hunks)cmd/serve.go(3 hunks)internal/controller/ledger/controller_default.go(14 hunks)internal/controller/ledger/log_process.go(3 hunks)internal/controller/ledger/log_process_test.go(5 hunks)internal/controller/ledger/schema.go(1 hunks)internal/controller/system/controller.go(3 hunks)internal/controller/system/module.go(2 hunks)pkg/testserver/server.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/controller/ledger/log_process_test.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/controller_default.go
🧬 Code graph analysis (8)
pkg/testserver/server.go (1)
cmd/serve.go (1)
BindFlag(61-61)
internal/controller/ledger/schema.go (1)
cmd/serve.go (1)
SchemaEnforcementMode(72-72)
cmd/config.go (3)
cmd/root.go (1)
ExperimentalExporters(21-21)cmd/serve.go (2)
SemconvMetricsNames(71-71)SchemaEnforcementMode(72-72)internal/controller/ledger/schema.go (1)
SchemaEnforcementMode(3-3)
internal/controller/system/module.go (4)
cmd/serve.go (1)
SchemaEnforcementMode(72-72)internal/controller/ledger/schema.go (1)
SchemaEnforcementMode(3-3)internal/controller/ledger/controller_default.go (1)
WithSchemaEnforcementMode(624-628)internal/controller/system/controller.go (1)
WithSchemaEnforcementMode(312-316)
cmd/serve.go (2)
internal/controller/ledger/schema.go (1)
SchemaEnforcementMode(3-3)pkg/client/formance.go (1)
String(30-30)
internal/controller/system/controller.go (3)
cmd/serve.go (1)
SchemaEnforcementMode(72-72)internal/controller/ledger/schema.go (1)
SchemaEnforcementMode(3-3)internal/controller/ledger/controller_default.go (2)
WithSchemaEnforcementMode(624-628)DefaultController(34-57)
internal/controller/ledger/log_process.go (5)
internal/controller/ledger/schema.go (2)
SchemaEnforcementMode(3-3)SchemaEnforcementStrict(7-7)internal/log.go (2)
LogPayload(201-205)Log(89-102)internal/storage/ledger/store.go (1)
Store(25-50)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/controller/ledger/errors.go (3)
ErrNotFound(14-14)ErrSchemaNotFound(249-252)ErrSchemaNotSpecified(288-290)
internal/controller/ledger/controller_default.go (6)
internal/controller/ledger/schema.go (2)
SchemaEnforcementMode(3-3)SchemaEnforcementAudit(9-9)internal/controller/ledger/controller.go (4)
RevertTransaction(108-113)DeleteTransactionMetadata(125-128)DeleteAccountMetadata(130-133)InsertSchema(135-138)internal/controller/system/controller.go (2)
DefaultController(57-75)WithSchemaEnforcementMode(312-316)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/controller/ledger/store.go (1)
Store(29-67)internal/schema.go (1)
Schema(15-21)
🔇 Additional comments (2)
internal/controller/ledger/controller_default.go (2)
59-78: LGTM! Clean schema insertion with duplicate handling.The
InsertSchemaimplementation properly handles duplicate schema versions by catching constraint violations and returning a specific error type.
143-143: Good default choice for schema enforcement mode.Defaulting to
SchemaEnforcementAuditis a sensible choice that allows observability without breaking existing functionality, enabling gradual migration to strict enforcement.
Integrate in logs.
Integrate in events.