Merge pull request 'fixed currency pair validation' (#85) from currency-84 into main
Some checks failed
ci/woodpecker/push/chain_gateway Pipeline is pending
ci/woodpecker/push/db Pipeline is pending
ci/woodpecker/push/frontend Pipeline is pending
ci/woodpecker/push/fx_ingestor Pipeline is pending
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/mntx_gateway Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/billing_fees Pipeline failed
ci/woodpecker/push/bff Pipeline failed
Some checks failed
ci/woodpecker/push/chain_gateway Pipeline is pending
ci/woodpecker/push/db Pipeline is pending
ci/woodpecker/push/frontend Pipeline is pending
ci/woodpecker/push/fx_ingestor Pipeline is pending
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/mntx_gateway Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/billing_fees Pipeline failed
ci/woodpecker/push/bff Pipeline failed
Reviewed-on: #85
This commit was merged in pull request #85.
This commit is contained in:
@@ -20,41 +20,17 @@ type AssetResolver interface {
|
||||
// Precompile regex for efficiency.
|
||||
var currencySyntax = regexp.MustCompile(`^[A-Z0-9]{2,10}$`)
|
||||
|
||||
func ValidateMoney(m *model.Money, assetResolver AssetResolver) error {
|
||||
if m == nil {
|
||||
return merrors.InvalidArgument("money is required", "intent.amount")
|
||||
}
|
||||
|
||||
//
|
||||
// 1) Basic presence
|
||||
//
|
||||
if strings.TrimSpace(m.Amount) == "" {
|
||||
return merrors.InvalidArgument("amount is required", "intent.amount")
|
||||
}
|
||||
if strings.TrimSpace(m.Currency) == "" {
|
||||
// ValidateCurrency validates currency syntax and checks dictionary via assetResolver.
|
||||
func ValidateCurrency(cur string, assetResolver AssetResolver) error {
|
||||
// Basic presence
|
||||
if strings.TrimSpace(cur) == "" {
|
||||
return merrors.InvalidArgument("currency is required", "intent.currency")
|
||||
}
|
||||
|
||||
//
|
||||
// 2) Validate decimal amount
|
||||
//
|
||||
amount, err := decimal.NewFromString(m.Amount)
|
||||
if err != nil {
|
||||
return merrors.InvalidArgument("invalid decimal amount", "intent.amount")
|
||||
}
|
||||
// Normalize
|
||||
cur = strings.ToUpper(strings.TrimSpace(cur))
|
||||
|
||||
if amount.IsNegative() {
|
||||
return merrors.InvalidArgument("amount must be >= 0", "intent.amount")
|
||||
}
|
||||
|
||||
//
|
||||
// 3) Normalize currency
|
||||
//
|
||||
cur := strings.ToUpper(strings.TrimSpace(m.Currency))
|
||||
|
||||
//
|
||||
// 4) Syntax validation first — reject malformed tickers early
|
||||
//
|
||||
// Syntax check
|
||||
if !currencySyntax.MatchString(cur) {
|
||||
return merrors.InvalidArgument(
|
||||
"invalid currency format (must be A–Z0–9, length 2–10)",
|
||||
@@ -62,9 +38,7 @@ func ValidateMoney(m *model.Money, assetResolver AssetResolver) error {
|
||||
)
|
||||
}
|
||||
|
||||
//
|
||||
// 5) Dictionary / environment validation
|
||||
//
|
||||
// Dictionary validation
|
||||
if assetResolver == nil {
|
||||
return merrors.InvalidArgument("asset resolver is not configured", "intent.currency")
|
||||
}
|
||||
@@ -76,37 +50,48 @@ func ValidateMoney(m *model.Money, assetResolver AssetResolver) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateMoney(m *model.Money, assetResolver AssetResolver) error {
|
||||
if m == nil {
|
||||
return merrors.InvalidArgument("money is required", "intent.amount")
|
||||
}
|
||||
|
||||
// 1) Basic presence
|
||||
if strings.TrimSpace(m.Amount) == "" {
|
||||
return merrors.InvalidArgument("amount is required", "intent.amount")
|
||||
}
|
||||
|
||||
// 2) Validate decimal amount
|
||||
amount, err := decimal.NewFromString(m.Amount)
|
||||
if err != nil {
|
||||
return merrors.InvalidArgument("invalid decimal amount", "intent.amount")
|
||||
}
|
||||
if amount.IsNegative() {
|
||||
return merrors.InvalidArgument("amount must be >= 0", "intent.amount")
|
||||
}
|
||||
|
||||
// 3) Validate currency via helper
|
||||
if err := ValidateCurrency(m.Currency, assetResolver); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type CurrencyPair struct {
|
||||
Base string `json:"base"`
|
||||
Quote string `json:"quote"`
|
||||
}
|
||||
|
||||
func (p *CurrencyPair) Validate() error {
|
||||
if p.Base == "" {
|
||||
return merrors.InvalidArgument("base currency is required", "intent.fx.pair.base")
|
||||
if p == nil {
|
||||
return merrors.InvalidArgument("currency pair is required", "currncy_pair")
|
||||
}
|
||||
if p.Quote == "" {
|
||||
return merrors.InvalidArgument("quote currency is required", "intent.fx.pair.quote")
|
||||
if err := ValidateCurrency(p.Base, nil); err != nil {
|
||||
return merrors.InvalidArgument("invalid base currency in pair: "+err.Error(), "currency_pair.base")
|
||||
}
|
||||
|
||||
if len(p.Base) != 3 {
|
||||
return merrors.InvalidArgument("base currency must be 3 letters", "intent.fx.pair.base")
|
||||
if err := ValidateCurrency(p.Quote, nil); err != nil {
|
||||
return merrors.InvalidArgument("invalid quote currency in pair: "+err.Error(), "currency_pair.quote")
|
||||
}
|
||||
if len(p.Quote) != 3 {
|
||||
return merrors.InvalidArgument("quote currency must be 3 letters", "intent.fx.pair.quote")
|
||||
}
|
||||
|
||||
for _, c := range p.Base {
|
||||
if c < 'A' || c > 'Z' {
|
||||
return merrors.InvalidArgument("base currency must be uppercase A-Z", "intent.fx.pair.base")
|
||||
}
|
||||
}
|
||||
for _, c := range p.Quote {
|
||||
if c < 'A' || c > 'Z' {
|
||||
return merrors.InvalidArgument("quote currency must be uppercase A-Z", "intent.fx.pair.quote")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user