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

@ -73,11 +73,7 @@ type Service struct {
labels *rwLabels
addressMode usertypes.AddressMode
refreshSubscriber *userevents.RefreshChanneledSubscriber
addressSubscriber *userevents.AddressChanneledSubscriber
userSubscriber *userevents.UserChanneledSubscriber
messageSubscriber *userevents.MessageChanneledSubscriber
labelSubscriber *userevents.LabelChanneledSubscriber
subscription *userevents.EventChanneledSubscriber
gluonIDProvider GluonIDProvider
syncStateProvider SyncStateProvider
@ -95,6 +91,7 @@ type Service struct {
connectors map[string]*Connector
maxSyncMemory uint64
showAllMail bool
syncHandler *syncHandler
}
func NewService(
@ -135,11 +132,7 @@ func NewService(
eventProvider: eventProvider,
eventPublisher: eventPublisher,
refreshSubscriber: userevents.NewRefreshSubscriber(subscriberName),
addressSubscriber: userevents.NewAddressSubscriber(subscriberName),
userSubscriber: userevents.NewUserSubscriber(subscriberName),
messageSubscriber: userevents.NewMessageSubscriber(subscriberName),
labelSubscriber: userevents.NewLabelSubscriber(subscriberName),
subscription: userevents.NewEventSubscriber(subscriberName),
panicHandler: panicHandler,
sendRecorder: sendRecorder,
@ -241,6 +234,44 @@ func (s *Service) Close() {
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
s.log.Info("Starting 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.eventSubscription.Remove(s.eventWatcher)
syncHandler := newSyncHandler(ctx, s.panicHandler)
defer syncHandler.Close()
s.syncHandler = newSyncHandler(ctx, s.panicHandler)
defer s.syncHandler.Close()
syncHandler.launch(s)
s.syncHandler.launch(s)
subscription := userevents.Subscription{
User: s.userSubscriber,
Refresh: s.refreshSubscriber,
Address: s.addressSubscriber,
Labels: s.labelSubscriber,
Messages: s.messageSubscriber,
eventHandler := userevents.EventHandler{
UserHandler: s,
AddressHandler: s,
RefreshHandler: s,
LabelHandler: s,
MessageHandler: s,
}
s.eventProvider.Subscribe(subscription)
defer s.eventProvider.Unsubscribe(subscription)
s.eventProvider.Subscribe(s.subscription)
defer s.eventProvider.Unsubscribe(s.subscription)
for {
select {
@ -275,25 +306,25 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo
}
switch r := req.Value().(type) {
case *setAddressModeReq:
err := s.setAddressMode(ctx, syncHandler, r.mode)
err := s.setAddressMode(ctx, s.syncHandler, r.mode)
req.Reply(ctx, nil, err)
case *resyncReq:
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)
s.log.Info("Resync reply sent, handling as refresh event")
case *cancelSyncReq:
s.log.Info("Cancelling sync")
syncHandler.Cancel()
s.syncHandler.Cancel()
req.Reply(ctx, nil, nil)
case *resumeSyncReq:
s.log.Info("Resuming sync")
// Cancel previous run, if any, just in case.
syncHandler.CancelAndWait()
syncHandler.launch(s)
s.syncHandler.CancelAndWait()
s.syncHandler.launch(s)
req.Reply(ctx, nil, nil)
case *getLabelsReq:
labels := s.labels.GetLabelMap()
@ -319,7 +350,7 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo
s.log.Error("Received unknown request")
}
case err, ok := <-syncHandler.OnSyncFinishedCH():
case err, ok := <-s.syncHandler.OnSyncFinishedCH():
{
if !ok {
continue
@ -334,46 +365,18 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo
s.eventProvider.Resume()
}
case update, ok := <-syncHandler.updater.ch:
case update, ok := <-s.syncHandler.updater.ch:
if !ok {
continue
}
s.onSyncUpdate(ctx, update)
case e, ok := <-s.userSubscriber.OnEventCh():
case e, ok := <-s.subscription.OnEventCh():
if !ok {
continue
}
e.Consume(func(user proton.User) error {
return s.onUserEvent(user)
})
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)
e.Consume(func(event proton.Event) error {
return eventHandler.OnEvent(ctx, event)
})
case e, ok := <-s.eventWatcher.GetChannel():
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) {
connectors := make(map[string]*Connector)

View File

@ -26,7 +26,7 @@ import (
"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")
if s.addressMode == usertypes.AddressModeCombined {

View File

@ -32,7 +32,7 @@ import (
"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")
for _, event := range events {

View File

@ -36,7 +36,7 @@ import (
"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")
for _, event := range events {