chore(GODT-2848): Simplify User Event Service

Rather than having the services subscribe to each individual event type,
have only one subscriber for the whole event object. This spreads the
logic to the service through the `EventHandler` type.

This is important for the next stage where the IMAP service will be
required to apply events from the past.
This commit is contained in:
Leander Beernaert
2023-08-09 16:09:21 +02:00
parent e8ce8942be
commit 4ee6da4baa
16 changed files with 597 additions and 878 deletions

View File

@ -284,7 +284,7 @@ mocks:
mockgen --package mocks github.com/ProtonMail/proton-bridge/v3/internal/services/userevents \ mockgen --package mocks github.com/ProtonMail/proton-bridge/v3/internal/services/userevents \
EventSource,EventIDStore > internal/services/userevents/mocks/mocks.go EventSource,EventIDStore > internal/services/userevents/mocks/mocks.go
mockgen --package userevents github.com/ProtonMail/proton-bridge/v3/internal/services/userevents \ mockgen --package userevents github.com/ProtonMail/proton-bridge/v3/internal/services/userevents \
MessageSubscriber,LabelSubscriber,AddressSubscriber,RefreshSubscriber,UserSubscriber,UserUsedSpaceSubscriber > tmp EventSubscriber,MessageEventHandler,LabelEventHandler,AddressEventHandler,RefreshEventHandler,UserEventHandler,UserUsedSpaceEventHandler > tmp
mv tmp internal/services/userevents/mocks_test.go mv tmp internal/services/userevents/mocks_test.go
mockgen --package mocks github.com/ProtonMail/proton-bridge/v3/internal/events EventPublisher \ mockgen --package mocks github.com/ProtonMail/proton-bridge/v3/internal/events EventPublisher \
> internal/events/mocks/mocks.go > internal/events/mocks/mocks.go

View File

