Merge branch 'develop' into feature/bma/metro070

This commit is contained in:
Benoit Marty 2025-10-23 11:30:25 +02:00 committed by GitHub
commit 76493f52ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 306 additions and 233 deletions

View file

@ -12,9 +12,11 @@ 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.matrix.api.exception.ClientException
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 io.element.android.libraries.wellknown.api.WellknownRetrieverResult
import timber.log.Timber
@ContributesBinding(SessionScope::class)
@ -24,29 +26,40 @@ class DefaultSessionWellknownRetriever(
) : SessionWellknownRetriever {
private val domain by lazy { matrixClient.userIdServerName() }
override suspend fun getWellKnown(): WellKnown? {
override suspend fun getWellKnown(): WellknownRetrieverResult<WellKnown> {
val url = "https://$domain/.well-known/matrix/client"
return matrixClient
.getUrl(url)
.mapCatchingExceptions {
val data = String(it)
json().decodeFromString(InternalWellKnown.serializer(), data)
json().decodeFromString<InternalWellKnown>(data).map()
}
.onFailure { Timber.e(it, "Failed to retrieve .well-known from $domain") }
.map { it.map() }
.getOrNull()
.toWellknownRetrieverResult()
}
override suspend fun getElementWellKnown(): ElementWellKnown? {
override suspend fun getElementWellKnown(): WellknownRetrieverResult<ElementWellKnown> {
val url = "https://$domain/.well-known/element/element.json"
return matrixClient
.getUrl(url)
.mapCatchingExceptions {
val data = String(it)
json().decodeFromString(InternalElementWellKnown.serializer(), data)
json().decodeFromString<InternalElementWellKnown>(data).map()
}
.onFailure { Timber.e(it, "Failed to retrieve Element .well-known from $domain") }
.map { it.map() }
.getOrNull()
.toWellknownRetrieverResult()
}
private fun <T> Result<T>.toWellknownRetrieverResult(): WellknownRetrieverResult<T> = fold(
onSuccess = {
WellknownRetrieverResult.Success(it)
},
onFailure = {
Timber.e(it, "Failed to retrieve Element .well-known from $domain")
// This check on message value is not ideal but this is what we got from the SDK.
if ((it as? ClientException.Generic)?.message?.contains("404") == true) {
WellknownRetrieverResult.NotFound
} else {
WellknownRetrieverResult.Error(it as Exception)
}
}
)
}

View file

@ -9,45 +9,71 @@ package io.element.android.libraries.wellknown.impl
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.uri.ensureProtocol
import io.element.android.libraries.network.RetrofitFactory
import io.element.android.libraries.wellknown.api.ElementWellKnown
import io.element.android.libraries.wellknown.api.WellKnown
import io.element.android.libraries.wellknown.api.WellknownRetriever
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
import retrofit2.HttpException
import timber.log.Timber
import java.net.HttpURLConnection
@ContributesBinding(AppScope::class)
class DefaultWellknownRetriever(
private val retrofitFactory: RetrofitFactory,
) : WellknownRetriever {
override suspend fun getWellKnown(baseUrl: String): WellKnown? {
val wellknownApi = buildWellknownApi(baseUrl) ?: return null
return try {
wellknownApi.getWellKnown().map()
} catch (e: Exception) {
Timber.e(e, "Failed to retrieve well-known data for $baseUrl")
null
}
override suspend fun getWellKnown(baseUrl: String): WellknownRetrieverResult<WellKnown> {
return buildWellknownApi(baseUrl)
.map { wellknownApi ->
try {
val result = wellknownApi.getWellKnown().map()
WellknownRetrieverResult.Success(result)
} catch (e: Exception) {
Timber.e(e, "Failed to retrieve well-known data for $baseUrl")
if ((e as? HttpException)?.code() == HttpURLConnection.HTTP_NOT_FOUND) {
WellknownRetrieverResult.NotFound
} else {
WellknownRetrieverResult.Error(e)
}
}
}
.fold(
onSuccess = { it },
onFailure = { WellknownRetrieverResult.Error(it as Exception) }
)
}
override suspend fun getElementWellKnown(baseUrl: String): ElementWellKnown? {
val wellknownApi = buildWellknownApi(baseUrl) ?: return null
return try {
wellknownApi.getElementWellKnown().map()
} catch (e: Exception) {
Timber.e(e, "Failed to retrieve Element well-known data for $baseUrl")
null
}
override suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult<ElementWellKnown> {
return buildWellknownApi(baseUrl)
.map { wellknownApi ->
try {
val result = wellknownApi.getElementWellKnown().map()
WellknownRetrieverResult.Success(result)
} catch (e: Exception) {
// Is it a 404?
Timber.e(e, "Failed to retrieve Element well-known data for $baseUrl")
if ((e as? HttpException)?.code() == HttpURLConnection.HTTP_NOT_FOUND) {
WellknownRetrieverResult.NotFound
} else {
WellknownRetrieverResult.Error(e)
}
}
}
.fold(
onSuccess = { it },
onFailure = { WellknownRetrieverResult.Error(it as Exception) }
)
}
private fun buildWellknownApi(accountProviderUrl: String): WellknownAPI? {
return try {
private fun buildWellknownApi(accountProviderUrl: String): Result<WellknownAPI> {
return runCatchingExceptions {
retrofitFactory.create(accountProviderUrl.ensureProtocol())
.create(WellknownAPI::class.java)
} catch (e: Exception) {
}.onFailure { e ->
// If the base URL is not valid, we cannot retrieve the well-known data
Timber.e(e, "Failed to create Retrofit instance for $accountProviderUrl")
null
}
}
}

View file

@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.wellknown.api.ElementWellKnown
import io.element.android.libraries.wellknown.api.WellKnown
import io.element.android.libraries.wellknown.api.WellKnownBaseConfig
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.test.runTest
@ -29,9 +30,11 @@ class DefaultSessionWellknownRetrieverTest {
getUrlLambda = getUrlLambda,
)
assertThat(sut.getWellKnown()).isEqualTo(
WellKnown(
homeServer = null,
identityServer = null,
WellknownRetrieverResult.Success(
WellKnown(
homeServer = null,
identityServer = null,
)
)
)
getUrlLambda.assertions().isCalledOnce()
@ -55,13 +58,15 @@ class DefaultSessionWellknownRetrieverTest {
}
)
assertThat(sut.getWellKnown()).isEqualTo(
WellKnown(
homeServer = WellKnownBaseConfig(
baseURL = "https://example.org",
),
identityServer = WellKnownBaseConfig(
baseURL = "https://identity.example.org",
),
WellknownRetrieverResult.Success(
WellKnown(
homeServer = WellKnownBaseConfig(
baseURL = "https://example.org",
),
identityServer = WellKnownBaseConfig(
baseURL = "https://identity.example.org",
),
)
)
)
}
@ -81,13 +86,15 @@ class DefaultSessionWellknownRetrieverTest {
}
)
assertThat(sut.getWellKnown()).isEqualTo(
WellKnown(
homeServer = WellKnownBaseConfig(
baseURL = "https://example.org",
),
identityServer = WellKnownBaseConfig(
baseURL = null,
),
WellknownRetrieverResult.Success(
WellKnown(
homeServer = WellKnownBaseConfig(
baseURL = "https://example.org",
),
identityServer = WellKnownBaseConfig(
baseURL = null,
),
)
)
)
}
@ -110,13 +117,15 @@ class DefaultSessionWellknownRetrieverTest {
},
)
assertThat(sut.getWellKnown()).isEqualTo(
WellKnown(
homeServer = WellKnownBaseConfig(
baseURL = "https://example.org",
),
identityServer = WellKnownBaseConfig(
baseURL = "https://identity.example.org",
),
WellknownRetrieverResult.Success(
WellKnown(
homeServer = WellKnownBaseConfig(
baseURL = "https://example.org",
),
identityServer = WellKnownBaseConfig(
baseURL = "https://identity.example.org",
),
)
)
)
}
@ -135,7 +144,7 @@ class DefaultSessionWellknownRetrieverTest {
)
}
)
assertThat(sut.getWellKnown()).isNull()
assertThat(sut.getWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java)
}
@Test
@ -145,7 +154,7 @@ class DefaultSessionWellknownRetrieverTest {
Result.failure(AN_EXCEPTION)
}
)
assertThat(sut.getWellKnown()).isNull()
assertThat(sut.getWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java)
}
@Test
@ -157,11 +166,13 @@ class DefaultSessionWellknownRetrieverTest {
getUrlLambda = getUrlLambda,
)
assertThat(sut.getElementWellKnown()).isEqualTo(
ElementWellKnown(
registrationHelperUrl = null,
enforceElementPro = null,
rageshakeUrl = null,
brandColor = null,
WellknownRetrieverResult.Success(
ElementWellKnown(
registrationHelperUrl = null,
enforceElementPro = null,
rageshakeUrl = null,
brandColor = null,
)
)
)
getUrlLambda.assertions().isCalledOnce()
@ -183,11 +194,13 @@ class DefaultSessionWellknownRetrieverTest {
}
)
assertThat(sut.getElementWellKnown()).isEqualTo(
ElementWellKnown(
registrationHelperUrl = "a_registration_url",
enforceElementPro = true,
rageshakeUrl = "a_rageshake_url",
brandColor = "#FF0000",
WellknownRetrieverResult.Success(
ElementWellKnown(
registrationHelperUrl = "a_registration_url",
enforceElementPro = true,
rageshakeUrl = "a_rageshake_url",
brandColor = "#FF0000",
)
)
)
}
@ -207,11 +220,13 @@ class DefaultSessionWellknownRetrieverTest {
},
)
assertThat(sut.getElementWellKnown()).isEqualTo(
ElementWellKnown(
registrationHelperUrl = "a_registration_url",
enforceElementPro = true,
rageshakeUrl = "a_rageshake_url",
brandColor = null,
WellknownRetrieverResult.Success(
ElementWellKnown(
registrationHelperUrl = "a_registration_url",
enforceElementPro = true,
rageshakeUrl = "a_rageshake_url",
brandColor = null,
)
)
)
}
@ -228,7 +243,7 @@ class DefaultSessionWellknownRetrieverTest {
)
}
)
assertThat(sut.getElementWellKnown()).isNull()
assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java)
}
@Test
@ -238,7 +253,7 @@ class DefaultSessionWellknownRetrieverTest {
Result.failure(AN_EXCEPTION)
}
)
assertThat(sut.getElementWellKnown()).isNull()
assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java)
}
private fun createDefaultSessionWellknownRetriever(