Skip to content

feat: add capability to specify 'force' parameter on bulkd endpoint when creating transactions #939

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,8 @@ Accept: application/json
"property2": {
"admin": "true"
}
}
},
"force": true
Copy link

Choose a reason for hiding this comment

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

not sure it's worth the change, but I'd put it to false here to show it's the default (and as forcing something is dangerous, you don't want people to copy/paste this without knowing what that param does)

}
}
]
Expand Down Expand Up @@ -1329,7 +1330,8 @@ Idempotency-Key: string
"property2": {
"admin": "true"
}
}
},
"force": true
}
```

Expand Down Expand Up @@ -2721,7 +2723,8 @@ Authorization ( Scopes: ledger:write )
"property2": {
"admin": "true"
}
}
},
"force": true
}

```
Expand All @@ -2741,6 +2744,7 @@ Authorization ( Scopes: ledger:write )
|metadata|[V2Metadata](#schemav2metadata)|true|none|none|
|accountMetadata|object|false|none|none|
|» **additionalProperties**|[V2Metadata](#schemav2metadata)|false|none|none|
|force|boolean|false|none|none|

#### Enumerated Values

Expand Down Expand Up @@ -3443,7 +3447,8 @@ Authorization ( Scopes: ledger:write )
"property2": {
"admin": "true"
}
}
},
"force": true
}
}
]
Expand Down Expand Up @@ -3517,7 +3522,8 @@ Authorization ( Scopes: ledger:write )
"property2": {
"admin": "true"
}
}
},
"force": true
}
}

