diff --git a/enterprise b/enterprise index cca96c4fc2..60de13ffd7 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit cca96c4fc24b6e2fd1ccf9265c8283ac9bf63dc0 +Subproject commit 60de13ffd7dea97fd4fec6fb6af49b5288e67ff2 diff --git a/features/enterprise/api/build.gradle.kts b/features/enterprise/api/build.gradle.kts index 1c541c5080..5e7f81e4b4 100644 --- a/features/enterprise/api/build.gradle.kts +++ b/features/enterprise/api/build.gradle.kts @@ -17,4 +17,5 @@ dependencies { implementation(projects.libraries.compound) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.wellknown.api) } diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index 92d8b9b646..7b595966d4 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -11,6 +11,7 @@ package io.element.android.features.enterprise.api import androidx.compose.ui.graphics.Color import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow interface EnterpriseService { @@ -41,6 +42,11 @@ interface EnterpriseService { */ fun getNoisyNotificationChannelId(sessionId: SessionId): String? + /** + * Gets the overridden Element Well-Known data if it has been set, or null if not set. + */ + fun overriddenElementWellKnown(): ElementWellKnown? + companion object { const val ANY_ACCOUNT_PROVIDER = "*" } diff --git a/features/enterprise/impl-foss/build.gradle.kts b/features/enterprise/impl-foss/build.gradle.kts index f0c63f5655..a45c8ae167 100644 --- a/features/enterprise/impl-foss/build.gradle.kts +++ b/features/enterprise/impl-foss/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { api(projects.features.enterprise.api) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.wellknown.api) testCommonDependencies(libs) testImplementation(projects.libraries.matrix.test) diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index 6e3ed5d3cc..bc2a3f1bf1 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -15,6 +15,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.wellknown.api.ElementWellKnown import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @@ -45,4 +46,6 @@ class DefaultEnterpriseService : EnterpriseService { } override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null + + override fun overriddenElementWellKnown(): ElementWellKnown? = null } diff --git a/features/enterprise/test/build.gradle.kts b/features/enterprise/test/build.gradle.kts index c37fc53de3..99dbe733dd 100644 --- a/features/enterprise/test/build.gradle.kts +++ b/features/enterprise/test/build.gradle.kts @@ -18,5 +18,6 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.compound) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.wellknown.api) implementation(projects.tests.testutils) } diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt index 805c75be6a..f6379bef30 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -13,6 +13,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.flow.Flow @@ -31,6 +32,7 @@ class FakeEnterpriseService( private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() }, private val tweakMasUrlResult: (String, String) -> String = { _, _ -> lambdaError() }, + private val overrideWellKnownResult: () -> ElementWellKnown? = { lambdaError() }, ) : EnterpriseService { private val brandColorState = MutableStateFlow(initialBrandColor) private val semanticColorsState = MutableStateFlow(initialSemanticColors) @@ -79,4 +81,8 @@ class FakeEnterpriseService( override fun getNoisyNotificationChannelId(sessionId: SessionId): String? { return getNoisyNotificationChannelIdResult(sessionId) } + + override fun overriddenElementWellKnown(): ElementWellKnown? { + return overrideWellKnownResult() + } } diff --git a/features/preferences/api/build.gradle.kts b/features/preferences/api/build.gradle.kts index b5a08b9435..c43259918d 100644 --- a/features/preferences/api/build.gradle.kts +++ b/features/preferences/api/build.gradle.kts @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") id("kotlin-parcelize") } diff --git a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/ExtraDeveloperOptionsRenderer.kt b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/ExtraDeveloperOptionsRenderer.kt new file mode 100644 index 0000000000..7d7eec9d8b --- /dev/null +++ b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/ExtraDeveloperOptionsRenderer.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2026 Element Creations 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.features.preferences.api + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +/** + * Component for rendering extra developer options in the preferences screen. + */ +interface ExtraDeveloperOptionsRenderer { + @Composable + fun Render(modifier: Modifier) +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt index 98c7d89633..a8a8c63eed 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt @@ -18,6 +18,7 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer import io.element.android.libraries.architecture.callback import io.element.android.libraries.designsystem.showkase.getBrowserIntent import io.element.android.libraries.di.SessionScope @@ -28,6 +29,7 @@ class DeveloperSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val presenter: DeveloperSettingsPresenter, + private val extraDeveloperOptionsRenderer: ExtraDeveloperOptionsRenderer, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { fun navigateToPushHistory() @@ -51,6 +53,7 @@ class DeveloperSettingsNode( onOpenShowkase = ::openShowkase, onPushHistoryClick = callback::navigateToPushHistory, onBackClick = callback::onDone, + extraOptions = { extraDeveloperOptionsRenderer.Render(Modifier) }, ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 45009d1f71..0e5f4026ef 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -43,6 +43,7 @@ fun DeveloperSettingsView( onPushHistoryClick: () -> Unit, onBackClick: () -> Unit, modifier: Modifier = Modifier, + extraOptions: @Composable () -> Unit = {}, ) { if (state.showLoader) { ProgressDialog() @@ -97,6 +98,9 @@ fun DeveloperSettingsView( ) } } + + extraOptions() + val cache = state.cacheSize PreferenceCategory(title = "Cache") { ListItem( diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/NoopExtraDeveloperOptionsRenderer.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/NoopExtraDeveloperOptionsRenderer.kt new file mode 100644 index 0000000000..da0540a93f --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/NoopExtraDeveloperOptionsRenderer.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2026 Element Creations 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.features.preferences.impl.developer + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer + +@ContributesBinding(AppScope::class) +class NoopExtraDeveloperOptionsRenderer : ExtraDeveloperOptionsRenderer { + @Composable + override fun Render(modifier: Modifier) { + // No-op + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsNode.kt index ae5e710d4b..89ef240a95 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsNode.kt @@ -18,6 +18,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.libraries.architecture.callback import io.element.android.libraries.designsystem.showkase.getBrowserIntent @@ -28,6 +29,7 @@ class AppDeveloperSettingsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val presenter: AppDeveloperSettingsPresenter, + private val extraDeveloperOptionsRenderer: ExtraDeveloperOptionsRenderer, ) : Node(buildContext, plugins = plugins) { private val callback: PreferencesEntryPoint.DeveloperSettingsCallback = callback() @@ -45,6 +47,7 @@ class AppDeveloperSettingsNode( modifier = modifier, onOpenShowkase = ::openShowkase, onBackClick = callback::onDone, + extraOptions = { extraDeveloperOptionsRenderer.Render(Modifier) }, ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPage.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPage.kt index 81e1304e7b..d7d62aa325 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPage.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPage.kt @@ -24,6 +24,7 @@ fun AppDeveloperSettingsPage( onOpenShowkase: () -> Unit, onBackClick: () -> Unit, modifier: Modifier = Modifier, + extraOptions: @Composable () -> Unit = {}, ) { BackHandler( onBack = onBackClick, @@ -40,6 +41,7 @@ fun AppDeveloperSettingsPage( onOpenShowkase = onOpenShowkase, modifier = Modifier.padding(top = 8.dp) ) + extraOptions() } } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt new file mode 100644 index 0000000000..db0a323a5f --- /dev/null +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnownParser.kt @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2026 Element Creations 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.wellknown.api + +fun interface ElementWellKnownParser { + fun parse(json: String): Result +} diff --git a/libraries/wellknown/impl/build.gradle.kts b/libraries/wellknown/impl/build.gradle.kts index 1e2c4d7d61..6e7cc5ef1c 100644 --- a/libraries/wellknown/impl/build.gradle.kts +++ b/libraries/wellknown/impl/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.network) implementation(projects.libraries.cachestore.api) + implementation(projects.features.enterprise.api) implementation(projects.services.toolbox.api) testCommonDependencies(libs) @@ -41,5 +42,6 @@ dependencies { testImplementation(projects.libraries.cachestore.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.wellknown.test) + testImplementation(projects.features.enterprise.test) testImplementation(projects.services.toolbox.test) } diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultElementWellKnownParser.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultElementWellKnownParser.kt new file mode 100644 index 0000000000..4cf72366e0 --- /dev/null +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultElementWellKnownParser.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026 Element Creations 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.wellknown.impl + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.androidutils.json.JsonProvider +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.ElementWellKnownParser + +@ContributesBinding(AppScope::class) +class DefaultElementWellKnownParser( + private val jsonProvider: JsonProvider, +) : ElementWellKnownParser { + override fun parse(json: String): Result { + return runCatchingExceptions { + val decoder = jsonProvider() + decoder.decodeFromString(json).map() + } + } +} 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 3446cdcb65..66654dd228 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,13 +9,14 @@ package io.element.android.libraries.wellknown.impl import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.androidutils.json.JsonProvider +import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.ElementWellKnownParser import io.element.android.libraries.wellknown.api.ElementWellknownStore import io.element.android.libraries.wellknown.api.SessionWellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult @@ -26,14 +27,20 @@ import timber.log.Timber @ContributesBinding(SessionScope::class) class DefaultSessionWellknownRetriever( private val matrixClient: MatrixClient, - private val json: JsonProvider, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val elementWellknownStore: ElementWellknownStore, + private val elementWellKnownParser: ElementWellKnownParser, + private val enterpriseService: EnterpriseService, ) : SessionWellknownRetriever { private val domain by lazy { matrixClient.userIdServerName() } override suspend fun getElementWellKnown(): WellknownRetrieverResult { + val overriddenElementWellKnown = enterpriseService.overriddenElementWellKnown() + if (overriddenElementWellKnown != null) { + return WellknownRetrieverResult.Success(overriddenElementWellKnown) + } + val cacheData = elementWellknownStore.get(domain) return when (cacheData) { is WellknownRetrieverResult.Success -> { @@ -69,7 +76,7 @@ class DefaultSessionWellknownRetriever( .getUrl(url) .mapCatchingExceptions { val data = String(it) - val parsed = json().decodeFromString(data).map() + val parsed = elementWellKnownParser.parse(data).getOrThrow() // Also store in cache, if valid elementWellknownStore.update(domain, data) .onFailure { exception -> diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt index c3cfcc5019..50c8caeafc 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStore.kt @@ -9,11 +9,11 @@ package io.element.android.libraries.wellknown.impl import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.cachestore.api.CacheData import io.element.android.libraries.cachestore.api.CacheStore import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.ElementWellKnownParser import io.element.android.libraries.wellknown.api.ElementWellknownStore import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.services.toolbox.api.systemclock.SystemClock @@ -21,14 +21,14 @@ import io.element.android.services.toolbox.api.systemclock.SystemClock @ContributesBinding(AppScope::class) class DefaultWellKnownStore( private val cacheStore: CacheStore, - private val json: JsonProvider, + private val elementWellKnownParser: ElementWellKnownParser, private val systemClock: SystemClock, ) : ElementWellknownStore { override suspend fun get(domain: String): WellknownRetrieverResult { return runCatchingExceptions { val cachedData = cacheStore.getData(key(domain)) if (cachedData != null) { - val data = json().decodeFromString(cachedData.value).map() + val data = elementWellKnownParser.parse(cachedData.value).getOrThrow() if (systemClock.epochMillis() > cachedData.updatedAt + CACHE_VALIDITY_MILLIS) { WellknownRetrieverResult.Outdated(data) } else { diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt index a43584d542..10f668cfb0 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.wellknown.impl import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding +import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.uri.ensureProtocol @@ -28,8 +29,14 @@ class DefaultWellknownRetriever( private val retrofitFactory: RetrofitFactory, private val elementWellknownStore: ElementWellknownStore, private val jsonProvider: JsonProvider, + private val enterpriseService: EnterpriseService, ) : WellknownRetriever { override suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult { + val overriddenElementWellKnown = enterpriseService.overriddenElementWellKnown() + if (overriddenElementWellKnown != null) { + return WellknownRetrieverResult.Success(overriddenElementWellKnown) + } + val domain = URL(baseUrl).host return buildWellknownApi(baseUrl) .map { wellknownApi -> 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 6a2a12774b..f54cf919d9 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 @@ -11,15 +11,16 @@ package io.element.android.libraries.wellknown.impl import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.wellknown.test.FakeElementWellknownStore import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.androidutils.json.DefaultJsonProvider -import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase import io.element.android.libraries.wellknown.api.ElementWellKnown +import io.element.android.libraries.wellknown.api.ElementWellKnownParser import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -276,7 +277,7 @@ class DefaultSessionWellknownRetrieverTest { Result.success("{}".toByteArray()) }, cacheStore = cacheStore, - jsonProvider = JsonProvider { error("Failed to parse JSON") } + elementWellKnownParser = { Result.failure(IllegalStateException("Failed to parse JSON")) } ) assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) // Ensure that the cache is deleted after the failure to parse it @@ -320,20 +321,43 @@ class DefaultSessionWellknownRetrieverTest { ) } + @Test + fun `get element wellknown was overridden`() = runTest { + val getLambda = lambdaRecorder> { Result.failure(IllegalStateException("BOOM")) } + val wellKnown = anElementWellKnown() + + val sut = createDefaultSessionWellknownRetriever( + getUrlLambda = getLambda, + enterpriseService = FakeEnterpriseService( + overrideWellKnownResult = { wellKnown } + ) + ) + + // The overridden value is returned + assertThat(sut.getElementWellKnown()).isEqualTo( + WellknownRetrieverResult.Success(wellKnown) + ) + + // And the endpoint is never hit + getLambda.assertions().isNeverCalled() + } + private fun parsedWellKnownContent() = DefaultJsonProvider().invoke().decodeFromString(WELLKNOWN_CONTENT).map() private fun TestScope.createDefaultSessionWellknownRetriever( cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(), getUrlLambda: (String) -> Result, - jsonProvider: JsonProvider = DefaultJsonProvider(), + elementWellKnownParser: ElementWellKnownParser = DefaultElementWellKnownParser(DefaultJsonProvider()), + enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }), ) = DefaultSessionWellknownRetriever( matrixClient = FakeMatrixClient( userIdServerNameLambda = { "user.domain.org" }, getUrlLambda = getUrlLambda, ), - json = jsonProvider, sessionCoroutineScope = backgroundScope, elementWellknownStore = cacheStore, + elementWellKnownParser = elementWellKnownParser, + enterpriseService = enterpriseService, ) companion object { diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt index 11ec521fed..b53041faf8 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellKnownStoreTest.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.cachestore.api.CacheData import io.element.android.libraries.sessionstorage.test.InMemoryCacheStore import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase +import io.element.android.libraries.wellknown.api.ElementWellKnownParser import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP import io.element.android.services.toolbox.test.systemclock.FakeSystemClock @@ -150,9 +151,10 @@ class DefaultWellKnownStoreTest { private fun createDefaultWellKnownStore( cacheStore: InMemoryCacheStore = InMemoryCacheStore(), systemClock: FakeSystemClock = FakeSystemClock(), + elementWellKnownParser: ElementWellKnownParser = DefaultElementWellKnownParser(jsonProvider), ) = DefaultWellKnownStore( cacheStore = cacheStore, - json = jsonProvider, systemClock = systemClock, + elementWellKnownParser = elementWellKnownParser, ) } diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt index 3ee5b2b91f..96ae4c2b17 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetrieverTest.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.wellknown.impl import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.wellknown.test.FakeElementWellknownStore import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.androidutils.json.DefaultJsonProvider @@ -19,6 +20,7 @@ import io.element.android.libraries.network.RetrofitFactory import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.WellknownRetrieverResult +import io.element.android.tests.testutils.lambda.lambdaRecorder import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -236,6 +238,27 @@ class DefaultWellknownRetrieverTest { assertThat(cacheStore.get(WELLKNOWN_URL)).isEqualTo(WellknownRetrieverResult.NotFound) } + @Test + fun `get element wellknown was overridden`() = runTest { + val getLambda = lambdaRecorder { mockCall() } + val wellKnown = anElementWellKnown() + + val sut = createDefaultWellknownRetriever( + callFactory = getLambda, + enterpriseService = FakeEnterpriseService( + overrideWellKnownResult = { wellKnown } + ) + ) + + // The overridden value is returned + assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo( + WellknownRetrieverResult.Success(wellKnown) + ) + + // And the endpoint is never hit + getLambda.assertions().isNeverCalled() + } + private fun defaultResponse( body: String = WELLKNOWN_CONTENT, status: Int = 200, @@ -251,6 +274,7 @@ class DefaultWellknownRetrieverTest { callFactory: Call.Factory = Call.Factory { _: Request -> mockCall(defaultResponse()) }, cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(), jsonProvider: JsonProvider = DefaultJsonProvider(), + enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }), ) = DefaultWellknownRetriever( retrofitFactory = RetrofitFactory( callFactory = { callFactory }, @@ -258,6 +282,7 @@ class DefaultWellknownRetrieverTest { ), jsonProvider = jsonProvider, elementWellknownStore = cacheStore, + enterpriseService = enterpriseService, ) companion object { diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt index 97def07130..914941d766 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt @@ -88,6 +88,8 @@ class KonsistPreviewTest { "BackgroundVerticalGradientPreview", "ColorAliasesPreview", "EmojiItemWithPopupPreview", + "EnterpriseExtraDeveloperOptionsRendererBottomSheetPreview", + "EnterpriseExtraDeveloperOptionsRendererListItemPreview", "FocusedEventPreview", "GradientFloatingActionButtonCircleShapePreview", "HeaderFooterPageScrollablePreview",