better error tracing
This commit is contained in:
@@ -82,6 +82,7 @@ func selectGatewayForActions(ctx context.Context, registry GatewayRegistry, rail
|
|||||||
network = strings.ToUpper(strings.TrimSpace(network))
|
network = strings.ToUpper(strings.TrimSpace(network))
|
||||||
|
|
||||||
eligible := make([]*model.GatewayInstanceDescriptor, 0)
|
eligible := make([]*model.GatewayInstanceDescriptor, 0)
|
||||||
|
var lastErr error
|
||||||
for _, entry := range all {
|
for _, entry := range all {
|
||||||
if entry == nil || !entry.IsEnabled {
|
if entry == nil || !entry.IsEnabled {
|
||||||
continue
|
continue
|
||||||
@@ -94,7 +95,8 @@ func selectGatewayForActions(ctx context.Context, registry GatewayRegistry, rail
|
|||||||
}
|
}
|
||||||
ok := true
|
ok := true
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if !isGatewayEligible(entry, rail, network, currency, action, dir, amt) {
|
if err := isGatewayEligible(entry, rail, network, currency, action, dir, amt); err != nil {
|
||||||
|
lastErr = err
|
||||||
ok = false
|
ok = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -106,6 +108,9 @@ func selectGatewayForActions(ctx context.Context, registry GatewayRegistry, rail
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(eligible) == 0 {
|
if len(eligible) == 0 {
|
||||||
|
if lastErr != nil {
|
||||||
|
return nil, merrors.NoData("no eligible gateway instance found: " + lastErr.Error())
|
||||||
|
}
|
||||||
return nil, merrors.NoData("no eligible gateway instance found")
|
return nil, merrors.NoData("no eligible gateway instance found")
|
||||||
}
|
}
|
||||||
sort.Slice(eligible, func(i, j int) bool {
|
sort.Slice(eligible, func(i, j int) bool {
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ func (g railGatewayDependency) resolveDynamic(ctx context.Context, step *model.P
|
|||||||
}
|
}
|
||||||
|
|
||||||
candidates := make([]*model.GatewayInstanceDescriptor, 0)
|
candidates := make([]*model.GatewayInstanceDescriptor, 0)
|
||||||
|
var lastErr error
|
||||||
for _, entry := range items {
|
for _, entry := range items {
|
||||||
if entry == nil || !entry.IsEnabled {
|
if entry == nil || !entry.IsEnabled {
|
||||||
continue
|
continue
|
||||||
@@ -132,13 +133,17 @@ func (g railGatewayDependency) resolveDynamic(ctx context.Context, step *model.P
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if step.Action != model.RailOperationUnspecified {
|
if step.Action != model.RailOperationUnspecified {
|
||||||
if !isGatewayEligible(entry, step.Rail, "", currency, step.Action, sendDirectionForRail(step.Rail), amount) {
|
if err := isGatewayEligible(entry, step.Rail, "", currency, step.Action, sendDirectionForRail(step.Rail), amount); err != nil {
|
||||||
|
lastErr = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
candidates = append(candidates, entry)
|
candidates = append(candidates, entry)
|
||||||
}
|
}
|
||||||
if len(candidates) == 0 {
|
if len(candidates) == 0 {
|
||||||
|
if lastErr != nil {
|
||||||
|
return nil, merrors.InvalidArgument("rail gateway: missing gateway for rail: " + lastErr.Error())
|
||||||
|
}
|
||||||
return nil, merrors.InvalidArgument("rail gateway: missing gateway for rail")
|
return nil, merrors.InvalidArgument("rail gateway: missing gateway for rail")
|
||||||
}
|
}
|
||||||
sort.Slice(candidates, func(i, j int) bool {
|
sort.Slice(candidates, func(i, j int) bool {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -45,8 +46,8 @@ func validateGatewayAction(gw *model.GatewayInstanceDescriptor, network string,
|
|||||||
amt = value
|
amt = value
|
||||||
currency = strings.ToUpper(strings.TrimSpace(amount.GetCurrency()))
|
currency = strings.ToUpper(strings.TrimSpace(amount.GetCurrency()))
|
||||||
}
|
}
|
||||||
if !isGatewayEligible(gw, gw.Rail, network, currency, action, dir, amt) {
|
if err := isGatewayEligible(gw, gw.Rail, network, currency, action, dir, amt); err != nil {
|
||||||
return merrors.InvalidArgument("plan builder: gateway instance is not eligible")
|
return merrors.InvalidArgument("plan builder: gateway instance is not eligible: " + err.Error())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -92,16 +93,21 @@ func selectGateway(ctx context.Context, registry GatewayRegistry, rail model.Rai
|
|||||||
network = strings.ToUpper(strings.TrimSpace(network))
|
network = strings.ToUpper(strings.TrimSpace(network))
|
||||||
|
|
||||||
eligible := make([]*model.GatewayInstanceDescriptor, 0)
|
eligible := make([]*model.GatewayInstanceDescriptor, 0)
|
||||||
|
var lastErr error
|
||||||
for _, gw := range all {
|
for _, gw := range all {
|
||||||
if instanceID != "" && !strings.EqualFold(strings.TrimSpace(gw.InstanceID), instanceID) {
|
if instanceID != "" && !strings.EqualFold(strings.TrimSpace(gw.InstanceID), instanceID) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !isGatewayEligible(gw, rail, network, currency, action, dir, amt) {
|
if err := isGatewayEligible(gw, rail, network, currency, action, dir, amt); err != nil {
|
||||||
|
lastErr = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
eligible = append(eligible, gw)
|
eligible = append(eligible, gw)
|
||||||
}
|
}
|
||||||
if len(eligible) == 0 {
|
if len(eligible) == 0 {
|
||||||
|
if lastErr != nil {
|
||||||
|
return nil, merrors.InvalidArgument("plan builder: no eligible gateway instance found: " + lastErr.Error())
|
||||||
|
}
|
||||||
return nil, merrors.InvalidArgument("plan builder: no eligible gateway instance found")
|
return nil, merrors.InvalidArgument("plan builder: no eligible gateway instance found")
|
||||||
}
|
}
|
||||||
sort.Slice(eligible, func(i, j int) bool {
|
sort.Slice(eligible, func(i, j int) bool {
|
||||||
@@ -110,15 +116,44 @@ func selectGateway(ctx context.Context, registry GatewayRegistry, rail model.Rai
|
|||||||
return eligible[0], nil
|
return eligible[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGatewayEligible(gw *model.GatewayInstanceDescriptor, rail model.Rail, network, currency string, action model.RailOperation, dir sendDirection, amount decimal.Decimal) bool {
|
type gatewayIneligibleError struct {
|
||||||
if gw == nil || !gw.IsEnabled {
|
reason string
|
||||||
return false
|
}
|
||||||
|
|
||||||
|
func (e gatewayIneligibleError) Error() string {
|
||||||
|
return e.reason
|
||||||
|
}
|
||||||
|
|
||||||
|
func gatewayIneligible(reason string) error {
|
||||||
|
if strings.TrimSpace(reason) == "" {
|
||||||
|
reason = "gateway instance is not eligible"
|
||||||
|
}
|
||||||
|
return gatewayIneligibleError{reason: reason}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendDirectionLabel(dir sendDirection) string {
|
||||||
|
switch dir {
|
||||||
|
case sendDirectionOut:
|
||||||
|
return "out"
|
||||||
|
case sendDirectionIn:
|
||||||
|
return "in"
|
||||||
|
default:
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isGatewayEligible(gw *model.GatewayInstanceDescriptor, rail model.Rail, network, currency string, action model.RailOperation, dir sendDirection, amount decimal.Decimal) error {
|
||||||
|
if gw == nil {
|
||||||
|
return gatewayIneligible("gateway instance is required")
|
||||||
|
}
|
||||||
|
if !gw.IsEnabled {
|
||||||
|
return gatewayIneligible("gateway instance is disabled")
|
||||||
}
|
}
|
||||||
if gw.Rail != rail {
|
if gw.Rail != rail {
|
||||||
return false
|
return gatewayIneligible(fmt.Sprintf("rail mismatch: want %s got %s", rail, gw.Rail))
|
||||||
}
|
}
|
||||||
if network != "" && gw.Network != "" && !strings.EqualFold(gw.Network, network) {
|
if network != "" && gw.Network != "" && !strings.EqualFold(gw.Network, network) {
|
||||||
return false
|
return gatewayIneligible(fmt.Sprintf("network mismatch: want %s got %s", network, gw.Network))
|
||||||
}
|
}
|
||||||
if currency != "" && len(gw.Currencies) > 0 {
|
if currency != "" && len(gw.Currencies) > 0 {
|
||||||
found := false
|
found := false
|
||||||
@@ -129,20 +164,20 @@ func isGatewayEligible(gw *model.GatewayInstanceDescriptor, rail model.Rail, net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return false
|
return gatewayIneligible("currency not supported: " + currency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !capabilityAllowsAction(gw.Capabilities, action, dir) {
|
if !capabilityAllowsAction(gw.Capabilities, action, dir) {
|
||||||
return false
|
return gatewayIneligible(fmt.Sprintf("capability does not allow action=%s dir=%s", action, sendDirectionLabel(dir)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if currency != "" {
|
if currency != "" {
|
||||||
if !amountWithinLimits(gw.Limits, currency, amount, action) {
|
if err := amountWithinLimits(gw.Limits, currency, amount, action); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func capabilityAllowsAction(cap model.RailCapabilities, action model.RailOperation, dir sendDirection) bool {
|
func capabilityAllowsAction(cap model.RailCapabilities, action model.RailOperation, dir sendDirection) bool {
|
||||||
@@ -169,7 +204,7 @@ func capabilityAllowsAction(cap model.RailCapabilities, action model.RailOperati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func amountWithinLimits(limits model.Limits, currency string, amount decimal.Decimal, action model.RailOperation) bool {
|
func amountWithinLimits(limits model.Limits, currency string, amount decimal.Decimal, action model.RailOperation) error {
|
||||||
min := firstLimitValue(limits.MinAmount, "")
|
min := firstLimitValue(limits.MinAmount, "")
|
||||||
max := firstLimitValue(limits.MaxAmount, "")
|
max := firstLimitValue(limits.MaxAmount, "")
|
||||||
perTxMin := firstLimitValue(limits.PerTxMinAmount, "")
|
perTxMin := firstLimitValue(limits.PerTxMinAmount, "")
|
||||||
@@ -186,31 +221,31 @@ func amountWithinLimits(limits model.Limits, currency string, amount decimal.Dec
|
|||||||
|
|
||||||
if min != "" {
|
if min != "" {
|
||||||
if val, err := decimal.NewFromString(min); err == nil && amount.LessThan(val) {
|
if val, err := decimal.NewFromString(min); err == nil && amount.LessThan(val) {
|
||||||
return false
|
return gatewayIneligible(fmt.Sprintf("amount %s %s below min limit %s", amount.String(), currency, val.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if perTxMin != "" {
|
if perTxMin != "" {
|
||||||
if val, err := decimal.NewFromString(perTxMin); err == nil && amount.LessThan(val) {
|
if val, err := decimal.NewFromString(perTxMin); err == nil && amount.LessThan(val) {
|
||||||
return false
|
return gatewayIneligible(fmt.Sprintf("amount %s %s below per-tx min limit %s", amount.String(), currency, val.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if max != "" {
|
if max != "" {
|
||||||
if val, err := decimal.NewFromString(max); err == nil && amount.GreaterThan(val) {
|
if val, err := decimal.NewFromString(max); err == nil && amount.GreaterThan(val) {
|
||||||
return false
|
return gatewayIneligible(fmt.Sprintf("amount %s %s exceeds max limit %s", amount.String(), currency, val.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if perTxMax != "" {
|
if perTxMax != "" {
|
||||||
if val, err := decimal.NewFromString(perTxMax); err == nil && amount.GreaterThan(val) {
|
if val, err := decimal.NewFromString(perTxMax); err == nil && amount.GreaterThan(val) {
|
||||||
return false
|
return gatewayIneligible(fmt.Sprintf("amount %s %s exceeds per-tx max limit %s", amount.String(), currency, val.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if action == model.RailOperationFee && maxFee != "" {
|
if action == model.RailOperationFee && maxFee != "" {
|
||||||
if val, err := decimal.NewFromString(maxFee); err == nil && amount.GreaterThan(val) {
|
if val, err := decimal.NewFromString(maxFee); err == nil && amount.GreaterThan(val) {
|
||||||
return false
|
return gatewayIneligible(fmt.Sprintf("fee amount %s %s exceeds max fee limit %s", amount.String(), currency, val.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func firstLimitValue(primary, fallback string) string {
|
func firstLimitValue(primary, fallback string) string {
|
||||||
|
|||||||
Reference in New Issue
Block a user