storagenode/heldamount: added api for heldamount history separated by periods

Change-Id: I170010364269822848bc6cd051e0e0fb3df95d91
This commit is contained in:
Qweder93 2020-04-28 16:07:50 +03:00 committed by Nikolai Siedov
parent 0ae0cea93b
commit 16cd9b06ec
7 changed files with 245 additions and 0 deletions

View File

@ -152,6 +152,39 @@ func (heldAmount *HeldAmount) PayStubPeriod(w http.ResponseWriter, r *http.Reque
}
}
// HeldbackHistory returns heldback for each % period for specific satellite.
func (heldAmount *HeldAmount) HeldbackHistory(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
w.Header().Set(contentType, applicationJSON)
segmentParams := mux.Vars(r)
id, ok := segmentParams["id"]
if !ok {
heldAmount.serveJSONError(w, http.StatusBadRequest, ErrNotificationsAPI.Wrap(err))
return
}
satelliteID, err := storj.NodeIDFromString(id)
if err != nil {
heldAmount.serveJSONError(w, http.StatusBadRequest, ErrHeldAmountAPI.Wrap(err))
return
}
heldbackHistory, err := heldAmount.service.AllHeldbackHistory(ctx, satelliteID)
if err != nil {
heldAmount.serveJSONError(w, http.StatusInternalServerError, ErrHeldAmountAPI.Wrap(err))
return
}
if err := json.NewEncoder(w).Encode(heldbackHistory); err != nil {
heldAmount.log.Error("failed to encode json response", zap.Error(ErrHeldAmountAPI.Wrap(err)))
return
}
}
// serveJSONError writes JSON error to response output stream.
func (heldAmount *HeldAmount) serveJSONError(w http.ResponseWriter, status int, err error) {
w.WriteHeader(status)

View File

@ -340,6 +340,52 @@ func TestHeldAmountApi(t *testing.T) {
require.Equal(t, "{\"error\":\"heldAmount console web error: wrong period format: period has wrong format\"}\n", string(body5))
})
t.Run("test HeldbackHistory", func(t *testing.T) {
// should return all heldback history inserted earlier
url := fmt.Sprintf("%s/heldback/%s", baseURL, satellite.ID().String())
res, err := http.Get(url)
require.NoError(t, err)
require.NotNil(t, res)
require.Equal(t, http.StatusOK, res.StatusCode)
period75 := heldamount.HeldbackPeriod{
PercentageRate: 75,
Held: paystub2.Held + paystub3.Held,
}
var periods []heldamount.HeldbackPeriod
periods = append(periods, period75)
expected, err := json.Marshal(periods)
require.NoError(t, err)
defer func() {
err = res.Body.Close()
require.NoError(t, err)
}()
body, err := ioutil.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t, string(expected)+"\n", string(body))
// should return 400 because of bad period.
url = fmt.Sprintf("%s/heldback/%s", baseURL, satellite.ID().String()+"11")
res2, err := http.Get(url)
require.NoError(t, err)
require.NotNil(t, res2)
require.Equal(t, http.StatusBadRequest, res2.StatusCode)
defer func() {
err = res2.Body.Close()
require.NoError(t, err)
}()
body2, err := ioutil.ReadAll(res2.Body)
require.NoError(t, err)
require.Equal(t, "{\"error\":\"heldAmount console web error: node ID error: checksum error\"}\n", string(body2))
})
},
)
}

View File

@ -81,6 +81,7 @@ func NewServer(logger *zap.Logger, assets http.FileSystem, notifications *notifi
heldAmountRouter.StrictSlash(true)
heldAmountRouter.HandleFunc("/paystubs/{period}", heldAmountController.PayStubMonthly).Methods(http.MethodGet)
heldAmountRouter.HandleFunc("/paystubs/{start}/{end}", heldAmountController.PayStubPeriod).Methods(http.MethodGet)
heldAmountRouter.HandleFunc("/heldback/{id}", heldAmountController.HeldbackHistory).Methods(http.MethodGet)
if assets != nil {
fs := http.FileServer(assets)

View File

@ -120,6 +120,13 @@ func TestHeldAmountDB(t *testing.T) {
assert.Equal(t, len(stubs), 0)
assert.NoError(t, err)
})
t.Run("Test SatellitesHeldbackHistory", func(t *testing.T) {
heldback, err := heldAmount.SatellitesHeldbackHistory(ctx, satelliteID)
assert.NoError(t, err)
assert.Equal(t, heldback[0].Held, paystub.Held)
assert.Equal(t, heldback[0].Period, paystub.Period)
})
})
}
@ -225,3 +232,58 @@ func TestAllPayStubPeriodCached(t *testing.T) {
require.Equal(t, 0, len(payStubs))
})
}
func TestAllHeldbackHistory(t *testing.T) {
storagenodedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db storagenode.DB) {
heldAmountDB := db.HeldAmount()
service := heldamount.NewService(nil, heldAmountDB, rpc.Dialer{}, nil)
id := storj.NodeID{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
payStub := heldamount.PayStub{
SatelliteID: id,
Created: time.Now().UTC(),
Codes: "code",
UsageAtRest: 1,
UsageGet: 2,
UsagePut: 3,
UsageGetRepair: 4,
UsagePutRepair: 5,
UsageGetAudit: 6,
CompAtRest: 7,
CompGet: 8,
CompPut: 9,
CompGetRepair: 10,
CompPutRepair: 11,
CompGetAudit: 12,
SurgePercent: 13,
Held: 14,
Owed: 15,
Disposed: 16,
Paid: 17,
}
for j := 1; j < 5; j++ {
payStub.Period = fmt.Sprintf("2020-0%d", j)
err := heldAmountDB.StorePayStub(ctx, payStub)
require.NoError(t, err)
}
heldback, err := service.AllHeldbackHistory(ctx, id)
require.NoError(t, err)
var expectedResult []heldamount.HeldbackPeriod
period75 := heldamount.HeldbackPeriod{
PercentageRate: 75,
Held: payStub.Held * 3,
}
period50 := heldamount.HeldbackPeriod{
PercentageRate: 50,
Held: payStub.Held,
}
expectedResult = append(expectedResult, period75, period50)
require.Equal(t, expectedResult, heldback)
})
}

