service backend
This commit is contained in:
161
api/chain/gateway/storage/mongo/store/deposits.go
Normal file
161
api/chain/gateway/storage/mongo/store/deposits.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/chain/gateway/storage"
|
||||
"github.com/tech/sendico/chain/gateway/storage/model"
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
ri "github.com/tech/sendico/pkg/db/repository/index"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDepositPageSize int64 = 100
|
||||
maxDepositPageSize int64 = 500
|
||||
)
|
||||
|
||||
type Deposits struct {
|
||||
logger mlogger.Logger
|
||||
repo repository.Repository
|
||||
}
|
||||
|
||||
// NewDeposits constructs a Mongo-backed deposits store.
|
||||
func NewDeposits(logger mlogger.Logger, db *mongo.Database) (*Deposits, error) {
|
||||
if db == nil {
|
||||
return nil, merrors.InvalidArgument("mongo database is nil")
|
||||
}
|
||||
repo := repository.CreateMongoRepository(db, mservice.ChainDeposits)
|
||||
indexes := []*ri.Definition{
|
||||
{
|
||||
Keys: []ri.Key{{Field: "depositRef", Sort: ri.Asc}},
|
||||
Unique: true,
|
||||
},
|
||||
{
|
||||
Keys: []ri.Key{{Field: "walletRef", Sort: ri.Asc}, {Field: "status", Sort: ri.Asc}},
|
||||
},
|
||||
{
|
||||
Keys: []ri.Key{{Field: "txHash", Sort: ri.Asc}},
|
||||
Unique: true,
|
||||
},
|
||||
}
|
||||
for _, def := range indexes {
|
||||
if err := repo.CreateIndex(def); err != nil {
|
||||
logger.Error("failed to ensure deposit index", zap.Error(err), zap.String("collection", repo.Collection()))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
childLogger := logger.Named("deposits")
|
||||
childLogger.Debug("deposits store initialised")
|
||||
|
||||
return &Deposits{logger: childLogger, repo: repo}, nil
|
||||
}
|
||||
|
||||
func (d *Deposits) Record(ctx context.Context, deposit *model.Deposit) error {
|
||||
if deposit == nil {
|
||||
return merrors.InvalidArgument("depositsStore: nil deposit")
|
||||
}
|
||||
deposit.Normalize()
|
||||
if strings.TrimSpace(deposit.DepositRef) == "" {
|
||||
return merrors.InvalidArgument("depositsStore: empty depositRef")
|
||||
}
|
||||
if deposit.Status == "" {
|
||||
deposit.Status = model.DepositStatusPending
|
||||
}
|
||||
if deposit.ObservedAt.IsZero() {
|
||||
deposit.ObservedAt = time.Now().UTC()
|
||||
}
|
||||
if deposit.RecordedAt.IsZero() {
|
||||
deposit.RecordedAt = time.Now().UTC()
|
||||
}
|
||||
if deposit.LastStatusAt.IsZero() {
|
||||
deposit.LastStatusAt = time.Now().UTC()
|
||||
}
|
||||
|
||||
existing := &model.Deposit{}
|
||||
err := d.repo.FindOneByFilter(ctx, repository.Filter("depositRef", deposit.DepositRef), existing)
|
||||
switch {
|
||||
case err == nil:
|
||||
existing.Status = deposit.Status
|
||||
existing.ObservedAt = deposit.ObservedAt
|
||||
existing.RecordedAt = deposit.RecordedAt
|
||||
existing.LastStatusAt = time.Now().UTC()
|
||||
if deposit.Amount != nil {
|
||||
existing.Amount = deposit.Amount
|
||||
}
|
||||
if deposit.BlockID != "" {
|
||||
existing.BlockID = deposit.BlockID
|
||||
}
|
||||
if deposit.TxHash != "" {
|
||||
existing.TxHash = deposit.TxHash
|
||||
}
|
||||
if deposit.Network != "" {
|
||||
existing.Network = deposit.Network
|
||||
}
|
||||
if deposit.TokenSymbol != "" {
|
||||
existing.TokenSymbol = deposit.TokenSymbol
|
||||
}
|
||||
if deposit.ContractAddress != "" {
|
||||
existing.ContractAddress = deposit.ContractAddress
|
||||
}
|
||||
if deposit.SourceAddress != "" {
|
||||
existing.SourceAddress = deposit.SourceAddress
|
||||
}
|
||||
if err := d.repo.Update(ctx, existing); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case errors.Is(err, merrors.ErrNoData):
|
||||
if err := d.repo.Insert(ctx, deposit, repository.Filter("depositRef", deposit.DepositRef)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Deposits) ListPending(ctx context.Context, network string, limit int32) ([]*model.Deposit, error) {
|
||||
query := repository.Query().Filter(repository.Field("status"), model.DepositStatusPending)
|
||||
if net := strings.TrimSpace(network); net != "" {
|
||||
query = query.Filter(repository.Field("network"), strings.ToLower(net))
|
||||
}
|
||||
pageSize := sanitizeDepositLimit(limit)
|
||||
query = query.Sort(repository.Field("observedAt"), true).Limit(&pageSize)
|
||||
|
||||
deposits := make([]*model.Deposit, 0, pageSize)
|
||||
decoder := func(cur *mongo.Cursor) error {
|
||||
item := &model.Deposit{}
|
||||
if err := cur.Decode(item); err != nil {
|
||||
return err
|
||||
}
|
||||
deposits = append(deposits, item)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := d.repo.FindManyByFilter(ctx, query, decoder); err != nil && !errors.Is(err, merrors.ErrNoData) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return deposits, nil
|
||||
}
|
||||
|
||||
func sanitizeDepositLimit(requested int32) int64 {
|
||||
if requested <= 0 {
|
||||
return defaultDepositPageSize
|
||||
}
|
||||
if requested > int32(maxDepositPageSize) {
|
||||
return maxDepositPageSize
|
||||
}
|
||||
return int64(requested)
|
||||
}
|
||||
|
||||
var _ storage.DepositsStore = (*Deposits)(nil)
|
||||
Reference in New Issue
Block a user