storj/satellite/satellitedb/accountfreezeevents.go
Wilfred Asomani 6308da2cc0 satellite/{payment,console,analytics} extend freeze functionality for violation freeze
This change extends the account freeze functionality account for
violation freezes as well.
Also, debug level logs in the freeze chore have been changed to info.
It adds an analytics event for when an invoice is found that belongs to
a user frozen for violation.
And finally adds whether a user is frozen for violation to the
/account/freezestatus response.

Issue: https://github.com/storj/storj-private/issues/386

Change-Id: Id8e40282dc8fd8f242da52791ab8ddbbef3da2bc
2023-10-10 18:39:29 +00:00

203 lines
5.8 KiB
Go

// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package satellitedb
import (
"context"
"encoding/json"
"github.com/zeebo/errs"
"storj.io/common/uuid"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/satellitedb/dbx"
)
// Ensure that accountFreezeEvents implements console.AccountFreezeEvents.
var _ console.AccountFreezeEvents = (*accountFreezeEvents)(nil)
// accountFreezeEvents is an implementation of console.AccountFreezeEvents.
type accountFreezeEvents struct {
db *satelliteDB
}
// 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 {
return nil, Error.New("event is nil")
}
createFields := dbx.AccountFreezeEvent_Create_Fields{}
if event.Limits != nil {
limitBytes, err := json.Marshal(event.Limits)
if err != nil {
return nil, err
}
createFields.Limits = dbx.AccountFreezeEvent_Limits(limitBytes)
}
dbxEvent, err := events.db.Replace_AccountFreezeEvent(ctx,
dbx.AccountFreezeEvent_UserId(event.UserID.Bytes()),
dbx.AccountFreezeEvent_Event(int(event.Type)),
createFields,
)
if err != nil {
return nil, err
}
return fromDBXAccountFreezeEvent(dbxEvent)
}
// Get is a method for querying account freeze event from the database by user ID and event type.
func (events *accountFreezeEvents) Get(ctx context.Context, userID uuid.UUID, eventType console.AccountFreezeEventType) (event *console.AccountFreezeEvent, err error) {
defer mon.Task()(&ctx)(&err)
dbxEvent, err := events.db.Get_AccountFreezeEvent_By_UserId_And_Event(ctx,
dbx.AccountFreezeEvent_UserId(userID.Bytes()),
dbx.AccountFreezeEvent_Event(int(eventType)),
)
if err != nil {
return nil, err
}
return fromDBXAccountFreezeEvent(dbxEvent)
}
// GetAllEvents is a method for querying all account freeze events from the database.
func (events *accountFreezeEvents) GetAllEvents(ctx context.Context, cursor console.FreezeEventsCursor) (freezeEvents *console.FreezeEventsPage, err error) {
defer mon.Task()(&ctx)(&err)
if cursor.Limit <= 0 {
return nil, errs.New("limit cannot be zero or less")
}
page := console.FreezeEventsPage{
Events: make([]console.AccountFreezeEvent, 0, cursor.Limit),
}
if cursor.StartingAfter == nil {
cursor.StartingAfter = &uuid.UUID{}
}
rows, err := events.db.Query(ctx, events.db.Rebind(`
SELECT user_id, event
FROM account_freeze_events
WHERE user_id > ?
ORDER BY user_id LIMIT ?
`), cursor.StartingAfter, cursor.Limit+1)
if err != nil {
return nil, Error.Wrap(err)
}
defer func() { err = errs.Combine(err, rows.Close()) }()
count := 0
for rows.Next() {
count++
if count > cursor.Limit {
// we are done with this page; do not include this event
page.Next = true
break
}
var event dbx.AccountFreezeEvent
err = rows.Scan(&event.UserId, &event.Event)
if err != nil {
return nil, err
}
eventToSend, err := fromDBXAccountFreezeEvent(&event)
if err != nil {
return nil, err
}
page.Events = append(page.Events, *eventToSend)
}
return &page, rows.Err()
}
// GetAll is a method for querying all account freeze events from the database by user ID.
func (events *accountFreezeEvents) GetAll(ctx context.Context, userID uuid.UUID) (freezes *console.UserFreezeEvents, err error) {
defer mon.Task()(&ctx)(&err)
// dbxEvents will have a max length of 3.
// because there's at most 1 instance each of 3 types of events for a user.
dbxEvents, err := events.db.All_AccountFreezeEvent_By_UserId(ctx,
dbx.AccountFreezeEvent_UserId(userID.Bytes()),
)
if err != nil {
return nil, err
}
freezes = &console.UserFreezeEvents{}
for _, event := range dbxEvents {
if console.AccountFreezeEventType(event.Event) == console.BillingFreeze {
freezes.BillingFreeze, err = fromDBXAccountFreezeEvent(event)
if err != nil {
return nil, err
}
continue
}
if console.AccountFreezeEventType(event.Event) == console.ViolationFreeze {
freezes.ViolationFreeze, err = fromDBXAccountFreezeEvent(event)
if err != nil {
return nil, err
}
continue
}
freezes.BillingWarning, err = fromDBXAccountFreezeEvent(event)
if err != nil {
return nil, err
}
}
return freezes, nil
}
// 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)
_, err = events.db.Delete_AccountFreezeEvent_By_UserId(ctx, dbx.AccountFreezeEvent_UserId(userID.Bytes()))
return err
}
// DeleteByUserIDAndEvent is a method for deleting all account `eventType` events from the database by user ID.
func (events *accountFreezeEvents) DeleteByUserIDAndEvent(ctx context.Context, userID uuid.UUID, eventType console.AccountFreezeEventType) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = events.db.Delete_AccountFreezeEvent_By_UserId_And_Event(ctx,
dbx.AccountFreezeEvent_UserId(userID.Bytes()),
dbx.AccountFreezeEvent_Event(int(eventType)),
)
return err
}
// fromDBXAccountFreezeEvent converts *dbx.AccountFreezeEvent to *console.AccountFreezeEvent.
func fromDBXAccountFreezeEvent(dbxEvent *dbx.AccountFreezeEvent) (_ *console.AccountFreezeEvent, err error) {
if dbxEvent == nil {
return nil, Error.New("dbx event is nil")
}
userID, err := uuid.FromBytes(dbxEvent.UserId)
if err != nil {
return nil, err
}
event := &console.AccountFreezeEvent{
UserID: userID,
Type: console.AccountFreezeEventType(dbxEvent.Event),
CreatedAt: dbxEvent.CreatedAt,
}
if dbxEvent.Limits != nil {
err := json.Unmarshal(dbxEvent.Limits, &event.Limits)
if err != nil {
return nil, err
}
}
return event, nil
}