From df48ed5a2d1bccbeda9f71c50533ddfbcac10750 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Oct 2025 17:31:15 +0200 Subject: [PATCH] Introduce JsonProvider. It will ensure that classes are using the correct Json instances in the unit tests. --- .../io/element/android/x/di/AppModule.kt | 7 ----- .../impl/utils/WidgetMessageSerializer.kt | 8 +++--- .../call/ui/CallScreenPresenterTest.kt | 4 +-- .../screens/createaccount/MessageParser.kt | 6 ++-- .../createaccount/DefaultMessageParserTest.kt | 4 +-- libraries/androidutils/build.gradle.kts | 1 + .../androidutils/json/JsonProvider.kt | 28 +++++++++++++++++++ libraries/network/build.gradle.kts | 1 + .../libraries/network/RetrofitFactory.kt | 6 ++-- .../NotificationResolverQueue.kt | 11 +++++++- .../workmanager/FetchNotificationsWorker.kt | 14 +++++++--- .../SyncNotificationWorkManagerRequest.kt | 9 +++--- .../push/impl/push/DefaultPushHandlerTest.kt | 2 ++ .../FetchNotificationWorkerTest.kt | 4 +-- .../SyncNotificationWorkManagerRequestTest.kt | 16 +++++++++-- .../unifiedpush/PushDataUnifiedPush.kt | 1 + .../unifiedpush/UnifiedPushParser.kt | 6 ++-- .../unifiedpush/UnifiedPushParserTest.kt | 8 ++---- libraries/wellknown/impl/build.gradle.kts | 3 +- .../impl/DefaultSessionWellknownRetriever.kt | 8 +++--- .../DefaultSessionWellknownRetrieverTest.kt | 8 +++--- 21 files changed, 103 insertions(+), 52 deletions(-) create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/json/JsonProvider.kt diff --git a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt index 02f132d34a..d98a05321d 100644 --- a/app/src/main/kotlin/io/element/android/x/di/AppModule.kt +++ b/app/src/main/kotlin/io/element/android/x/di/AppModule.kt @@ -35,7 +35,6 @@ import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope import kotlinx.coroutines.plus -import kotlinx.serialization.json.Json import java.io.File @BindingContainer @@ -121,10 +120,4 @@ object AppModule { fun providesEmojibaseProvider(@ApplicationContext context: Context): EmojibaseProvider { return DefaultEmojibaseProvider(context) } - - @Provides - @SingleIn(AppScope::class) - fun providesJson(): Json = Json { - ignoreUnknownKeys = true - } } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WidgetMessageSerializer.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WidgetMessageSerializer.kt index 2beaea7061..6f98889b6f 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WidgetMessageSerializer.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WidgetMessageSerializer.kt @@ -9,18 +9,18 @@ package io.element.android.features.call.impl.utils import dev.zacsweers.metro.Inject import io.element.android.features.call.impl.data.WidgetMessage +import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.core.extensions.runCatchingExceptions -import kotlinx.serialization.json.Json @Inject class WidgetMessageSerializer( - private val json: Json, + private val json: JsonProvider, ) { fun deserialize(message: String): Result { - return runCatchingExceptions { json.decodeFromString(WidgetMessage.serializer(), message) } + return runCatchingExceptions { json().decodeFromString(WidgetMessage.serializer(), message) } } fun serialize(message: WidgetMessage): String { - return json.encodeToString(WidgetMessage.serializer(), message) + return json().encodeToString(WidgetMessage.serializer(), message) } } diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt index c3a68994da..ba0307f119 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt @@ -20,6 +20,7 @@ import io.element.android.features.call.impl.utils.WidgetMessageSerializer import io.element.android.features.call.utils.FakeActiveCallManager import io.element.android.features.call.utils.FakeCallWidgetProvider import io.element.android.features.call.utils.FakeWidgetMessageInterceptor +import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.sync.SyncState @@ -47,7 +48,6 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json import org.junit.Rule import org.junit.Test import kotlin.time.Duration.Companion.seconds @@ -412,7 +412,7 @@ class CallScreenPresenterTest { languageTagProvider = FakeLanguageTagProvider("en-US"), appForegroundStateService = appForegroundStateService, appCoroutineScope = backgroundScope, - widgetMessageSerializer = WidgetMessageSerializer(Json { ignoreUnknownKeys = true }), + widgetMessageSerializer = WidgetMessageSerializer(DefaultJsonProvider()), ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt index 8762bc5e46..b28eef9fa4 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/createaccount/MessageParser.kt @@ -11,8 +11,8 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.matrix.api.auth.external.ExternalSession -import kotlinx.serialization.json.Json interface MessageParser { /** @@ -26,10 +26,10 @@ interface MessageParser { @Inject class DefaultMessageParser( private val accountProviderDataSource: AccountProviderDataSource, - private val json: Json, + private val json: JsonProvider, ) : MessageParser { override fun parse(message: String): ExternalSession { - val response = json.decodeFromString(MobileRegistrationResponse.serializer(), message) + val response = json().decodeFromString(MobileRegistrationResponse.serializer(), message) val userId = response.userId ?: error("No user ID in response") val homeServer = response.homeServer ?: accountProviderDataSource.flow.value.url val accessToken = response.accessToken ?: error("No access token in response") diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt index fa97a2b1fc..bc8735ed06 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/createaccount/DefaultMessageParserTest.kt @@ -11,9 +11,9 @@ import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.AuthenticationConfig import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource +import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.matrix.api.auth.external.ExternalSession import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.Json import org.junit.Assert.assertThrows import org.junit.Test @@ -70,7 +70,7 @@ class DefaultMessageParserTest { private fun createDefaultMessageParser(): DefaultMessageParser { return DefaultMessageParser( accountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), - json = Json { ignoreUnknownKeys = true }, + json = DefaultJsonProvider(), ) } } diff --git a/libraries/androidutils/build.gradle.kts b/libraries/androidutils/build.gradle.kts index ac1e317b48..000be4e349 100644 --- a/libraries/androidutils/build.gradle.kts +++ b/libraries/androidutils/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation(libs.androidx.recyclerview) implementation(libs.androidx.exifinterface) implementation(libs.androidx.datastore.preferences) + implementation(libs.serialization.json) api(libs.androidx.browser) testCommonDependencies(libs) diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/json/JsonProvider.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/json/JsonProvider.kt new file mode 100644 index 0000000000..08226060db --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/json/JsonProvider.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.androidutils.json + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.Provider +import dev.zacsweers.metro.SingleIn +import kotlinx.serialization.json.Json + +/** + * Provides a Json instance configured to ignore unknown keys. + */ +interface JsonProvider : Provider + +@ContributesBinding(AppScope::class) +@SingleIn(AppScope::class) +@Inject +class DefaultJsonProvider : JsonProvider { + private val json: Json by lazy { Json { ignoreUnknownKeys = true } } + override fun invoke() = json +} diff --git a/libraries/network/build.gradle.kts b/libraries/network/build.gradle.kts index b702ff7881..f81ad5e1e8 100644 --- a/libraries/network/build.gradle.kts +++ b/libraries/network/build.gradle.kts @@ -23,6 +23,7 @@ android { setupDependencyInjection() dependencies { + implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.di) implementation(projects.libraries.matrix.api) diff --git a/libraries/network/src/main/kotlin/io/element/android/libraries/network/RetrofitFactory.kt b/libraries/network/src/main/kotlin/io/element/android/libraries/network/RetrofitFactory.kt index 261d7d02ba..db9c93f84e 100644 --- a/libraries/network/src/main/kotlin/io/element/android/libraries/network/RetrofitFactory.kt +++ b/libraries/network/src/main/kotlin/io/element/android/libraries/network/RetrofitFactory.kt @@ -9,8 +9,8 @@ package io.element.android.libraries.network import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Provider +import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.core.uri.ensureTrailingSlash -import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import retrofit2.Retrofit @@ -19,11 +19,11 @@ import retrofit2.converter.kotlinx.serialization.asConverterFactory @Inject class RetrofitFactory( private val okHttpClient: Provider, - private val json: Provider, + private val json: Provider, ) { fun create(baseUrl: String): Retrofit = Retrofit.Builder() .baseUrl(baseUrl.ensureTrailingSlash()) - .addConverterFactory(json().asConverterFactory("application/json".toMediaType())) + .addConverterFactory(json()().asConverterFactory("application/json".toMediaType())) .callFactory { request -> okHttpClient().newCall(request) } .build() } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResolverQueue.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResolverQueue.kt index 3826a8031c..928f7bfee1 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResolverQueue.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResolverQueue.kt @@ -11,6 +11,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn +import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags @@ -49,10 +50,12 @@ class DefaultNotificationResolverQueue( private val appCoroutineScope: CoroutineScope, private val workManagerScheduler: WorkManagerScheduler, private val featureFlagService: FeatureFlagService, + private val json: JsonProvider, ) : NotificationResolverQueue { companion object { private const val BATCH_WINDOW_MS = 250L } + private val requestQueue = Channel(capacity = 100) private var currentProcessingJob: Job? = null @@ -94,7 +97,13 @@ class DefaultNotificationResolverQueue( if (featureFlagService.isFeatureEnabled(FeatureFlags.SyncNotificationsWithWorkManager)) { for ((sessionId, requests) in groupedRequestsById) { - workManagerScheduler.submit(SyncNotificationWorkManagerRequest(sessionId, requests)) + workManagerScheduler.submit( + SyncNotificationWorkManagerRequest( + sessionId = sessionId, + notificationEventRequests = requests, + json = json, + ) + ) } } else { val sessionIds = groupedRequestsById.keys diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/FetchNotificationsWorker.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/FetchNotificationsWorker.kt index 79f9449b2c..4839bc193f 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/FetchNotificationsWorker.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/FetchNotificationsWorker.kt @@ -18,6 +18,7 @@ import dev.zacsweers.metro.ContributesIntoMap import dev.zacsweers.metro.binding import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus +import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.di.annotations.ApplicationContext @@ -33,7 +34,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull -import kotlinx.serialization.json.Json import timber.log.Timber import kotlin.time.Duration.Companion.seconds @@ -47,13 +47,13 @@ class FetchNotificationsWorker( private val workManagerScheduler: WorkManagerScheduler, private val syncOnNotifiableEvent: SyncOnNotifiableEvent, private val coroutineDispatchers: CoroutineDispatchers, - private val json: Json, + private val json: JsonProvider, ) : CoroutineWorker(context, workerParams) { override suspend fun doWork(): Result = withContext(coroutineDispatchers.io) { Timber.d("FetchNotificationsWorker started") val rawRequestsJson = inputData.getString("requests") ?: return@withContext Result.failure() val requests = runCatchingExceptions { - json.decodeFromString>(rawRequestsJson).map { it.toRequest() } + json().decodeFromString>(rawRequestsJson).map { it.toRequest() } }.getOrElse { Timber.e(it, "Failed to deserialize notification requests") return@withContext Result.failure() @@ -93,7 +93,13 @@ class FetchNotificationsWorker( for (failedSessionId in failedSyncForSessions) { val requestsToRetry = groupedRequests[failedSessionId] ?: continue Timber.d("Re-scheduling ${requestsToRetry.size} failed notification requests for session $failedSessionId") - workManagerScheduler.submit(SyncNotificationWorkManagerRequest(failedSessionId, requestsToRetry)) + workManagerScheduler.submit( + SyncNotificationWorkManagerRequest( + sessionId = failedSessionId, + notificationEventRequests = requestsToRetry, + json = json, + ) + ) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/SyncNotificationWorkManagerRequest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/SyncNotificationWorkManagerRequest.kt index 45955a08b5..db08b0041d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/SyncNotificationWorkManagerRequest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/SyncNotificationWorkManagerRequest.kt @@ -11,6 +11,7 @@ import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OutOfQuotaPolicy import androidx.work.WorkRequest import androidx.work.workDataOf +import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -21,22 +22,20 @@ import io.element.android.libraries.workmanager.api.WorkManagerRequestType import io.element.android.libraries.workmanager.api.workManagerTag import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json import timber.log.Timber import java.security.InvalidParameterException class SyncNotificationWorkManagerRequest( private val sessionId: SessionId, private val notificationEventRequests: List, + private val json: JsonProvider, ) : WorkManagerRequest { - private val json = Json { ignoreUnknownKeys = true } - override fun build(): Result { if (notificationEventRequests.isEmpty()) { return Result.failure(InvalidParameterException("notificationEventRequests cannot be empty")) } - val json = runCatchingExceptions { json.encodeToString(notificationEventRequests.map { it.toData() }) } + val json = runCatchingExceptions { json().encodeToString(notificationEventRequests.map { it.toData() }) } .getOrElse { Timber.e(it, "Failed to serialize notification requests") return Result.failure(it) @@ -50,7 +49,7 @@ class SyncNotificationWorkManagerRequest( .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setTraceTag(workManagerTag(sessionId, WorkManagerRequestType.NOTIFICATION_SYNC)) // TODO investigate using this instead of the resolver queue - // .setInputMerger() + // .setInputMerger() .build() ) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt index 6ea36779bb..335e44db42 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt @@ -13,6 +13,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.call.api.CallType import io.element.android.features.call.test.FakeElementCallEntryPoint +import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService @@ -714,6 +715,7 @@ class DefaultPushHandlerTest { appCoroutineScope = backgroundScope, workManagerScheduler = workManagerScheduler, featureFlagService = featureFlagService, + json = DefaultJsonProvider(), ), appCoroutineScope = backgroundScope, fallbackNotificationFactory = FallbackNotificationFactory( diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/workmanager/FetchNotificationWorkerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/workmanager/FetchNotificationWorkerTest.kt index f25df8a13c..4a98ed970a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/workmanager/FetchNotificationWorkerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/workmanager/FetchNotificationWorkerTest.kt @@ -18,6 +18,7 @@ import com.google.common.truth.Truth.assertThat import com.google.common.util.concurrent.ListenableFuture import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.networkmonitor.test.FakeNetworkMonitor +import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.push.api.push.SyncOnNotifiableEvent import io.element.android.libraries.push.impl.notifications.FakeNotifiableEventResolver import io.element.android.libraries.push.impl.notifications.NotificationResolverQueue @@ -33,7 +34,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json import org.junit.Test import org.junit.runner.RunWith import java.util.UUID @@ -175,7 +175,7 @@ class FetchNotificationWorkerTest { workManagerScheduler = workManagerScheduler, syncOnNotifiableEvent = syncOnNotifiableEvent, coroutineDispatchers = testCoroutineDispatchers(), - json = Json {}, + json = DefaultJsonProvider(), ) private fun TestScope.createWorkerParams( diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/workmanager/SyncNotificationWorkManagerRequestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/workmanager/SyncNotificationWorkManagerRequestTest.kt index f8ea92b0cc..91937a2a67 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/workmanager/SyncNotificationWorkManagerRequestTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/workmanager/SyncNotificationWorkManagerRequestTest.kt @@ -10,7 +10,10 @@ package io.element.android.libraries.push.impl.workmanager import androidx.work.OneTimeWorkRequest import androidx.work.hasKeyWithValueOfType import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.androidutils.json.DefaultJsonProvider +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.push.api.push.NotificationEventRequest import io.element.android.libraries.push.impl.notifications.fixtures.aNotificationEventRequest import io.element.android.libraries.workmanager.api.WorkManagerRequestType import io.element.android.libraries.workmanager.api.workManagerTag @@ -20,7 +23,7 @@ import org.junit.Test class SyncNotificationWorkManagerRequestTest { @Test fun `build - success`() = runTest { - val request = SyncNotificationWorkManagerRequest( + val request = createSyncNotificationWorkManagerRequest( sessionId = A_SESSION_ID, notificationEventRequests = listOf(aNotificationEventRequest()) ) @@ -37,7 +40,7 @@ class SyncNotificationWorkManagerRequestTest { @Test fun `build - empty list of requests fails`() = runTest { - val request = SyncNotificationWorkManagerRequest( + val request = createSyncNotificationWorkManagerRequest( sessionId = A_SESSION_ID, notificationEventRequests = emptyList() ) @@ -48,3 +51,12 @@ class SyncNotificationWorkManagerRequestTest { // TODO add test for invalid serialization (how?) } + +private fun createSyncNotificationWorkManagerRequest( + sessionId: SessionId, + notificationEventRequests: List, +) = SyncNotificationWorkManagerRequest( + sessionId = sessionId, + notificationEventRequests = notificationEventRequests, + json = DefaultJsonProvider(), +) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt index c994f466aa..24932d7570 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt @@ -39,6 +39,7 @@ data class PushDataUnifiedPushNotification( @SerialName("event_id") val eventId: String? = null, @SerialName("room_id") val roomId: String? = null, @SerialName("counts") val counts: PushDataUnifiedPushCounts? = null, + @SerialName("prio") val prio: String? = null, ) @Serializable diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParser.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParser.kt index 14a0bf3e85..503df2cae0 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParser.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParser.kt @@ -8,15 +8,15 @@ package io.element.android.libraries.pushproviders.unifiedpush import dev.zacsweers.metro.Inject +import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.pushproviders.api.PushData -import kotlinx.serialization.json.Json @Inject class UnifiedPushParser( - private val json: Json, + private val json: JsonProvider, ) { fun parse(message: ByteArray, clientSecret: String): PushData? { - return tryOrNull { json.decodeFromString(String(message)) }?.toPushData(clientSecret) + return tryOrNull { json().decodeFromString(String(message)) }?.toPushData(clientSecret) } } diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParserTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParserTest.kt index a269f906fe..384abf7a67 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParserTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParserTest.kt @@ -8,11 +8,11 @@ package io.element.android.libraries.pushproviders.unifiedpush import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.pushproviders.api.PushData import io.element.android.tests.testutils.assertThrowsInDebug -import kotlinx.serialization.json.Json import org.junit.Test class UnifiedPushParserTest { @@ -83,8 +83,6 @@ private fun String.mutate(oldValue: String, newValue: String): ByteArray { return replace(oldValue, newValue).toByteArray() } -fun createUnifiedPushParser( - json: Json = Json { ignoreUnknownKeys = true }, -) = UnifiedPushParser( - json = json, +fun createUnifiedPushParser() = UnifiedPushParser( + json = DefaultJsonProvider(), ) diff --git a/libraries/wellknown/impl/build.gradle.kts b/libraries/wellknown/impl/build.gradle.kts index 4de8ecc0fd..efdf8c0660 100644 --- a/libraries/wellknown/impl/build.gradle.kts +++ b/libraries/wellknown/impl/build.gradle.kts @@ -27,8 +27,9 @@ dependencies { implementation(platform(libs.network.retrofit.bom)) implementation(libs.network.retrofit) implementation(libs.serialization.json) - implementation(projects.libraries.architecture) implementation(projects.libraries.core) + implementation(projects.libraries.androidutils) + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.network) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt index c34a0a12d3..6116435970 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt @@ -9,20 +9,20 @@ package io.element.android.libraries.wellknown.impl import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject +import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.SessionWellknownRetriever import io.element.android.libraries.wellknown.api.WellKnown -import kotlinx.serialization.json.Json import timber.log.Timber @ContributesBinding(SessionScope::class) @Inject class DefaultSessionWellknownRetriever( private val matrixClient: MatrixClient, - private val json: Json, + private val json: JsonProvider, ) : SessionWellknownRetriever { private val domain by lazy { matrixClient.userIdServerName() } @@ -32,7 +32,7 @@ class DefaultSessionWellknownRetriever( .getUrl(url) .mapCatchingExceptions { val data = String(it) - json.decodeFromString(InternalWellKnown.serializer(), data) + json().decodeFromString(InternalWellKnown.serializer(), data) } .onFailure { Timber.e(it, "Failed to retrieve .well-known from $domain") } .map { it.map() } @@ -45,7 +45,7 @@ class DefaultSessionWellknownRetriever( .getUrl(url) .mapCatchingExceptions { val data = String(it) - json.decodeFromString(InternalElementWellKnown.serializer(), data) + json().decodeFromString(InternalElementWellKnown.serializer(), data) } .onFailure { Timber.e(it, "Failed to retrieve Element .well-known from $domain") } .map { it.map() } diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt index 2c788cae63..d19530befb 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.wellknown.impl import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.wellknown.api.ElementWellKnown @@ -16,7 +17,6 @@ import io.element.android.libraries.wellknown.api.WellKnownBaseConfig import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.test.runTest -import kotlinx.serialization.json.Json import org.junit.Test class DefaultSessionWellknownRetrieverTest { @@ -107,7 +107,7 @@ class DefaultSessionWellknownRetrieverTest { "other": true }""".trimIndent().toByteArray() ) - } + }, ) assertThat(sut.getWellKnown()).isEqualTo( WellKnown( @@ -201,7 +201,7 @@ class DefaultSessionWellknownRetrieverTest { "other": true }""".trimIndent().toByteArray() ) - } + }, ) assertThat(sut.getElementWellKnown()).isEqualTo( ElementWellKnown( @@ -244,6 +244,6 @@ class DefaultSessionWellknownRetrieverTest { userIdServerNameLambda = { "user.domain.org" }, getUrlLambda = getUrlLambda, ), - json = Json { ignoreUnknownKeys = true }, + json = DefaultJsonProvider(), ) }