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

@ -1,5 +1,5 @@
// 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
@ -12,55 +12,55 @@ import (
gomock "github.com/golang/mock/gomock"
)
// MockMessageSubscriber is a mock of MessageSubscriber interface.
type MockMessageSubscriber struct {
// MockEventSubscriber is a mock of EventSubscriber interface.
type MockEventSubscriber struct {
ctrl *gomock.Controller
recorder *MockMessageSubscriberMockRecorder
recorder *MockEventSubscriberMockRecorder
}
// MockMessageSubscriberMockRecorder is the mock recorder for MockMessageSubscriber.
type MockMessageSubscriberMockRecorder struct {
mock *MockMessageSubscriber
// MockEventSubscriberMockRecorder is the mock recorder for MockEventSubscriber.
type MockEventSubscriberMockRecorder struct {
mock *MockEventSubscriber
}
// NewMockMessageSubscriber creates a new mock instance.
func NewMockMessageSubscriber(ctrl *gomock.Controller) *MockMessageSubscriber {
mock := &MockMessageSubscriber{ctrl: ctrl}
mock.recorder = &MockMessageSubscriberMockRecorder{mock}
// NewMockEventSubscriber creates a new mock instance.
func NewMockEventSubscriber(ctrl *gomock.Controller) *MockEventSubscriber {
mock := &MockEventSubscriber{ctrl: ctrl}
mock.recorder = &MockEventSubscriberMockRecorder{mock}
return mock
}
// 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
}
// cancel mocks base method.
func (m *MockMessageSubscriber) cancel() {
func (m *MockEventSubscriber) cancel() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "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()
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.
func (m *MockMessageSubscriber) close() {
func (m *MockEventSubscriber) close() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "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()
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.
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()
ret := m.ctrl.Call(m, "handle", arg0, arg1)
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.
func (mr *MockMessageSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call {
func (mr *MockEventSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call {
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.
func (m *MockMessageSubscriber) name() string {
func (m *MockEventSubscriber) name() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "name")
ret0, _ := ret[0].(string)
@ -82,382 +82,229 @@ func (m *MockMessageSubscriber) name() string {
}
// name indicates an expected call of name.
func (mr *MockMessageSubscriberMockRecorder) name() *gomock.Call {
func (mr *MockEventSubscriberMockRecorder) name() *gomock.Call {
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.
type MockLabelSubscriber struct {
// MockMessageEventHandler is a mock of MessageEventHandler interface.
type MockMessageEventHandler struct {
ctrl *gomock.Controller
recorder *MockLabelSubscriberMockRecorder
recorder *MockMessageEventHandlerMockRecorder
}
// MockLabelSubscriberMockRecorder is the mock recorder for MockLabelSubscriber.
type MockLabelSubscriberMockRecorder struct {
mock *MockLabelSubscriber
// MockMessageEventHandlerMockRecorder is the mock recorder for MockMessageEventHandler.
type MockMessageEventHandlerMockRecorder struct {
mock *MockMessageEventHandler
}
// NewMockLabelSubscriber creates a new mock instance.
func NewMockLabelSubscriber(ctrl *gomock.Controller) *MockLabelSubscriber {
mock := &MockLabelSubscriber{ctrl: ctrl}
mock.recorder = &MockLabelSubscriberMockRecorder{mock}
// NewMockMessageEventHandler creates a new mock instance.
func NewMockMessageEventHandler(ctrl *gomock.Controller) *MockMessageEventHandler {
mock := &MockMessageEventHandler{ctrl: ctrl}
mock.recorder = &MockMessageEventHandlerMockRecorder{mock}
return mock
}
// 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
}
// cancel mocks base method.
func (m *MockLabelSubscriber) cancel() {
// HandleMessageEvents mocks base method.
func (m *MockMessageEventHandler) HandleMessageEvents(arg0 context.Context, arg1 []proton.MessageEvent) error {
m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel")
}
// 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)
ret := m.ctrl.Call(m, "HandleMessageEvents", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// handle indicates an expected call of handle.
func (mr *MockLabelSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call {
// HandleMessageEvents indicates an expected call of HandleMessageEvents.
func (mr *MockMessageEventHandlerMockRecorder) HandleMessageEvents(arg0, arg1 interface{}) *gomock.Call {
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.
func (m *MockLabelSubscriber) name() string {
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 {
// MockLabelEventHandler is a mock of LabelEventHandler interface.
type MockLabelEventHandler struct {
ctrl *gomock.Controller
recorder *MockAddressSubscriberMockRecorder
recorder *MockLabelEventHandlerMockRecorder
}
// MockAddressSubscriberMockRecorder is the mock recorder for MockAddressSubscriber.
type MockAddressSubscriberMockRecorder struct {
mock *MockAddressSubscriber
// MockLabelEventHandlerMockRecorder is the mock recorder for MockLabelEventHandler.
type MockLabelEventHandlerMockRecorder struct {
mock *MockLabelEventHandler
}
// NewMockAddressSubscriber creates a new mock instance.
func NewMockAddressSubscriber(ctrl *gomock.Controller) *MockAddressSubscriber {
mock := &MockAddressSubscriber{ctrl: ctrl}
mock.recorder = &MockAddressSubscriberMockRecorder{mock}
// NewMockLabelEventHandler creates a new mock instance.
func NewMockLabelEventHandler(ctrl *gomock.Controller) *MockLabelEventHandler {
mock := &MockLabelEventHandler{ctrl: ctrl}
mock.recorder = &MockLabelEventHandlerMockRecorder{mock}
return mock
}
// 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
}
// cancel mocks base method.
func (m *MockAddressSubscriber) cancel() {
// HandleLabelEvents mocks base method.
func (m *MockLabelEventHandler) HandleLabelEvents(arg0 context.Context, arg1 []proton.LabelEvent) error {
m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel")
}
// 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)
ret := m.ctrl.Call(m, "HandleLabelEvents", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// handle indicates an expected call of handle.
func (mr *MockAddressSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call {
// HandleLabelEvents indicates an expected call of HandleLabelEvents.
func (mr *MockLabelEventHandlerMockRecorder) HandleLabelEvents(arg0, arg1 interface{}) *gomock.Call {
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.
func (m *MockAddressSubscriber) name() string {
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 {
// MockAddressEventHandler is a mock of AddressEventHandler interface.
type MockAddressEventHandler struct {
ctrl *gomock.Controller
recorder *MockRefreshSubscriberMockRecorder
recorder *MockAddressEventHandlerMockRecorder
}
// MockRefreshSubscriberMockRecorder is the mock recorder for MockRefreshSubscriber.
type MockRefreshSubscriberMockRecorder struct {
mock *MockRefreshSubscriber
// MockAddressEventHandlerMockRecorder is the mock recorder for MockAddressEventHandler.
type MockAddressEventHandlerMockRecorder struct {
mock *MockAddressEventHandler
}
// NewMockRefreshSubscriber creates a new mock instance.
func NewMockRefreshSubscriber(ctrl *gomock.Controller) *MockRefreshSubscriber {
mock := &MockRefreshSubscriber{ctrl: ctrl}
mock.recorder = &MockRefreshSubscriberMockRecorder{mock}
// NewMockAddressEventHandler creates a new mock instance.
func NewMockAddressEventHandler(ctrl *gomock.Controller) *MockAddressEventHandler {
mock := &MockAddressEventHandler{ctrl: ctrl}
mock.recorder = &MockAddressEventHandlerMockRecorder{mock}
return mock
}
// 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
}
// cancel mocks base method.
func (m *MockRefreshSubscriber) cancel() {
// HandleAddressEvents mocks base method.
func (m *MockAddressEventHandler) HandleAddressEvents(arg0 context.Context, arg1 []proton.AddressEvent) error {
m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel")
}
// 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)
ret := m.ctrl.Call(m, "HandleAddressEvents", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// handle indicates an expected call of handle.
func (mr *MockRefreshSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call {
// HandleAddressEvents indicates an expected call of HandleAddressEvents.
func (mr *MockAddressEventHandlerMockRecorder) HandleAddressEvents(arg0, arg1 interface{}) *gomock.Call {
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.
func (m *MockRefreshSubscriber) name() string {
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 {
// MockRefreshEventHandler is a mock of RefreshEventHandler interface.
type MockRefreshEventHandler struct {
ctrl *gomock.Controller
recorder *MockUserSubscriberMockRecorder
recorder *MockRefreshEventHandlerMockRecorder
}
// MockUserSubscriberMockRecorder is the mock recorder for MockUserSubscriber.
type MockUserSubscriberMockRecorder struct {
mock *MockUserSubscriber
// MockRefreshEventHandlerMockRecorder is the mock recorder for MockRefreshEventHandler.
type MockRefreshEventHandlerMockRecorder struct {
mock *MockRefreshEventHandler
}
// NewMockUserSubscriber creates a new mock instance.
func NewMockUserSubscriber(ctrl *gomock.Controller) *MockUserSubscriber {
mock := &MockUserSubscriber{ctrl: ctrl}
mock.recorder = &MockUserSubscriberMockRecorder{mock}
// NewMockRefreshEventHandler creates a new mock instance.
func NewMockRefreshEventHandler(ctrl *gomock.Controller) *MockRefreshEventHandler {
mock := &MockRefreshEventHandler{ctrl: ctrl}
mock.recorder = &MockRefreshEventHandlerMockRecorder{mock}
return mock
}
// 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
}
// cancel mocks base method.
func (m *MockUserSubscriber) cancel() {
// HandleRefreshEvent mocks base method.
func (m *MockRefreshEventHandler) HandleRefreshEvent(arg0 context.Context, arg1 proton.RefreshFlag) error {
m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel")
}
// 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)
ret := m.ctrl.Call(m, "HandleRefreshEvent", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// handle indicates an expected call of handle.
func (mr *MockUserSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call {
// HandleRefreshEvent indicates an expected call of HandleRefreshEvent.
func (mr *MockRefreshEventHandlerMockRecorder) HandleRefreshEvent(arg0, arg1 interface{}) *gomock.Call {
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.
func (m *MockUserSubscriber) name() string {
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 {
// MockUserEventHandler is a mock of UserEventHandler interface.
type MockUserEventHandler struct {
ctrl *gomock.Controller
recorder *MockUserUsedSpaceSubscriberMockRecorder
recorder *MockUserEventHandlerMockRecorder
}
// MockUserUsedSpaceSubscriberMockRecorder is the mock recorder for MockUserUsedSpaceSubscriber.
type MockUserUsedSpaceSubscriberMockRecorder struct {
mock *MockUserUsedSpaceSubscriber
// MockUserEventHandlerMockRecorder is the mock recorder for MockUserEventHandler.
type MockUserEventHandlerMockRecorder struct {
mock *MockUserEventHandler
}
// NewMockUserUsedSpaceSubscriber creates a new mock instance.
func NewMockUserUsedSpaceSubscriber(ctrl *gomock.Controller) *MockUserUsedSpaceSubscriber {
mock := &MockUserUsedSpaceSubscriber{ctrl: ctrl}
mock.recorder = &MockUserUsedSpaceSubscriberMockRecorder{mock}
// NewMockUserEventHandler creates a new mock instance.
func NewMockUserEventHandler(ctrl *gomock.Controller) *MockUserEventHandler {
mock := &MockUserEventHandler{ctrl: ctrl}
mock.recorder = &MockUserEventHandlerMockRecorder{mock}
return mock
}
// 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
}
// cancel mocks base method.
func (m *MockUserUsedSpaceSubscriber) cancel() {
// HandleUserEvent mocks base method.
func (m *MockUserEventHandler) HandleUserEvent(arg0 context.Context, arg1 *proton.User) error {
m.ctrl.T.Helper()
m.ctrl.Call(m, "cancel")
}
// 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)
ret := m.ctrl.Call(m, "HandleUserEvent", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// handle indicates an expected call of handle.
func (mr *MockUserUsedSpaceSubscriberMockRecorder) handle(arg0, arg1 interface{}) *gomock.Call {
// HandleUserEvent indicates an expected call of HandleUserEvent.
func (mr *MockUserEventHandlerMockRecorder) HandleUserEvent(arg0, arg1 interface{}) *gomock.Call {
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.
func (m *MockUserUsedSpaceSubscriber) name() string {
// MockUserUsedSpaceEventHandler is a mock of UserUsedSpaceEventHandler interface.
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()
ret := m.ctrl.Call(m, "name")
ret0, _ := ret[0].(string)
ret := m.ctrl.Call(m, "HandleUsedSpaceEvent", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// name indicates an expected call of name.
func (mr *MockUserUsedSpaceSubscriberMockRecorder) name() *gomock.Call {
// HandleUsedSpaceEvent indicates an expected call of HandleUsedSpaceEvent.
func (mr *MockUserUsedSpaceEventHandlerMockRecorder) HandleUsedSpaceEvent(arg0, arg1 interface{}) *gomock.Call {
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
panicHandler async.PanicHandler
userSubscriberList userSubscriberList
addressSubscribers addressSubscriberList
labelSubscribers labelSubscriberList
messageSubscribers messageSubscriberList
refreshSubscribers refreshSubscriberList
userUsedSpaceSubscriber userUsedSpaceSubscriberList
subscriberList eventSubscriberList
pendingSubscriptionsLock sync.Mutex
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.
// This method can safely be called during event handling.
func (s *Service) Subscribe(subscription Subscription) {
func (s *Service) Subscribe(subscription EventSubscriber) {
s.pendingSubscriptionsLock.Lock()
defer s.pendingSubscriptionsLock.Unlock()
@ -157,7 +100,7 @@ func (s *Service) Subscribe(subscription Subscription) {
// Unsubscribe removes subscribers from the service.
// This method can safely be called during event handling.
func (s *Service) Unsubscribe(subscription Subscription) {
func (s *Service) Unsubscribe(subscription EventSubscriber) {
subscription.cancel()
s.pendingSubscriptionsLock.Lock()
@ -288,7 +231,7 @@ func (s *Service) Close() {
s.pendingSubscriptionsLock.Lock()
defer s.pendingSubscriptionsLock.Unlock()
processed := xmaps.Set[Subscription]{}
processed := xmaps.Set[EventSubscriber]{}
// Cleanup pending removes.
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")
if event.Refresh&proton.RefreshMail != 0 {
s.log.Info("Handling 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
s.log.Info("Received refresh event")
}
// Start with user events.
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
return s.subscriberList.PublishParallel(ctx, event, s.panicHandler, s.eventTimeout)
}
func unpackPublisherError(err error) (string, error) {
var addressErr *addressPublishError
var labelErr *labelPublishError
var messageErr *messagePublishError
var refreshErr *refreshPublishError
var userErr *userPublishError
var usedSpaceErr *userUsedEventPublishError
var publishErr *eventPublishError
switch {
case errors.As(err, &userErr):
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
if errors.As(err, &publishErr) {
return publishErr.subscriber.name(), publishErr.error
}
return "", err
}
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)
}
func (s *Service) addSubscription(subscription Subscription) {
if subscription.User != nil {
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) addSubscription(subscription EventSubscriber) {
s.subscriberList.Add(subscription)
}
func (s *Service) removeSubscription(subscription Subscription) {
if subscription.User != nil {
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)
}
func (s *Service) removeSubscription(subscription EventSubscriber) {
s.subscriberList.Remove(subscription)
}
type pendingOp int
@ -498,5 +347,5 @@ const (
type pendingSubscription struct {
op pendingOp
sub Subscription
sub EventSubscriber
}

View File

@ -52,9 +52,9 @@ func TestServiceHandleEventError_SubscriberEventUnwrapping(t *testing.T) {
lastEventID := "PrevEvent"
event := proton.Event{EventID: "MyEvent"}
subscriber := &noOpSubscriber[proton.AddressEvent]{}
subscriber := &noOpSubscriber[proton.Event]{}
err := &addressPublishError{
err := &eventPublishError{
subscriber: subscriber,
error: &proton.NetError{},
}
@ -192,7 +192,7 @@ func (n noOpSubscriber[T]) name() string { //nolint:unused
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
}

View File

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

View File

@ -87,7 +87,7 @@ func TestService_RetryEventOnNonCatastrophicFailure(t *testing.T) {
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
eventIDStore := mocks.NewMockEventIDStore(mockCtrl)
eventSource := mocks.NewMockEventSource(mockCtrl)
subscriber := NewMockMessageSubscriber(mockCtrl)
subscriber := NewMockMessageEventHandler(mockCtrl)
firstEventID := "EVENT01"
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)
// Subscriber expectations.
subscriber.EXPECT().name().AnyTimes().Return("Foo")
{
firstCall := subscriber.EXPECT().handle(gomock.Any(), gomock.Eq(messageEvents)).Times(1).Return(io.ErrUnexpectedEOF)
subscriber.EXPECT().handle(gomock.Any(), gomock.Eq(messageEvents)).After(firstCall).Times(1).Return(nil)
firstCall := subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).Times(1).Return(io.ErrUnexpectedEOF)
subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).After(firstCall).Times(1).Return(nil)
}
service := NewService(
@ -129,7 +128,7 @@ func TestService_RetryEventOnNonCatastrophicFailure(t *testing.T) {
time.Second,
async.NoopPanicHandler{},
)
service.Subscribe(Subscription{Messages: subscriber})
service.Subscribe(NewCallbackSubscriber("foo", EventHandler{MessageHandler: subscriber}))
require.NoError(t, service.Start(context.Background(), group))
service.Resume()
@ -142,7 +141,7 @@ func TestService_OnBadEventServiceIsPaused(t *testing.T) {
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
eventIDStore := mocks.NewMockEventIDStore(mockCtrl)
eventSource := mocks.NewMockEventSource(mockCtrl)
subscriber := NewMockMessageSubscriber(mockCtrl)
subscriber := NewMockMessageEventHandler(mockCtrl)
firstEventID := "EVENT01"
secondEventID := "EVENT02"
@ -164,8 +163,7 @@ func TestService_OnBadEventServiceIsPaused(t *testing.T) {
// Subscriber expectations.
badEventErr := fmt.Errorf("I will cause bad event")
subscriber.EXPECT().name().AnyTimes().Return("Foo")
subscriber.EXPECT().handle(gomock.Any(), gomock.Eq(messageEvents)).Times(1).Return(badEventErr)
subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).Times(1).Return(badEventErr)
service := NewService(
"foo",
@ -184,7 +182,7 @@ func TestService_OnBadEventServiceIsPaused(t *testing.T) {
OldEventID: firstEventID,
NewEventID: secondEventID,
EventInfo: secondEvent[0].String(),
Error: badEventErr,
Error: fmt.Errorf("failed to apply message events: %w", badEventErr),
}).Do(func(_ context.Context, event events.Event) {
group.Go(context.Background(), "", "", func(_ context.Context) {
// 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))
service.Resume()
group.Wait()
@ -205,7 +203,7 @@ func TestService_UnsubscribeDuringEventHandlingDoesNotCauseDeadlock(t *testing.T
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
eventIDStore := mocks.NewMockEventIDStore(mockCtrl)
eventSource := mocks.NewMockEventSource(mockCtrl)
subscriber := NewMockMessageSubscriber(mockCtrl)
subscriber := NewMockMessageEventHandler(mockCtrl)
firstEventID := "EVENT01"
secondEventID := "EVENT02"
@ -241,16 +239,15 @@ func TestService_UnsubscribeDuringEventHandlingDoesNotCauseDeadlock(t *testing.T
async.NoopPanicHandler{},
)
subscription := NewCallbackSubscriber("foo", EventHandler{MessageHandler: subscriber})
// Subscriber expectations.
subscriber.EXPECT().name().AnyTimes().Return("Foo")
subscriber.EXPECT().cancel().Times(1)
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})
subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).Times(1).DoAndReturn(func(_ context.Context, _ []proton.MessageEvent) error {
service.Unsubscribe(subscription)
return nil
})
service.Subscribe(Subscription{Messages: subscriber})
service.Subscribe(subscription)
require.NoError(t, service.Start(context.Background(), group))
service.Resume()
group.Wait()
@ -262,7 +259,6 @@ func TestService_UnsubscribeBeforeHandlingEventIsNotConsideredError(t *testing.T
eventPublisher := mocks2.NewMockEventPublisher(mockCtrl)
eventIDStore := mocks.NewMockEventIDStore(mockCtrl)
eventSource := mocks.NewMockEventSource(mockCtrl)
subscriber := NewMessageSubscriber("My subscriber")
firstEventID := "EVENT01"
secondEventID := "EVENT02"
@ -299,16 +295,42 @@ func TestService_UnsubscribeBeforeHandlingEventIsNotConsideredError(t *testing.T
async.NoopPanicHandler{},
)
subscription := NewEventSubscriber("Foo")
// start subscriber
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
<-time.NewTicker(500 * time.Millisecond).C
})
service.Subscribe(Subscription{Messages: subscriber})
service.Subscribe(subscription)
require.NoError(t, service.Start(context.Background(), group))
service.Resume()
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.
type Subscribable interface {
Subscribe(subscription Subscription)
Unsubscribe(subscription Subscription)
Subscribe(subscription EventSubscriber)
Unsubscribe(subscription EventSubscriber)
}
type NoOpSubscribable struct{}
func (n NoOpSubscribable) Subscribe(_ Subscription) {
func (n NoOpSubscribable) Subscribe(_ EventSubscriber) {
// Does nothing
}
func (n NoOpSubscribable) Unsubscribe(_ Subscription) {
func (n NoOpSubscribable) Unsubscribe(_ EventSubscriber) {
// Does nothing
}

View File

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