diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ff4610ab..98a32e2b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## EventManager Version [1.19.2] + +Nov 19, 2021 + +### New Features + +- Added two new callbacks to `EventListener`: + - `onSuccess` will be called when the modifications are executed with no issues. + - `onFailure` will be called after the modifications failed too many times and `resetAll` has run. + ## EventManager Version [1.19.1] Nov 18, 2021 diff --git a/event-manager/build.gradle.kts b/event-manager/build.gradle.kts index 4a4a56215..ff570dc28 100644 --- a/event-manager/build.gradle.kts +++ b/event-manager/build.gradle.kts @@ -24,7 +24,7 @@ plugins { kotlin("android") } -libVersion = Version(1, 19, 1) +libVersion = Version(1, 19, 2) android() diff --git a/event-manager/data/src/main/kotlin/me/proton/core/eventmanager/data/EventManagerImpl.kt b/event-manager/data/src/main/kotlin/me/proton/core/eventmanager/data/EventManagerImpl.kt index 8c44821eb..86f168c6b 100644 --- a/event-manager/data/src/main/kotlin/me/proton/core/eventmanager/data/EventManagerImpl.kt +++ b/event-manager/data/src/main/kotlin/me/proton/core/eventmanager/data/EventManagerImpl.kt @@ -100,7 +100,7 @@ class EventManagerImpl @AssistedInject constructor( State.NotifyPrepare -> notifyPrepare(metadata) State.NotifyEvents -> notifyPrepare(metadata) State.NotifyResetAll -> notifyResetAll(metadata) - State.NotifyComplete -> notifyComplete(metadata) + State.NotifyComplete -> notifyComplete(metadata, true) State.Completed -> Unit } } @@ -166,7 +166,7 @@ class EventManagerImpl @AssistedInject constructor( CoreLogger.e(LogTag.NOTIFY_ERROR, it) enqueue(requireNotNull(metadata.eventId), immediately = true) }.onSuccess { - notifyComplete(metadata) + notifyComplete(metadata, success = false) } } @@ -211,11 +211,11 @@ class EventManagerImpl @AssistedInject constructor( CoreLogger.e(LogTag.NOTIFY_ERROR, it) enqueue(metadata.eventId, immediately = true) }.onSuccess { - notifyComplete(metadata) + notifyComplete(metadata, success = true) } } - private suspend fun notifyComplete(metadata: EventMetadata) { + private suspend fun notifyComplete(metadata: EventMetadata, success: Boolean) { runCatching( config = config, eventId = requireNotNull(metadata.eventId), @@ -225,6 +225,11 @@ class EventManagerImpl @AssistedInject constructor( ) { // Fully sequential and ordered. eventListenersByOrder.values.flatten().forEach { eventListener -> + if (success) { + eventListener.notifySuccess(config) + } else { + eventListener.notifyFailure(config) + } eventListener.notifyComplete(config) } }.onFailure { @@ -284,7 +289,7 @@ class EventManagerImpl @AssistedInject constructor( isStarted = true } - private suspend fun internalStop() { + private fun internalStop() { if (!isStarted) return observeAccountJob?.cancel() diff --git a/event-manager/data/src/test/kotlin/me/proton/core/eventmanager/data/EventManagerImplTest.kt b/event-manager/data/src/test/kotlin/me/proton/core/eventmanager/data/EventManagerImplTest.kt index 329bfdabb..6907fc126 100644 --- a/event-manager/data/src/test/kotlin/me/proton/core/eventmanager/data/EventManagerImplTest.kt +++ b/event-manager/data/src/test/kotlin/me/proton/core/eventmanager/data/EventManagerImplTest.kt @@ -279,4 +279,62 @@ class EventManagerImplTest { } assertEquals(calendarId, calendarEventListener.config.asCalendar().calendarId) } + + @Test + fun notifyCompleteCallsOnSuccess() = runBlocking { + // GIVEN + coEvery { eventMetadataRepository.get(any()) } returns + listOf( + EventMetadata( + UserId("userId"), + EventId("eventId"), + EventManagerConfig.Calendar(UserId("userId"), "calendarId"), + state = State.NotifyComplete, + createdAt = 0L, + ) + ) + + // WHEN + calendarManager.process() + + // THEN + coVerify(exactly = 1) { calendarEventListener.onSuccess(any()) } + coVerify(exactly = 1) { calendarEventListener.onComplete(any()) } + + coVerify(exactly = 0) { calendarEventListener.onFailure(any()) } + coVerify(exactly = 0) { calendarEventListener.onResetAll(any()) } + coVerify(exactly = 0) { calendarEventListener.onPrepare(any(), any()) } + coVerify(exactly = 0) { calendarEventListener.onCreate(any(), any()) } + coVerify(exactly = 0) { calendarEventListener.onUpdate(any(), any()) } + coVerify(exactly = 0) { calendarEventListener.onDelete(any(), any()) } + } + + @Test + fun notifyResetAllCallsOnFailure() = runBlocking { + // GIVEN + coEvery { eventMetadataRepository.get(any()) } returns + listOf( + EventMetadata( + UserId("userId"), + EventId("eventId"), + EventManagerConfig.Calendar(UserId("userId"), "calendarId"), + state = State.NotifyResetAll, + createdAt = 0L, + ) + ) + + // WHEN + calendarManager.process() + + // THEN + coVerify(exactly = 1) { calendarEventListener.onResetAll(any()) } + coVerify(exactly = 1) { calendarEventListener.onFailure(any()) } + coVerify(exactly = 1) { calendarEventListener.onComplete(any()) } + + coVerify(exactly = 0) { calendarEventListener.onPrepare(any(), any()) } + coVerify(exactly = 0) { calendarEventListener.onCreate(any(), any()) } + coVerify(exactly = 0) { calendarEventListener.onUpdate(any(), any()) } + coVerify(exactly = 0) { calendarEventListener.onDelete(any(), any()) } + coVerify(exactly = 0) { calendarEventListener.onSuccess(any()) } + } } diff --git a/event-manager/domain/src/main/kotlin/me/proton/core/eventmanager/domain/EventListener.kt b/event-manager/domain/src/main/kotlin/me/proton/core/eventmanager/domain/EventListener.kt index e79d8b918..19b6c3bce 100644 --- a/event-manager/domain/src/main/kotlin/me/proton/core/eventmanager/domain/EventListener.kt +++ b/event-manager/domain/src/main/kotlin/me/proton/core/eventmanager/domain/EventListener.kt @@ -131,6 +131,20 @@ abstract class EventListener : TransactionHandler { setActionMap(config, emptyList()) } + /** + * Notify successful completion of the [EventListener]'s loop. Called before [onComplete]. + * + * Note: No transaction wraps this function. + */ + suspend fun notifySuccess(config: EventManagerConfig) = onSuccess(config) + + /** + * Notify completion with failures of the [EventListener]'s loop. Called before [onComplete] and after [onResetAll]. + * + * Note: No transaction wraps this function. + */ + suspend fun notifyFailure(config: EventManagerConfig) = onFailure(config) + /** * Deserialize [response] into a typed list of [Event]. */ @@ -146,12 +160,31 @@ abstract class EventListener : TransactionHandler { open suspend fun onPrepare(config: EventManagerConfig, entities: List) = Unit /** - * Called at the end of a set of modifications, whether it is successful or not. + * Called at the end of a set of modifications, whether it is successful or not. It can be used to clean up any + * resources that are no longer needed. * * @see onPrepare */ open suspend fun onComplete(config: EventManagerConfig) = Unit + /** + * Called at the end of a set of modifications when it is successful. + * + * Note: [onComplete] will be called right after. + * + * @see onComplete + */ + open suspend fun onSuccess(config: EventManagerConfig) = Unit + + /** + * Called at the end of a set of modifications after it failed. + * + * Note: [onComplete] will be called right after. + * + * @see onComplete + */ + open suspend fun onFailure(config: EventManagerConfig) = Unit + /** * Called to created entities in persistence. *