@ -73,11 +73,7 @@ type Service struct {
labels *rwLabels labels *rwLabels
addressMode usertypes.AddressMode addressMode usertypes.AddressMode
refreshSubscriber *userevents.RefreshChanneledSubscriber subscription *userevents.EventChanneledSubscriber
addressSubscriber *userevents.AddressChanneledSubscriber
userSubscriber *userevents.UserChanneledSubscriber
messageSubscriber *userevents.MessageChanneledSubscriber
labelSubscriber *userevents.LabelChanneledSubscriber
gluonIDProvider GluonIDProvider gluonIDProvider GluonIDProvider
syncStateProvider SyncStateProvider syncStateProvider SyncStateProvider
@ -95,6 +91,7 @@ type Service struct {
connectors map[string]*Connector connectors map[string]*Connector
maxSyncMemory uint64 maxSyncMemory uint64
showAllMail bool showAllMail bool
syncHandler *syncHandler
} }
func NewService( func NewService(
@ -135,11 +132,7 @@ func NewService(
eventProvider: eventProvider, eventProvider: eventProvider,
eventPublisher: eventPublisher, eventPublisher: eventPublisher,
refreshSubscriber: userevents.NewRefreshSubscriber(subscriberName), subscription: userevents.NewEventSubscriber(subscriberName),
addressSubscriber: userevents.NewAddressSubscriber(subscriberName),
userSubscriber: userevents.NewUserSubscriber(subscriberName),
messageSubscriber: userevents.NewMessageSubscriber(subscriberName),
labelSubscriber: userevents.NewLabelSubscriber(subscriberName),
panicHandler: panicHandler, panicHandler: panicHandler,
sendRecorder: sendRecorder, sendRecorder: sendRecorder,
@ -241,6 +234,44 @@ func (s *Service) Close() {
s.connectors = make(map[string]*Connector) s.connectors = make(map[string]*Connector)
} }
func (s *Service) HandleRefreshEvent(ctx context.Context, _ proton.RefreshFlag) error {
s.log.Debug("handling refresh event")
if err := s.identityState.Write(func(identity *useridentity.State) error {
return identity.OnRefreshEvent(ctx)
}); err != nil {
s.log.WithError(err).Error("Failed to apply refresh event to identity state")
return err
}
s.syncHandler.CancelAndWait()
if err := s.removeConnectorsFromServer(ctx, s.connectors, true); err != nil {
return err
}
if err := s.syncStateProvider.ClearSyncStatus(); err != nil {
return fmt.Errorf("failed to clear sync status:%w", err)
}
if err := s.addConnectorsToServer(ctx, s.connectors); err != nil {
return err
}
s.syncHandler.launch(s)
return nil
}
func (s *Service) HandleUserEvent(_ context.Context, user *proton.User) error {
s.log.Debug("handling user event")
return s.identityState.Write(func(identity *useridentity.State) error {
identity.OnUserEvent(*user)
return nil
})
}
func (s *Service) run(ctx context.Context) { //nolint gocyclo func (s *Service) run(ctx context.Context) { //nolint gocyclo
s.log.Info("Starting IMAP Service") s.log.Info("Starting IMAP Service")
defer s.log.Info("Exiting IMAP Service") defer s.log.Info("Exiting IMAP Service")
@ -248,21 +279,21 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo
defer s.cpc.Close() defer s.cpc.Close()
defer s.eventSubscription.Remove(s.eventWatcher) defer s.eventSubscription.Remove(s.eventWatcher)
syncHandler := newSyncHandler(ctx, s.panicHandler) s.syncHandler = newSyncHandler(ctx, s.panicHandler)
defer syncHandler.Close() defer s.syncHandler.Close()
syncHandler.launch(s) s.syncHandler.launch(s)
subscription := userevents.Subscription{ eventHandler := userevents.EventHandler{
User: s.userSubscriber, UserHandler: s,
Refresh: s.refreshSubscriber, AddressHandler: s,
Address: s.addressSubscriber, RefreshHandler: s,
Labels: s.labelSubscriber, LabelHandler: s,
Messages: s.messageSubscriber, MessageHandler: s,
} }
s.eventProvider.Subscribe(subscription) s.eventProvider.Subscribe(s.subscription)
defer s.eventProvider.Unsubscribe(subscription) defer s.eventProvider.Unsubscribe(s.subscription)
for { for {
select { select {
@ -275,25 +306,25 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo
} }
switch r := req.Value().(type) { switch r := req.Value().(type) {
case *setAddressModeReq: case *setAddressModeReq:
err := s.setAddressMode(ctx, syncHandler, r.mode) err := s.setAddressMode(ctx, s.syncHandler, r.mode)
req.Reply(ctx, nil, err) req.Reply(ctx, nil, err)
case *resyncReq: case *resyncReq:
s.log.Info("Received resync request, handling as refresh event") s.log.Info("Received resync request, handling as refresh event")
err := s.onRefreshEvent(ctx, syncHandler) err := s.HandleRefreshEvent(ctx, 0)
req.Reply(ctx, nil, err) req.Reply(ctx, nil, err)
s.log.Info("Resync reply sent, handling as refresh event") s.log.Info("Resync reply sent, handling as refresh event")
case *cancelSyncReq: case *cancelSyncReq:
s.log.Info("Cancelling sync") s.log.Info("Cancelling sync")
syncHandler.Cancel() s.syncHandler.Cancel()
req.Reply(ctx, nil, nil) req.Reply(ctx, nil, nil)
case *resumeSyncReq: case *resumeSyncReq:
s.log.Info("Resuming sync") s.log.Info("Resuming sync")
// Cancel previous run, if any, just in case. // Cancel previous run, if any, just in case.
syncHandler.CancelAndWait() s.syncHandler.CancelAndWait()
syncHandler.launch(s) s.syncHandler.launch(s)
req.Reply(ctx, nil, nil) req.Reply(ctx, nil, nil)
case *getLabelsReq: case *getLabelsReq:
labels := s.labels.GetLabelMap() labels := s.labels.GetLabelMap()
@ -319,7 +350,7 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo
s.log.Error("Received unknown request") s.log.Error("Received unknown request")
} }
case err, ok := <-syncHandler.OnSyncFinishedCH(): case err, ok := <-s.syncHandler.OnSyncFinishedCH():
{ {
if !ok { if !ok {
continue continue
@ -334,46 +365,18 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo
s.eventProvider.Resume() s.eventProvider.Resume()
} }
case update, ok := <-syncHandler.updater.ch: case update, ok := <-s.syncHandler.updater.ch:
if !ok { if !ok {
continue continue
} }
s.onSyncUpdate(ctx, update) s.onSyncUpdate(ctx, update)
case e, ok := <-s.userSubscriber.OnEventCh(): case e, ok := <-s.subscription.OnEventCh():
if !ok { if !ok {
continue continue
} }
e.Consume(func(user proton.User) error { e.Consume(func(event proton.Event) error {
return s.onUserEvent(user) return eventHandler.OnEvent(ctx, event)
})
case e, ok := <-s.addressSubscriber.OnEventCh():
if !ok {
continue
}
e.Consume(func(events []proton.AddressEvent) error {
return s.onAddressEvent(ctx, events)
})
case e, ok := <-s.labelSubscriber.OnEventCh():
if !ok {
continue
}
e.Consume(func(events []proton.LabelEvent) error {
return s.onLabelEvent(ctx, events)
})
case e, ok := <-s.messageSubscriber.OnEventCh():
if !ok {
continue
}
e.Consume(func(events []proton.MessageEvent) error {
return s.onMessageEvent(ctx, events)
})
case e, ok := <-s.refreshSubscriber.OnEventCh():
if !ok {
continue
}
e.Consume(func(_ proton.RefreshFlag) error {
return s.onRefreshEvent(ctx, syncHandler)
}) })
case e, ok := <-s.eventWatcher.GetChannel(): case e, ok := <-s.eventWatcher.GetChannel():
if !ok { if !ok {
@ -389,43 +392,6 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo
} }
} }
func (s *Service) onRefreshEvent(ctx context.Context, handler *syncHandler) error {
s.log.Debug("handling refresh event")
if err := s.identityState.Write(func(identity *useridentity.State) error {
return identity.OnRefreshEvent(ctx)
}); err != nil {
s.log.WithError(err).Error("Failed to apply refresh event to identity state")
return err
}
handler.CancelAndWait()
if err := s.removeConnectorsFromServer(ctx, s.connectors, true); err != nil {
return err
}
if err := s.syncStateProvider.ClearSyncStatus(); err != nil {
return fmt.Errorf("failed to clear sync status:%w", err)
}
if err := s.addConnectorsToServer(ctx, s.connectors); err != nil {
return err
}
handler.launch(s)
return nil
}
func (s *Service) onUserEvent(user proton.User) error {
s.log.Debug("handling user event")
return s.identityState.Write(func(identity *useridentity.State) error {
identity.OnUserEvent(user)
return nil
})
}
func (s *Service) buildConnectors() (map[string]*Connector, error) { func (s *Service) buildConnectors() (map[string]*Connector, error) {
connectors := make(map[string]*Connector) connectors := make(map[string]*Connector)

View File

@ -26,7 +26,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/usertypes" "github.com/ProtonMail/proton-bridge/v3/internal/usertypes"
) )
func (s *Service) onAddressEvent(ctx context.Context, events []proton.AddressEvent) error { func (s *Service) HandleAddressEvents(ctx context.Context, events []proton.AddressEvent) error {
s.log.Debug("handling address event") s.log.Debug("handling address event")
if s.addressMode == usertypes.AddressModeCombined { if s.addressMode == usertypes.AddressModeCombined {

View File

@ -32,7 +32,7 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
func (s *Service) onLabelEvent(ctx context.Context, events []proton.LabelEvent) error { func (s *Service) HandleLabelEvents(ctx context.Context, events []proton.LabelEvent) error {
s.log.Debug("handling label event") s.log.Debug("handling label event")
for _, event := range events { for _, event := range events {

View File

@ -36,7 +36,7 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
func (s *Service) onMessageEvent(ctx context.Context, events []proton.MessageEvent) error { func (s *Service) HandleMessageEvents(ctx context.Context, events []proton.MessageEvent) error {
s.log.Debug("handling message event") s.log.Debug("handling message event")
for _, event := range events { for _, event := range events {

View File

@ -57,10 +57,8 @@ type Service struct {
identityState *useridentity.State identityState *useridentity.State
telemetry Telemetry telemetry Telemetry
eventService userevents.Subscribable eventService userevents.Subscribable
refreshSubscriber *userevents.RefreshChanneledSubscriber subscription *userevents.EventChanneledSubscriber
addressSubscriber *userevents.AddressChanneledSubscriber
userSubscriber *userevents.UserChanneledSubscriber
addressMode usertypes.AddressMode addressMode usertypes.AddressMode
serverManager ServerManager serverManager ServerManager
@ -100,9 +98,7 @@ func NewService(
identityState: identityState, identityState: identityState,
eventService: eventService, eventService: eventService,
refreshSubscriber: userevents.NewRefreshSubscriber(subscriberName), subscription: userevents.NewEventSubscriber(subscriberName),
userSubscriber: userevents.NewUserSubscriber(subscriberName),
addressSubscriber: userevents.NewAddressSubscriber(subscriberName),
addressMode: mode, addressMode: mode,
serverManager: serverManager, serverManager: serverManager,
@ -168,19 +164,38 @@ func (s *Service) UserID() string {
return s.userID return s.userID
} }
func (s *Service) HandleRefreshEvent(ctx context.Context, _ proton.RefreshFlag) error {
s.log.Debug("Handling refresh event")
return s.identityState.OnRefreshEvent(ctx)
}
func (s *Service) HandleAddressEvents(_ context.Context, events []proton.AddressEvent) error {
s.log.Debug("Handling Address Event")
s.identityState.OnAddressEvents(events)
return nil
}
func (s *Service) HandleUserEvent(_ context.Context, user *proton.User) error {
s.log.Debug("Handling user event")
s.identityState.OnUserEvent(*user)
return nil
}
func (s *Service) run(ctx context.Context) { func (s *Service) run(ctx context.Context) {
s.log.Info("Starting service main loop") s.log.Info("Starting service main loop")
defer s.log.Info("Exiting service main loop") defer s.log.Info("Exiting service main loop")
defer s.cpc.Close() defer s.cpc.Close()
subscription := userevents.Subscription{ eventHandler := userevents.EventHandler{
User: s.userSubscriber, AddressHandler: s,
Refresh: s.refreshSubscriber, RefreshHandler: s,
Address: s.addressSubscriber, UserHandler: s,
} }
s.eventService.Subscribe(subscription) s.eventService.Subscribe(s.subscription)
defer s.eventService.Unsubscribe(subscription) defer s.eventService.Unsubscribe(s.subscription)
for { for {
select { select {
@ -219,34 +234,12 @@ func (s *Service) run(ctx context.Context) {
default: default:
s.log.Error("Received unknown request") s.log.Error("Received unknown request")
} }
case e, ok := <-s.userSubscriber.OnEventCh(): case e, ok := <-s.subscription.OnEventCh():
if !ok { if !ok {
continue continue
} }
e.Consume(func(event proton.Event) error {
s.log.Debug("Handling user event") return eventHandler.OnEvent(ctx, event)
e.Consume(func(user proton.User) error {
s.identityState.OnUserEvent(user)
return nil
})
case e, ok := <-s.refreshSubscriber.OnEventCh():
if !ok {
continue
}
s.log.Debug("Handling refresh event")
e.Consume(func(_ proton.RefreshFlag) error {
return s.identityState.OnRefreshEvent(ctx)
})
case e, ok := <-s.addressSubscriber.OnEventCh():
if !ok {
continue
}
s.log.Debug("Handling Address Event")
e.Consume(func(evt []proton.AddressEvent) error {
s.identityState.OnAddressEvents(evt)
return nil
}) })
} }
} }

View File

@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ProtonMail/proton-bridge/v3/internal/services/userevents (interfaces: MessageSubscriber,LabelSubscriber,AddressSubscriber,RefreshSubscriber,UserSubscriber,UserUsedSpaceSubscriber) // Source: github.com/ProtonMail/proton-bridge/v3/internal/services/userevents (interfaces: EventSubscriber,MessageEventHandler,LabelEventHandler,AddressEventHandler,RefreshEventHandler,UserEventHandler,UserUsedSpaceEventHandler)
// Package userevents is a generated GoMock package. // Package userevents is a generated GoMock package.
package userevents package userevents
@ -12,55 +12,55 @@ import (
gomock "github.com/golang/mock/gomock" gomock "github.com/golang/mock/gomock"
) )
// MockMessageSubscriber is a mock of MessageSubscriber interface. // MockEventSubscriber is a mock of EventSubscriber interface.
type MockMessageSubscriber struct { type MockEventSubscriber struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockMessageSubscriberMockRecorder recorder *MockEventSubscriberMockRecorder
} }
// MockMessageSubscriberMockRecorder is the mock recorder for MockMessageSubscriber. // MockEventSubscriberMockRecorder is the mock recorder for MockEventSubscriber.
type MockMessageSubscriberMockRecorder struct { type MockEventSubscriberMockRecorder struct {
mock *MockMessageSubscriber mock *MockEventSubscriber
} }
// NewMockMessageSubscriber creates a new mock instance. // NewMockEventSubscriber creates a new mock instance.
func NewMockMessageSubscriber(ctrl *gomock.Controller) *MockMessageSubscriber { func NewMockEventSubscriber(ctrl *gomock.Controller) *MockEventSubscriber {
mock := &MockMessageSubscriber{ctrl: ctrl} mock := &MockEventSubscriber{ctrl: ctrl}
mock.recorder = &MockMessageSubscriberMockRecorder{mock} mock.recorder = &MockEventSubscriberMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use. // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockMessageSubscriber) EXPECT() *MockMessageSubscriberMockRecorder { func (m *MockEventSubscriber) EXPECT() *MockEventSubscriberMockRecorder {
return m.recorder return m.recorder
} }
// cancel mocks base method. // cancel mocks base method.
func (m *MockMessageSubscriber) cancel() { func (m *MockEventSubscriber) cancel() {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel") m.ctrl.Call(m, "cancel")
} }
// cancel indicates an expected call of cancel. // cancel indicates an expected call of cancel.
func (mr *MockMessageSubscriberMockRecorder) cancel() *gomock.Call { func (mr *MockEventSubscriberMockRecorder) cancel() *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "cancel", reflect.TypeOf((*MockMessageSubscriber)(nil).cancel)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "cancel", reflect.TypeOf((*MockEventSubscriber)(nil).cancel))
} }
// close mocks base method. // close mocks base method.
func (m *MockMessageSubscriber) close() { func (m *MockEventSubscriber) close() {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "close") m.ctrl.Call(m, "close")
} }
// close indicates an expected call of close. // close indicates an expected call of close.
func (mr *MockMessageSubscriberMockRecorder) close() *gomock.Call { func (mr *MockEventSubscriberMockRecorder) close() *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "close", reflect.TypeOf((*MockMessageSubscriber)(nil).close)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "close", reflect.TypeOf((*MockEventSubscriber)(nil).close))
} }
// handle mocks base method. // handle mocks base method.
func (m *MockMessageSubscriber) handle(arg0 context.Context, arg1 []proton.MessageEvent) error { func (m *MockEventSubscriber) handle(arg0 context.Context, arg1 proton.Event) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "handle", arg0, arg1) ret := m.ctrl.Call(m, "handle", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
@ -68,13 +68,13 @@ func (m *MockMessageSubscriber) handle(arg0 context.Context, arg1 []proton.Messa
} }
// handle indicates an expected call of handle. // handle indicates an expected call of handle.
func (mr *MockMessageSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call { func (mr *MockEventSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handle", reflect.TypeOf((*MockMessageSubscriber)(nil).handle), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handle", reflect.TypeOf((*MockEventSubscriber)(nil).handle), arg0, arg1)
} }
// name mocks base method. // name mocks base method.
func (m *MockMessageSubscriber) name() string { func (m *MockEventSubscriber) name() string {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "name") ret := m.ctrl.Call(m, "name")
ret0, _ := ret[0].(string) ret0, _ := ret[0].(string)
@ -82,382 +82,229 @@ func (m *MockMessageSubscriber) name() string {
} }
// name indicates an expected call of name. // name indicates an expected call of name.
func (mr *MockMessageSubscriberMockRecorder) name() *gomock.Call { func (mr *MockEventSubscriberMockRecorder) name() *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "name", reflect.TypeOf((*MockMessageSubscriber)(nil).name)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "name", reflect.TypeOf((*MockEventSubscriber)(nil).name))
} }
// MockLabelSubscriber is a mock of LabelSubscriber interface. // MockMessageEventHandler is a mock of MessageEventHandler interface.
type MockLabelSubscriber struct { type MockMessageEventHandler struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLabelSubscriberMockRecorder recorder *MockMessageEventHandlerMockRecorder
} }
// MockLabelSubscriberMockRecorder is the mock recorder for MockLabelSubscriber. // MockMessageEventHandlerMockRecorder is the mock recorder for MockMessageEventHandler.
type MockLabelSubscriberMockRecorder struct { type MockMessageEventHandlerMockRecorder struct {
mock *MockLabelSubscriber mock *MockMessageEventHandler
} }
// NewMockLabelSubscriber creates a new mock instance. // NewMockMessageEventHandler creates a new mock instance.
func NewMockLabelSubscriber(ctrl *gomock.Controller) *MockLabelSubscriber { func NewMockMessageEventHandler(ctrl *gomock.Controller) *MockMessageEventHandler {
mock := &MockLabelSubscriber{ctrl: ctrl} mock := &MockMessageEventHandler{ctrl: ctrl}
mock.recorder = &MockLabelSubscriberMockRecorder{mock} mock.recorder = &MockMessageEventHandlerMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use. // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockLabelSubscriber) EXPECT() *MockLabelSubscriberMockRecorder { func (m *MockMessageEventHandler) EXPECT() *MockMessageEventHandlerMockRecorder {
return m.recorder return m.recorder
} }
// cancel mocks base method. // HandleMessageEvents mocks base method.
func (m *MockLabelSubscriber) cancel() { func (m *MockMessageEventHandler) HandleMessageEvents(arg0 context.Context, arg1 []proton.MessageEvent) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel") ret := m.ctrl.Call(m, "HandleMessageEvents", arg0, arg1)
}
// cancel indicates an expected call of cancel.
func (mr *MockLabelSubscriberMockRecorder) cancel() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "cancel", reflect.TypeOf((*MockLabelSubscriber)(nil).cancel))
}
// close mocks base method.
func (m *MockLabelSubscriber) close() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "close")
}
// close indicates an expected call of close.
func (mr *MockLabelSubscriberMockRecorder) close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "close", reflect.TypeOf((*MockLabelSubscriber)(nil).close))
}
// handle mocks base method.
func (m *MockLabelSubscriber) handle(arg0 context.Context, arg1 []proton.LabelEvent) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "handle", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// handle indicates an expected call of handle. // HandleMessageEvents indicates an expected call of HandleMessageEvents.
func (mr *MockLabelSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call { func (mr *MockMessageEventHandlerMockRecorder) HandleMessageEvents(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handle", reflect.TypeOf((*MockLabelSubscriber)(nil).handle), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessageEvents", reflect.TypeOf((*MockMessageEventHandler)(nil).HandleMessageEvents), arg0, arg1)
} }
// name mocks base method. // MockLabelEventHandler is a mock of LabelEventHandler interface.
func (m *MockLabelSubscriber) name() string { type MockLabelEventHandler struct {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "name")
ret0, _ := ret[0].(string)
return ret0
}
// name indicates an expected call of name.
func (mr *MockLabelSubscriberMockRecorder) name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "name", reflect.TypeOf((*MockLabelSubscriber)(nil).name))
}
// MockAddressSubscriber is a mock of AddressSubscriber interface.
type MockAddressSubscriber struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockAddressSubscriberMockRecorder recorder *MockLabelEventHandlerMockRecorder
} }
// MockAddressSubscriberMockRecorder is the mock recorder for MockAddressSubscriber. // MockLabelEventHandlerMockRecorder is the mock recorder for MockLabelEventHandler.
type MockAddressSubscriberMockRecorder struct { type MockLabelEventHandlerMockRecorder struct {
mock *MockAddressSubscriber mock *MockLabelEventHandler
} }
// NewMockAddressSubscriber creates a new mock instance. // NewMockLabelEventHandler creates a new mock instance.
func NewMockAddressSubscriber(ctrl *gomock.Controller) *MockAddressSubscriber { func NewMockLabelEventHandler(ctrl *gomock.Controller) *MockLabelEventHandler {
mock := &MockAddressSubscriber{ctrl: ctrl} mock := &MockLabelEventHandler{ctrl: ctrl}
mock.recorder = &MockAddressSubscriberMockRecorder{mock} mock.recorder = &MockLabelEventHandlerMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use. // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockAddressSubscriber) EXPECT() *MockAddressSubscriberMockRecorder { func (m *MockLabelEventHandler) EXPECT() *MockLabelEventHandlerMockRecorder {
return m.recorder return m.recorder
} }
// cancel mocks base method. // HandleLabelEvents mocks base method.
func (m *MockAddressSubscriber) cancel() { func (m *MockLabelEventHandler) HandleLabelEvents(arg0 context.Context, arg1 []proton.LabelEvent) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel") ret := m.ctrl.Call(m, "HandleLabelEvents", arg0, arg1)
}
// cancel indicates an expected call of cancel.
func (mr *MockAddressSubscriberMockRecorder) cancel() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "cancel", reflect.TypeOf((*MockAddressSubscriber)(nil).cancel))
}
// close mocks base method.
func (m *MockAddressSubscriber) close() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "close")
}
// close indicates an expected call of close.
func (mr *MockAddressSubscriberMockRecorder) close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "close", reflect.TypeOf((*MockAddressSubscriber)(nil).close))
}
// handle mocks base method.
func (m *MockAddressSubscriber) handle(arg0 context.Context, arg1 []proton.AddressEvent) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "handle", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// handle indicates an expected call of handle. // HandleLabelEvents indicates an expected call of HandleLabelEvents.
func (mr *MockAddressSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call { func (mr *MockLabelEventHandlerMockRecorder) HandleLabelEvents(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handle", reflect.TypeOf((*MockAddressSubscriber)(nil).handle), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleLabelEvents", reflect.TypeOf((*MockLabelEventHandler)(nil).HandleLabelEvents), arg0, arg1)
} }
// name mocks base method. // MockAddressEventHandler is a mock of AddressEventHandler interface.
func (m *MockAddressSubscriber) name() string { type MockAddressEventHandler struct {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "name")
ret0, _ := ret[0].(string)
return ret0
}
// name indicates an expected call of name.
func (mr *MockAddressSubscriberMockRecorder) name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "name", reflect.TypeOf((*MockAddressSubscriber)(nil).name))
}
// MockRefreshSubscriber is a mock of RefreshSubscriber interface.
type MockRefreshSubscriber struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockRefreshSubscriberMockRecorder recorder *MockAddressEventHandlerMockRecorder
} }
// MockRefreshSubscriberMockRecorder is the mock recorder for MockRefreshSubscriber. // MockAddressEventHandlerMockRecorder is the mock recorder for MockAddressEventHandler.
type MockRefreshSubscriberMockRecorder struct { type MockAddressEventHandlerMockRecorder struct {
mock *MockRefreshSubscriber mock *MockAddressEventHandler
} }
// NewMockRefreshSubscriber creates a new mock instance. // NewMockAddressEventHandler creates a new mock instance.
func NewMockRefreshSubscriber(ctrl *gomock.Controller) *MockRefreshSubscriber { func NewMockAddressEventHandler(ctrl *gomock.Controller) *MockAddressEventHandler {
mock := &MockRefreshSubscriber{ctrl: ctrl} mock := &MockAddressEventHandler{ctrl: ctrl}
mock.recorder = &MockRefreshSubscriberMockRecorder{mock} mock.recorder = &MockAddressEventHandlerMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use. // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockRefreshSubscriber) EXPECT() *MockRefreshSubscriberMockRecorder { func (m *MockAddressEventHandler) EXPECT() *MockAddressEventHandlerMockRecorder {
return m.recorder return m.recorder
} }
// cancel mocks base method. // HandleAddressEvents mocks base method.
func (m *MockRefreshSubscriber) cancel() { func (m *MockAddressEventHandler) HandleAddressEvents(arg0 context.Context, arg1 []proton.AddressEvent) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel") ret := m.ctrl.Call(m, "HandleAddressEvents", arg0, arg1)
}
// cancel indicates an expected call of cancel.
func (mr *MockRefreshSubscriberMockRecorder) cancel() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "cancel", reflect.TypeOf((*MockRefreshSubscriber)(nil).cancel))
}
// close mocks base method.
func (m *MockRefreshSubscriber) close() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "close")
}
// close indicates an expected call of close.
func (mr *MockRefreshSubscriberMockRecorder) close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "close", reflect.TypeOf((*MockRefreshSubscriber)(nil).close))
}
// handle mocks base method.
func (m *MockRefreshSubscriber) handle(arg0 context.Context, arg1 proton.RefreshFlag) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "handle", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// handle indicates an expected call of handle. // HandleAddressEvents indicates an expected call of HandleAddressEvents.
func (mr *MockRefreshSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call { func (mr *MockAddressEventHandlerMockRecorder) HandleAddressEvents(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handle", reflect.TypeOf((*MockRefreshSubscriber)(nil).handle), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleAddressEvents", reflect.TypeOf((*MockAddressEventHandler)(nil).HandleAddressEvents), arg0, arg1)
} }
// name mocks base method. // MockRefreshEventHandler is a mock of RefreshEventHandler interface.
func (m *MockRefreshSubscriber) name() string { type MockRefreshEventHandler struct {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "name")
ret0, _ := ret[0].(string)
return ret0
}
// name indicates an expected call of name.
func (mr *MockRefreshSubscriberMockRecorder) name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "name", reflect.TypeOf((*MockRefreshSubscriber)(nil).name))
}
// MockUserSubscriber is a mock of UserSubscriber interface.
type MockUserSubscriber struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockUserSubscriberMockRecorder recorder *MockRefreshEventHandlerMockRecorder
} }
// MockUserSubscriberMockRecorder is the mock recorder for MockUserSubscriber. // MockRefreshEventHandlerMockRecorder is the mock recorder for MockRefreshEventHandler.
type MockUserSubscriberMockRecorder struct { type MockRefreshEventHandlerMockRecorder struct {
mock *MockUserSubscriber mock *MockRefreshEventHandler
} }
// NewMockUserSubscriber creates a new mock instance. // NewMockRefreshEventHandler creates a new mock instance.
func NewMockUserSubscriber(ctrl *gomock.Controller) *MockUserSubscriber { func NewMockRefreshEventHandler(ctrl *gomock.Controller) *MockRefreshEventHandler {
mock := &MockUserSubscriber{ctrl: ctrl} mock := &MockRefreshEventHandler{ctrl: ctrl}
mock.recorder = &MockUserSubscriberMockRecorder{mock} mock.recorder = &MockRefreshEventHandlerMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use. // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockUserSubscriber) EXPECT() *MockUserSubscriberMockRecorder { func (m *MockRefreshEventHandler) EXPECT() *MockRefreshEventHandlerMockRecorder {
return m.recorder return m.recorder
} }
// cancel mocks base method. // HandleRefreshEvent mocks base method.
func (m *MockUserSubscriber) cancel() { func (m *MockRefreshEventHandler) HandleRefreshEvent(arg0 context.Context, arg1 proton.RefreshFlag) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel") ret := m.ctrl.Call(m, "HandleRefreshEvent", arg0, arg1)
}
// cancel indicates an expected call of cancel.
func (mr *MockUserSubscriberMockRecorder) cancel() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "cancel", reflect.TypeOf((*MockUserSubscriber)(nil).cancel))
}
// close mocks base method.
func (m *MockUserSubscriber) close() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "close")
}
// close indicates an expected call of close.
func (mr *MockUserSubscriberMockRecorder) close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "close", reflect.TypeOf((*MockUserSubscriber)(nil).close))
}
// handle mocks base method.
func (m *MockUserSubscriber) handle(arg0 context.Context, arg1 proton.User) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "handle", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// handle indicates an expected call of handle. // HandleRefreshEvent indicates an expected call of HandleRefreshEvent.
func (mr *MockUserSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call { func (mr *MockRefreshEventHandlerMockRecorder) HandleRefreshEvent(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handle", reflect.TypeOf((*MockUserSubscriber)(nil).handle), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleRefreshEvent", reflect.TypeOf((*MockRefreshEventHandler)(nil).HandleRefreshEvent), arg0, arg1)
} }
// name mocks base method. // MockUserEventHandler is a mock of UserEventHandler interface.
func (m *MockUserSubscriber) name() string { type MockUserEventHandler struct {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "name")
ret0, _ := ret[0].(string)
return ret0
}
// name indicates an expected call of name.
func (mr *MockUserSubscriberMockRecorder) name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "name", reflect.TypeOf((*MockUserSubscriber)(nil).name))
}
// MockUserUsedSpaceSubscriber is a mock of UserUsedSpaceSubscriber interface.
type MockUserUsedSpaceSubscriber struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockUserUsedSpaceSubscriberMockRecorder recorder *MockUserEventHandlerMockRecorder
} }
// MockUserUsedSpaceSubscriberMockRecorder is the mock recorder for MockUserUsedSpaceSubscriber. // MockUserEventHandlerMockRecorder is the mock recorder for MockUserEventHandler.
type MockUserUsedSpaceSubscriberMockRecorder struct { type MockUserEventHandlerMockRecorder struct {
mock *MockUserUsedSpaceSubscriber mock *MockUserEventHandler
} }
// NewMockUserUsedSpaceSubscriber creates a new mock instance. // NewMockUserEventHandler creates a new mock instance.
func NewMockUserUsedSpaceSubscriber(ctrl *gomock.Controller) *MockUserUsedSpaceSubscriber { func NewMockUserEventHandler(ctrl *gomock.Controller) *MockUserEventHandler {
mock := &MockUserUsedSpaceSubscriber{ctrl: ctrl} mock := &MockUserEventHandler{ctrl: ctrl}
mock.recorder = &MockUserUsedSpaceSubscriberMockRecorder{mock} mock.recorder = &MockUserEventHandlerMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use. // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockUserUsedSpaceSubscriber) EXPECT() *MockUserUsedSpaceSubscriberMockRecorder { func (m *MockUserEventHandler) EXPECT() *MockUserEventHandlerMockRecorder {
return m.recorder return m.recorder
} }
// cancel mocks base method. // HandleUserEvent mocks base method.
func (m *MockUserUsedSpaceSubscriber) cancel() { func (m *MockUserEventHandler) HandleUserEvent(arg0 context.Context, arg1 *proton.User) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel") ret := m.ctrl.Call(m, "HandleUserEvent", arg0, arg1)
}
// cancel indicates an expected call of cancel.
func (mr *MockUserUsedSpaceSubscriberMockRecorder) cancel() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "cancel", reflect.TypeOf((*MockUserUsedSpaceSubscriber)(nil).cancel))
}
// close mocks base method.
func (m *MockUserUsedSpaceSubscriber) close() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "close")
}
// close indicates an expected call of close.
func (mr *MockUserUsedSpaceSubscriberMockRecorder) close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "close", reflect.TypeOf((*MockUserUsedSpaceSubscriber)(nil).close))
}
// handle mocks base method.
func (m *MockUserUsedSpaceSubscriber) handle(arg0 context.Context, arg1 int) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "handle", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// handle indicates an expected call of handle. // HandleUserEvent indicates an expected call of HandleUserEvent.
func (mr *MockUserUsedSpaceSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call { func (mr *MockUserEventHandlerMockRecorder) HandleUserEvent(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handle", reflect.TypeOf((*MockUserUsedSpaceSubscriber)(nil).handle), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleUserEvent", reflect.TypeOf((*MockUserEventHandler)(nil).HandleUserEvent), arg0, arg1)
} }
// name mocks base method. // MockUserUsedSpaceEventHandler is a mock of UserUsedSpaceEventHandler interface.
func (m *MockUserUsedSpaceSubscriber) name() string { type MockUserUsedSpaceEventHandler struct {
ctrl *gomock.Controller
recorder *MockUserUsedSpaceEventHandlerMockRecorder
}
// MockUserUsedSpaceEventHandlerMockRecorder is the mock recorder for MockUserUsedSpaceEventHandler.
type MockUserUsedSpaceEventHandlerMockRecorder struct {
mock *MockUserUsedSpaceEventHandler
}
// NewMockUserUsedSpaceEventHandler creates a new mock instance.
func NewMockUserUsedSpaceEventHandler(ctrl *gomock.Controller) *MockUserUsedSpaceEventHandler {
mock := &MockUserUsedSpaceEventHandler{ctrl: ctrl}
mock.recorder = &MockUserUsedSpaceEventHandlerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockUserUsedSpaceEventHandler) EXPECT() *MockUserUsedSpaceEventHandlerMockRecorder {
return m.recorder
}
// HandleUsedSpaceEvent mocks base method.
func (m *MockUserUsedSpaceEventHandler) HandleUsedSpaceEvent(arg0 context.Context, arg1 int) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "name") ret := m.ctrl.Call(m, "HandleUsedSpaceEvent", arg0, arg1)
ret0, _ := ret[0].(string) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// name indicates an expected call of name. // HandleUsedSpaceEvent indicates an expected call of HandleUsedSpaceEvent.
func (mr *MockUserUsedSpaceSubscriberMockRecorder) name() *gomock.Call { func (mr *MockUserUsedSpaceEventHandlerMockRecorder) HandleUsedSpaceEvent(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "name", reflect.TypeOf((*MockUserUsedSpaceSubscriber)(nil).name)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleUsedSpaceEvent", reflect.TypeOf((*MockUserUsedSpaceEventHandler)(nil).HandleUsedSpaceEvent), arg0, arg1)
} }

