mnd/payouts: estimations replaced with expectations

Added expectations endpoint (estimations and distributed), added
coalesce to db query, so in case of empty payouts db 0 will be returned instead of error.

Change-Id: I535f14ef097876448d8949bc302895b25da2b6e7
This commit is contained in:
Qweder93 2021-05-24 20:13:47 +03:00 committed by Yaroslav Vorobiov
parent 83e82eb473
commit 79172777bd
6 changed files with 84 additions and 63 deletions

View File

@ -53,8 +53,8 @@ func (controller *Payouts) GetAllNodesTotalEarned(w http.ResponseWriter, r *http
}
}
// NodeEstimations handles node's estimated.
func (controller *Payouts) NodeEstimations(w http.ResponseWriter, r *http.Request) {
// NodeExpectations handles node's estimated and undistributed.
func (controller *Payouts) NodeExpectations(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
@ -74,33 +74,33 @@ func (controller *Payouts) NodeEstimations(w http.ResponseWriter, r *http.Reques
return
}
estimations, err := controller.service.NodeEstimations(ctx, nodeID)
expectations, err := controller.service.NodeExpectations(ctx, nodeID)
if err != nil {
controller.serveError(w, http.StatusInternalServerError, ErrPayouts.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(estimations); err != nil {
if err = json.NewEncoder(w).Encode(expectations); err != nil {
controller.log.Error("failed to write json response", zap.Error(err))
return
}
}
// Estimations handles nodes estimated earnings.
func (controller *Payouts) Estimations(w http.ResponseWriter, r *http.Request) {
// Expectations handles nodes estimated and undistributed earnings.
func (controller *Payouts) Expectations(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
w.Header().Add("Content-Type", "application/json")
estimations, err := controller.service.Estimations(ctx)
expectations, err := controller.service.Expectations(ctx)
if err != nil {
controller.serveError(w, http.StatusInternalServerError, ErrPayouts.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(estimations); err != nil {
if err = json.NewEncoder(w).Encode(expectations); err != nil {
controller.log.Error("failed to write json response", zap.Error(err))
return
}

View File

@ -80,8 +80,8 @@ func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, payouts *pa
payoutsRouter.HandleFunc("/summary/{period}", payoutsController.PeriodSummary).Methods(http.MethodGet)
payoutsRouter.HandleFunc("/summary", payoutsController.Summary).Methods(http.MethodGet)
payoutsRouter.HandleFunc("/total-earned", payoutsController.GetAllNodesTotalEarned).Methods(http.MethodGet)
payoutsRouter.HandleFunc("/estimations/{nodeID}", payoutsController.NodeEstimations).Methods(http.MethodGet)
payoutsRouter.HandleFunc("/estimations", payoutsController.Estimations).Methods(http.MethodGet)
payoutsRouter.HandleFunc("/expectations/{nodeID}", payoutsController.NodeExpectations).Methods(http.MethodGet)
payoutsRouter.HandleFunc("/expectations", payoutsController.Expectations).Methods(http.MethodGet)
if server.config.StaticDir != "" {
router.PathPrefix("/static/").Handler(http.StripPrefix("/static", fs))

View File

@ -41,3 +41,9 @@ func (summary *Summary) Add(held, paid int64, id storj.NodeID, name string) {
NodeName: name,
})
}
// Expectations contains estimated and undistributed payouts.
type Expectations struct {
CurrentMonthEstimation int64 `json:"currentMonthEstimation"`
Undistributed int64 `json:"undistributed"`
}

View File

@ -308,54 +308,55 @@ func (service *Service) getAllSatellitesAllTime(ctx context.Context, node nodes.
return response.PayoutInfo, nil
}
// NodeEstimations returns node's estimated earnings.
func (service *Service) NodeEstimations(ctx context.Context, nodeID storj.NodeID) (_ int64, err error) {
// NodeExpectations returns node's estimated and undistributed earnings.
func (service *Service) NodeExpectations(ctx context.Context, nodeID storj.NodeID) (_ Expectations, err error) {
defer mon.Task()(&ctx)(&err)
node, err := service.nodes.Get(ctx, nodeID)
if err != nil {
return 0, Error.Wrap(err)
return Expectations{}, Error.Wrap(err)
}
est, err := service.nodeEstimations(ctx, node)
expectation, err := service.nodeExpectations(ctx, node)
if err != nil {
return 0, Error.Wrap(err)
return Expectations{}, Error.Wrap(err)
}
return est, nil
return expectation, nil
}
// Estimations returns all nodes estimated earnings.
func (service *Service) Estimations(ctx context.Context) (_ int64, err error) {
// Expectations returns all nodes estimated and undistributed earnings.
func (service *Service) Expectations(ctx context.Context) (_ Expectations, err error) {
defer mon.Task()(&ctx)(&err)
var estimations int64
var expectations Expectations
list, err := service.nodes.List(ctx)
if err != nil {
return 0, Error.Wrap(err)
return Expectations{}, Error.Wrap(err)
}
for _, node := range list {
est, err := service.nodeEstimations(ctx, node)
expectation, err := service.nodeExpectations(ctx, node)
if err != nil {
return 0, Error.Wrap(err)
return Expectations{}, Error.Wrap(err)
}
estimations += est
expectations.Undistributed += expectation.Undistributed
expectations.CurrentMonthEstimation += expectation.CurrentMonthEstimation
}
return estimations, nil
return expectations, nil
}
// nodeEstimations retrieves data from a single node.
func (service *Service) nodeEstimations(ctx context.Context, node nodes.Node) (_ int64, err error) {
// nodeExpectations retrieves data from a single node.
func (service *Service) nodeExpectations(ctx context.Context, node nodes.Node) (_ Expectations, err error) {
conn, err := service.dialer.DialNodeURL(ctx, storj.NodeURL{
ID: node.ID,
Address: node.PublicAddress,
})
if err != nil {
return 0, Error.Wrap(err)
return Expectations{}, Error.Wrap(err)
}
defer func() {
@ -369,10 +370,15 @@ func (service *Service) nodeEstimations(ctx context.Context, node nodes.Node) (_
estimated, err := payoutClient.EstimatedPayoutTotal(ctx, &multinodepb.EstimatedPayoutTotalRequest{Header: header})
if err != nil {
return 0, Error.Wrap(err)
return Expectations{}, Error.Wrap(err)
}
return estimated.EstimatedEarnings, nil
undistributed, err := payoutClient.Undistributed(ctx, &multinodepb.UndistributedRequest{Header: header})
if err != nil {
return Expectations{}, Error.Wrap(err)
}
return Expectations{Undistributed: undistributed.Total, CurrentMonthEstimation: estimated.EstimatedEarnings}, nil
}
// getAmount returns earned from node.

View File

@ -456,46 +456,54 @@ func TestPayouts(t *testing.T) {
})
}
func TestPayoutsUndistributedEndpoint(t *testing.T) {
func TestUndistributed(t *testing.T) {
storagenodedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db storagenode.DB) {
payoutdb := db.Payout()
satelliteID1 := testrand.NodeID()
satelliteID2 := testrand.NodeID()
err := payoutdb.StorePayStub(ctx, payouts.PayStub{
SatelliteID: satelliteID2,
Period: "2020-01",
Distributed: 150,
Paid: 250,
t.Run("empty db no error", func(t *testing.T) {
undistributed, err := payoutdb.GetUndistributed(ctx)
require.NoError(t, err)
require.EqualValues(t, undistributed, 0)
})
require.NoError(t, err)
err = payoutdb.StorePayStub(ctx, payouts.PayStub{
SatelliteID: satelliteID2,
Period: "2020-02",
Distributed: 250,
Paid: 350,
t.Run("few paystubs with different satellites", func(t *testing.T) {
err := payoutdb.StorePayStub(ctx, payouts.PayStub{
SatelliteID: satelliteID2,
Period: "2020-01",
Distributed: 150,
Paid: 250,
})
require.NoError(t, err)
err = payoutdb.StorePayStub(ctx, payouts.PayStub{
SatelliteID: satelliteID2,
Period: "2020-02",
Distributed: 250,
Paid: 350,
})
require.NoError(t, err)
err = payoutdb.StorePayStub(ctx, payouts.PayStub{
SatelliteID: satelliteID1,
Period: "2020-01",
Distributed: 100,
Paid: 300,
})
require.NoError(t, err)
err = payoutdb.StorePayStub(ctx, payouts.PayStub{
SatelliteID: satelliteID1,
Period: "2020-02",
Distributed: 400,
Paid: 500,
})
require.NoError(t, err)
undistributed, err := payoutdb.GetUndistributed(ctx)
require.NoError(t, err)
require.EqualValues(t, undistributed, 500)
})
require.NoError(t, err)
err = payoutdb.StorePayStub(ctx, payouts.PayStub{
SatelliteID: satelliteID1,
Period: "2020-01",
Distributed: 100,
Paid: 300,
})
require.NoError(t, err)
err = payoutdb.StorePayStub(ctx, payouts.PayStub{
SatelliteID: satelliteID1,
Period: "2020-02",
Distributed: 400,
Paid: 500,
})
require.NoError(t, err)
undistributed, err := payoutdb.GetUndistributed(ctx)
require.NoError(t, err)
require.EqualValues(t, undistributed, 500)
})
}

View File

@ -572,13 +572,14 @@ func (db *payoutDB) GetUndistributed(ctx context.Context) (_ int64, err error) {
var distributed, paid int64
rowPayment := db.QueryRowContext(ctx,
`SELECT SUM(distributed), SUM(paid) FROM paystubs`)
`SELECT COALESCE(SUM(distributed),0), COALESCE(SUM(paid), 0) FROM paystubs`)
err = rowPayment.Scan(&distributed, &paid)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return 0, payouts.ErrNoPayStubForPeriod.Wrap(err)
}
return 0, ErrPayout.Wrap(err)
}