Skip to content

Commit 2a50b9a

Browse files
authored
When mapping invalid notification event, only drop that one (#5137)
Previously, this meant the code processing the whole notification batch result failed and other notifications would be lost too.
1 parent 1afcce2 commit 2a50b9a

File tree

5 files changed

+77
-34
lines changed

5 files changed

+77
-34
lines changed

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package io.element.android.libraries.matrix.impl.notification
99

1010
import io.element.android.libraries.core.bool.orFalse
11+
import io.element.android.libraries.core.extensions.runCatchingExceptions
1112
import io.element.android.libraries.matrix.api.core.EventId
1213
import io.element.android.libraries.matrix.api.core.RoomId
1314
import io.element.android.libraries.matrix.api.core.SessionId
@@ -30,43 +31,47 @@ class NotificationMapper(
3031
eventId: EventId,
3132
roomId: RoomId,
3233
notificationItem: NotificationItem
33-
): NotificationData {
34-
return notificationItem.use { item ->
35-
val isDm = isDm(
36-
isDirect = item.roomInfo.isDirect,
37-
activeMembersCount = item.roomInfo.joinedMembersCount.toInt(),
38-
)
39-
NotificationData(
40-
sessionId = sessionId,
41-
eventId = eventId,
42-
// FIXME once the `NotificationItem` in the SDK returns the thread id
43-
threadId = null,
44-
roomId = roomId,
45-
senderAvatarUrl = item.senderInfo.avatarUrl,
46-
senderDisplayName = item.senderInfo.displayName,
47-
senderIsNameAmbiguous = item.senderInfo.isNameAmbiguous,
48-
roomAvatarUrl = item.roomInfo.avatarUrl ?: item.senderInfo.avatarUrl.takeIf { isDm },
49-
roomDisplayName = item.roomInfo.displayName,
50-
isDirect = item.roomInfo.isDirect,
51-
isDm = isDm,
52-
isEncrypted = item.roomInfo.isEncrypted.orFalse(),
53-
isNoisy = item.isNoisy.orFalse(),
54-
timestamp = item.timestamp() ?: clock.epochMillis(),
55-
content = item.event.use { notificationContentMapper.map(it) },
56-
hasMention = item.hasMention.orFalse(),
57-
)
34+
): Result<NotificationData> {
35+
return runCatchingExceptions {
36+
notificationItem.use { item ->
37+
val isDm = isDm(
38+
isDirect = item.roomInfo.isDirect,
39+
activeMembersCount = item.roomInfo.joinedMembersCount.toInt(),
40+
)
41+
NotificationData(
42+
sessionId = sessionId,
43+
eventId = eventId,
44+
// FIXME once the `NotificationItem` in the SDK returns the thread id
45+
threadId = null,
46+
roomId = roomId,
47+
senderAvatarUrl = item.senderInfo.avatarUrl,
48+
senderDisplayName = item.senderInfo.displayName,
49+
senderIsNameAmbiguous = item.senderInfo.isNameAmbiguous,
50+
roomAvatarUrl = item.roomInfo.avatarUrl ?: item.senderInfo.avatarUrl.takeIf { isDm },
51+
roomDisplayName = item.roomInfo.displayName,
52+
isDirect = item.roomInfo.isDirect,
53+
isDm = isDm,
54+
isEncrypted = item.roomInfo.isEncrypted.orFalse(),
55+
isNoisy = item.isNoisy.orFalse(),
56+
timestamp = item.timestamp() ?: clock.epochMillis(),
57+
content = item.event.use { notificationContentMapper.map(it) }.getOrThrow(),
58+
hasMention = item.hasMention.orFalse(),
59+
)
60+
}
5861
}
5962
}
6063
}
6164

6265
class NotificationContentMapper {
6366
private val timelineEventToNotificationContentMapper = TimelineEventToNotificationContentMapper()
6467

65-
fun map(notificationEvent: NotificationEvent): NotificationContent =
68+
fun map(notificationEvent: NotificationEvent): Result<NotificationContent> =
6669
when (notificationEvent) {
6770
is NotificationEvent.Timeline -> timelineEventToNotificationContentMapper.map(notificationEvent.event)
68-
is NotificationEvent.Invite -> NotificationContent.Invite(
69-
senderId = UserId(notificationEvent.sender),
71+
is NotificationEvent.Invite -> Result.success(
72+
NotificationContent.Invite(
73+
senderId = UserId(notificationEvent.sender),
74+
)
7075
)
7176
}
7277
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ class RustNotificationService(
5353
is BatchNotificationResult.Ok -> {
5454
when (val status = result.status) {
5555
is NotificationStatus.Event -> {
56-
put(eventId, Result.success(notificationMapper.map(sessionId, eventId, roomId, status.item)))
56+
val result = notificationMapper.map(sessionId, eventId, roomId, status.item)
57+
result.onFailure { Timber.e(it, "Could not map notification event $eventId") }
58+
put(eventId, result)
5759
}
5860
is NotificationStatus.EventNotFound -> {
5961
Timber.e("Could not retrieve event for notification with $eventId - event not found")

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package io.element.android.libraries.matrix.impl.notification
99

10+
import io.element.android.libraries.core.extensions.runCatchingExceptions
1011
import io.element.android.libraries.matrix.api.core.EventId
1112
import io.element.android.libraries.matrix.api.core.UserId
1213
import io.element.android.libraries.matrix.api.notification.CallNotifyType
@@ -21,10 +22,12 @@ import org.matrix.rustcomponents.sdk.TimelineEventType
2122
import org.matrix.rustcomponents.sdk.use
2223

2324
class TimelineEventToNotificationContentMapper {
24-
fun map(timelineEvent: TimelineEvent): NotificationContent {
25-
return timelineEvent.use {
26-
timelineEvent.eventType().use { eventType ->
27-
eventType.toContent(senderId = UserId(timelineEvent.senderId()))
25+
fun map(timelineEvent: TimelineEvent): Result<NotificationContent> {
26+
return runCatchingExceptions {
27+
timelineEvent.use {
28+
timelineEvent.eventType().use { eventType ->
29+
eventType.toContent(senderId = UserId(timelineEvent.senderId()))
30+
}
2831
}
2932
}
3033
}

libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiTimelineEvent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import org.matrix.rustcomponents.sdk.NoPointer
1414
import org.matrix.rustcomponents.sdk.TimelineEvent
1515
import org.matrix.rustcomponents.sdk.TimelineEventType
1616

17-
class FakeFfiTimelineEvent(
17+
open class FakeFfiTimelineEvent(
1818
val timestamp: ULong = A_FAKE_TIMESTAMP.toULong(),
1919
val timelineEventType: TimelineEventType = aRustTimelineEventTypeMessageLike(),
2020
val senderId: String = A_USER_ID_2.value,

libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationServiceTest.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ import io.element.android.libraries.matrix.api.exception.NotificationResolverExc
1212
import io.element.android.libraries.matrix.api.notification.NotificationContent
1313
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
1414
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustBatchNotificationResult
15+
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustNotificationEventTimeline
16+
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustNotificationItem
1517
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiNotificationClient
18+
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineEvent
1619
import io.element.android.libraries.matrix.test.AN_EVENT_ID
20+
import io.element.android.libraries.matrix.test.AN_EVENT_ID_2
1721
import io.element.android.libraries.matrix.test.A_MESSAGE
1822
import io.element.android.libraries.matrix.test.A_ROOM_ID
1923
import io.element.android.libraries.matrix.test.A_SESSION_ID
@@ -26,6 +30,8 @@ import kotlinx.coroutines.test.TestScope
2630
import kotlinx.coroutines.test.runTest
2731
import org.junit.Test
2832
import org.matrix.rustcomponents.sdk.NotificationClient
33+
import org.matrix.rustcomponents.sdk.NotificationStatus
34+
import org.matrix.rustcomponents.sdk.TimelineEventType
2935

3036
class RustNotificationServiceTest {
3137
@Test
@@ -49,6 +55,33 @@ class RustNotificationServiceTest {
4955
)
5056
}
5157

58+
@Test
59+
fun `test mapping invalid item only drops that item`() = runTest {
60+
val error = IllegalStateException("This event type is not supported")
61+
val faultyEvent = object : FakeFfiTimelineEvent() {
62+
override fun eventType(): TimelineEventType {
63+
throw error
64+
}
65+
}
66+
val notificationClient = FakeFfiNotificationClient(
67+
notificationItemResult = mapOf(
68+
AN_EVENT_ID.value to aRustBatchNotificationResult(
69+
notificationStatus = NotificationStatus.Event(aRustNotificationItem(aRustNotificationEventTimeline(faultyEvent)))
70+
),
71+
AN_EVENT_ID_2.value to aRustBatchNotificationResult()
72+
),
73+
)
74+
val sut = createRustNotificationService(
75+
notificationClient = notificationClient,
76+
)
77+
val result = sut.getNotifications(mapOf(A_ROOM_ID to listOf(AN_EVENT_ID, AN_EVENT_ID_2))).getOrThrow()
78+
val exception = result[AN_EVENT_ID]!!.exceptionOrNull()
79+
assertThat(exception).isEqualTo(error)
80+
81+
val successfulResult = result[AN_EVENT_ID_2]
82+
assertThat(successfulResult?.isSuccess).isTrue()
83+
}
84+
5285
@Test
5386
fun `test unable to resolve event`() = runTest {
5487
val notificationClient = FakeFfiNotificationClient(

0 commit comments

Comments
 (0)