satellite/{console,satellitedb}: add account freeze service
This change adds an account freeze service with methods for checking if a user is frozen, freezing a user, and unfreezing a user. Furthermore, methods for altering the usage limits of a user or project have been implemented for use by the account freeze service. Change-Id: I77fecfac5c152f134bec90165acfe4f1dea957e7
This commit is contained in:
parent
7b851b42f7
commit
471f9e4e10
@ -5,21 +5,26 @@ package console
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/common/uuid"
|
||||
)
|
||||
|
||||
// ErrAccountFreeze is the class for errors that occur during operation of the account freeze service.
|
||||
var ErrAccountFreeze = errs.Class("account freeze service")
|
||||
|
||||
// AccountFreezeEvents exposes methods to manage the account freeze events table in database.
|
||||
//
|
||||
// architecture: Database
|
||||
type AccountFreezeEvents interface {
|
||||
// Insert is a method for inserting account freeze event into the database.
|
||||
Insert(ctx context.Context, event *AccountFreezeEvent) (*AccountFreezeEvent, error)
|
||||
// Upsert is a method for updating an account freeze event if it exists and inserting it otherwise.
|
||||
Upsert(ctx context.Context, event *AccountFreezeEvent) (*AccountFreezeEvent, error)
|
||||
// Get is a method for querying account freeze event from the database by user ID and event type.
|
||||
Get(ctx context.Context, userID uuid.UUID, eventType AccountFreezeEventType) (*AccountFreezeEvent, error)
|
||||
// UpdateLimits is a method for updating the limits of an account freeze event by user ID and event type.
|
||||
UpdateLimits(ctx context.Context, userID uuid.UUID, eventType AccountFreezeEventType, limits *AccountFreezeEventLimits) error
|
||||
// DeleteAllByUserID is a method for deleting all account freeze events from the database by user ID.
|
||||
DeleteAllByUserID(ctx context.Context, userID uuid.UUID) error
|
||||
}
|
||||
@ -47,3 +52,140 @@ const (
|
||||
// Warning signifies that the user has been warned that they may be frozen soon.
|
||||
Warning AccountFreezeEventType = 1
|
||||
)
|
||||
|
||||
// AccountFreezeService encapsulates operations concerning account freezes.
|
||||
type AccountFreezeService struct {
|
||||
freezeEventsDB AccountFreezeEvents
|
||||
usersDB Users
|
||||
projectsDB Projects
|
||||
}
|
||||
|
||||
// NewAccountFreezeService creates a new account freeze service.
|
||||
func NewAccountFreezeService(freezeEventsDB AccountFreezeEvents, usersDB Users, projectsDB Projects) *AccountFreezeService {
|
||||
return &AccountFreezeService{
|
||||
freezeEventsDB: freezeEventsDB,
|
||||
usersDB: usersDB,
|
||||
projectsDB: projectsDB,
|
||||
}
|
||||
}
|
||||
|
||||
// IsUserFrozen returns whether the user specified by the given ID is frozen.
|
||||
func (s *AccountFreezeService) IsUserFrozen(ctx context.Context, userID uuid.UUID) (_ bool, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
_, err = s.freezeEventsDB.Get(ctx, userID, Freeze)
|
||||
switch {
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
return false, nil
|
||||
case err != nil:
|
||||
return false, ErrAccountFreeze.Wrap(err)
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// FreezeUser freezes the user specified by the given ID.
|
||||
func (s *AccountFreezeService) FreezeUser(ctx context.Context, userID uuid.UUID) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
user, err := s.usersDB.Get(ctx, userID)
|
||||
if err != nil {
|
||||
return ErrAccountFreeze.Wrap(err)
|
||||
}
|
||||
|
||||
event, err := s.freezeEventsDB.Get(ctx, userID, Freeze)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
event = &AccountFreezeEvent{
|
||||
UserID: userID,
|
||||
Type: Freeze,
|
||||
Limits: &AccountFreezeEventLimits{
|
||||
User: UsageLimits{
|
||||
Storage: user.ProjectStorageLimit,
|
||||
Bandwidth: user.ProjectBandwidthLimit,
|
||||
Segment: user.ProjectSegmentLimit,
|
||||
},
|
||||
Projects: make(map[uuid.UUID]UsageLimits),
|
||||
},
|
||||
}
|
||||
} else if err != nil {
|
||||
return ErrAccountFreeze.Wrap(err)
|
||||
}
|
||||
|
||||
userLimits := UsageLimits{
|
||||
Storage: user.ProjectStorageLimit,
|
||||
Bandwidth: user.ProjectBandwidthLimit,
|
||||
Segment: user.ProjectSegmentLimit,
|
||||
}
|
||||
// If user limits have been zeroed already, we should not override what is in the freeze table.
|
||||
if userLimits != (UsageLimits{}) {
|
||||
event.Limits.User = userLimits
|
||||
}
|
||||
|
||||
projects, err := s.projectsDB.GetOwn(ctx, userID)
|
||||
if err != nil {
|
||||
return ErrAccountFreeze.Wrap(err)
|
||||
}
|
||||
for _, p := range projects {
|
||||
projLimits := UsageLimits{}
|
||||
if p.StorageLimit != nil {
|
||||
projLimits.Storage = p.StorageLimit.Int64()
|
||||
}
|
||||
if p.BandwidthLimit != nil {
|
||||
projLimits.Bandwidth = p.BandwidthLimit.Int64()
|
||||
}
|
||||
if p.SegmentLimit != nil {
|
||||
projLimits.Segment = *p.SegmentLimit
|
||||
}
|
||||
// If project limits have been zeroed already, we should not override what is in the freeze table.
|
||||
if projLimits != (UsageLimits{}) {
|
||||
event.Limits.Projects[p.ID] = projLimits
|
||||
}
|
||||
}
|
||||
|
||||
_, err = s.freezeEventsDB.Upsert(ctx, event)
|
||||
if err != nil {
|
||||
return ErrAccountFreeze.Wrap(err)
|
||||
}
|
||||
|
||||
err = s.usersDB.UpdateUserProjectLimits(ctx, userID, UsageLimits{})
|
||||
if err != nil {
|
||||
return ErrAccountFreeze.Wrap(err)
|
||||
}
|
||||
|
||||
for _, proj := range projects {
|
||||
err := s.projectsDB.UpdateUsageLimits(ctx, proj.ID, UsageLimits{})
|
||||
if err != nil {
|
||||
return ErrAccountFreeze.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnfreezeUser reverses the freeze placed on the user specified by the given ID.
|
||||
func (s *AccountFreezeService) UnfreezeUser(ctx context.Context, userID uuid.UUID) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
event, err := s.freezeEventsDB.Get(ctx, userID, Freeze)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return ErrAccountFreeze.New("user is not frozen")
|
||||
}
|
||||
|
||||
if event.Limits == nil {
|
||||
return ErrAccountFreeze.New("freeze event limits are nil")
|
||||
}
|
||||
|
||||
for id, limits := range event.Limits.Projects {
|
||||
err := s.projectsDB.UpdateUsageLimits(ctx, id, limits)
|
||||
if err != nil {
|
||||
return ErrAccountFreeze.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = s.usersDB.UpdateUserProjectLimits(ctx, userID, event.Limits.User)
|
||||
if err != nil {
|
||||
return ErrAccountFreeze.Wrap(err)
|
||||
}
|
||||
|
||||
return ErrAccountFreeze.Wrap(s.freezeEventsDB.DeleteAllByUserID(ctx, userID))
|
||||
}
|
||||
|
210
satellite/console/accountfreezes_test.go
Normal file
210
satellite/console/accountfreezes_test.go
Normal file
@ -0,0 +1,210 @@
|
||||
// Copyright (C) 2022 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package console_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"storj.io/common/testcontext"
|
||||
"storj.io/storj/private/testplanet"
|
||||
"storj.io/storj/satellite/console"
|
||||
)
|
||||
|
||||
func getUserLimits(u *console.User) console.UsageLimits {
|
||||
return console.UsageLimits{
|
||||
Storage: u.ProjectStorageLimit,
|
||||
Bandwidth: u.ProjectBandwidthLimit,
|
||||
Segment: u.ProjectSegmentLimit,
|
||||
}
|
||||
}
|
||||
|
||||
func getProjectLimits(p *console.Project) console.UsageLimits {
|
||||
return console.UsageLimits{
|
||||
Storage: p.StorageLimit.Int64(),
|
||||
Bandwidth: p.BandwidthLimit.Int64(),
|
||||
Segment: *p.SegmentLimit,
|
||||
}
|
||||
}
|
||||
|
||||
func randUsageLimits() console.UsageLimits {
|
||||
return console.UsageLimits{Storage: rand.Int63(), Bandwidth: rand.Int63(), Segment: rand.Int63()}
|
||||
}
|
||||
|
||||
func TestAccountFreeze(t *testing.T) {
|
||||
testplanet.Run(t, testplanet.Config{
|
||||
SatelliteCount: 1,
|
||||
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||
sat := planet.Satellites[0]
|
||||
usersDB := sat.DB.Console().Users()
|
||||
projectsDB := sat.DB.Console().Projects()
|
||||
service := console.NewAccountFreezeService(sat.DB.Console().AccountFreezeEvents(), usersDB, projectsDB)
|
||||
|
||||
userLimits := randUsageLimits()
|
||||
user, err := sat.AddUser(ctx, console.CreateUser{
|
||||
FullName: "Test User",
|
||||
Email: "user@mail.test",
|
||||
}, 2)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, usersDB.UpdateUserProjectLimits(ctx, user.ID, userLimits))
|
||||
|
||||
projLimits := randUsageLimits()
|
||||
proj, err := sat.AddProject(ctx, user.ID, "")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, projectsDB.UpdateUsageLimits(ctx, proj.ID, projLimits))
|
||||
|
||||
frozen, err := service.IsUserFrozen(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, frozen)
|
||||
|
||||
require.NoError(t, service.FreezeUser(ctx, user.ID))
|
||||
|
||||
user, err = usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, getUserLimits(user))
|
||||
|
||||
proj, err = projectsDB.Get(ctx, proj.ID)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, getProjectLimits(proj))
|
||||
|
||||
frozen, err = service.IsUserFrozen(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, frozen)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccountUnfreeze(t *testing.T) {
|
||||
testplanet.Run(t, testplanet.Config{
|
||||
SatelliteCount: 1,
|
||||
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||
sat := planet.Satellites[0]
|
||||
usersDB := sat.DB.Console().Users()
|
||||
projectsDB := sat.DB.Console().Projects()
|
||||
service := console.NewAccountFreezeService(sat.DB.Console().AccountFreezeEvents(), usersDB, projectsDB)
|
||||
|
||||
userLimits := randUsageLimits()
|
||||
user, err := sat.AddUser(ctx, console.CreateUser{
|
||||
FullName: "Test User",
|
||||
Email: "user@mail.test",
|
||||
}, 2)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, usersDB.UpdateUserProjectLimits(ctx, user.ID, userLimits))
|
||||
|
||||
projLimits := randUsageLimits()
|
||||
proj, err := sat.AddProject(ctx, user.ID, "")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, projectsDB.UpdateUsageLimits(ctx, proj.ID, projLimits))
|
||||
|
||||
require.NoError(t, service.FreezeUser(ctx, user.ID))
|
||||
require.NoError(t, service.UnfreezeUser(ctx, user.ID))
|
||||
|
||||
user, err = usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, userLimits, getUserLimits(user))
|
||||
|
||||
proj, err = projectsDB.Get(ctx, proj.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, projLimits, getProjectLimits(proj))
|
||||
|
||||
frozen, err := service.IsUserFrozen(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, frozen)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccountFreezeAlreadyFrozen(t *testing.T) {
|
||||
testplanet.Run(t, testplanet.Config{
|
||||
SatelliteCount: 1,
|
||||
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||
sat := planet.Satellites[0]
|
||||
usersDB := sat.DB.Console().Users()
|
||||
projectsDB := sat.DB.Console().Projects()
|
||||
service := console.NewAccountFreezeService(sat.DB.Console().AccountFreezeEvents(), usersDB, projectsDB)
|
||||
|
||||
userLimits := randUsageLimits()
|
||||
user, err := sat.AddUser(ctx, console.CreateUser{
|
||||
FullName: "Test User",
|
||||
Email: "user@mail.test",
|
||||
}, 2)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, usersDB.UpdateUserProjectLimits(ctx, user.ID, userLimits))
|
||||
|
||||
proj1Limits := randUsageLimits()
|
||||
proj1, err := sat.AddProject(ctx, user.ID, "")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, projectsDB.UpdateUsageLimits(ctx, proj1.ID, proj1Limits))
|
||||
|
||||
// Freezing a frozen user should freeze any projects that were unable to be frozen prior.
|
||||
// The limits stored for projects frozen by the prior freeze should not be modified.
|
||||
t.Run("Project limits", func(t *testing.T) {
|
||||
require.NoError(t, service.FreezeUser(ctx, user.ID))
|
||||
|
||||
proj2Limits := randUsageLimits()
|
||||
proj2, err := sat.AddProject(ctx, user.ID, "")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, projectsDB.UpdateUsageLimits(ctx, proj2.ID, proj2Limits))
|
||||
|
||||
require.NoError(t, service.FreezeUser(ctx, user.ID))
|
||||
|
||||
user, err := usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, getUserLimits(user))
|
||||
|
||||
proj2, err = projectsDB.Get(ctx, proj2.ID)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, getProjectLimits(proj2))
|
||||
|
||||
require.NoError(t, service.UnfreezeUser(ctx, user.ID))
|
||||
|
||||
user, err = usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, userLimits, getUserLimits(user))
|
||||
|
||||
proj1, err = projectsDB.Get(ctx, proj1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, proj1Limits, getProjectLimits(proj1))
|
||||
|
||||
proj2, err = projectsDB.Get(ctx, proj2.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, proj2Limits, getProjectLimits(proj2))
|
||||
})
|
||||
|
||||
// Freezing a frozen user should freeze the user's limits if they were unable to be frozen prior.
|
||||
t.Run("Unfrozen user limits", func(t *testing.T) {
|
||||
user, err := usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, service.FreezeUser(ctx, user.ID))
|
||||
require.NoError(t, usersDB.UpdateUserProjectLimits(ctx, user.ID, userLimits))
|
||||
require.NoError(t, service.FreezeUser(ctx, user.ID))
|
||||
|
||||
user, err = usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, getUserLimits(user))
|
||||
|
||||
require.NoError(t, service.UnfreezeUser(ctx, user.ID))
|
||||
|
||||
user, err = usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, userLimits, getUserLimits(user))
|
||||
})
|
||||
|
||||
// Freezing a frozen user should not modify user limits stored by the prior freeze.
|
||||
t.Run("Frozen user limits", func(t *testing.T) {
|
||||
require.NoError(t, service.FreezeUser(ctx, user.ID))
|
||||
require.NoError(t, service.FreezeUser(ctx, user.ID))
|
||||
|
||||
user, err = usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, getUserLimits(user))
|
||||
|
||||
require.NoError(t, service.UnfreezeUser(ctx, user.ID))
|
||||
user, err = usersDB.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, userLimits, getUserLimits(user))
|
||||
})
|
||||
})
|
||||
}
|
@ -51,6 +51,9 @@ type Projects interface {
|
||||
GetMaxBuckets(ctx context.Context, id uuid.UUID) (*int, error)
|
||||
// UpdateBucketLimit is a method for updating projects bucket limit.
|
||||
UpdateBucketLimit(ctx context.Context, id uuid.UUID, newLimit int) error
|
||||
|
||||
// UpdateUsageLimits is a method for updating project's usage limits.
|
||||
UpdateUsageLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
|
||||
}
|
||||
|
||||
// UsageLimitsConfig is a configuration struct for default per-project usage limits.
|
||||
|
@ -39,6 +39,8 @@ type Users interface {
|
||||
Update(ctx context.Context, userID uuid.UUID, request UpdateUserRequest) error
|
||||
// UpdatePaidTier sets whether the user is in the paid tier.
|
||||
UpdatePaidTier(ctx context.Context, id uuid.UUID, paidTier bool, projectBandwidthLimit, projectStorageLimit memory.Size, projectSegmentLimit int64, projectLimit int) error
|
||||
// UpdateUserProjectLimits is a method to update the user's usage limits for new projects.
|
||||
UpdateUserProjectLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
|
||||
// GetProjectLimit is a method to get the users project limit
|
||||
GetProjectLimit(ctx context.Context, id uuid.UUID) (limit int, err error)
|
||||
// GetUserProjectLimits is a method to get the users storage and bandwidth limits for new projects.
|
||||
|
@ -20,8 +20,8 @@ type accountFreezeEvents struct {
|
||||
db dbx.Methods
|
||||
}
|
||||
|
||||
// Insert is a method for inserting account freeze event into the database.
|
||||
func (events *accountFreezeEvents) Insert(ctx context.Context, event *console.AccountFreezeEvent) (_ *console.AccountFreezeEvent, err error) {
|
||||
// Upsert is a method for updating an account freeze event if it exists and inserting it otherwise.
|
||||
func (events *accountFreezeEvents) Upsert(ctx context.Context, event *console.AccountFreezeEvent) (_ *console.AccountFreezeEvent, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if event == nil {
|
||||
@ -37,7 +37,7 @@ func (events *accountFreezeEvents) Insert(ctx context.Context, event *console.Ac
|
||||
createFields.Limits = dbx.AccountFreezeEvent_Limits(limitBytes)
|
||||
}
|
||||
|
||||
dbxEvent, err := events.db.Create_AccountFreezeEvent(ctx,
|
||||
dbxEvent, err := events.db.Replace_AccountFreezeEvent(ctx,
|
||||
dbx.AccountFreezeEvent_UserId(event.UserID.Bytes()),
|
||||
dbx.AccountFreezeEvent_Event(int(event.Type)),
|
||||
createFields,
|
||||
@ -64,26 +64,6 @@ func (events *accountFreezeEvents) Get(ctx context.Context, userID uuid.UUID, ev
|
||||
return fromDBXAccountFreezeEvent(dbxEvent)
|
||||
}
|
||||
|
||||
// UpdateLimits is a method for updating the limits of an account freeze event by user ID and event type.
|
||||
func (events *accountFreezeEvents) UpdateLimits(ctx context.Context, userID uuid.UUID, eventType console.AccountFreezeEventType, limits *console.AccountFreezeEventLimits) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
limitBytes, err := json.Marshal(limits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = events.db.Update_AccountFreezeEvent_By_UserId_And_Event(ctx,
|
||||
dbx.AccountFreezeEvent_UserId(userID.Bytes()),
|
||||
dbx.AccountFreezeEvent_Event(int(eventType)),
|
||||
dbx.AccountFreezeEvent_Update_Fields{
|
||||
Limits: dbx.AccountFreezeEvent_Limits(limitBytes),
|
||||
},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteAllByUserID is a method for deleting all account freeze events from the database by user ID.
|
||||
func (events *accountFreezeEvents) DeleteAllByUserID(ctx context.Context, userID uuid.UUID) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
@ -22,11 +22,7 @@ import (
|
||||
func TestAccountFreezeEvents(t *testing.T) {
|
||||
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
||||
randUsageLimits := func() console.UsageLimits {
|
||||
return console.UsageLimits{
|
||||
Storage: rand.Int63(),
|
||||
Bandwidth: rand.Int63(),
|
||||
Segment: rand.Int63(),
|
||||
}
|
||||
return console.UsageLimits{Storage: rand.Int63(), Bandwidth: rand.Int63(), Segment: rand.Int63()}
|
||||
}
|
||||
|
||||
event := &console.AccountFreezeEvent{
|
||||
@ -44,12 +40,12 @@ func TestAccountFreezeEvents(t *testing.T) {
|
||||
eventsDB := db.Console().AccountFreezeEvents()
|
||||
|
||||
t.Run("Can't insert nil event", func(t *testing.T) {
|
||||
_, err := eventsDB.Insert(ctx, nil)
|
||||
_, err := eventsDB.Upsert(ctx, nil)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Insert event", func(t *testing.T) {
|
||||
dbEvent, err := eventsDB.Insert(ctx, event)
|
||||
dbEvent, err := eventsDB.Upsert(ctx, event)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dbEvent)
|
||||
require.WithinDuration(t, time.Now(), dbEvent.CreatedAt, time.Minute)
|
||||
@ -57,11 +53,6 @@ func TestAccountFreezeEvents(t *testing.T) {
|
||||
require.Equal(t, event, dbEvent)
|
||||
})
|
||||
|
||||
t.Run("Can't insert duplicate event", func(t *testing.T) {
|
||||
_, err := eventsDB.Insert(ctx, event)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Get event", func(t *testing.T) {
|
||||
dbEvent, err := eventsDB.Get(ctx, event.UserID, event.Type)
|
||||
require.NoError(t, err)
|
||||
@ -71,18 +62,22 @@ func TestAccountFreezeEvents(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Update event limits", func(t *testing.T) {
|
||||
limits := &console.AccountFreezeEventLimits{
|
||||
event.Limits = &console.AccountFreezeEventLimits{
|
||||
User: randUsageLimits(),
|
||||
Projects: map[uuid.UUID]console.UsageLimits{
|
||||
testrand.UUID(): randUsageLimits(),
|
||||
},
|
||||
}
|
||||
require.NoError(t, eventsDB.UpdateLimits(ctx, event.UserID, event.Type, limits))
|
||||
|
||||
_, err := eventsDB.Upsert(ctx, event)
|
||||
require.NoError(t, err)
|
||||
dbEvent, err := eventsDB.Get(ctx, event.UserID, event.Type)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, limits, dbEvent.Limits)
|
||||
require.Equal(t, event.Limits, dbEvent.Limits)
|
||||
|
||||
require.NoError(t, eventsDB.UpdateLimits(ctx, event.UserID, event.Type, nil))
|
||||
event.Limits = nil
|
||||
_, err = eventsDB.Upsert(ctx, event)
|
||||
require.NoError(t, err)
|
||||
dbEvent, err = eventsDB.Get(ctx, event.UserID, event.Type)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, dbEvent.Limits)
|
||||
|
@ -10,7 +10,7 @@ model account_freeze_event (
|
||||
field created_at timestamp ( default current_timestamp )
|
||||
)
|
||||
|
||||
create account_freeze_event()
|
||||
create account_freeze_event( replace )
|
||||
|
||||
read one (
|
||||
select account_freeze_event
|
||||
|
@ -12830,7 +12830,7 @@ type WalletAddress_Row struct {
|
||||
WalletAddress []byte
|
||||
}
|
||||
|
||||
func (obj *pgxImpl) Create_AccountFreezeEvent(ctx context.Context,
|
||||
func (obj *pgxImpl) Replace_AccountFreezeEvent(ctx context.Context,
|
||||
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
|
||||
account_freeze_event_event AccountFreezeEvent_Event_Field,
|
||||
optional AccountFreezeEvent_Create_Fields) (
|
||||
@ -12844,7 +12844,7 @@ func (obj *pgxImpl) Create_AccountFreezeEvent(ctx context.Context,
|
||||
var __placeholders = &__sqlbundle_Hole{SQL: __sqlbundle_Literal("?, ?, ?")}
|
||||
var __clause = &__sqlbundle_Hole{SQL: __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("("), __columns, __sqlbundle_Literal(") VALUES ("), __placeholders, __sqlbundle_Literal(")")}}}
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("INSERT INTO account_freeze_events "), __clause, __sqlbundle_Literal(" RETURNING account_freeze_events.user_id, account_freeze_events.event, account_freeze_events.limits, account_freeze_events.created_at")}}
|
||||
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("INSERT INTO account_freeze_events "), __clause, __sqlbundle_Literal(" ON CONFLICT ( user_id, event ) DO UPDATE SET user_id = EXCLUDED.user_id, event = EXCLUDED.event, limits = EXCLUDED.limits, created_at = EXCLUDED.created_at RETURNING account_freeze_events.user_id, account_freeze_events.event, account_freeze_events.limits, account_freeze_events.created_at")}}
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, __user_id_val, __event_val, __limits_val)
|
||||
@ -20781,7 +20781,7 @@ func (obj *pgxImpl) deleteAll(ctx context.Context) (count int64, err error) {
|
||||
|
||||
}
|
||||
|
||||
func (obj *pgxcockroachImpl) Create_AccountFreezeEvent(ctx context.Context,
|
||||
func (obj *pgxcockroachImpl) Replace_AccountFreezeEvent(ctx context.Context,
|
||||
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
|
||||
account_freeze_event_event AccountFreezeEvent_Event_Field,
|
||||
optional AccountFreezeEvent_Create_Fields) (
|
||||
@ -20795,7 +20795,7 @@ func (obj *pgxcockroachImpl) Create_AccountFreezeEvent(ctx context.Context,
|
||||
var __placeholders = &__sqlbundle_Hole{SQL: __sqlbundle_Literal("?, ?, ?")}
|
||||
var __clause = &__sqlbundle_Hole{SQL: __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("("), __columns, __sqlbundle_Literal(") VALUES ("), __placeholders, __sqlbundle_Literal(")")}}}
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("INSERT INTO account_freeze_events "), __clause, __sqlbundle_Literal(" RETURNING account_freeze_events.user_id, account_freeze_events.event, account_freeze_events.limits, account_freeze_events.created_at")}}
|
||||
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPSERT INTO account_freeze_events "), __clause, __sqlbundle_Literal(" RETURNING account_freeze_events.user_id, account_freeze_events.event, account_freeze_events.limits, account_freeze_events.created_at")}}
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, __user_id_val, __event_val, __limits_val)
|
||||
@ -29171,19 +29171,6 @@ func (rx *Rx) CreateNoReturn_StorjscanWallet(ctx context.Context,
|
||||
|
||||
}
|
||||
|
||||
func (rx *Rx) Create_AccountFreezeEvent(ctx context.Context,
|
||||
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
|
||||
account_freeze_event_event AccountFreezeEvent_Event_Field,
|
||||
optional AccountFreezeEvent_Create_Fields) (
|
||||
account_freeze_event *AccountFreezeEvent, err error) {
|
||||
var tx *Tx
|
||||
if tx, err = rx.getTx(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Create_AccountFreezeEvent(ctx, account_freeze_event_user_id, account_freeze_event_event, optional)
|
||||
|
||||
}
|
||||
|
||||
func (rx *Rx) Create_ApiKey(ctx context.Context,
|
||||
api_key_id ApiKey_Id_Field,
|
||||
api_key_project_id ApiKey_ProjectId_Field,
|
||||
@ -30515,6 +30502,19 @@ func (rx *Rx) ReplaceNoReturn_StoragenodePaystub(ctx context.Context,
|
||||
|
||||
}
|
||||
|
||||
func (rx *Rx) Replace_AccountFreezeEvent(ctx context.Context,
|
||||
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
|
||||
account_freeze_event_event AccountFreezeEvent_Event_Field,
|
||||
optional AccountFreezeEvent_Create_Fields) (
|
||||
account_freeze_event *AccountFreezeEvent, err error) {
|
||||
var tx *Tx
|
||||
if tx, err = rx.getTx(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Replace_AccountFreezeEvent(ctx, account_freeze_event_user_id, account_freeze_event_event, optional)
|
||||
|
||||
}
|
||||
|
||||
func (rx *Rx) UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
|
||||
accounting_timestamps_name AccountingTimestamps_Name_Field,
|
||||
update AccountingTimestamps_Update_Fields) (
|
||||
@ -30989,12 +30989,6 @@ type Methods interface {
|
||||
storjscan_wallet_wallet_address StorjscanWallet_WalletAddress_Field) (
|
||||
err error)
|
||||
|
||||
Create_AccountFreezeEvent(ctx context.Context,
|
||||
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
|
||||
account_freeze_event_event AccountFreezeEvent_Event_Field,
|
||||
optional AccountFreezeEvent_Create_Fields) (
|
||||
account_freeze_event *AccountFreezeEvent, err error)
|
||||
|
||||
Create_ApiKey(ctx context.Context,
|
||||
api_key_id ApiKey_Id_Field,
|
||||
api_key_project_id ApiKey_ProjectId_Field,
|
||||
@ -31612,6 +31606,12 @@ type Methods interface {
|
||||
storagenode_paystub_distributed StoragenodePaystub_Distributed_Field) (
|
||||
err error)
|
||||
|
||||
Replace_AccountFreezeEvent(ctx context.Context,
|
||||
account_freeze_event_user_id AccountFreezeEvent_UserId_Field,
|
||||
account_freeze_event_event AccountFreezeEvent_Event_Field,
|
||||
optional AccountFreezeEvent_Create_Fields) (
|
||||
account_freeze_event *AccountFreezeEvent, err error)
|
||||
|
||||
UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
|
||||
accounting_timestamps_name AccountingTimestamps_Name_Field,
|
||||
update AccountingTimestamps_Update_Fields) (
|
||||
|
@ -455,3 +455,18 @@ func (projects *projects) GetMaxBuckets(ctx context.Context, id uuid.UUID) (maxB
|
||||
}
|
||||
return dbxRow.MaxBuckets, nil
|
||||
}
|
||||
|
||||
// UpdateUsageLimits is a method for updating project's bandwidth, storage, and segment limits.
|
||||
func (projects *projects) UpdateUsageLimits(ctx context.Context, id uuid.UUID, limits console.UsageLimits) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
_, err = projects.db.Update_Project_By_Id(ctx,
|
||||
dbx.Project_Id(id[:]),
|
||||
dbx.Project_Update_Fields{
|
||||
BandwidthLimit: dbx.Project_BandwidthLimit(limits.Bandwidth),
|
||||
UsageLimit: dbx.Project_UsageLimit(limits.Storage),
|
||||
SegmentLimit: dbx.Project_SegmentLimit(limits.Segment),
|
||||
},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
package satellitedb_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -54,3 +55,23 @@ func TestProjectsGetSalt(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateProjectUsageLimits(t *testing.T) {
|
||||
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
||||
limits := console.UsageLimits{Storage: rand.Int63(), Bandwidth: rand.Int63(), Segment: rand.Int63()}
|
||||
projectsRepo := db.Console().Projects()
|
||||
|
||||
proj, err := projectsRepo.Insert(ctx, &console.Project{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, proj)
|
||||
|
||||
err = projectsRepo.UpdateUsageLimits(ctx, proj.ID, limits)
|
||||
require.NoError(t, err)
|
||||
|
||||
proj, err = projectsRepo.Get(ctx, proj.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, limits.Bandwidth, proj.BandwidthLimit.Int64())
|
||||
require.Equal(t, limits.Storage, proj.StorageLimit.Int64())
|
||||
require.Equal(t, limits.Segment, *proj.SegmentLimit)
|
||||
})
|
||||
}
|
||||
|
@ -240,6 +240,23 @@ func (users *users) UpdatePaidTier(ctx context.Context, id uuid.UUID, paidTier b
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateUserProjectLimits is a method to update the user's usage limits for new projects.
|
||||
func (users *users) UpdateUserProjectLimits(ctx context.Context, id uuid.UUID, limits console.UsageLimits) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
_, err = users.db.Update_User_By_Id(
|
||||
ctx,
|
||||
dbx.User_Id(id[:]),
|
||||
dbx.User_Update_Fields{
|
||||
ProjectBandwidthLimit: dbx.User_ProjectBandwidthLimit(limits.Bandwidth),
|
||||
ProjectStorageLimit: dbx.User_ProjectStorageLimit(limits.Storage),
|
||||
ProjectSegmentLimit: dbx.User_ProjectSegmentLimit(limits.Segment),
|
||||
},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetProjectLimit is a method to get the users project limit.
|
||||
func (users *users) GetProjectLimit(ctx context.Context, id uuid.UUID) (limit int, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
@ -4,6 +4,7 @@
|
||||
package satellitedb_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -301,3 +302,27 @@ func TestUpdateUser(t *testing.T) {
|
||||
require.Equal(t, u, updatedUser)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateUserProjectLimits(t *testing.T) {
|
||||
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
||||
limits := console.UsageLimits{Storage: rand.Int63(), Bandwidth: rand.Int63(), Segment: rand.Int63()}
|
||||
usersRepo := db.Console().Users()
|
||||
|
||||
user, err := usersRepo.Insert(ctx, &console.User{
|
||||
ID: testrand.UUID(),
|
||||
FullName: "User",
|
||||
Email: "test@mail.test",
|
||||
PasswordHash: []byte("123a123"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = usersRepo.UpdateUserProjectLimits(ctx, user.ID, limits)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err = usersRepo.Get(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, limits.Bandwidth, user.ProjectBandwidthLimit)
|
||||
require.Equal(t, limits.Storage, user.ProjectStorageLimit)
|
||||
require.Equal(t, limits.Segment, user.ProjectSegmentLimit)
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user