Skip to content

Commit 7c982a3

Browse files
authored
Add fallback notifications from UTDs to the push history (#5047)
1 parent d53457e commit 7c982a3

File tree

7 files changed

+93
-9
lines changed

7 files changed

+93
-9
lines changed

libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,12 @@ class DefaultNotifiableEventResolver @Inject constructor(
236236
}
237237
NotificationContent.MessageLike.RoomEncrypted -> {
238238
Timber.tag(loggerTag.value).w("Notification with encrypted content -> fallback")
239-
val fallbackNotifiableEvent = fallbackNotificationFactory.create(userId, roomId, eventId)
239+
val fallbackNotifiableEvent = fallbackNotificationFactory.create(
240+
sessionId = userId,
241+
roomId = roomId,
242+
eventId = eventId,
243+
cause = "Unable to decrypt event content",
244+
)
240245
ResolvedPushEvent.Event(fallbackNotifiableEvent)
241246
}
242247
is NotificationContent.MessageLike.RoomRedaction -> {

libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FallbackNotificationFactory.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class FallbackNotificationFactory @Inject constructor(
2424
sessionId: SessionId,
2525
roomId: RoomId,
2626
eventId: EventId,
27+
cause: String?,
2728
): FallbackNotifiableEvent = FallbackNotifiableEvent(
2829
sessionId = sessionId,
2930
roomId = roomId,
@@ -34,5 +35,6 @@ class FallbackNotificationFactory @Inject constructor(
3435
isUpdated = false,
3536
timestamp = clock.epochMillis(),
3637
description = stringProvider.getString(R.string.notification_fallback_content),
38+
cause = cause,
3739
)
3840
}

libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/FallbackNotifiableEvent.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ data class FallbackNotifiableEvent(
2525
override val isRedacted: Boolean,
2626
override val isUpdated: Boolean,
2727
val timestamp: Long,
28+
val cause: String?,
2829
) : NotifiableEvent

libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import io.element.android.libraries.push.impl.notifications.FallbackNotification
2727
import io.element.android.libraries.push.impl.notifications.NotificationEventRequest
2828
import io.element.android.libraries.push.impl.notifications.NotificationResolverQueue
2929
import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels
30+
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
3031
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
3132
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
3233
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
@@ -90,13 +91,23 @@ class DefaultPushHandler @Inject constructor(
9091
} else {
9192
result.fold(
9293
onSuccess = {
93-
pushHistoryService.onSuccess(
94-
providerInfo = request.providerInfo,
95-
eventId = request.eventId,
96-
roomId = request.roomId,
97-
sessionId = request.sessionId,
98-
comment = "Push handled successfully",
99-
)
94+
if (it is ResolvedPushEvent.Event && it.notifiableEvent is FallbackNotifiableEvent) {
95+
pushHistoryService.onUnableToResolveEvent(
96+
providerInfo = request.providerInfo,
97+
eventId = request.eventId,
98+
roomId = request.roomId,
99+
sessionId = request.sessionId,
100+
reason = it.notifiableEvent.cause.orEmpty(),
101+
)
102+
} else {
103+
pushHistoryService.onSuccess(
104+
providerInfo = request.providerInfo,
105+
eventId = request.eventId,
106+
roomId = request.roomId,
107+
sessionId = request.sessionId,
108+
comment = "Push handled successfully",
109+
)
110+
}
100111
},
101112
onFailure = { exception ->
102113
if (exception is NotificationResolverException.EventFilteredOut) {
@@ -140,7 +151,14 @@ class DefaultPushHandler @Inject constructor(
140151
}
141152
else -> {
142153
Timber.tag(loggerTag.value).e(exception, "Failed to resolve push event")
143-
ResolvedPushEvent.Event(fallbackNotificationFactory.create(request.sessionId, request.roomId, request.eventId))
154+
ResolvedPushEvent.Event(
155+
fallbackNotificationFactory.create(
156+
sessionId = request.sessionId,
157+
roomId = request.roomId,
158+
eventId = request.eventId,
159+
cause = exception.message,
160+
)
161+
)
144162
}
145163
}
146164
}.getOrNull() ?: continue

libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,7 @@ class DefaultNotifiableEventResolverTest {
625625
isRedacted = false,
626626
isUpdated = false,
627627
timestamp = A_FAKE_TIMESTAMP,
628+
cause = "Unable to decrypt event content",
628629
)
629630
)
630631
assertThat(result.getEvent(request)).isEqualTo(Result.success(expectedResult))

libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class DefaultNotificationCreatorTest {
7171
isRedacted = false,
7272
isUpdated = false,
7373
timestamp = A_FAKE_TIMESTAMP,
74+
cause = null,
7475
)
7576
)
7677
result.commonAssertions(

libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package io.element.android.libraries.push.impl.push
1111

1212
import app.cash.turbine.test
13+
import com.google.common.truth.Truth.assertThat
1314
import io.element.android.features.call.api.CallType
1415
import io.element.android.features.call.test.FakeElementCallEntryPoint
1516
import io.element.android.libraries.core.meta.BuildMeta
@@ -38,6 +39,7 @@ import io.element.android.libraries.push.impl.notifications.NotificationResolver
3839
import io.element.android.libraries.push.impl.notifications.channels.FakeNotificationChannels
3940
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableCallEvent
4041
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
42+
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
4143
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
4244
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
4345
import io.element.android.libraries.push.impl.test.DefaultTestPush
@@ -65,6 +67,7 @@ import kotlin.time.Duration.Companion.milliseconds
6567

6668
private const val A_PUSHER_INFO = "info"
6769

70+
@Suppress("LargeClass")
6871
class DefaultPushHandlerTest {
6972
@Test
7073
fun `check handleInvalid behavior`() = runTest {
@@ -628,6 +631,59 @@ class DefaultPushHandlerTest {
628631
.isCalledExactly(2)
629632
}
630633

634+
@Test
635+
fun `when receiving a fallback event, we notify the push history service about it not being resolved`() = runTest {
636+
val aNotifiableFallbackEvent = FallbackNotifiableEvent(
637+
sessionId = A_SESSION_ID,
638+
roomId = A_ROOM_ID,
639+
eventId = AN_EVENT_ID,
640+
editedEventId = null,
641+
description = "A fallback notification",
642+
canBeReplaced = false,
643+
isRedacted = false,
644+
isUpdated = false,
645+
timestamp = 0L,
646+
cause = "Unable to decrypt event",
647+
)
648+
val notifiableEventResult =
649+
lambdaRecorder<SessionId, List<NotificationEventRequest>, Result<Map<NotificationEventRequest, Result<ResolvedPushEvent>>>> { _, _ ->
650+
val request = NotificationEventRequest(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID, A_PUSHER_INFO)
651+
Result.success(mapOf(request to Result.success(ResolvedPushEvent.Event(aNotifiableFallbackEvent))))
652+
}
653+
val onNotifiableEventsReceived = lambdaRecorder<List<NotifiableEvent>, Unit> {}
654+
val incrementPushCounterResult = lambdaRecorder<Unit> {}
655+
var receivedFallbackEvent = false
656+
val onPushReceivedResult =
657+
lambdaRecorder<String, EventId?, RoomId?, SessionId?, Boolean, Boolean, String?, Unit> { _, _, _, _, isResolved, _, comment ->
658+
receivedFallbackEvent = !isResolved && comment == "Unable to resolve event: ${aNotifiableFallbackEvent.cause}"
659+
}
660+
val pushHistoryService = FakePushHistoryService(
661+
onPushReceivedResult = onPushReceivedResult,
662+
)
663+
val aPushData = PushData(
664+
eventId = AN_EVENT_ID,
665+
roomId = A_ROOM_ID,
666+
unread = 0,
667+
clientSecret = A_SECRET,
668+
)
669+
val defaultPushHandler = createDefaultPushHandler(
670+
onNotifiableEventsReceived = onNotifiableEventsReceived,
671+
notifiableEventsResult = notifiableEventResult,
672+
pushClientSecret = FakePushClientSecret(
673+
getUserIdFromSecretResult = { A_USER_ID }
674+
),
675+
incrementPushCounterResult = incrementPushCounterResult,
676+
pushHistoryService = pushHistoryService,
677+
)
678+
defaultPushHandler.handle(aPushData, A_PUSHER_INFO)
679+
680+
advanceTimeBy(300.milliseconds)
681+
682+
onNotifiableEventsReceived.assertions().isCalledOnce()
683+
684+
assertThat(receivedFallbackEvent).isTrue()
685+
}
686+
631687
private fun TestScope.createDefaultPushHandler(
632688
onNotifiableEventsReceived: (List<NotifiableEvent>) -> Unit = { lambdaError() },
633689
onRedactedEventsReceived: (List<ResolvedPushEvent.Redaction>) -> Unit = { lambdaError() },

0 commit comments

Comments
 (0)