Files
sendico/api/ledger/client/client.go
Stephan D 62a6631b9a
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
service backend
2025-11-07 18:35:26 +01:00

143 lines
5.0 KiB
Go

package client
import (
"context"
"crypto/tls"
"errors"
"fmt"
"strings"
"time"
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
// Client exposes typed helpers around the ledger gRPC API.
type Client interface {
PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error)
PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error)
GetBalance(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error)
GetJournalEntry(ctx context.Context, req *ledgerv1.GetEntryRequest) (*ledgerv1.JournalEntryResponse, error)
GetStatement(ctx context.Context, req *ledgerv1.GetStatementRequest) (*ledgerv1.StatementResponse, error)
Close() error
}
type grpcLedgerClient interface {
PostCreditWithCharges(ctx context.Context, in *ledgerv1.PostCreditRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
PostDebitWithCharges(ctx context.Context, in *ledgerv1.PostDebitRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
TransferInternal(ctx context.Context, in *ledgerv1.TransferRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
ApplyFXWithCharges(ctx context.Context, in *ledgerv1.FXRequest, opts ...grpc.CallOption) (*ledgerv1.PostResponse, error)
GetBalance(ctx context.Context, in *ledgerv1.GetBalanceRequest, opts ...grpc.CallOption) (*ledgerv1.BalanceResponse, error)
GetJournalEntry(ctx context.Context, in *ledgerv1.GetEntryRequest, opts ...grpc.CallOption) (*ledgerv1.JournalEntryResponse, error)
GetStatement(ctx context.Context, in *ledgerv1.GetStatementRequest, opts ...grpc.CallOption) (*ledgerv1.StatementResponse, error)
}
type ledgerClient struct {
cfg Config
conn *grpc.ClientConn
client grpcLedgerClient
}
// New dials the ledger endpoint and returns a ready client.
func New(ctx context.Context, cfg Config, opts ...grpc.DialOption) (Client, error) {
cfg.setDefaults()
if strings.TrimSpace(cfg.Address) == "" {
return nil, errors.New("ledger: address is required")
}
dialCtx, cancel := context.WithTimeout(ctx, cfg.DialTimeout)
defer cancel()
dialOpts := make([]grpc.DialOption, 0, len(opts)+1)
dialOpts = append(dialOpts, opts...)
if cfg.Insecure {
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
} else {
dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
}
conn, err := grpc.DialContext(dialCtx, cfg.Address, dialOpts...)
if err != nil {
return nil, fmt.Errorf("ledger: dial %s: %w", cfg.Address, err)
}
return &ledgerClient{
cfg: cfg,
conn: conn,
client: ledgerv1.NewLedgerServiceClient(conn),
}, nil
}
// NewWithClient injects a pre-built ledger client (useful for tests).
func NewWithClient(cfg Config, lc grpcLedgerClient) Client {
cfg.setDefaults()
return &ledgerClient{
cfg: cfg,
client: lc,
}
}
func (c *ledgerClient) Close() error {
if c.conn != nil {
return c.conn.Close()
}
return nil
}
func (c *ledgerClient) PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
ctx, cancel := c.callContext(ctx)
defer cancel()
return c.client.PostCreditWithCharges(ctx, req)
}
func (c *ledgerClient) PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
ctx, cancel := c.callContext(ctx)
defer cancel()
return c.client.PostDebitWithCharges(ctx, req)
}
func (c *ledgerClient) TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
ctx, cancel := c.callContext(ctx)
defer cancel()
return c.client.TransferInternal(ctx, req)
}
func (c *ledgerClient) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error) {
ctx, cancel := c.callContext(ctx)
defer cancel()
return c.client.ApplyFXWithCharges(ctx, req)
}
func (c *ledgerClient) GetBalance(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error) {
ctx, cancel := c.callContext(ctx)
defer cancel()
return c.client.GetBalance(ctx, req)
}
func (c *ledgerClient) GetJournalEntry(ctx context.Context, req *ledgerv1.GetEntryRequest) (*ledgerv1.JournalEntryResponse, error) {
ctx, cancel := c.callContext(ctx)
defer cancel()
return c.client.GetJournalEntry(ctx, req)
}
func (c *ledgerClient) GetStatement(ctx context.Context, req *ledgerv1.GetStatementRequest) (*ledgerv1.StatementResponse, error) {
ctx, cancel := c.callContext(ctx)
defer cancel()
return c.client.GetStatement(ctx, req)
}
func (c *ledgerClient) callContext(ctx context.Context) (context.Context, context.CancelFunc) {
timeout := c.cfg.CallTimeout
if timeout <= 0 {
timeout = 3 * time.Second
}
return context.WithTimeout(ctx, timeout)
}