View File

@ -22,6 +22,8 @@ type DB interface {
GetPayStub(ctx context.Context, satelliteID storj.NodeID, period string) (*PayStub, error)
// AllPayStubs retrieves paystub data from all satellites in specific period from DB.
AllPayStubs(ctx context.Context, period string) ([]PayStub, error)
// SatellitesHeldbackHistory retrieves heldback history for specific satellite from DB.
SatellitesHeldbackHistory(ctx context.Context, satelliteID storj.NodeID) ([]Heldback, error)
}
// ErrNoPayStubForPeriod represents errors from the heldamount database.
@ -51,3 +53,9 @@ type PayStub struct {
Disposed int64 `json:"disposed"`
Paid int64 `json:"paid"`
}
// Heldback is node's heldback amount for period.
type Heldback struct {
Period string `json:"period"`
Held int64 `json:"held"`
}

View File

@ -239,6 +239,67 @@ func (service *Service) AllPayStubsPeriodCached(ctx context.Context, periodStart
return payStubs, nil
}
// HeldbackPeriod amount of held for specific percent rate period.
type HeldbackPeriod struct {
PercentageRate int
Held int64
}
// AllHeldbackHistory retrieves heldback history for all specific satellite from storagenode database.
func (service *Service) AllHeldbackHistory(ctx context.Context, id storj.NodeID) (result []HeldbackPeriod, err error) {
defer mon.Task()(&ctx, &id)(&err)
heldback, err := service.db.SatellitesHeldbackHistory(ctx, id)
if err != nil {
return nil, ErrHeldAmountService.Wrap(err)
}
var total75, total50, total25, total0 int64
for i, t := range heldback {
switch i {
case 0, 1, 2:
total75 += t.Held
case 3, 4, 5:
total50 += t.Held
case 6, 7, 8:
total25 += t.Held
default:
total0 += t.Held
}
}
period75percent := HeldbackPeriod{
PercentageRate: 75,
Held: total75,
}
period50percent := HeldbackPeriod{
PercentageRate: 50,
Held: total50,
}
period25percent := HeldbackPeriod{
PercentageRate: 25,
Held: total25,
}
period0percent := HeldbackPeriod{
PercentageRate: 0,
Held: total0,
}
result = append(result, period75percent)
switch {
case len(heldback) > 3:
result = append(result, period50percent)
case len(heldback) > 6:
result = append(result, period25percent)
case len(heldback) > 9:
result = append(result, period0percent)
}
return result, nil
}
// dial dials the HeldAmount client for the satellite by id
func (service *Service) dial(ctx context.Context, satelliteID storj.NodeID) (_ *Client, err error) {
defer mon.Task()(&ctx)(&err)

View File

@ -218,3 +218,37 @@ func (db *heldamountDB) AllPayStubs(ctx context.Context, period string) (_ []hel
return paystubList, nil
}
// SatellitesHeldbackHistory retrieves heldback history for specific satellite.
func (db *heldamountDB) SatellitesHeldbackHistory(ctx context.Context, id storj.NodeID) (_ []heldamount.Heldback, err error) {
defer mon.Task()(&ctx)(&err)
query := `SELECT
period,
held
FROM paystubs WHERE satellite_id = ? ORDER BY period ASC`
rows, err := db.QueryContext(ctx, query, id)
if err != nil {
return nil, err
}
defer func() { err = errs.Combine(err, rows.Close()) }()
var heldback []heldamount.Heldback
for rows.Next() {
var held heldamount.Heldback
err := rows.Scan(&held.Period, &held.Held)
if err != nil {
return nil, ErrHeldAmount.Wrap(err)
}
heldback = append(heldback, held)
}
if err = rows.Err(); err != nil {
return nil, ErrHeldAmount.Wrap(err)
}
return heldback, nil
}