View File

@ -57,12 +57,7 @@ type Service struct {
paused uint32 paused uint32
panicHandler async.PanicHandler panicHandler async.PanicHandler
userSubscriberList userSubscriberList subscriberList eventSubscriberList
addressSubscribers addressSubscriberList
labelSubscribers labelSubscriberList
messageSubscribers messageSubscriberList
refreshSubscribers refreshSubscriberList
userUsedSpaceSubscriber userUsedSpaceSubscriberList
pendingSubscriptionsLock sync.Mutex pendingSubscriptionsLock sync.Mutex
pendingSubscriptions []pendingSubscription pendingSubscriptions []pendingSubscription
@ -94,61 +89,9 @@ func NewService(
} }
} }
type Subscription struct {
User UserSubscriber
Refresh RefreshSubscriber
Address AddressSubscriber
Labels LabelSubscriber
Messages MessageSubscriber
UserUsedSpace UserUsedSpaceSubscriber
}
// cancel subscription subscribers if applicable, see `subscriber.cancel` for more information.
func (s Subscription) cancel() {
if s.User != nil {
s.User.cancel()
}
if s.Refresh != nil {
s.Refresh.cancel()
}
if s.Address != nil {
s.Address.cancel()
}
if s.Labels != nil {
s.Labels.cancel()
}
if s.Messages != nil {
s.Messages.cancel()
}
if s.UserUsedSpace != nil {
s.UserUsedSpace.cancel()
}
}
func (s Subscription) close() {
if s.User != nil {
s.User.close()
}
if s.Refresh != nil {
s.Refresh.close()
}
if s.Address != nil {
s.Address.close()
}
if s.Labels != nil {
s.Labels.close()
}
if s.Messages != nil {
s.Messages.close()
}
if s.UserUsedSpace != nil {
s.UserUsedSpace.close()
}
}
// Subscribe adds new subscribers to the service. // Subscribe adds new subscribers to the service.
// This method can safely be called during event handling. // This method can safely be called during event handling.
func (s *Service) Subscribe(subscription Subscription) { func (s *Service) Subscribe(subscription EventSubscriber) {
s.pendingSubscriptionsLock.Lock() s.pendingSubscriptionsLock.Lock()
defer s.pendingSubscriptionsLock.Unlock() defer s.pendingSubscriptionsLock.Unlock()
@ -157,7 +100,7 @@ func (s *Service) Subscribe(subscription Subscription) {
// Unsubscribe removes subscribers from the service. // Unsubscribe removes subscribers from the service.
// This method can safely be called during event handling. // This method can safely be called during event handling.
func (s *Service) Unsubscribe(subscription Subscription) { func (s *Service) Unsubscribe(subscription EventSubscriber) {
subscription.cancel() subscription.cancel()
s.pendingSubscriptionsLock.Lock() s.pendingSubscriptionsLock.Lock()
@ -288,7 +231,7 @@ func (s *Service) Close() {
s.pendingSubscriptionsLock.Lock() s.pendingSubscriptionsLock.Lock()
defer s.pendingSubscriptionsLock.Unlock() defer s.pendingSubscriptionsLock.Unlock()
processed := xmaps.Set[Subscription]{} processed := xmaps.Set[EventSubscriber]{}
// Cleanup pending removes. // Cleanup pending removes.
for _, s := range s.pendingSubscriptions { for _, s := range s.pendingSubscriptions {
@ -313,70 +256,20 @@ func (s *Service) handleEvent(ctx context.Context, lastEventID string, event pro
}).Info("Received new API event") }).Info("Received new API event")
if event.Refresh&proton.RefreshMail != 0 { if event.Refresh&proton.RefreshMail != 0 {
s.log.Info("Handling refresh event") s.log.Info("Received refresh event")
if err := s.refreshSubscribers.Publish(ctx, event.Refresh, s.eventTimeout); err != nil {
return fmt.Errorf("failed to apply refresh event: %w", err)
}
return nil
} }
// Start with user events. return s.subscriberList.PublishParallel(ctx, event, s.panicHandler, s.eventTimeout)
if event.User != nil {
if err := s.userSubscriberList.PublishParallel(ctx, *event.User, s.panicHandler, s.eventTimeout); err != nil {
return fmt.Errorf("failed to apply user event: %w", err)
}
}
// Next Address events
if err := s.addressSubscribers.PublishParallel(ctx, event.Addresses, s.panicHandler, s.eventTimeout); err != nil {
return fmt.Errorf("failed to apply address events: %w", err)
}
// Next label events
if err := s.labelSubscribers.PublishParallel(ctx, event.Labels, s.panicHandler, s.eventTimeout); err != nil {
return fmt.Errorf("failed to apply label events: %w", err)
}
// Next message events
if err := s.messageSubscribers.PublishParallel(ctx, event.Messages, s.panicHandler, s.eventTimeout); err != nil {
return fmt.Errorf("failed to apply message events: %w", err)
}
// Finally user used space events
if event.UsedSpace != nil {
if err := s.userUsedSpaceSubscriber.PublishParallel(ctx, *event.UsedSpace, s.panicHandler, s.eventTimeout); err != nil {
return fmt.Errorf("failed to apply message events: %w", err)
}
}
return nil
} }
func unpackPublisherError(err error) (string, error) { func unpackPublisherError(err error) (string, error) {
var addressErr *addressPublishError var publishErr *eventPublishError
var labelErr *labelPublishError
var messageErr *messagePublishError
var refreshErr *refreshPublishError
var userErr *userPublishError
var usedSpaceErr *userUsedEventPublishError
switch { if errors.As(err, &publishErr) {
case errors.As(err, &userErr): return publishErr.subscriber.name(), publishErr.error
return userErr.subscriber.name(), userErr.error
case errors.As(err, &addressErr):
return addressErr.subscriber.name(), addressErr.error
case errors.As(err, &labelErr):
return labelErr.subscriber.name(), labelErr.error
case errors.As(err, &messageErr):
return messageErr.subscriber.name(), messageErr.error
case errors.As(err, &refreshErr):
return refreshErr.subscriber.name(), refreshErr.error
case errors.As(err, &usedSpaceErr):
return usedSpaceErr.subscriber.name(), usedSpaceErr.error
default:
return "", err
} }
return "", err
} }
func (s *Service) handleEventError(ctx context.Context, lastEventID string, event proton.Event, err error) (string, error) { func (s *Service) handleEventError(ctx context.Context, lastEventID string, event proton.Event, err error) (string, error) {
@ -437,56 +330,12 @@ func (s *Service) onBadEvent(ctx context.Context, event events.UserBadEvent) {
s.eventPublisher.PublishEvent(ctx, event) s.eventPublisher.PublishEvent(ctx, event)
} }
func (s *Service) addSubscription(subscription Subscription) { func (s *Service) addSubscription(subscription EventSubscriber) {
if subscription.User != nil { s.subscriberList.Add(subscription)
s.userSubscriberList.Add(subscription.User)
}
if subscription.Refresh != nil {
s.refreshSubscribers.Add(subscription.Refresh)
}
if subscription.Address != nil {
s.addressSubscribers.Add(subscription.Address)
}
if subscription.Labels != nil {
s.labelSubscribers.Add(subscription.Labels)
}
if subscription.Messages != nil {
s.messageSubscribers.Add(subscription.Messages)
}
if subscription.UserUsedSpace != nil {
s.userUsedSpaceSubscriber.Add(subscription.UserUsedSpace)
}
} }
func (s *Service) removeSubscription(subscription Subscription) { func (s *Service) removeSubscription(subscription EventSubscriber) {
if subscription.User != nil { s.subscriberList.Remove(subscription)
s.userSubscriberList.Remove(subscription.User)
}
if subscription.Refresh != nil {
s.refreshSubscribers.Remove(subscription.Refresh)
}
if subscription.Address != nil {
s.addressSubscribers.Remove(subscription.Address)
}
if subscription.Labels != nil {
s.labelSubscribers.Remove(subscription.Labels)
}
if subscription.Messages != nil {
s.messageSubscribers.Remove(subscription.Messages)
}
if subscription.UserUsedSpace != nil {
s.userUsedSpaceSubscriber.Remove(subscription.UserUsedSpace)
}
} }
type pendingOp int type pendingOp int
@ -498,5 +347,5 @@ const (
type pendingSubscription struct { type pendingSubscription struct {
op pendingOp op pendingOp
sub Subscription sub EventSubscriber
} }

View File

@ -52,9 +52,9 @@ func TestServiceHandleEventError_SubscriberEventUnwrapping(t *testing.T) {
lastEventID := "PrevEvent" lastEventID := "PrevEvent"
event := proton.Event{EventID: "MyEvent"} event := proton.Event{EventID: "MyEvent"}
subscriber := &noOpSubscriber[proton.AddressEvent]{} subscriber := &noOpSubscriber[proton.Event]{}
err := &addressPublishError{ err := &eventPublishError{
subscriber: subscriber, subscriber: subscriber,
error: &proton.NetError{}, error: &proton.NetError{},
} }
@ -192,7 +192,7 @@ func (n noOpSubscriber[T]) name() string { //nolint:unused
return "NoopSubscriber" return "NoopSubscriber"
} }
func (n noOpSubscriber[T]) handle(_ context.Context, _ []T) error { //nolint:unused func (n noOpSubscriber[T]) handle(_ context.Context, _ T) error { //nolint:unused
return nil return nil
} }

View File

@ -37,26 +37,26 @@ func TestServiceHandleEvent_CheckEventCategoriesHandledInOrder(t *testing.T) {
eventPublisher := mocks.NewMockEventPublisher(mockCtrl) eventPublisher := mocks.NewMockEventPublisher(mockCtrl)
eventIDStore := NewInMemoryEventIDStore() eventIDStore := NewInMemoryEventIDStore()
refreshHandler := NewMockRefreshSubscriber(mockCtrl) refreshHandler := NewMockRefreshEventHandler(mockCtrl)
refreshHandler.EXPECT().handle(gomock.Any(), gomock.Any()).Times(2).Return(nil) refreshHandler.EXPECT().HandleRefreshEvent(gomock.Any(), gomock.Any()).Times(2).Return(nil)
userHandler := NewMockUserSubscriber(mockCtrl) userHandler := NewMockUserEventHandler(mockCtrl)
userCall := userHandler.EXPECT().handle(gomock.Any(), gomock.Any()).Times(1).Return(nil) userCall := userHandler.EXPECT().HandleUserEvent(gomock.Any(), gomock.Any()).Times(1).Return(nil)
addressHandler := NewMockAddressSubscriber(mockCtrl) addressHandler := NewMockAddressEventHandler(mockCtrl)
addressCall := addressHandler.EXPECT().handle(gomock.Any(), gomock.Any()).After(userCall).Times(1).Return(nil) addressCall := addressHandler.EXPECT().HandleAddressEvents(gomock.Any(), gomock.Any()).After(userCall).Times(1).Return(nil)
labelHandler := NewMockLabelSubscriber(mockCtrl) labelHandler := NewMockLabelEventHandler(mockCtrl)
labelCall := labelHandler.EXPECT().handle(gomock.Any(), gomock.Any()).After(addressCall).Times(1).Return(nil) labelCall := labelHandler.EXPECT().HandleLabelEvents(gomock.Any(), gomock.Any()).After(addressCall).Times(1).Return(nil)
messageHandler := NewMockMessageSubscriber(mockCtrl) messageHandler := NewMockMessageEventHandler(mockCtrl)
messageCall := messageHandler.EXPECT().handle(gomock.Any(), gomock.Any()).After(labelCall).Times(1).Return(nil) messageCall := messageHandler.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Any()).After(labelCall).Times(1).Return(nil)
userSpaceHandler := NewMockUserUsedSpaceSubscriber(mockCtrl) userSpaceHandler := NewMockUserUsedSpaceEventHandler(mockCtrl)
userSpaceCall := userSpaceHandler.EXPECT().handle(gomock.Any(), gomock.Any()).After(messageCall).Times(1).Return(nil) userSpaceCall := userSpaceHandler.EXPECT().HandleUsedSpaceEvent(gomock.Any(), gomock.Any()).After(messageCall).Times(1).Return(nil)
secondRefreshHandler := NewMockRefreshSubscriber(mockCtrl) secondRefreshHandler := NewMockRefreshEventHandler(mockCtrl)
secondRefreshHandler.EXPECT().handle(gomock.Any(), gomock.Any()).After(userSpaceCall).Times(1).Return(nil) secondRefreshHandler.EXPECT().HandleRefreshEvent(gomock.Any(), gomock.Any()).After(userSpaceCall).Times(1).Return(nil)
service := NewService( service := NewService(
"foo", "foo",
@ -65,27 +65,31 @@ func TestServiceHandleEvent_CheckEventCategoriesHandledInOrder(t *testing.T) {
eventPublisher, eventPublisher,
100*time.Millisecond, 100*time.Millisecond,
time.Millisecond, time.Millisecond,
time.Second, 10*time.Second,
async.NoopPanicHandler{}, async.NoopPanicHandler{},
) )
service.addSubscription(Subscription{ subscription := NewCallbackSubscriber("test", EventHandler{
User: userHandler, UserHandler: userHandler,
Refresh: refreshHandler, RefreshHandler: refreshHandler,
Address: addressHandler, AddressHandler: addressHandler,
Labels: labelHandler, LabelHandler: labelHandler,
Messages: messageHandler, MessageHandler: messageHandler,
UserUsedSpace: userSpaceHandler, UsedSpaceHandler: userSpaceHandler,
}) })
service.addSubscription(subscription)
// Simulate 1st refresh. // Simulate 1st refresh.
require.NoError(t, service.handleEvent(context.Background(), "", proton.Event{Refresh: proton.RefreshMail})) require.NoError(t, service.handleEvent(context.Background(), "", proton.Event{Refresh: proton.RefreshMail}))
// Simulate Regular event. // Simulate Regular event.
usedSpace := 20 usedSpace := 20
require.NoError(t, service.handleEvent(context.Background(), "", proton.Event{ require.NoError(t, service.handleEvent(context.Background(), "", proton.Event{
User: new(proton.User), User: new(proton.User),
Addresses: []proton.AddressEvent{}, Addresses: []proton.AddressEvent{
{},
},
Labels: []proton.LabelEvent{ Labels: []proton.LabelEvent{
{}, {},
}, },
@ -95,9 +99,9 @@ func TestServiceHandleEvent_CheckEventCategoriesHandledInOrder(t *testing.T) {
UsedSpace: &usedSpace, UsedSpace: &usedSpace,
})) }))
service.addSubscription(Subscription{ service.addSubscription(NewCallbackSubscriber("test", EventHandler{
Refresh: secondRefreshHandler, RefreshHandler: secondRefreshHandler,
}) }))
// Simulate 2nd refresh. // Simulate 2nd refresh.
require.NoError(t, service.handleEvent(context.Background(), "", proton.Event{Refresh: proton.RefreshMail})) require.NoError(t, service.handleEvent(context.Background(), "", proton.Event{Refresh: proton.RefreshMail}))
@ -109,11 +113,10 @@ func TestServiceHandleEvent_CheckEventFailureCausesError(t *testing.T) {
eventPublisher := mocks.NewMockEventPublisher(mockCtrl) eventPublisher := mocks.NewMockEventPublisher(mockCtrl)
eventIDStore := NewInMemoryEventIDStore() eventIDStore := NewInMemoryEventIDStore()
addressHandler := NewMockAddressSubscriber(mockCtrl) addressHandler := NewMockAddressEventHandler(mockCtrl)
addressHandler.EXPECT().name().MinTimes(1).Return("Hello") addressHandler.EXPECT().HandleAddressEvents(gomock.Any(), gomock.Any()).Times(1).Return(fmt.Errorf("failed"))
addressHandler.EXPECT().handle(gomock.Any(), gomock.Any()).Times(1).Return(fmt.Errorf("failed"))
messageHandler := NewMockMessageSubscriber(mockCtrl) messageHandler := NewMockMessageEventHandler(mockCtrl)
service := NewService( service := NewService(
"foo", "foo",
@ -126,16 +129,18 @@ func TestServiceHandleEvent_CheckEventFailureCausesError(t *testing.T) {
async.NoopPanicHandler{}, async.NoopPanicHandler{},
) )
service.addSubscription(Subscription{ subscription := NewCallbackSubscriber("test", EventHandler{
Address: addressHandler, AddressHandler: addressHandler,
Messages: messageHandler, MessageHandler: messageHandler,
}) })
service.addSubscription(subscription)
err := service.handleEvent(context.Background(), "", proton.Event{Addresses: []proton.AddressEvent{{}}}) err := service.handleEvent(context.Background(), "", proton.Event{Addresses: []proton.AddressEvent{{}}})
require.Error(t, err) require.Error(t, err)
publisherErr := new(addressPublishError) publisherErr := new(eventPublishError)
require.True(t, errors.As(err, &publisherErr)) require.True(t, errors.As(err, &publisherErr))
require.Equal(t, publisherErr.subscriber, addressHandler) require.Equal(t, publisherErr.subscriber, subscription)
} }
func TestServiceHandleEvent_CheckEventFailureCausesErrorParallel(t *testing.T) { func TestServiceHandleEvent_CheckEventFailureCausesErrorParallel(t *testing.T) {
@ -144,12 +149,11 @@ func TestServiceHandleEvent_CheckEventFailureCausesErrorParallel(t *testing.T) {
eventPublisher := mocks.NewMockEventPublisher(mockCtrl) eventPublisher := mocks.NewMockEventPublisher(mockCtrl)
eventIDStore := NewInMemoryEventIDStore() eventIDStore := NewInMemoryEventIDStore()
addressHandler := NewMockAddressSubscriber(mockCtrl) addressHandler := NewMockAddressEventHandler(mockCtrl)
addressHandler.EXPECT().name().MinTimes(1).Return("Hello") addressHandler.EXPECT().HandleAddressEvents(gomock.Any(), gomock.Any()).Times(1).Return(fmt.Errorf("failed"))
addressHandler.EXPECT().handle(gomock.Any(), gomock.Any()).Times(1).Return(fmt.Errorf("failed"))
addressHandler2 := NewMockAddressSubscriber(mockCtrl) addressHandler2 := NewMockAddressEventHandler(mockCtrl)
addressHandler2.EXPECT().handle(gomock.Any(), gomock.Any()).MaxTimes(1).Return(nil) addressHandler2.EXPECT().HandleAddressEvents(gomock.Any(), gomock.Any()).MaxTimes(1).Return(nil)
service := NewService( service := NewService(
"foo", "foo",
@ -162,19 +166,21 @@ func TestServiceHandleEvent_CheckEventFailureCausesErrorParallel(t *testing.T) {
async.NoopPanicHandler{}, async.NoopPanicHandler{},
) )
service.addSubscription(Subscription{ subscription := NewCallbackSubscriber("test", EventHandler{
Address: addressHandler, AddressHandler: addressHandler,
}) })
service.addSubscription(Subscription{ service.addSubscription(subscription)
Address: addressHandler2,
}) service.addSubscription(NewCallbackSubscriber("test2", EventHandler{
AddressHandler: addressHandler2,
}))
err := service.handleEvent(context.Background(), "", proton.Event{Addresses: []proton.AddressEvent{{}}}) err := service.handleEvent(context.Background(), "", proton.Event{Addresses: []proton.AddressEvent{{}}})
require.Error(t, err) require.Error(t, err)
publisherErr := new(addressPublishError) publisherErr := new(eventPublishError)
require.True(t, errors.As(err, &publisherErr)) require.True(t, errors.As(err, &publisherErr))
require.Equal(t, publisherErr.subscriber, addressHandler) require.Equal(t, publisherErr.subscriber, subscription)
} }
func TestServiceHandleEvent_SubscriberTimeout(t *testing.T) { func TestServiceHandleEvent_SubscriberTimeout(t *testing.T) {
@ -183,13 +189,11 @@ func TestServiceHandleEvent_SubscriberTimeout(t *testing.T) {
eventPublisher := mocks.NewMockEventPublisher(mockCtrl) eventPublisher := mocks.NewMockEventPublisher(mockCtrl)
eventIDStore := NewInMemoryEventIDStore() eventIDStore := NewInMemoryEventIDStore()
addressHandler := NewMockAddressSubscriber(mockCtrl) addressHandler := NewMockAddressEventHandler(mockCtrl)
addressHandler.EXPECT().name().AnyTimes().Return("Ok") addressHandler.EXPECT().HandleAddressEvents(gomock.Any(), gomock.Any()).MaxTimes(1).Return(nil)
addressHandler.EXPECT().handle(gomock.Any(), gomock.Any()).MaxTimes(1).Return(nil)
addressHandler2 := NewMockAddressSubscriber(mockCtrl) addressHandler2 := NewMockAddressEventHandler(mockCtrl)
addressHandler2.EXPECT().name().AnyTimes().Return("Timeout") addressHandler2.EXPECT().HandleAddressEvents(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, _ []proton.AddressEvent) error {
addressHandler2.EXPECT().handle(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, _ []proton.AddressEvent) error {
timer := time.NewTimer(time.Second) timer := time.NewTimer(time.Second)
select { select {
@ -211,19 +215,21 @@ func TestServiceHandleEvent_SubscriberTimeout(t *testing.T) {
async.NoopPanicHandler{}, async.NoopPanicHandler{},
) )
service.addSubscription(Subscription{ subscription := NewCallbackSubscriber("test", EventHandler{
Address: addressHandler, AddressHandler: addressHandler2,
}) })
service.addSubscription(Subscription{ service.addSubscription(subscription)
Address: addressHandler2,
}) service.addSubscription(NewCallbackSubscriber("test2", EventHandler{
AddressHandler: addressHandler,
}))
// Simulate 1st refresh. // Simulate 1st refresh.
err := service.handleEvent(context.Background(), "", proton.Event{Addresses: []proton.AddressEvent{{}}}) err := service.handleEvent(context.Background(), "", proton.Event{Addresses: []proton.AddressEvent{{}}})
require.Error(t, err) require.Error(t, err)
if publisherErr := new(addressPublishError); errors.As(err, &publisherErr) { if publisherErr := new(eventPublishError); errors.As(err, &publisherErr) {
require.Equal(t, publisherErr.subscriber, addressHandler) require.Equal(t, publisherErr.subscriber, subscription)
require.True(t, errors.Is(publisherErr.error, ErrPublishTimeoutExceeded)) require.True(t, errors.Is(publisherErr.error, ErrPublishTimeoutExceeded))
} else { } else {
require.True(t, errors.Is(err, ErrPublishTimeoutExceeded)) require.True(t, errors.Is(err, ErrPublishTimeoutExceeded))

View File

@ -87,7 +87,7 @@ func TestService_RetryEventOnNonCatastrophicFailure(t *testing.T) {
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl) eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
eventIDStore := mocks.NewMockEventIDStore(mockCtrl) eventIDStore := mocks.NewMockEventIDStore(mockCtrl)
eventSource := mocks.NewMockEventSource(mockCtrl) eventSource := mocks.NewMockEventSource(mockCtrl)
subscriber := NewMockMessageSubscriber(mockCtrl) subscriber := NewMockMessageEventHandler(mockCtrl)
firstEventID := "EVENT01" firstEventID := "EVENT01"
secondEventID := "EVENT02" secondEventID := "EVENT02"
@ -113,10 +113,9 @@ func TestService_RetryEventOnNonCatastrophicFailure(t *testing.T) {
eventSource.EXPECT().GetEvent(gomock.Any(), gomock.Eq(firstEventID)).MinTimes(1).Return(secondEvent, false, nil) eventSource.EXPECT().GetEvent(gomock.Any(), gomock.Eq(firstEventID)).MinTimes(1).Return(secondEvent, false, nil)
// Subscriber expectations. // Subscriber expectations.
subscriber.EXPECT().name().AnyTimes().Return("Foo")
{ {
firstCall := subscriber.EXPECT().handle(gomock.Any(), gomock.Eq(messageEvents)).Times(1).Return(io.ErrUnexpectedEOF) firstCall := subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).Times(1).Return(io.ErrUnexpectedEOF)
subscriber.EXPECT().handle(gomock.Any(), gomock.Eq(messageEvents)).After(firstCall).Times(1).Return(nil) subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).After(firstCall).Times(1).Return(nil)
} }
service := NewService( service := NewService(
@ -129,7 +128,7 @@ func TestService_RetryEventOnNonCatastrophicFailure(t *testing.T) {
time.Second, time.Second,
async.NoopPanicHandler{}, async.NoopPanicHandler{},
) )
service.Subscribe(Subscription{Messages: subscriber}) service.Subscribe(NewCallbackSubscriber("foo", EventHandler{MessageHandler: subscriber}))
require.NoError(t, service.Start(context.Background(), group)) require.NoError(t, service.Start(context.Background(), group))
service.Resume() service.Resume()
@ -142,7 +141,7 @@ func TestService_OnBadEventServiceIsPaused(t *testing.T) {
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl) eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
eventIDStore := mocks.NewMockEventIDStore(mockCtrl) eventIDStore := mocks.NewMockEventIDStore(mockCtrl)
eventSource := mocks.NewMockEventSource(mockCtrl) eventSource := mocks.NewMockEventSource(mockCtrl)
subscriber := NewMockMessageSubscriber(mockCtrl) subscriber := NewMockMessageEventHandler(mockCtrl)
firstEventID := "EVENT01" firstEventID := "EVENT01"
secondEventID := "EVENT02" secondEventID := "EVENT02"
@ -164,8 +163,7 @@ func TestService_OnBadEventServiceIsPaused(t *testing.T) {
// Subscriber expectations. // Subscriber expectations.
badEventErr := fmt.Errorf("I will cause bad event") badEventErr := fmt.Errorf("I will cause bad event")
subscriber.EXPECT().name().AnyTimes().Return("Foo") subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).Times(1).Return(badEventErr)
subscriber.EXPECT().handle(gomock.Any(), gomock.Eq(messageEvents)).Times(1).Return(badEventErr)
service := NewService( service := NewService(
"foo", "foo",
@ -184,7 +182,7 @@ func TestService_OnBadEventServiceIsPaused(t *testing.T) {
OldEventID: firstEventID, OldEventID: firstEventID,
NewEventID: secondEventID, NewEventID: secondEventID,
EventInfo: secondEvent[0].String(), EventInfo: secondEvent[0].String(),
Error: badEventErr, Error: fmt.Errorf("failed to apply message events: %w", badEventErr),
}).Do(func(_ context.Context, event events.Event) { }).Do(func(_ context.Context, event events.Event) {
group.Go(context.Background(), "", "", func(_ context.Context) { group.Go(context.Background(), "", "", func(_ context.Context) {
// Use background context to avoid having the request cancelled // Use background context to avoid having the request cancelled
@ -193,7 +191,7 @@ func TestService_OnBadEventServiceIsPaused(t *testing.T) {
}) })
}) })
service.Subscribe(Subscription{Messages: subscriber}) service.Subscribe(NewCallbackSubscriber("foo", EventHandler{MessageHandler: subscriber}))
require.NoError(t, service.Start(context.Background(), group)) require.NoError(t, service.Start(context.Background(), group))
service.Resume() service.Resume()
group.Wait() group.Wait()
@ -205,7 +203,7 @@ func TestService_UnsubscribeDuringEventHandlingDoesNotCauseDeadlock(t *testing.T
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl) eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
eventIDStore := mocks.NewMockEventIDStore(mockCtrl) eventIDStore := mocks.NewMockEventIDStore(mockCtrl)
eventSource := mocks.NewMockEventSource(mockCtrl) eventSource := mocks.NewMockEventSource(mockCtrl)
subscriber := NewMockMessageSubscriber(mockCtrl) subscriber := NewMockMessageEventHandler(mockCtrl)
firstEventID := "EVENT01" firstEventID := "EVENT01"
secondEventID := "EVENT02" secondEventID := "EVENT02"
@ -241,16 +239,15 @@ func TestService_UnsubscribeDuringEventHandlingDoesNotCauseDeadlock(t *testing.T
async.NoopPanicHandler{}, async.NoopPanicHandler{},
) )
subscription := NewCallbackSubscriber("foo", EventHandler{MessageHandler: subscriber})
// Subscriber expectations. // Subscriber expectations.
subscriber.EXPECT().name().AnyTimes().Return("Foo") subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).Times(1).DoAndReturn(func(_ context.Context, _ []proton.MessageEvent) error {
subscriber.EXPECT().cancel().Times(1) service.Unsubscribe(subscription)
subscriber.EXPECT().close().Times(1)
subscriber.EXPECT().handle(gomock.Any(), gomock.Eq(messageEvents)).Times(1).DoAndReturn(func(_ context.Context, _ []proton.MessageEvent) error {
service.Unsubscribe(Subscription{Messages: subscriber})
return nil return nil
}) })
service.Subscribe(Subscription{Messages: subscriber}) service.Subscribe(subscription)
require.NoError(t, service.Start(context.Background(), group)) require.NoError(t, service.Start(context.Background(), group))
service.Resume() service.Resume()
group.Wait() group.Wait()
@ -262,7 +259,6 @@ func TestService_UnsubscribeBeforeHandlingEventIsNotConsideredError(t *testing.T
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl) eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
eventIDStore := mocks.NewMockEventIDStore(mockCtrl) eventIDStore := mocks.NewMockEventIDStore(mockCtrl)
eventSource := mocks.NewMockEventSource(mockCtrl) eventSource := mocks.NewMockEventSource(mockCtrl)
subscriber := NewMessageSubscriber("My subscriber")
firstEventID := "EVENT01" firstEventID := "EVENT01"
secondEventID := "EVENT02" secondEventID := "EVENT02"
@ -299,16 +295,42 @@ func TestService_UnsubscribeBeforeHandlingEventIsNotConsideredError(t *testing.T
async.NoopPanicHandler{}, async.NoopPanicHandler{},
) )
subscription := NewEventSubscriber("Foo")
// start subscriber // start subscriber
group.Go(context.Background(), "", "", func(_ context.Context) { group.Go(context.Background(), "", "", func(_ context.Context) {
defer service.Unsubscribe(Subscription{Messages: subscriber}) defer service.Unsubscribe(subscription)
// Simulate the reception of an event, but it is never handled due to unexpected exit // Simulate the reception of an event, but it is never handled due to unexpected exit
<-time.NewTicker(500 * time.Millisecond).C <-time.NewTicker(500 * time.Millisecond).C
}) })
service.Subscribe(Subscription{Messages: subscriber}) service.Subscribe(subscription)
require.NoError(t, service.Start(context.Background(), group)) require.NoError(t, service.Start(context.Background(), group))
service.Resume() service.Resume()
group.Wait() group.Wait()
} }
type CallbackSubscriber struct {
handler EventHandler
n string
}
func NewCallbackSubscriber(name string, handler EventHandler) *CallbackSubscriber {
return &CallbackSubscriber{handler: handler, n: name}
}
func (c CallbackSubscriber) name() string { //nolint: unused
return c.n
}
func (c CallbackSubscriber) handle(ctx context.Context, t proton.Event) error { //nolint: unused
return c.handler.OnEvent(ctx, t)
}
func (c CallbackSubscriber) cancel() { //nolint: unused
// Nothing to do.
}
func (c CallbackSubscriber) close() { //nolint: unused
// Nothing to do.
}

View File

@ -19,16 +19,16 @@ package userevents
// Subscribable represents a type that allows the registration of event subscribers. // Subscribable represents a type that allows the registration of event subscribers.
type Subscribable interface { type Subscribable interface {
Subscribe(subscription Subscription) Subscribe(subscription EventSubscriber)
Unsubscribe(subscription Subscription) Unsubscribe(subscription EventSubscriber)
} }
type NoOpSubscribable struct{} type NoOpSubscribable struct{}
func (n NoOpSubscribable) Subscribe(_ Subscription) { func (n NoOpSubscribable) Subscribe(_ EventSubscriber) {
// Does nothing // Does nothing
} }
func (n NoOpSubscribable) Unsubscribe(_ Subscription) { func (n NoOpSubscribable) Unsubscribe(_ EventSubscriber) {
// Does nothing // Does nothing
} }

View File

@ -31,43 +31,13 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
type AddressChanneledSubscriber = ChanneledSubscriber[[]proton.AddressEvent] type EventChanneledSubscriber = ChanneledSubscriber[proton.Event]
type LabelChanneledSubscriber = ChanneledSubscriber[[]proton.LabelEvent]
type MessageChanneledSubscriber = ChanneledSubscriber[[]proton.MessageEvent]
type UserChanneledSubscriber = ChanneledSubscriber[proton.User]
type RefreshChanneledSubscriber = ChanneledSubscriber[proton.RefreshFlag]
type UserUsedSpaceChanneledSubscriber = ChanneledSubscriber[int]
func NewMessageSubscriber(name string) *MessageChanneledSubscriber { func newSubscriber(name string) *EventChanneledSubscriber {
return newChanneledSubscriber[[]proton.MessageEvent](name) return newChanneledSubscriber[proton.Event](name)
} }
func NewAddressSubscriber(name string) *AddressChanneledSubscriber { type EventSubscriber = subscriber[proton.Event]
return newChanneledSubscriber[[]proton.AddressEvent](name)
}
func NewLabelSubscriber(name string) *LabelChanneledSubscriber {
return newChanneledSubscriber[[]proton.LabelEvent](name)
}
func NewRefreshSubscriber(name string) *RefreshChanneledSubscriber {
return newChanneledSubscriber[proton.RefreshFlag](name)
}
func NewUserSubscriber(name string) *UserChanneledSubscriber {
return newChanneledSubscriber[proton.User](name)
}
func NewUserUsedSpaceSubscriber(name string) *UserUsedSpaceChanneledSubscriber {
return newChanneledSubscriber[int](name)
}
type AddressSubscriber = subscriber[[]proton.AddressEvent]
type LabelSubscriber = subscriber[[]proton.LabelEvent]
type MessageSubscriber = subscriber[[]proton.MessageEvent]
type RefreshSubscriber = subscriber[proton.RefreshFlag]
type UserSubscriber = subscriber[proton.User]
type UserUsedSpaceSubscriber = subscriber[int]
// Subscriber is the main entry point of interacting with user generated events. // Subscriber is the main entry point of interacting with user generated events.
type subscriber[T any] interface { type subscriber[T any] interface {
@ -87,12 +57,7 @@ type subscriberList[T any] struct {
subscribers []subscriber[T] subscribers []subscriber[T]
} }
type addressSubscriberList = subscriberList[[]proton.AddressEvent] type eventSubscriberList = subscriberList[proton.Event]
type labelSubscriberList = subscriberList[[]proton.LabelEvent]
type messageSubscriberList = subscriberList[[]proton.MessageEvent]
type refreshSubscriberList = subscriberList[proton.RefreshFlag]
type userSubscriberList = subscriberList[proton.User]
type userUsedSpaceSubscriberList = subscriberList[int]
func (s *subscriberList[T]) Add(subscriber subscriber[T]) { func (s *subscriberList[T]) Add(subscriber subscriber[T]) {
if !slices.Contains(s.subscribers, subscriber) { if !slices.Contains(s.subscribers, subscriber) {
@ -117,12 +82,7 @@ type publishError[T any] struct {
var ErrPublishTimeoutExceeded = errors.New("event publish timed out") var ErrPublishTimeoutExceeded = errors.New("event publish timed out")
type addressPublishError = publishError[[]proton.AddressEvent] type eventPublishError = publishError[proton.Event]
type labelPublishError = publishError[[]proton.LabelEvent]
type messagePublishError = publishError[[]proton.MessageEvent]
type refreshPublishError = publishError[proton.RefreshFlag]
type userPublishError = publishError[proton.User]
type userUsedEventPublishError = publishError[int]
func (p publishError[T]) Error() string { func (p publishError[T]) Error() string {
return fmt.Sprintf("Event publish failed on (%v): %v", p.subscriber.name(), p.error.Error()) return fmt.Sprintf("Event publish failed on (%v): %v", p.subscriber.name(), p.error.Error())

View File

@ -0,0 +1,106 @@
// Copyright (c) 2023 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package userevents
import (
"context"
"fmt"
"github.com/ProtonMail/go-proton-api"
)
type Subscription = EventSubscriber
func NewEventSubscriber(name string) *EventChanneledSubscriber {
return newSubscriber(name)
}
type EventHandler struct {
RefreshHandler RefreshEventHandler
AddressHandler AddressEventHandler
UserHandler UserEventHandler
LabelHandler LabelEventHandler
MessageHandler MessageEventHandler
UsedSpaceHandler UserUsedSpaceEventHandler
}
func (e EventHandler) OnEvent(ctx context.Context, event proton.Event) error {
if event.Refresh&proton.RefreshMail != 0 && e.RefreshHandler != nil {
return e.RefreshHandler.HandleRefreshEvent(ctx, event.Refresh)
}
// Start with user events.
if event.User != nil && e.UserHandler != nil {
if err := e.UserHandler.HandleUserEvent(ctx, event.User); err != nil {
return fmt.Errorf("failed to apply user event: %w", err)
}
}
// Next Address events
if len(event.Addresses) != 0 && e.AddressHandler != nil {
if err := e.AddressHandler.HandleAddressEvents(ctx, event.Addresses); err != nil {
return fmt.Errorf("failed to apply address events: %w", err)
}
}
// Next label events
if len(event.Labels) != 0 && e.LabelHandler != nil {
if err := e.LabelHandler.HandleLabelEvents(ctx, event.Labels); err != nil {
return fmt.Errorf("failed to apply label events: %w", err)
}
}
// Next message events
if len(event.Messages) != 0 && e.MessageHandler != nil {
if err := e.MessageHandler.HandleMessageEvents(ctx, event.Messages); err != nil {
return fmt.Errorf("failed to apply message events: %w", err)
}
}
// Finally user used space events
if event.UsedSpace != nil && e.UsedSpaceHandler != nil {
if err := e.UsedSpaceHandler.HandleUsedSpaceEvent(ctx, *event.UsedSpace); err != nil {
return fmt.Errorf("failed to apply message events: %w", err)
}
}
return nil
}
type RefreshEventHandler interface {
HandleRefreshEvent(ctx context.Context, flag proton.RefreshFlag) error
}
type UserEventHandler interface {
HandleUserEvent(ctx context.Context, user *proton.User) error
}
type UserUsedSpaceEventHandler interface {
HandleUsedSpaceEvent(ctx context.Context, newSpace int) error
}
type AddressEventHandler interface {
HandleAddressEvents(ctx context.Context, events []proton.AddressEvent) error
}
type LabelEventHandler interface {
HandleLabelEvents(ctx context.Context, events []proton.LabelEvent) error
}
type MessageEventHandler interface {
HandleMessageEvents(ctx context.Context, events []proton.MessageEvent) error
}

View File

@ -47,10 +47,7 @@ type Service struct {
log *logrus.Entry log *logrus.Entry
identity State identity State
userSubscriber *userevents.UserChanneledSubscriber subscription *userevents.EventChanneledSubscriber
addressSubscriber *userevents.AddressChanneledSubscriber
usedSpaceSubscriber *userevents.UserUsedSpaceChanneledSubscriber
refreshSubscriber *userevents.RefreshChanneledSubscriber
bridgePassProvider BridgePassProvider bridgePassProvider BridgePassProvider
telemetry Telemetry telemetry Telemetry
@ -74,12 +71,9 @@ func NewService(
"service": "user-identity", "service": "user-identity",
"user": state.User.ID, "user": state.User.ID,
}), }),
userSubscriber: userevents.NewUserSubscriber(subscriberName), subscription: userevents.NewEventSubscriber(subscriberName),
refreshSubscriber: userevents.NewRefreshSubscriber(subscriberName), bridgePassProvider: bridgePassProvider,
addressSubscriber: userevents.NewAddressSubscriber(subscriberName), telemetry: telemetry,
usedSpaceSubscriber: userevents.NewUserUsedSpaceSubscriber(subscriberName),
bridgePassProvider: bridgePassProvider,
telemetry: telemetry,
} }
} }
@ -108,134 +102,30 @@ func (s *Service) CheckAuth(ctx context.Context, email string, password []byte)
}) })
} }
func (s *Service) run(ctx context.Context) { func (s *Service) HandleUsedSpaceEvent(ctx context.Context, newSpace int) error {
s.log.WithFields(logrus.Fields{ s.log.Info("Handling User Space Changed event")
"numAddr": len(s.identity.Addresses),
}).Info("Starting user identity service")
defer s.log.Info("Exiting Service")
s.registerSubscription() if s.identity.OnUserSpaceChanged(newSpace) {
defer s.unregisterSubscription() s.eventPublisher.PublishEvent(ctx, events.UsedSpaceChanged{
UserID: s.identity.User.ID,
defer s.cpc.Close() UsedSpace: newSpace,
})
for {
select {
case <-ctx.Done():
return
case r, ok := <-s.cpc.ReceiveCh():
if !ok {
continue
}
switch req := r.Value().(type) {
case *resyncReq:
err := s.identity.OnRefreshEvent(ctx)
r.Reply(ctx, nil, err)
case *getUserReq:
r.Reply(ctx, s.identity.User, nil)
case *getAddressesReq:
r.Reply(ctx, maps.Clone(s.identity.Addresses), nil)
case *checkAuthReq:
id, err := s.identity.CheckAuth(req.email, req.password, s.bridgePassProvider, s.telemetry)
r.Reply(ctx, id, err)
default:
s.log.Error("Invalid request")
}
case evt, ok := <-s.userSubscriber.OnEventCh():
if !ok {
continue
}
evt.Consume(func(user proton.User) error {
s.onUserEvent(ctx, user)
return nil
})
case evt, ok := <-s.refreshSubscriber.OnEventCh():
if !ok {
continue
}
evt.Consume(func(_ proton.RefreshFlag) error {
return s.onRefreshEvent(ctx)
})
case evt, ok := <-s.usedSpaceSubscriber.OnEventCh():
if !ok {
continue
}
evt.Consume(func(usedSpace int) error {
s.onUserSpaceChanged(ctx, usedSpace)
return nil
})
case evt, ok := <-s.addressSubscriber.OnEventCh():
if !ok {
continue
}
evt.Consume(func(events []proton.AddressEvent) error {
return s.onAddressEvent(ctx, events)
})
}
} }
return nil
} }
func (s *Service) registerSubscription() { func (s *Service) HandleUserEvent(ctx context.Context, user *proton.User) error {
s.eventService.Subscribe(userevents.Subscription{
Refresh: s.refreshSubscriber,
User: s.userSubscriber,
Address: s.addressSubscriber,
UserUsedSpace: s.usedSpaceSubscriber,
})
}
func (s *Service) unregisterSubscription() {
s.eventService.Unsubscribe(userevents.Subscription{
Refresh: s.refreshSubscriber,
User: s.userSubscriber,
Address: s.addressSubscriber,
UserUsedSpace: s.usedSpaceSubscriber,
})
}
func (s *Service) onUserEvent(ctx context.Context, user proton.User) {
s.log.WithField("username", logging.Sensitive(user.Name)).Info("Handling user event") s.log.WithField("username", logging.Sensitive(user.Name)).Info("Handling user event")
s.identity.OnUserEvent(user) s.identity.OnUserEvent(*user)
s.eventPublisher.PublishEvent(ctx, events.UserChanged{ s.eventPublisher.PublishEvent(ctx, events.UserChanged{
UserID: user.ID, UserID: user.ID,
}) })
}
func (s *Service) onRefreshEvent(ctx context.Context) error {
s.log.Info("Handling refresh event")
if err := s.identity.OnRefreshEvent(ctx); err != nil {
s.log.WithError(err).Error("Failed to handle refresh event")
return err
}
s.eventPublisher.PublishEvent(ctx, events.UserRefreshed{
UserID: s.identity.User.ID,
CancelEventPool: false,
})
return nil return nil
} }
func (s *Service) onUserSpaceChanged(ctx context.Context, value int) { func (s *Service) HandleAddressEvents(ctx context.Context, addressEvents []proton.AddressEvent) error {
s.log.Info("Handling User Space Changed event")
if !s.identity.OnUserSpaceChanged(value) {
return
}
s.eventPublisher.PublishEvent(ctx, events.UsedSpaceChanged{
UserID: s.identity.User.ID,
UsedSpace: value,
})
}
func (s *Service) onAddressEvent(ctx context.Context, addressEvents []proton.AddressEvent) error {
s.log.Infof("Handling Address Events (%v)", len(addressEvents)) s.log.Infof("Handling Address Events (%v)", len(addressEvents))
for idx, event := range addressEvents { for idx, event := range addressEvents {
@ -305,6 +195,86 @@ func (s *Service) onAddressEvent(ctx context.Context, addressEvents []proton.Add
return nil return nil
} }
func (s *Service) HandleRefreshEvent(ctx context.Context, _ proton.RefreshFlag) error {
s.log.Info("Handling refresh event")
if err := s.identity.OnRefreshEvent(ctx); err != nil {
s.log.WithError(err).Error("Failed to handle refresh event")
return err
}
s.eventPublisher.PublishEvent(ctx, events.UserRefreshed{
UserID: s.identity.User.ID,
CancelEventPool: false,
})
return nil
}
func (s *Service) run(ctx context.Context) {
s.log.WithFields(logrus.Fields{
"numAddr": len(s.identity.Addresses),
}).Info("Starting user identity service")
defer s.log.Info("Exiting Service")
eventHandler := userevents.EventHandler{
UserHandler: s,
AddressHandler: s,
UsedSpaceHandler: s,
RefreshHandler: s,
}
s.registerSubscription()
defer s.unregisterSubscription()
defer s.cpc.Close()
for {
select {
case <-ctx.Done():
return
case r, ok := <-s.cpc.ReceiveCh():
if !ok {
continue
}
switch req := r.Value().(type) {
case *resyncReq:
err := s.identity.OnRefreshEvent(ctx)
r.Reply(ctx, nil, err)
case *getUserReq:
r.Reply(ctx, s.identity.User, nil)
case *getAddressesReq:
r.Reply(ctx, maps.Clone(s.identity.Addresses), nil)
case *checkAuthReq:
id, err := s.identity.CheckAuth(req.email, req.password, s.bridgePassProvider, s.telemetry)
r.Reply(ctx, id, err)
default:
s.log.Error("Invalid request")
}
case evt, ok := <-s.subscription.OnEventCh():
if !ok {
continue
}
evt.Consume(func(event proton.Event) error {
return eventHandler.OnEvent(ctx, event)
})
}
}
}
func (s *Service) registerSubscription() {
s.eventService.Subscribe(s.subscription)
}
func (s *Service) unregisterSubscription() {
s.eventService.Unsubscribe(s.subscription)
}
func sortAddresses(addr []proton.Address) []proton.Address { func sortAddresses(addr []proton.Address) []proton.Address {
slices.SortFunc(addr, func(a, b proton.Address) bool { slices.SortFunc(addr, func(a, b proton.Address) bool {
return a.Order < b.Order return a.Order < b.Order

View File

@ -39,7 +39,7 @@ func TestService_OnUserEvent(t *testing.T) {
eventPublisher.EXPECT().PublishEvent(gomock.Any(), gomock.Eq(events.UserChanged{UserID: TestUserID})).Times(1) eventPublisher.EXPECT().PublishEvent(gomock.Any(), gomock.Eq(events.UserChanged{UserID: TestUserID})).Times(1)
service.onUserEvent(context.Background(), newTestUser()) require.NoError(t, service.HandleUserEvent(context.Background(), newTestUser()))
} }
func TestService_OnUserSpaceChanged(t *testing.T) { func TestService_OnUserSpaceChanged(t *testing.T) {
@ -50,10 +50,10 @@ func TestService_OnUserSpaceChanged(t *testing.T) {
eventPublisher.EXPECT().PublishEvent(gomock.Any(), gomock.Eq(events.UsedSpaceChanged{UserID: TestUserID, UsedSpace: 1024})).Times(1) eventPublisher.EXPECT().PublishEvent(gomock.Any(), gomock.Eq(events.UsedSpaceChanged{UserID: TestUserID, UsedSpace: 1024})).Times(1)
// Original value, no changes. // Original value, no changes.
service.onUserSpaceChanged(context.Background(), 0) require.NoError(t, service.HandleUsedSpaceEvent(context.Background(), 0))
// New value, event should be published. // New value, event should be published.
service.onUserSpaceChanged(context.Background(), 1024) require.NoError(t, service.HandleUsedSpaceEvent(context.Background(), 1024))
require.Equal(t, 1024, service.identity.User.UsedSpace) require.Equal(t, 1024, service.identity.User.UsedSpace)
} }
@ -68,14 +68,14 @@ func TestService_OnRefreshEvent(t *testing.T) {
newAddresses := newTestAddressesRefreshed() newAddresses := newTestAddressesRefreshed()
{ {
getUserCall := provider.EXPECT().GetUser(gomock.Any()).Times(1).Return(newUser, nil) getUserCall := provider.EXPECT().GetUser(gomock.Any()).Times(1).Return(*newUser, nil)
provider.EXPECT().GetAddresses(gomock.Any()).After(getUserCall).Times(1).Return(newAddresses, nil) provider.EXPECT().GetAddresses(gomock.Any()).After(getUserCall).Times(1).Return(newAddresses, nil)
} }
// Original value, no changes. // Original value, no changes.
require.NoError(t, service.onRefreshEvent(context.Background())) require.NoError(t, service.HandleRefreshEvent(context.Background(), 0))
require.Equal(t, newUser, service.identity.User) require.Equal(t, *newUser, service.identity.User)
require.Equal(t, newAddresses, service.identity.AddressesSorted) require.Equal(t, newAddresses, service.identity.AddressesSorted)
} }
@ -96,7 +96,7 @@ func TestService_OnAddressCreated(t *testing.T) {
Email: newAddress.Email, Email: newAddress.Email,
})).Times(1) })).Times(1)
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: "", ID: "",
@ -121,7 +121,7 @@ func TestService_OnAddressCreatedDisabledDoesNotProduceEvent(t *testing.T) {
Status: proton.AddressStatusEnabled, Status: proton.AddressStatusEnabled,
} }
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: "", ID: "",
@ -146,7 +146,7 @@ func TestService_OnAddressCreatedDuplicateDoesNotProduceEvent(t *testing.T) {
Status: proton.AddressStatusDisabled, Status: proton.AddressStatusDisabled,
} }
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: "", ID: "",
@ -177,7 +177,7 @@ func TestService_OnAddressUpdated(t *testing.T) {
Email: newAddress.Email, Email: newAddress.Email,
})).Times(1) })).Times(1)
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: "", ID: "",
@ -221,7 +221,7 @@ func TestService_OnAddressUpdatedDisableFollowedByEnable(t *testing.T) {
})).Times(1).After(disabledCall) })).Times(1).After(disabledCall)
} }
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: "", ID: "",
@ -234,7 +234,7 @@ func TestService_OnAddressUpdatedDisableFollowedByEnable(t *testing.T) {
require.Equal(t, newAddressDisabled, service.identity.Addresses[newAddressEnabled.ID]) require.Equal(t, newAddressDisabled, service.identity.Addresses[newAddressEnabled.ID])
err = service.onAddressEvent(context.Background(), []proton.AddressEvent{ err = service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: "", ID: "",
@ -265,7 +265,7 @@ func TestService_OnAddressUpdateCreatedIfNotExists(t *testing.T) {
Email: newAddress.Email, Email: newAddress.Email,
})).Times(1) })).Times(1)
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: "", ID: "",
@ -296,7 +296,7 @@ func TestService_OnAddressDeleted(t *testing.T) {
Email: address.Email, Email: address.Email,
})).Times(1) })).Times(1)
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: address.ID, ID: address.ID,
@ -320,7 +320,7 @@ func TestService_OnAddressDeleteDisabledDoesNotProduceEvent(t *testing.T) {
Status: proton.AddressStatusDisabled, Status: proton.AddressStatusDisabled,
} }
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: address.ID, ID: address.ID,
@ -344,7 +344,7 @@ func TestService_OnAddressDeletedUnknownDoesNotProduceEvent(t *testing.T) {
Status: proton.AddressStatusEnabled, Status: proton.AddressStatusEnabled,
} }
err := service.onAddressEvent(context.Background(), []proton.AddressEvent{ err := service.HandleAddressEvents(context.Background(), []proton.AddressEvent{
{ {
EventItem: proton.EventItem{ EventItem: proton.EventItem{
ID: address.ID, ID: address.ID,
@ -364,12 +364,12 @@ func newTestService(_ *testing.T, mockCtrl *gomock.Controller) (*Service, *mocks
telemetry := mocks.NewMockTelemetry(mockCtrl) telemetry := mocks.NewMockTelemetry(mockCtrl)
bridgePassProvider := NewFixedBridgePassProvider([]byte("hello")) bridgePassProvider := NewFixedBridgePassProvider([]byte("hello"))
service := NewService(subscribable, eventPublisher, NewState(user, newTestAddresses(), provider), bridgePassProvider, telemetry) service := NewService(subscribable, eventPublisher, NewState(*user, newTestAddresses(), provider), bridgePassProvider, telemetry)
return service, eventPublisher, provider return service, eventPublisher, provider
} }
func newTestUser() proton.User { func newTestUser() *proton.User {
return proton.User{ return &proton.User{
ID: TestUserID, ID: TestUserID,
Name: "Foo", Name: "Foo",
DisplayName: "Foo", DisplayName: "Foo",
@ -383,8 +383,8 @@ func newTestUser() proton.User {
} }
} }
func newTestUserRefreshed() proton.User { func newTestUserRefreshed() *proton.User {
return proton.User{ return &proton.User{
ID: TestUserID, ID: TestUserID,
Name: "Alternate", Name: "Alternate",
DisplayName: "Universe", DisplayName: "Universe",