diff --git a/multinode/console/controllers/payouts.go b/multinode/console/controllers/payouts.go index 958267269..dd833cc4b 100644 --- a/multinode/console/controllers/payouts.go +++ b/multinode/console/controllers/payouts.go @@ -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 } diff --git a/multinode/console/server/server.go b/multinode/console/server/server.go index 2a1f32205..26174a11d 100644 --- a/multinode/console/server/server.go +++ b/multinode/console/server/server.go @@ -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)) diff --git a/multinode/payouts/payouts.go b/multinode/payouts/payouts.go index 946d6353d..5d3d9ae4a 100644 --- a/multinode/payouts/payouts.go +++ b/multinode/payouts/payouts.go @@ -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"` +} diff --git a/multinode/payouts/service.go b/multinode/payouts/service.go index d8fccba18..121eead09 100644 --- a/multinode/payouts/service.go +++ b/multinode/payouts/service.go @@ -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. diff --git a/storagenode/payouts/db_test.go b/storagenode/payouts/db_test.go index 1934854eb..e5ba64eee 100644 --- a/storagenode/payouts/db_test.go +++ b/storagenode/payouts/db_test.go @@ -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) }) } diff --git a/storagenode/storagenodedb/payouts.go b/storagenode/storagenodedb/payouts.go index 654394ade..4103c1aa6 100644 --- a/storagenode/storagenodedb/payouts.go +++ b/storagenode/storagenodedb/payouts.go @@ -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) }