forked from Silverfish/proton-bridge
GODT-1351: Cache and update of space bytes in user object.
This commit is contained in:
@ -42,7 +42,7 @@ Item {
|
||||
|
||||
function spaceWithUnits(bytes){
|
||||
if (bytes*1 !== bytes ) return "0 kB"
|
||||
var units = ['B',"kB", "MB", "TB"];
|
||||
var units = ['B',"kB", "MB", "GB", "TB"];
|
||||
var i = parseInt(Math.floor(Math.log(bytes)/Math.log(1024)));
|
||||
|
||||
return Math.round(bytes*10 / Math.pow(1024, i))/10 + " " + units[i]
|
||||
|
||||
@ -287,8 +287,8 @@ func (qu *QMLUser) update(user types.User) {
|
||||
qu.SetLoggedIn(user.IsConnected())
|
||||
qu.SetSplitMode(!user.IsCombinedAddressMode())
|
||||
qu.SetSetupGuideSeen(false)
|
||||
qu.SetUsedBytes(1.0) // TODO
|
||||
qu.SetTotalBytes(10000.0) // TODO
|
||||
qu.SetUsedBytes(float32(user.UsedBytes()))
|
||||
qu.SetTotalBytes(float32(user.TotalBytes()))
|
||||
qu.SetPassword(user.GetBridgePassword())
|
||||
qu.SetAddresses(user.GetAddresses())
|
||||
}
|
||||
|
||||
@ -60,6 +60,8 @@ type UserManager interface {
|
||||
// User is an interface of user needed by frontend.
|
||||
type User interface {
|
||||
ID() string
|
||||
UsedBytes() int64
|
||||
TotalBytes() int64
|
||||
Username() string
|
||||
IsConnected() bool
|
||||
IsCombinedAddressMode() bool
|
||||
|
||||
@ -31,7 +31,7 @@ import (
|
||||
|
||||
type storeUserProvider interface {
|
||||
UserID() string
|
||||
GetSpace() (usedSpace, maxSpace uint, err error)
|
||||
GetSpaceKB() (usedSpace, maxSpace uint32, err error)
|
||||
GetMaxUpload() (int64, error)
|
||||
|
||||
GetAddress(addressID string) (storeAddressProvider, error)
|
||||
|
||||
@ -225,7 +225,7 @@ func (iu *imapUser) GetQuota(name string) (*imapquota.Status, error) {
|
||||
// Called from go-imap in goroutines - we need to handle panics for each function.
|
||||
defer iu.panicHandler.HandlePanic()
|
||||
|
||||
usedSpace, maxSpace, err := iu.storeUser.GetSpace()
|
||||
usedSpace, maxSpace, err := iu.storeUser.GetSpaceKB()
|
||||
if err != nil {
|
||||
log.Error("Failed getting quota: ", err)
|
||||
return nil, err
|
||||
@ -233,9 +233,8 @@ func (iu *imapUser) GetQuota(name string) (*imapquota.Status, error) {
|
||||
|
||||
resources := make(map[string][2]uint32)
|
||||
var list [2]uint32
|
||||
// Quota is "in units of 1024 octets" (or KB) and PM returns bytes.
|
||||
list[0] = uint32(usedSpace / 1024)
|
||||
list[1] = uint32(maxSpace / 1024)
|
||||
list[0] = usedSpace
|
||||
list[1] = maxSpace
|
||||
resources[imapquota.ResourceStorage] = list
|
||||
status := &imapquota.Status{
|
||||
Name: "",
|
||||
|
||||
@ -337,6 +337,11 @@ func (loop *eventLoop) processEvent(event *pmapi.Event) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if event.User != nil {
|
||||
loop.user.UpdateSpace(event.User)
|
||||
loop.listener.Emit(bridgeEvents.UserRefreshEvent, loop.user.ID())
|
||||
}
|
||||
|
||||
// One would expect that every event would contain MessageCount as part of
|
||||
// the event.Messages, but this is apparently not the case.
|
||||
// MessageCounts are served on an irregular basis, so we should update and
|
||||
|
||||
@ -207,6 +207,18 @@ func (mr *MockBridgeUserMockRecorder) Logout() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Logout", reflect.TypeOf((*MockBridgeUser)(nil).Logout))
|
||||
}
|
||||
|
||||
// UpdateSpace mocks base method.
|
||||
func (m *MockBridgeUser) UpdateSpace(arg0 *pmapi.User) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "UpdateSpace", arg0)
|
||||
}
|
||||
|
||||
// UpdateSpace indicates an expected call of UpdateSpace.
|
||||
func (mr *MockBridgeUserMockRecorder) UpdateSpace(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSpace", reflect.TypeOf((*MockBridgeUser)(nil).UpdateSpace), arg0)
|
||||
}
|
||||
|
||||
// UpdateUser mocks base method.
|
||||
func (m *MockBridgeUser) UpdateUser(arg0 context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@ -37,6 +37,7 @@ type BridgeUser interface {
|
||||
GetStoreAddresses() []string
|
||||
GetClient() pmapi.Client
|
||||
UpdateUser(context.Context) error
|
||||
UpdateSpace(*pmapi.User)
|
||||
CloseAllConnections()
|
||||
CloseConnection(string)
|
||||
Logout() error
|
||||
|
||||
@ -17,18 +17,47 @@
|
||||
|
||||
package store
|
||||
|
||||
import "math"
|
||||
|
||||
// UserID returns user ID.
|
||||
func (store *Store) UserID() string {
|
||||
return store.user.ID()
|
||||
}
|
||||
|
||||
// GetSpace returns used and total space in bytes.
|
||||
func (store *Store) GetSpace() (usedSpace, maxSpace uint, err error) {
|
||||
// GetSpaceKB returns used and total space in kilo bytes (needed for IMAP
|
||||
// Quota. Quota is "in units of 1024 octets" (or KB) and PM returns bytes.
|
||||
func (store *Store) GetSpaceKB() (usedSpace, maxSpace uint32, err error) {
|
||||
apiUser, err := store.client().CurrentUser(exposeContextForIMAP())
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return uint(apiUser.UsedSpace), uint(apiUser.MaxSpace), nil
|
||||
if apiUser.UsedSpace != nil {
|
||||
usedSpace = store.toKBandLimit(*apiUser.UsedSpace, usedSpaceType)
|
||||
}
|
||||
if apiUser.MaxSpace != nil {
|
||||
maxSpace = store.toKBandLimit(*apiUser.MaxSpace, maxSpaceType)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type spaceType string
|
||||
|
||||
const (
|
||||
usedSpaceType = spaceType("used")
|
||||
maxSpaceType = spaceType("max")
|
||||
)
|
||||
|
||||
func (store *Store) toKBandLimit(n int64, space spaceType) uint32 {
|
||||
if n < 0 {
|
||||
log.WithField("space", space).Warning("negative number of bytes")
|
||||
return uint32(0)
|
||||
}
|
||||
n /= 1024
|
||||
if n > math.MaxUint32 {
|
||||
log.WithField("space", space).Warning("too large number of bytes")
|
||||
return uint32(math.MaxUint32)
|
||||
}
|
||||
return uint32(n)
|
||||
}
|
||||
|
||||
// GetMaxUpload returns max size of message + all attachments in bytes.
|
||||
|
||||
@ -49,6 +49,8 @@ type User struct {
|
||||
userID string
|
||||
creds *credentials.Credentials
|
||||
|
||||
usedBytes, totalBytes int64
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
@ -122,6 +124,8 @@ func (u *User) connect(client pmapi.Client, creds *credentials.Credentials) erro
|
||||
u.store.StartWatcher()
|
||||
}
|
||||
|
||||
u.UpdateSpace(nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -201,6 +205,40 @@ func (u *User) ID() string {
|
||||
return u.userID
|
||||
}
|
||||
|
||||
// UsedBytes returns number of bytes used on server.
|
||||
func (u *User) UsedBytes() int64 {
|
||||
return u.usedBytes
|
||||
}
|
||||
|
||||
// TotalBytes returns number of bytes available on server.
|
||||
func (u *User) TotalBytes() int64 {
|
||||
return u.totalBytes
|
||||
}
|
||||
|
||||
// UpdateSpace will update TotalBytes and UsedBytes values from API user. If
|
||||
// pointer is nill it will get fresh user from API. API user can come from
|
||||
// update event which means it doesn't contain all data. Therefore only
|
||||
// positive values will be updated.
|
||||
func (u *User) UpdateSpace(apiUser *pmapi.User) {
|
||||
// If missing get latest pmapi.User from API instead of using cached
|
||||
// values from client.CurrentUser()
|
||||
if apiUser == nil {
|
||||
var err error
|
||||
apiUser, err = u.client.GetUser(context.Background())
|
||||
if err != nil {
|
||||
u.log.WithError(err).Warning("Cannot update user space")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if apiUser.UsedSpace != nil {
|
||||
u.usedBytes = *apiUser.UsedSpace
|
||||
}
|
||||
if apiUser.MaxSpace != nil {
|
||||
u.totalBytes = *apiUser.MaxSpace
|
||||
}
|
||||
}
|
||||
|
||||
// Username returns the user's username as found in the user's credentials.
|
||||
func (u *User) Username() string {
|
||||
u.lock.RLock()
|
||||
@ -351,7 +389,7 @@ func (u *User) UpdateUser(ctx context.Context) error {
|
||||
defer u.lock.Unlock()
|
||||
defer u.listener.Emit(events.UserRefreshEvent, u.userID)
|
||||
|
||||
_, err := u.client.UpdateUser(ctx)
|
||||
user, err := u.client.UpdateUser(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -367,6 +405,8 @@ func (u *User) UpdateUser(ctx context.Context) error {
|
||||
|
||||
u.creds = creds
|
||||
|
||||
u.UpdateSpace(user)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ func TestUpdateUser(t *testing.T) {
|
||||
defer cleanUpUserData(user)
|
||||
|
||||
gomock.InOrder(
|
||||
m.pmapiClient.EXPECT().UpdateUser(gomock.Any()).Return(nil, nil),
|
||||
m.pmapiClient.EXPECT().UpdateUser(gomock.Any()).Return(testPMAPIUser, nil),
|
||||
m.pmapiClient.EXPECT().ReloadKeys(gomock.Any(), testCredentials.MailboxPassword).Return(nil),
|
||||
m.pmapiClient.EXPECT().Addresses().Return([]*pmapi.Address{testPMAPIAddress}),
|
||||
|
||||
|
||||
@ -115,6 +115,10 @@ func (u *Users) watchEvents() {
|
||||
log.WithError(err).Error("Failed to load store after reconnecting")
|
||||
}
|
||||
}
|
||||
|
||||
if user.totalBytes == 0 {
|
||||
user.UpdateSpace(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,9 +116,14 @@ var (
|
||||
IsCombinedAddressMode: false,
|
||||
}
|
||||
|
||||
usedSpace = int64(1048576)
|
||||
maxSpace = int64(10485760)
|
||||
|
||||
testPMAPIUser = &pmapi.User{ //nolint[gochecknoglobals]
|
||||
ID: "user",
|
||||
Name: "username",
|
||||
ID: "user",
|
||||
Name: "username",
|
||||
UsedSpace: &usedSpace,
|
||||
MaxSpace: &maxSpace,
|
||||
}
|
||||
|
||||
testPMAPIUserDisconnected = &pmapi.User{ //nolint[gochecknoglobals]
|
||||
@ -297,6 +302,7 @@ func mockInitConnectedUser(t *testing.T, m mocks) {
|
||||
// Mock of user initialisation.
|
||||
m.pmapiClient.EXPECT().AddAuthRefreshHandler(gomock.Any())
|
||||
m.pmapiClient.EXPECT().IsUnlocked().Return(true).AnyTimes()
|
||||
m.pmapiClient.EXPECT().GetUser(gomock.Any()).Return(testPMAPIUser, nil) // load connected user
|
||||
|
||||
// Mock of store initialisation.
|
||||
gomock.InOrder(
|
||||
|
||||
Reference in New Issue
Block a user