Expand Down Expand Up @@ -3588,7 +3594,8 @@ xor
"property2": {
"admin": "true"
}
}
},
"force": true
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/api/bulking/bulker.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (b *Bulker) processElement(ctx context.Context, ctrl ledgercontroller.Contr

switch data.Action {
case ActionCreateTransaction:
rs, err := data.Data.(TransactionRequest).ToCore(false)
rs, err := data.Data.(TransactionRequest).ToCore()
if err != nil {
return nil, 0, fmt.Errorf("error parsing element: %s", err)
}
Expand Down
5 changes: 3 additions & 2 deletions internal/api/bulking/elements.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@ type TransactionRequest struct {
Metadata metadata.Metadata `json:"metadata" swaggertype:"object"`
AccountMetadata map[string]metadata.Metadata `json:"accountMetadata"`
Runtime ledgercontroller.RuntimeType `json:"runtime,omitempty"`
Force bool `json:"force"`
}

func (req TransactionRequest) ToCore(allowUnboundedOverdrafts bool) (*ledgercontroller.CreateTransaction, error) {
func (req TransactionRequest) ToCore() (*ledgercontroller.CreateTransaction, error) {

if _, err := req.Postings.Validate(); err != nil {
return nil, err
Expand All @@ -119,7 +120,7 @@ func (req TransactionRequest) ToCore(allowUnboundedOverdrafts bool) (*ledgercont
Metadata: req.Metadata,
}

runScript = ledgercontroller.TxToScriptData(txData, allowUnboundedOverdrafts)
runScript = ledgercontroller.TxToScriptData(txData, req.Force)
} else {
runScript = ledgercontroller.RunScript{
Script: req.Script.ToCore(),
Expand Down
9 changes: 8 additions & 1 deletion internal/api/v2/controllers_transactions_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@ func createTransaction(w http.ResponseWriter, r *http.Request) {
api.BadRequest(w, common.ErrNoPostings, errors.New("you need to pass either a posting array or a numscript script"))
return
}
createTransaction, err := payload.ToCore(api.QueryParamBool(r, "force"))

// nodes(gfyrag): parameter 'force' initially sent using a query param
// while we still support the feature, we can also send the 'force' parameter
// in the request payload.
// it allows to leverage the feature on bulk endpoint
payload.Force = payload.Force || api.QueryParamBool(r, "force")

createTransaction, err := payload.ToCore()
if err != nil {
api.BadRequest(w, common.ErrValidation, err)
return
Expand Down
3 changes: 3 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,7 @@ paths:
- name: force
in: query
description: Disable balance checks when passing postings
deprecated: true
Copy link

Choose a reason for hiding this comment

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

Add in the description what replaces it :)

schema:
type: boolean
example: true
Expand Down Expand Up @@ -3499,6 +3500,8 @@ components:
type: object
additionalProperties:
$ref: "#/components/schemas/V2Metadata"
force:
type: boolean
V2Stats:
type: object
properties:
Expand Down
3 changes: 3 additions & 0 deletions openapi/v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ paths:
- name: force
in: query
description: Disable balance checks when passing postings
deprecated: true
Copy link

Choose a reason for hiding this comment

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

as above

schema:
type: boolean
example: true
Expand Down Expand Up @@ -1656,6 +1657,8 @@ components:
type: object
additionalProperties:
$ref: "#/components/schemas/V2Metadata"
force:
type: boolean
V2Stats:
type: object
properties:
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/.speakeasy/gen.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
lockVersion: 2.0.0
id: a9ac79e1-e429-4ee3-96c4-ec973f19bec3
management:
docChecksum: aa90e15d37f175280618bd0108917475
docChecksum: ce0c8826dc64a0dde9cb29984a1013d1
docVersion: v2
speakeasyVersion: 1.517.3
generationVersion: 2.548.6
Expand Down
3 changes: 2 additions & 1 deletion pkg/client/docs/models/components/v2posttransaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
| `Runtime` | [*components.Runtime](../../models/components/runtime.md) | :heavy_minus_sign: | The numscript runtime used to execute the script. Uses "machine" by default, unless the "--experimental-numscript-interpreter" feature flag is passed. | |
| `Reference` | **string* | :heavy_minus_sign: | N/A | ref:001 |
| `Metadata` | map[string]*string* | :heavy_check_mark: | N/A | {<br/>"admin": "true"<br/>} |
| `AccountMetadata` | map[string]map[string]*string* | :heavy_minus_sign: | N/A | |
| `AccountMetadata` | map[string]map[string]*string* | :heavy_minus_sign: | N/A | |
| `Force` | **bool* | :heavy_minus_sign: | N/A | |
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
| `Ledger` | *string* | :heavy_check_mark: | Name of the ledger. | ledger001 |
| `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: | Disable balance checks when passing postings | true |
| ~~`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 |
| `V2PostTransaction` | [components.V2PostTransaction](../../models/components/v2posttransaction.md) | :heavy_check_mark: | The request body must contain at least one of the following objects:<br/> - `postings`: suitable for simple transactions<br/> - `script`: enabling more complex transactions with Numscript<br/> | |
8 changes: 8 additions & 0 deletions pkg/client/models/components/v2posttransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type V2PostTransaction struct {
Reference *string `json:"reference,omitempty"`
Metadata map[string]string `json:"metadata"`
AccountMetadata map[string]map[string]string `json:"accountMetadata,omitempty"`
Force *bool `json:"force,omitempty"`
}

func (v V2PostTransaction) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -125,3 +126,10 @@ func (o *V2PostTransaction) GetAccountMetadata() map[string]map[string]string {
}
return o.AccountMetadata
}

func (o *V2PostTransaction) GetForce() *bool {
if o == nil {
return nil
}
return o.Force
}
2 changes: 2 additions & 0 deletions pkg/client/models/operations/v2createtransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type V2CreateTransactionRequest struct {
// Use an idempotency key
IdempotencyKey *string `header:"style=simple,explode=false,name=Idempotency-Key"`
// Disable balance checks when passing postings
//
// Deprecated: This will be removed in a future release, please migrate away from it as soon as possible.
Force *bool `queryParam:"style=form,explode=true,name=force"`
// The request body must contain at least one of the following objects:
// - `postings`: suitable for simple transactions
Expand Down
25 changes: 24 additions & 1 deletion test/e2e/api_bulk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,30 @@ var _ = Context("Ledger engine tests", func() {
Expect(err).To(BeNil())
_, events = Subscribe(specContext, testServer, natsURL)
})
When("creating a bulk on a ledger", func() {
When("creating a bulk on a ledger with force parameter", func() {
It("should be ok", func(specContext SpecContext) {
_, err := Wait(specContext, DeferClient(testServer)).Ledger.V2.CreateBulk(ctx, operations.V2CreateBulkRequest{
RequestBody: []components.V2BulkElement{
components.CreateV2BulkElementCreateTransaction(components.V2BulkElementCreateTransaction{
Data: &components.V2PostTransaction{
Metadata: map[string]string{},
Postings: []components.V2Posting{{
Amount: big.NewInt(100),
Asset: "USD/2",
Destination: "user:1",
Source: "user:2",
}},
Timestamp: &now,
Force: pointer.For(true),
},
}),
},
Ledger: "default",
})
Expect(err).To(Succeed())
})
})
When("creating a valid bulk on a ledger", func() {
var (
now = time.Now().Round(time.Microsecond).UTC()
items []components.V2BulkElement
Expand Down
Loading