-
Notifications
You must be signed in to change notification settings - Fork 135
feat: transaction templates #1176
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?
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 transaction template support: new template and runtime types, schema Transactions storage and validation, template resolution/injection during transaction creation, script objects gain a template reference, DB migration for transactions and schema, and tests/docs updated for templated scripts. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API
participant Controller
participant Parser
participant SchemaStore
participant Database
Note over Client,SchemaStore: Insert schema with transaction templates
Client->>API: InsertSchema(SchemaData{Transactions: {...}})
API->>Controller: InsertSchema(payload)
Controller->>SchemaStore: validate templates
loop each template
Controller->>Parser: getParser(template.Runtime)
Parser->>Parser: parse(template.Script)
alt parse succeeds
Controller->>SchemaStore: accept template
else parse fails
Controller->>API: return InvalidSchema error
API->>Client: Error
end
end
Controller->>Database: persist schema (with transactions)
Note over Client,Controller: Create transaction using template
Client->>API: CreateTransaction(Script.Template="KEY", Vars=...)
API->>Controller: CreateTransaction(payload)
Controller->>SchemaStore: FindLatestSchema()
SchemaStore->>Controller: schema (with transactions)
Controller->>Controller: resolve template by key
alt template found
Controller->>Parser: getParser(template.Runtime)
Parser->>Parser: parse(template.Script)
Controller->>Controller: inject template.Script into payload.Plain
Controller->>Database: Create transaction row (includes Template)
Database-->>API: created transaction
API-->>Client: Return transaction (Template set)
else template missing
Controller->>API: return InvalidSchema error
API->>Client: Error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (1)
🧰 Additional context used🧬 Code graph analysis (1)internal/storage/ledger/schema_test.go (3)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
4a8518f to
c98d75c
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1176 +/- ##
==========================================
- Coverage 76.62% 75.62% -1.00%
==========================================
Files 197 198 +1
Lines 10273 10320 +47
==========================================
- Hits 7872 7805 -67
- Misses 1445 1458 +13
- Partials 956 1057 +101 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
d2a3c75 to
7346fe8
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.
2 issues found across 37 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="openapi/v2.yaml">
<violation number="1" location="openapi/v2.yaml:2353">
P1: The `script` object still requires `plain` as mandatory, but according to the PR description, clients should be able to use `script.template` + `vars` without providing `plain`. Consider removing the `required: - plain` or using a conditional schema (e.g., `oneOf`) to allow either `plain` or `template` to be provided.</violation>
</file>
<file name="internal/controller/ledger/controller_default.go">
<violation number="1" location="internal/controller/ledger/controller_default.go:479">
P2: Parameter renamed from `_schema` to `schema` but is not used in the function body. Either use the schema parameter (pass it to `store.CommitTransaction` instead of `nil`), or keep the underscore prefix to indicate it's intentionally unused.</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
| $ref: "#/components/schemas/V2AggregatedVolumes" | ||
| postCommitEffectiveVolumes: | ||
| $ref: "#/components/schemas/V2AggregatedVolumes" | ||
| template: |
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: The script object still requires plain as mandatory, but according to the PR description, clients should be able to use script.template + vars without providing plain. Consider removing the required: - plain or using a conditional schema (e.g., oneOf) to allow either plain or template to be provided.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At openapi/v2.yaml, line 2353:
<comment>The `script` object still requires `plain` as mandatory, but according to the PR description, clients should be able to use `script.template` + `vars` without providing `plain`. Consider removing the `required: - plain` or using a conditional schema (e.g., `oneOf`) to allow either `plain` or `template` to be provided.</comment>
<file context>
@@ -2350,12 +2350,15 @@ components:
$ref: "#/components/schemas/V2AggregatedVolumes"
postCommitEffectiveVolumes:
$ref: "#/components/schemas/V2AggregatedVolumes"
+ template:
+ type: string
required:
</file context>
✅ Addressed in 137b27b
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.
remove from required: 137b27b
| } | ||
|
|
||
| func (ctrl *DefaultController) revertTransaction(ctx context.Context, store Store, _schema *ledger.Schema, parameters Parameters[RevertTransaction]) (*ledger.RevertedTransaction, error) { | ||
| func (ctrl *DefaultController) revertTransaction(ctx context.Context, store Store, schema *ledger.Schema, parameters Parameters[RevertTransaction]) (*ledger.RevertedTransaction, error) { |
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: Parameter renamed from _schema to schema but is not used in the function body. Either use the schema parameter (pass it to store.CommitTransaction instead of nil), or keep the underscore prefix to indicate it's intentionally unused.
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 479:
<comment>Parameter renamed from `_schema` to `schema` but is not used in the function body. Either use the schema parameter (pass it to `store.CommitTransaction` instead of `nil`), or keep the underscore prefix to indicate it's intentionally unused.</comment>
<file context>
@@ -451,7 +476,7 @@ func (ctrl *DefaultController) CreateTransaction(ctx context.Context, parameters
}
-func (ctrl *DefaultController) revertTransaction(ctx context.Context, store Store, _schema *ledger.Schema, parameters Parameters[RevertTransaction]) (*ledger.RevertedTransaction, error) {
+func (ctrl *DefaultController) revertTransaction(ctx context.Context, store Store, schema *ledger.Schema, parameters Parameters[RevertTransaction]) (*ledger.RevertedTransaction, error) {
var (
hasBeenReverted bool
</file context>
✅ Addressed in 137b27b
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.
internal/api/v2/views_test.go
Outdated
| }, | ||
| }, | ||
| }, | ||
| "template": "", |
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.
maybe omit empty template?
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.
done 👍 8fa193b
| m, err := ctrl.getParser(parameters.Input).Parse(parameters.Input.Plain) | ||
| if schema != nil { | ||
| if parameters.Input.Template == "" { | ||
| return nil, newErrSchemaValidationError(parameters.SchemaVersion, fmt.Errorf("transactions on this ledger must use a template")) |
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.
Hum, is it a part of the specification?
internal/transaction_templates.go
Outdated
| } | ||
| for id, tmpl := range templates { | ||
| if tmpl.Runtime == "" { | ||
| tmpl.Runtime = RuntimeMachine |
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.
IMO, you should avoid putting logic on UnmarshalXXX functions.
Doing that will force you to implements the same stuff each time you'll implements a new encoding format.
I guess that's something which should be handled at service level.
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.
Addressed in: 8fa193b
I just added omitempty + validation step to avoid invalid runtimes
137b27b to
7eaef65
Compare
7eaef65 to
56ee801
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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
pkg/generate/generator.go (1)
50-55: Avoid sendingplain: ""(usenilwhen empty) to prevent unintended “script present” validation.
pointer.For(transactionRequest.Script.Plain)will always set a non-nil pointer—even when empty—which can accidentally trip “script is provided” logic (and may conflict with schema/template-only flows).- Script: &components.V2PostTransactionScript{ - Plain: pointer.For(transactionRequest.Script.Plain), + Script: &components.V2PostTransactionScript{ + Plain: func() *string { + if transactionRequest.Script.Plain == "" { + return nil + } + return pointer.For(transactionRequest.Script.Plain) + }(), Vars: collectionutils.ConvertMap(transactionRequest.Script.Vars, func(from any) string { return fmt.Sprint(from) }), },internal/errors.go (1)
91-104: Good addition, but ensure callers consistently wrap template/schema validation asErrInvalidSchema.
NewErrInvalidSchemais helpful; the main risk is some call sites returning raw validation errors instead ofErrInvalidSchema(see internal/schema.go). Optionally guard againstnilto avoidinvalid schema: <nil>surprises.internal/storage/bucket/migrations/44-add-schema/up.sql (1)
5-21: ConsiderDEFAULT '{}'::jsonbforschemas.transactions(NOT NULL + nil map risk).
If any writer inserts schemas without explicitly settingtransactions, NOT NULL without a default is fragile. Adding a default keeps the DB resilient even if callers forget to set it.create table schemas ( ledger varchar, version text not null, created_at timestamp without time zone not null default now(), chart jsonb not null, - transactions jsonb not null, + transactions jsonb not null default '{}'::jsonb, primary key (ledger, version) );internal/api/bulking/elements.go (1)
99-140: Potential validation hole: reject “no postings + no script/template” explicitly.
With the new conditional, an empty request can now reachledgercontroller.CreateTransactionwith a zero-valueRunScript(unless downstream rejects it). Safer to fail fast here with a clear error.func (req TransactionRequest) ToCore() (*ledgercontroller.CreateTransaction, error) { if _, err := req.Postings.Validate(); err != nil { return nil, err } var runScript ledgercontroller.RunScript if len(req.Postings) > 0 { txData := ledger.TransactionData{ Postings: req.Postings, Timestamp: req.Timestamp, Reference: req.Reference, Metadata: req.Metadata, } runScript = ledgercontroller.TxToScriptData(txData, req.Force) } else if req.Script.Plain != "" || req.Script.Template != "" { runScript = ledgercontroller.RunScript{ Script: req.Script.ToCore(), Timestamp: req.Timestamp, Reference: req.Reference, Metadata: req.Metadata, } + } else { + return nil, fmt.Errorf("invalid transaction request: either postings or script.template/script.plain must be provided") } return &ledgercontroller.CreateTransaction{ Runtime: req.Runtime, RunScript: runScript, AccountMetadata: req.AccountMetadata, }, nil }pkg/client/models/components/v2schemadata.go (1)
6-25: Minor API consistency: consider returning an empty map fromGetTransactions()(likeGetChart()).
Nil map is safe to range over, but returning{}consistently can reduce nil-check churn for SDK users.pkg/client/docs/sdks/v2/README.md (1)
1216-1254: Docs bug: example appears to send an invalid combination (schemaVersion + postings + template + plain).
Given the PR behavior, this snippet should demonstrate either postings/plain (no schema template flow) or template+vars (no postings, no plain). Also fix the hard tabs in the code block to satisfy MD010.V2PostTransaction: components.V2PostTransaction{ - Postings: []components.V2Posting{ - components.V2Posting{ - Amount: big.NewInt(100), - Asset: "COIN", - Destination: "users:002", - Source: "users:001", - }, - }, Script: &components.V2PostTransactionScript{ Template: client.String("CUSTOMER_DEPOSIT"), - Plain: client.String("vars {\n" + - "account $user\n" + - "}\n" + - "send [COIN 10] (\n" + - " source = @world\n" + - " destination = $user\n" + - ")\n" + - ""), Vars: map[string]string{ "user": "users:042", }, },If you want to keep a “plain script” example too, it should omit
Template(and, if schema-driven enforcement applies, omitSchemaVersionas well).docs/api/README.md (1)
726-764: Examples still show “mixed modes” (template + plain and/or postings), which contradicts the documented server rules.
In both Bulk and CreateTransaction examples,script.templateis shown alongsidescript.plain(and the example also includespostings). Per PR objectives, template execution must not be mixed with postings or ad‑hoc scripts, and when a schema is active transactions must use a template. Update examples to show either:
- Template mode:
script: { template, vars }(noplain, nopostings), or- Legacy mode (no schema):
postingsorscript.plainwithoutscript.template."script": { "template": "CUSTOMER_DEPOSIT", - "plain": "vars {\naccount $user\n}\nsend ...\n", "vars": { "user": "users:042" } },Also applies to: 1580-1612
♻️ Duplicate comments (1)
internal/controller/ledger/controller_default.go (1)
406-433: Schema enforcement: avoid double-reporting + avoid high-cardinality trace attributesIn audit mode,
Template == ""currently triggers an error report, then immediately falls into the “template not found” path (another error report), and then proceeds to parse/execute whateverPlainis (possibly empty).Also
attribute.String("schema_validation_failed", err.Error())risks high-cardinality attributes (template IDs / full error strings).Suggested shape:
if schema != nil { - if parameters.Input.Template == "" { - err := newErrSchemaValidationError(parameters.SchemaVersion, fmt.Errorf("transactions on this ledger must use a template")) - if ctrl.schemaEnforcementMode == SchemaEnforcementStrict { - return nil, err - } else { - trace.SpanFromContext(ctx).SetAttributes(attribute.String("schema_validation_failed", err.Error())) - logging.FromContext(ctx).Errorf("schema validation failed: %s", err) - } - } - if template, ok := schema.SchemaData.Transactions[parameters.Input.Template]; ok { + if parameters.Input.Template == "" { + err := newErrSchemaValidationError(parameters.SchemaVersion, fmt.Errorf("transactions on this ledger must use a template")) + if ctrl.schemaEnforcementMode == SchemaEnforcementStrict { + return nil, err + } + trace.SpanFromContext(ctx).SetAttributes( + attribute.Bool("schema_validation_failed", true), + attribute.String("schema_validation_reason", "missing_template"), + ) + logging.FromContext(ctx).Errorf("schema validation failed: %s", err) + } else if template, ok := schema.SchemaData.Transactions[parameters.Input.Template]; ok { parameters.Input.Plain = template.Script if parameters.Input.Runtime == "" { parameters.Input.Runtime = template.Runtime } } else { err := newErrSchemaValidationError(parameters.SchemaVersion, fmt.Errorf("failed to find transaction template `%s`", parameters.Input.Template)) if ctrl.schemaEnforcementMode == SchemaEnforcementStrict { return nil, err } else { - trace.SpanFromContext(ctx).SetAttributes(attribute.String("schema_validation_failed", err.Error())) + trace.SpanFromContext(ctx).SetAttributes( + attribute.Bool("schema_validation_failed", true), + attribute.String("schema_validation_reason", "unknown_template"), + ) logging.FromContext(ctx).Errorf("schema validation failed: %s", err) } } }Also: please confirm the intended spec in audit mode—should ad-hoc scripts still run when a schema is present, or should it be “template-only” regardless? (This is the same concern raised previously.) Based on past review comments, …
🧹 Nitpick comments (13)
internal/transaction.go (1)
37-53: Consider makingTransaction.Templatenullable (nullzeroor*string) to avoid persisting empty-string “unset”.
Not required, but it reduces ambiguity between “no template (legacy)” vs “template id accidentally empty”.internal/storage/ledger/resource_transactions.go (1)
35-52: Includingtemplatein the projection is the right move; consider adding it to the resource schema if filtering is desired.internal/api/v2/controllers_transactions_create.go (1)
20-35: Multi-type validation is good; improve the error to report all conflicting types (not just first two).
Currently if all three are provided, the message only mentions the first two.import ( "errors" "fmt" "net/http" + "strings" ... ) ... if len(txType) > 1 { - api.BadRequest(w, common.ErrValidation, fmt.Errorf("cannot pass %v and %v in the same request", txType[0], txType[1])) + api.BadRequest(w, common.ErrValidation, fmt.Errorf("cannot mix transaction types in the same request: %s", strings.Join(txType, ", "))) return } else if len(txType) == 0 {test/e2e/api_transactions_create_test.go (1)
102-111: Pointer migration looks consistent; consider adding at least one schema+template create test.
Thepointer.For(...)updates align withPlainbecoming nullable. Given the feature, it’d be valuable to add an e2e case that (1) inserts a schema with templates and (2) creates a transaction viascript.template(and asserts template info in the resulting transaction/event).Also applies to: 316-330, 489-501, 519-536, 552-562, 577-593, 602-618, 627-640
pkg/client/models/components/runtime.go (1)
10-35: Nice strict enum parsing; consider addingIsValid()(and optionallyMarshalJSON) for reuse.
This can reduce duplicated switch logic elsewhere and make validation callable without JSON roundtrips.internal/transaction_templates.go (1)
23-29: Avoid per-iteration allocations inTransactionTemplates.Validate()
[]RuntimeType{"", ...}is allocated on each loop iteration; also the error message doesn’t mention that""is accepted.func (t TransactionTemplates) Validate() error { for _, t := range t { - if !slices.Contains([]RuntimeType{"", RuntimeMachine, RuntimeExperimentalInterpreter}, t.Runtime) { - return fmt.Errorf("unexpected runtime `%s`: should be `%s` or `%s`", t.Runtime, RuntimeMachine, RuntimeExperimentalInterpreter) - } + switch t.Runtime { + case "", RuntimeMachine, RuntimeExperimentalInterpreter: + // ok + default: + return fmt.Errorf( + "unexpected runtime %q: should be %q or %q (or omitted)", + t.Runtime, RuntimeMachine, RuntimeExperimentalInterpreter, + ) + } } return nil }internal/controller/ledger/controller_default_test.go (2)
26-83:TestCreateTransactionWithoutSchema: consider using a non-emptyRunScript.PlainRight now
Parse(runScript.Plain)is parsing"". Even though it’s mocked, using a minimal valid script makes the test intent clearer and reduces “false confidence” if this ever stops being a pure mock.
85-220:TestCreateTransactionWithSchema: make template script + mocked postings consistentThe template script sends
EUR/2while the mocked execution returns aUSDposting; since the parser/runtime are mocked, this isn’t functionally wrong, but it’s confusing for future readers.- send [EUR/2 100] ( + send [USD 100] ( source = world destination = $dest )internal/controller/ledger/controller_default.go (4)
64-77: Schema template validation: prefer%qfor template IDs (and drop redundant cast)Minor polish: IDs are user-controlled strings; use
%q. Alsotemplate.Runtimeis already aledger.RuntimeType.for id, template := range schema.Transactions { - parser := ctrl.getParser(ledger.RuntimeType(template.Runtime)) + parser := ctrl.getParser(template.Runtime) _, err := parser.Parse(template.Script) if err != nil { - return nil, ledger.NewErrInvalidSchema(fmt.Errorf("invalid template %s: %w", id, err)) + return nil, ledger.NewErrInvalidSchema(fmt.Errorf("invalid template %q: %w", id, err)) } }
437-446: Small cleanup: inlinereturn m.Execute(...)No behavior change—just removes a temporary.
481-487: Don’t persist an empty template string
WithTemplate(parameters.Input.Template)is called unconditionally. If empty means “not set”, consider guarding it to avoid storing""(especially for no-schema flows).transaction := ledger.NewTransaction(). WithPostings(result.Postings...). WithMetadata(finalMetadata). WithTimestamp(parameters.Input.Timestamp). WithReference(parameters.Input.Reference). - WithTemplate(parameters.Input.Template) + WithTemplate(parameters.Input.Template) + + // If `WithTemplate("")` is meaningful today, ignore this; otherwise: + // if parameters.Input.Template != "" { transaction = transaction.WithTemplate(parameters.Input.Template) }
573-583: Unusedschemaparameters: consider_schemato satisfy linters/conventionsGo allows unused parameters, but many linters/code-style conventions prefer
_-prefixed names when intentionally unused. (You already do this inrevertTransaction.)Also applies to: 611-626, 633-644
test/e2e/api_schema_test.go (1)
446-470: Pointer construction inconsistency in ad-hoc script testElsewhere you use
&components.V2PostTransactionScript{...}; here you usepointer.For(components.V2PostTransactionScript{...}). Both work, but mixing styles makes the test harder to scan.- Script: pointer.For(components.V2PostTransactionScript{ + Script: &components.V2PostTransactionScript{ Plain: pointer.For(` send [EUR/2] ( source = $users:001 destination = $bank:001 )`), - }), + },Also applies to: 472-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 (8)
docs/events/CommittedTransactions.jsonis excluded by!**/*.jsondocs/events/InsertedSchema.jsonis excluded by!**/*.jsondocs/events/RevertedTransaction.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 (30)
docs/api/README.md(32 hunks)internal/README.md(29 hunks)internal/api/bulking/elements.go(2 hunks)internal/api/v2/controllers_transactions_create.go(2 hunks)internal/controller/ledger/controller.go(1 hunks)internal/controller/ledger/controller_default.go(8 hunks)internal/controller/ledger/controller_default_test.go(3 hunks)internal/errors.go(1 hunks)internal/machine/vm/run.go(1 hunks)internal/schema.go(2 hunks)internal/storage/bucket/migrations/44-add-schema/up.sql(1 hunks)internal/storage/ledger/resource_transactions.go(1 hunks)internal/transaction.go(2 hunks)internal/transaction_templates.go(1 hunks)pkg/client/docs/models/components/v2posttransactionscript.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/v2transaction.md(1 hunks)pkg/client/docs/models/components/v2transactiontemplate.md(1 hunks)pkg/client/docs/sdks/v2/README.md(1 hunks)pkg/client/models/components/runtime.go(1 hunks)pkg/client/models/components/v2posttransaction.go(1 hunks)pkg/client/models/components/v2schema.go(2 hunks)pkg/client/models/components/v2schemadata.go(2 hunks)pkg/client/models/components/v2transaction.go(2 hunks)pkg/client/models/components/v2transactiontemplate.go(1 hunks)pkg/generate/generator.go(1 hunks)test/e2e/api_ledgers_import_test.go(3 hunks)test/e2e/api_schema_test.go(15 hunks)test/e2e/api_transactions_create_test.go(8 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
pkg/client/models/components/runtime.go (1)
pkg/client/internal/utils/json.go (1)
UnmarshalJSON(117-251)
internal/schema.go (3)
internal/chart.go (1)
ChartOfAccounts(40-40)internal/transaction.go (1)
Transactions(15-17)internal/transaction_templates.go (1)
TransactionTemplates(21-21)
pkg/generate/generator.go (2)
internal/controller/ledger/controller.go (1)
Script(92-92)internal/api/v1/controllers_transactions_create.go (1)
Script(20-23)
pkg/client/models/components/v2transactiontemplate.go (3)
internal/controller/ledger/controller.go (1)
Script(92-92)internal/machine/vm/run.go (1)
Script(21-25)pkg/client/models/components/runtime.go (1)
Runtime(11-11)
pkg/client/models/components/v2schemadata.go (2)
internal/transaction.go (1)
Transactions(15-17)pkg/client/models/components/v2transactiontemplate.go (1)
V2TransactionTemplate(5-10)
internal/api/bulking/elements.go (5)
pkg/client/models/components/runtime.go (1)
Runtime(11-11)internal/transaction_templates.go (1)
RuntimeType(8-8)internal/controller/ledger/controller.go (1)
Script(92-92)internal/machine/vm/run.go (1)
Script(21-25)internal/api/v1/controllers_transactions_create.go (1)
Script(20-23)
internal/controller/ledger/controller.go (2)
pkg/client/models/components/runtime.go (1)
Runtime(11-11)internal/transaction_templates.go (1)
RuntimeType(8-8)
internal/api/v2/controllers_transactions_create.go (5)
internal/posting.go (1)
Postings(35-35)internal/controller/ledger/controller.go (1)
Script(92-92)internal/machine/vm/run.go (1)
Script(21-25)internal/api/v1/controllers_transactions_create.go (1)
Script(20-23)internal/api/common/errors.go (2)
ErrValidation(20-20)ErrNoPostings(22-22)
internal/controller/ledger/controller_default_test.go (5)
internal/schema.go (2)
Schema(16-22)SchemaData(11-14)internal/machine/vm/run.go (1)
Script(21-25)internal/log.go (3)
Log(89-102)InsertedSchemaLogType(25-25)NewTransactionLogType(22-22)internal/controller/ledger/parameters.go (1)
Parameters(3-8)internal/controller/ledger/numscript_runtime.go (1)
NumscriptExecutionResult(18-22)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/models/components/v2posttransactionscript.md
9-9: Hard tabs
Column: 391
(MD010, no-hard-tabs)
9-9: Hard tabs
Column: 412
(MD010, no-hard-tabs)
docs/api/README.md
5798-5798: Multiple headings with the same content
(MD024, no-duplicate-heading)
5827-5827: Multiple headings with the same content
(MD024, no-duplicate-heading)
5860-5860: Multiple headings with the same content
(MD024, no-duplicate-heading)
pkg/client/docs/sdks/v2/README.md
1231-1231: Hard tabs
Column: 18
(MD010, no-hard-tabs)
1232-1232: Hard tabs
Column: 18
(MD010, no-hard-tabs)
🔇 Additional comments (14)
internal/README.md (1)
68-75: Generated public API docs updated appropriately for templates/runtimes.
No concerns on the doc-side changes themselves (assuming gomarkdoc was regenerated from the updated Go types).Also applies to: 139-174, 165-174, 171-174
pkg/client/models/components/v2transaction.go (1)
11-26: Template field + accessor are consistent and backward compatible.Also applies to: 129-135
internal/controller/ledger/controller.go (1)
95-100: Runtime now correctly uses the sharedledger.RuntimeType.internal/transaction.go (1)
93-101: Fluent chaining fix +WithTemplatelook good.pkg/client/docs/models/components/v2transaction.md (1)
20-21: Docs updated to includeTemplate.pkg/client/docs/models/components/v2schemadata.md (1)
8-11: Docs update matches the new schema surface (Transactions templates).pkg/client/docs/models/components/v2transactiontemplate.md (1)
1-10: Model docs look aligned; please sanity-check the Runtime/feature-flag wording stays accurate.pkg/client/models/components/v2schema.go (1)
11-20: LGTM (and consistent with optionaltransactions,omitempty).Also applies to: 53-59
pkg/client/docs/models/components/v2schema.md (1)
8-13: Doc accuracy: verify whetherTransactionsis truly optional at the schema level.
If schemas can be inserted without templates,:heavy_minus_sign:is correct; if at least one template is required when a schema is present, the “Required” column and/or description should reflect that.pkg/client/models/components/v2transactiontemplate.go (1)
1-31: LGTM for the new SDK model + nil-safe accessors.test/e2e/api_ledgers_import_test.go (1)
186-231: Test updates forPlainbeing*stringare consistent.internal/controller/ledger/controller_default.go (1)
391-400: Runtime-based parser selection looks goodtest/e2e/api_schema_test.go (2)
57-79: Template-driven schema setup is clear; good reuse across versionsNice to see one
transactionTemplatesmap reused for v1/v2/v3 schema insertions.Also applies to: 87-112, 141-142, 171-172
232-252: Good assertion: verify persisted transaction includesTemplate
| type V2PostTransactionScript struct { | ||
| Plain string `json:"plain"` | ||
| Vars map[string]string `json:"vars,omitempty"` | ||
| Template *string `json:"template,omitempty"` | ||
| Plain *string `json:"plain,omitempty"` | ||
| Vars map[string]string `json:"vars,omitempty"` | ||
| } | ||
|
|
||
| func (o *V2PostTransactionScript) GetPlain() string { | ||
| func (o *V2PostTransactionScript) GetTemplate() *string { | ||
| if o == nil { | ||
| return "" | ||
| return nil | ||
| } | ||
| return o.Plain | ||
| return o.Template | ||
| } | ||
|
|
||
| func (o *V2PostTransactionScript) GetVars() map[string]string { | ||
| func (o *V2PostTransactionScript) GetPlain() *string { | ||
| if o == nil { | ||
| return nil | ||
| } | ||
| return o.Vars | ||
| return o.Plain | ||
| } | ||
|
|
||
| // Runtime - The numscript runtime used to execute the script. Uses "machine" by default, unless the "--experimental-numscript-interpreter" feature flag is passed. | ||
| type Runtime string | ||
|
|
||
| const ( | ||
| RuntimeExperimentalInterpreter Runtime = "experimental-interpreter" | ||
| RuntimeMachine Runtime = "machine" | ||
| ) | ||
|
|
||
| func (e Runtime) ToPointer() *Runtime { | ||
| return &e | ||
| } | ||
| func (e *Runtime) UnmarshalJSON(data []byte) error { | ||
| var v string | ||
| if err := json.Unmarshal(data, &v); err != nil { | ||
| return err | ||
| } | ||
| switch v { | ||
| case "experimental-interpreter": | ||
| fallthrough | ||
| case "machine": | ||
| *e = Runtime(v) | ||
| func (o *V2PostTransactionScript) GetVars() map[string]string { | ||
| if o == nil { | ||
| return nil | ||
| default: | ||
| return fmt.Errorf("invalid value for Runtime: %v", v) | ||
| } | ||
| return o.Vars | ||
| } |
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.
Breaking surface change: Plain is now *string (and Template added)
This changes JSON emission/consumption semantics (empty string vs omitted) and will likely break existing client code that assigns/reads Plain as a value type. Ensure the SDK changelog/migration guidance explicitly calls out:
script.plainis now optional/pointerscript.templateis the preferred path when schemas are used- mutual exclusivity expectations (template vs plain) from the API perspective
🤖 Prompt for AI Agents
In pkg/client/models/components/v2posttransaction.go around lines 10 to 35, the
struct change makes Plain a *string and adds Template which alters JSON
emission/consumption (empty string vs omitted) and is a breaking change; update
the SDK release notes and migration guidance to explicitly state that
script.plain is now optional/pointer, script.template is the preferred path when
schemas are used, and document the API expectation that template and plain are
mutually exclusive (include examples of old vs new JSON and recommended
client-side handling), and optionally add helper accessors or constructors that
return non-nil defaults or validation helpers to ease migration for existing
clients.
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 (3)
internal/machine/vm/run.go (1)
21-25: LGTM! Past review feedback addressed.The
omitemptytags have been correctly added toPlain,Template, andVarsfields as requested in the previous review, ensuring that empty fields are not emitted in JSON and preventing confusion between plain and template script modes.docs/api/README.md (2)
3985-3985: Clarify template field semantics (unresolved from previous review).The
templatefield is added toV2TransactionandV2PostTransaction.scriptbut lacks documentation on its presence/absence behavior. Prior feedback (lines 870–872) requested clarification on whether:
- (a)
templateis omitted/null when transaction is not from a template, or- (b)
templateis always present (possibly empty string when not from a template)Update field descriptions (e.g., at line 4007 and 4059) to explicitly state this behavior, and adjust all examples to consistently reflect the chosen semantics (e.g., remove
templatefrom non-template transaction examples or set it tonull).Also applies to: 4007-4007, 4059-4059
5784-5865: Address markdownlint MD024 duplicate heading errors (unresolved from previous review).Static analysis flags duplicate headings at lines 5798, 5827, and 5860 (Runtime, V2TransactionTemplate, V2TransactionTemplates sections). Since this file is auto-generated by Widdershins, you have two options:
- Regenerate the spec if duplicate heading definitions exist in the upstream OpenAPI specification.
- Disable MD024 locally by wrapping the generated schema block with:
<!-- markdownlint-disable MD024 --> [generated schema sections] <!-- markdownlint-enable MD024 -->If regeneration is not feasible, add disable/enable comments around the schema definitions (before line 5784 and after line 5865) to suppress the linter.
📜 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 (10)
docs/api/README.md(32 hunks)internal/machine/vm/run.go(1 hunks)internal/schema.go(2 hunks)pkg/client/docs/models/components/v2schema.md(1 hunks)pkg/client/docs/models/components/v2schemadata.md(1 hunks)pkg/client/docs/sdks/v2/README.md(2 hunks)pkg/client/models/components/v2schema.go(2 hunks)pkg/client/models/components/v2schemadata.go(2 hunks)test/e2e/api_idempotency_hit_header_test.go(1 hunks)test/e2e/api_schema_test.go(16 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/client/docs/models/components/v2schemadata.md
🧰 Additional context used
🧬 Code graph analysis (5)
test/e2e/api_idempotency_hit_header_test.go (2)
internal/transaction.go (1)
Transactions(15-17)pkg/client/models/components/v2transactiontemplate.go (1)
V2TransactionTemplate(5-10)
internal/schema.go (4)
internal/chart.go (1)
ChartOfAccounts(40-40)internal/transaction.go (1)
Transactions(15-17)internal/transaction_templates.go (1)
TransactionTemplates(21-21)internal/errors.go (1)
NewErrInvalidSchema(102-104)
test/e2e/api_schema_test.go (6)
pkg/client/models/components/v2transactiontemplate.go (1)
V2TransactionTemplate(5-10)internal/machine/vm/run.go (1)
Script(21-25)pkg/client/models/components/v2schemadata.go (1)
V2SchemaData(6-11)pkg/client/models/operations/v2createtransaction.go (1)
V2CreateTransactionRequest(9-25)pkg/client/models/components/v2posttransaction.go (2)
V2PostTransaction(37-47)V2PostTransactionScript(10-14)pkg/client/models/components/v2errorsenum.go (2)
V2ErrorsEnumNotFound(19-19)V2ErrorsEnumValidation(15-15)
pkg/client/models/components/v2schemadata.go (2)
internal/transaction.go (1)
Transactions(15-17)pkg/client/models/components/v2transactiontemplate.go (1)
V2TransactionTemplate(5-10)
pkg/client/models/components/v2schema.go (2)
internal/transaction.go (1)
Transactions(15-17)pkg/client/models/components/v2transactiontemplate.go (1)
V2TransactionTemplate(5-10)
🪛 markdownlint-cli2 (0.18.1)
pkg/client/docs/sdks/v2/README.md
1236-1236: Hard tabs
Column: 18
(MD010, no-hard-tabs)
1237-1237: Hard tabs
Column: 18
(MD010, no-hard-tabs)
docs/api/README.md
5798-5798: Multiple headings with the same content
(MD024, no-duplicate-heading)
5827-5827: Multiple headings with the same content
(MD024, no-duplicate-heading)
5860-5860: Multiple headings with the same content
(MD024, no-duplicate-heading)
🔇 Additional comments (13)
test/e2e/api_idempotency_hit_header_test.go (1)
486-486: LGTM! Appropriate initialization of new required field.The empty
Transactionsmap correctly satisfies the new schema requirement. Since this test focuses on idempotency header behavior rather than template functionality, the empty initialization is appropriate.pkg/client/docs/models/components/v2schema.md (1)
8-13: LGTM! Documentation accurately reflects the new field.The
Transactionsfield is properly documented with the correct type and marked as required, consistent with the implementation.pkg/client/models/components/v2schemadata.go (2)
9-10: LGTM! Consistent field addition.The
Transactionsfield follows the same pattern as the existingChartfield with appropriate JSON tags and comments.
20-25: LGTM! Nil-safe accessor follows existing pattern.The
GetTransactions()accessor correctly mirrors theGetChart()pattern, returning an empty map when the receiver is nil.pkg/client/models/components/v2schema.go (2)
18-19: LGTM! Field addition consistent with schema data.The
Transactionsfield correctly mirrors the structure inV2SchemaDatawith appropriate documentation.
54-59: LGTM! Nil-safe accessor follows established pattern.The
GetTransactions()accessor is consistent with other getter methods in the struct.test/e2e/api_schema_test.go (5)
57-79: LGTM! Well-structured template definitions.The two transaction templates provide good coverage:
WORLD_TO_BANK: Simple template with single variable for basic scenariosA_TO_B: More complex template with overdraft allowance for advanced testingThe inline numscript is clear and appropriate for test scenarios.
87-89: Good addition of world account to chart.Adding
worldwithDotSelfto the chart enables the@worldsource used in theWORLD_TO_BANKtemplate, ensuring schema validation passes.
247-252: Excellent verification of template field in response.Fetching the created transaction and asserting the
Templatefield matches expectations ensures the template reference is correctly persisted and returned. This validates a key aspect of the template feature.
428-445: Good coverage of non-existent schema error case.Testing that a non-existent schema version returns
NOT_FOUNDensures proper error handling when clients reference invalid schemas.
447-490: Excellent validation error coverage.These two tests ensure that when schema enforcement is enabled:
- Direct postings are rejected (lines 447-471)
- Ad-hoc plain scripts are rejected (lines 473-490)
This validates the core constraint that templates must be used with schema enforcement, preventing clients from bypassing the schema contract.
pkg/client/docs/sdks/v2/README.md (2)
281-286: LGTM! Clear documentation of Transactions field.The InsertSchema example clearly demonstrates how to include transaction templates in the schema, with a minimal but complete example.
1231-1239: LGTM! Good example of template usage.The CreateTransaction example clearly shows both
TemplateandPlainfields in the script, demonstrating the dual modes. The example is concise and practical.Note: The static analysis warning about hard tabs (lines 1236-1237) is a false positive—these are part of the numscript string literal and are intentional for formatting the script.
Summary by cubic
Adds transaction templates to ledger schemas and lets clients execute transactions by template ID. When schema enforcement is enabled, transactions must use a template; otherwise, postings or plain numscript still work.
New Features
Migration
Written for commit 655dbf6. Summary will update automatically on new commits.