bff dev upgrde

This commit is contained in:
Stephan D
2026-01-30 16:39:12 +01:00
parent 51f5b0804a
commit e1f58b0982
22 changed files with 969 additions and 185 deletions

View File

@@ -3,6 +3,7 @@ package srequest
import (
"strings"
"github.com/tech/sendico/pkg/ledgerconv"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/model"
"go.mongodb.org/mongo-driver/bson/primitive"
@@ -30,7 +31,7 @@ type CreateLedgerAccount struct {
AccountType LedgerAccountType `json:"accountType"`
Currency string `json:"currency"`
AllowNegative bool `json:"allowNegative,omitempty"`
IsSettlement bool `json:"isSettlement,omitempty"`
Role model.AccountRole `json:"role"`
Describable model.Describable `json:"describable"`
OwnerRef *primitive.ObjectID `json:"ownerRef,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
@@ -43,5 +44,10 @@ func (r *CreateLedgerAccount) Validate() error {
if strings.TrimSpace(string(r.AccountType)) == "" || strings.EqualFold(string(r.AccountType), string(LedgerAccountTypeUnspecified)) {
return merrors.InvalidArgument("accountType is required", "accountType")
}
if role := strings.TrimSpace(string(r.Role)); role != "" {
if _, ok := ledgerconv.ParseAccountRole(role); !ok || ledgerconv.IsAccountRoleUnspecified(role) {
return merrors.InvalidArgument("role is invalid", "role")
}
}
return nil
}

View File

@@ -21,7 +21,7 @@ type ledgerAccount struct {
Currency string `json:"currency"`
Status string `json:"status"`
AllowNegative bool `json:"allowNegative"`
IsSettlement bool `json:"isSettlement"`
Role string `json:"role"`
Metadata map[string]string `json:"metadata,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
@@ -96,7 +96,7 @@ func toLedgerAccount(acc *ledgerv1.LedgerAccount) ledgerAccount {
Currency: acc.GetCurrency(),
Status: acc.GetStatus().String(),
AllowNegative: acc.GetAllowNegative(),
IsSettlement: acc.GetIsSettlement(),
Role: acc.GetRole().String(),
Metadata: acc.GetMetadata(),
CreatedAt: acc.GetCreatedAt().AsTime(),
UpdatedAt: acc.GetUpdatedAt().AsTime(),

View File

@@ -1,6 +1,7 @@
package sresponse
import (
"fmt"
"net/http"
"strings"
"time"
@@ -8,7 +9,9 @@ import (
"github.com/tech/sendico/pkg/api/http/response"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -153,3 +156,137 @@ func chainNetworkValue(chain chainv1.ChainNetwork) string {
}
return strings.ToLower(trimmed)
}
// WalletsFromAccounts converts connector accounts to wallet response format.
// Used when querying multiple gateways via discovery.
func WalletsFromAccounts(logger mlogger.Logger, accounts []*connectorv1.Account, accessToken *TokenData) http.HandlerFunc {
dto := walletsResponse{
authResponse: authResponse{AccessToken: *accessToken},
}
dto.Wallets = make([]wallet, 0, len(accounts))
for _, acc := range accounts {
if acc == nil {
continue
}
dto.Wallets = append(dto.Wallets, accountToWallet(acc))
}
return response.Ok(logger, dto)
}
func accountToWallet(acc *connectorv1.Account) wallet {
if acc == nil {
return wallet{}
}
// Extract wallet details from provider details
details := map[string]interface{}{}
if acc.GetProviderDetails() != nil {
details = acc.GetProviderDetails().AsMap()
}
walletRef := ""
if ref := acc.GetRef(); ref != nil {
walletRef = strings.TrimSpace(ref.GetAccountId())
}
if v := stringFromDetails(details, "wallet_ref"); v != "" {
walletRef = v
}
organizationRef := stringFromDetails(details, "organization_ref")
ownerRef := strings.TrimSpace(acc.GetOwnerRef())
if v := stringFromDetails(details, "owner_ref"); v != "" {
ownerRef = v
}
chain := stringFromDetails(details, "network")
tokenSymbol := stringFromDetails(details, "token_symbol")
contractAddress := stringFromDetails(details, "contract_address")
depositAddress := stringFromDetails(details, "deposit_address")
name := ""
if d := acc.GetDescribable(); d != nil {
name = strings.TrimSpace(d.GetName())
}
if name == "" {
name = strings.TrimSpace(acc.GetLabel())
}
if name == "" {
name = walletRef
}
var description *string
if d := acc.GetDescribable(); d != nil && d.Description != nil {
if trimmed := strings.TrimSpace(d.GetDescription()); trimmed != "" {
description = &trimmed
}
}
status := acc.GetState().String()
// Convert connector state to wallet status format
switch acc.GetState() {
case connectorv1.AccountState_ACCOUNT_ACTIVE:
status = "MANAGED_WALLET_ACTIVE"
case connectorv1.AccountState_ACCOUNT_SUSPENDED:
status = "MANAGED_WALLET_SUSPENDED"
case connectorv1.AccountState_ACCOUNT_CLOSED:
status = "MANAGED_WALLET_CLOSED"
}
return wallet{
WalletRef: walletRef,
OrganizationRef: organizationRef,
OwnerRef: ownerRef,
Asset: walletAsset{
Chain: chain,
TokenSymbol: tokenSymbol,
ContractAddress: contractAddress,
},
DepositAddress: depositAddress,
Status: status,
Name: name,
Description: description,
CreatedAt: tsToString(acc.GetCreatedAt()),
UpdatedAt: tsToString(acc.GetUpdatedAt()),
}
}
func stringFromDetails(details map[string]interface{}, key string) string {
if details == nil {
return ""
}
if value, ok := details[key]; ok {
return strings.TrimSpace(fmt.Sprint(value))
}
return ""
}
// WalletBalanceFromConnector converts connector balance to wallet balance response format.
// Used when querying gateways via discovery.
func WalletBalanceFromConnector(logger mlogger.Logger, bal *connectorv1.Balance, accessToken *TokenData) http.HandlerFunc {
return response.Ok(logger, walletBalanceResponse{
Balance: connectorBalanceToWalletBalance(bal),
authResponse: authResponse{AccessToken: *accessToken},
})
}
func connectorBalanceToWalletBalance(b *connectorv1.Balance) walletBalance {
if b == nil {
return walletBalance{}
}
return walletBalance{
Available: connectorMoneyToModel(b.GetAvailable()),
PendingInbound: connectorMoneyToModel(b.GetPendingInbound()),
PendingOutbound: connectorMoneyToModel(b.GetPendingOutbound()),
CalculatedAt: tsToString(b.GetCalculatedAt()),
}
}
func connectorMoneyToModel(m *moneyv1.Money) *model.Money {
if m == nil {
return nil
}
return &model.Money{
Amount: m.GetAmount(),
Currency: m.GetCurrency(),
}
}