wormhole-explorer/notional/prices/repository.go

90 lines
2.3 KiB
Go

package prices
import (
"context"
"fmt"
"time"
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap"
)
type PriceDb struct {
ID string `bson:"_id" json:"id"`
CoingeckoID string `bson:"coingeckoId" json:"coingeckoId"`
Price string `bson:"price" json:"price"`
Datetime time.Time `bson:"dateTime" json:"dateTime"`
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}
type PriceRepository struct {
db *mongo.Database
collection *mongo.Collection
logger *zap.Logger
}
var ErrPriceNotFound = fmt.Errorf("price not found")
// NewPriceRepository creates a new price repository.
func NewPriceRepository(db *mongo.Database, logger *zap.Logger) *PriceRepository {
return &PriceRepository{
db: db,
collection: db.Collection("prices"),
logger: logger,
}
}
// Upsert upserts a price.
func (p *PriceRepository) Upsert(ctx context.Context, coingeckoID string, price decimal.Decimal, dateTime time.Time) error {
id := p.createID(coingeckoID, dateTime)
model := &PriceDb{
ID: id,
CoingeckoID: coingeckoID,
Price: price.Truncate(8).String(),
Datetime: dateTime,
UpdatedAt: time.Now(),
}
update := bson.M{
"$set": model,
"$setOnInsert": indexedAt(time.Now()),
"$inc": bson.D{{Key: "revision", Value: 1}},
}
opts := options.Update().SetUpsert(true)
var err error
_, err = p.collection.UpdateByID(ctx, model.ID, update, opts)
return err
}
// Find finds a price.
func (p *PriceRepository) Find(ctx context.Context, coingeckoID string, dateTime time.Time) (*PriceDb, error) {
id := p.createID(coingeckoID, dateTime)
var price PriceDb
err := p.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&price)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, ErrPriceNotFound
}
return nil, err
}
return &price, err
}
func (p *PriceRepository) createID(coingeckoID string, dateTime time.Time) string {
return fmt.Sprintf("%s-%s", coingeckoID, dateTime.Format(time.RFC3339))
}
func indexedAt(t time.Time) IndexingTimestamps {
return IndexingTimestamps{
IndexedAt: t,
}
}
type IndexingTimestamps struct {
IndexedAt time.Time `bson:"indexedAt"`
}