From 21a4308fef55f0218461a09d03fb11def010698d Mon Sep 17 00:00:00 2001 From: Levi Bard Date: Mon, 13 Nov 2023 10:05:28 +0100 Subject: [PATCH] Fix deserialization of the response from friendica on api/v2/instance (#4103) Fixes #4100 --------- Co-authored-by: Konrad Pozniak --- .../instanceinfo/InstanceInfoRepository.kt | 20 ++-- .../keylesspalace/tusky/entity/Instance.kt | 15 +-- .../ComposeActivity/ComposeActivityTest.kt | 92 ++++++++++++++++++- 3 files changed, 109 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/instanceinfo/InstanceInfoRepository.kt b/app/src/main/java/com/keylesspalace/tusky/components/instanceinfo/InstanceInfoRepository.kt index 4031181ed..c781ff90e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/instanceinfo/InstanceInfoRepository.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/instanceinfo/InstanceInfoRepository.kt @@ -64,17 +64,17 @@ class InstanceInfoRepository @Inject constructor( { instance -> val instanceEntity = InstanceInfoEntity( instance = instanceName, - maximumTootCharacters = instance.configuration.statuses.maxCharacters, - maxPollOptions = instance.configuration.polls.maxOptions, - maxPollOptionLength = instance.configuration.polls.maxCharactersPerOption, - minPollDuration = instance.configuration.polls.minExpirationSeconds, - maxPollDuration = instance.configuration.polls.maxExpirationSeconds, - charactersReservedPerUrl = instance.configuration.statuses.charactersReservedPerUrl, + maximumTootCharacters = instance.configuration.statuses?.maxCharacters ?: DEFAULT_CHARACTER_LIMIT, + maxPollOptions = instance.configuration.polls?.maxOptions ?: DEFAULT_MAX_OPTION_COUNT, + maxPollOptionLength = instance.configuration.polls?.maxCharactersPerOption ?: DEFAULT_MAX_OPTION_LENGTH, + minPollDuration = instance.configuration.polls?.minExpirationSeconds ?: DEFAULT_MIN_POLL_DURATION, + maxPollDuration = instance.configuration.polls?.maxExpirationSeconds ?: DEFAULT_MAX_POLL_DURATION, + charactersReservedPerUrl = instance.configuration.statuses?.charactersReservedPerUrl ?: DEFAULT_CHARACTERS_RESERVED_PER_URL, version = instance.version, - videoSizeLimit = instance.configuration.mediaAttachments.videoSizeLimitBytes.toInt(), - imageSizeLimit = instance.configuration.mediaAttachments.imageSizeLimitBytes.toInt(), - imageMatrixLimit = instance.configuration.mediaAttachments.imagePixelCountLimit.toInt(), - maxMediaAttachments = instance.configuration.statuses.maxMediaAttachments, + videoSizeLimit = instance.configuration.mediaAttachments?.videoSizeLimitBytes?.toInt() ?: DEFAULT_VIDEO_SIZE_LIMIT, + imageSizeLimit = instance.configuration.mediaAttachments?.imageSizeLimitBytes?.toInt() ?: DEFAULT_IMAGE_SIZE_LIMIT, + imageMatrixLimit = instance.configuration.mediaAttachments?.imagePixelCountLimit?.toInt() ?: DEFAULT_IMAGE_MATRIX_LIMIT, + maxMediaAttachments = instance.configuration.statuses?.maxMediaAttachments ?: DEFAULT_MAX_MEDIA_ATTACHMENTS, maxFields = instance.pleroma?.metadata?.fieldLimits?.maxFields, maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength, maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength, diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt index bf5aa2804..048367535 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt @@ -31,12 +31,12 @@ data class Instance( ) } data class Configuration( - val urls: Urls, - val accounts: Accounts, - val statuses: Statuses, - @SerializedName("media_attachments") val mediaAttachments: MediaAttachments, - val polls: Polls, - val translation: Translation, + val urls: Urls?, + val accounts: Accounts?, + val statuses: Statuses?, + @SerializedName("media_attachments") val mediaAttachments: MediaAttachments?, + val polls: Polls?, + val translation: Translation?, ) { data class Urls(@SerializedName("streaming_api") val streamingApi: String) data class Accounts(@SerializedName("max_featured_tags") val maxFeaturedTags: Int) @@ -46,7 +46,8 @@ data class Instance( @SerializedName("characters_reserved_per_url") val charactersReservedPerUrl: Int, ) data class MediaAttachments( - @SerializedName("supported_mime_types") val supportedMimeTypes: List, + // Warning: This is an array in mastodon and a dictionary in friendica + // @SerializedName("supported_mime_types") val supportedMimeTypes: List, @SerializedName("image_size_limit") val imageSizeLimitBytes: Long, @SerializedName("image_matrix_limit") val imagePixelCountLimit: Long, @SerializedName("video_size_limit") val videoSizeLimitBytes: Long, diff --git a/app/src/test/java/com/keylesspalace/tusky/components/compose/ComposeActivity/ComposeActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/components/compose/ComposeActivity/ComposeActivityTest.kt index ecff6d3be..451b5c09a 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/compose/ComposeActivity/ComposeActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/compose/ComposeActivity/ComposeActivityTest.kt @@ -22,6 +22,7 @@ import android.os.Looper.getMainLooper import android.widget.EditText import androidx.test.ext.junit.runners.AndroidJUnit4 import at.connyduck.calladapter.networkresult.NetworkResult +import com.google.gson.Gson import com.keylesspalace.tusky.R import com.keylesspalace.tusky.components.compose.ComposeActivity import com.keylesspalace.tusky.components.compose.ComposeViewModel @@ -95,6 +96,7 @@ class ComposeActivityTest { private var instanceV1ResponseCallback: (() -> InstanceV1)? = null private var instanceResponseCallback: (() -> Instance)? = null private var composeOptions: ComposeActivity.ComposeOptions? = null + private val gson = Gson() @Before fun setupActivity() { @@ -528,6 +530,15 @@ class ComposeActivityTest { assertEquals(language, activity.selectedLanguage) } + @Test + fun sampleFriendicaInstanceResponseIsDeserializable() { + // https://github.com/tuskyapp/Tusky/issues/4100 + instanceResponseCallback = { getSampleFriendicaInstance() } + setupActivity() + shadowOf(getMainLooper()).idle() + assertEquals(friendicaMaximum, activity.maximumTootCharacters) + } + private fun clickUp() { val menuItem = RoboMenuItem(android.R.id.home) activity.onOptionsItemSelected(menuItem) @@ -560,7 +571,7 @@ class ComposeActivityTest { InstanceInfoRepository.DEFAULT_MAX_MEDIA_ATTACHMENTS, charactersReservedPerUrl ?: InstanceInfoRepository.DEFAULT_CHARACTERS_RESERVED_PER_URL ), - Instance.Configuration.MediaAttachments(emptyList(), 0, 0, 0, 0, 0), + Instance.Configuration.MediaAttachments(0, 0, 0, 0, 0), Instance.Configuration.Polls(0, 0, 0, 0), Instance.Configuration.Translation(false), ) @@ -591,4 +602,83 @@ class ComposeActivityTest { polls = null ) } + + private fun getSampleFriendicaInstance(): Instance { + return gson.fromJson(sampleFriendicaResponse, Instance::class.java) + } + + companion object { + private const val friendicaMaximum = 200000 + + // https://github.com/tuskyapp/Tusky/issues/4100 + private val sampleFriendicaResponse = """{ + "domain": "loma.ml", + "title": "[ˈloma]", + "version": "2.8.0 (compatible; Friendica 2023.09-rc)", + "source_url": "https://git.friendi.ca/friendica/friendica", + "description": "loma.ml ist eine Friendica Community im Fediverse auf der vorwiegend DE \uD83C\uDDE9\uD83C\uDDEA gesprochen wird. \\r\\nServer in Germany/EU \uD83C\uDDE9\uD83C\uDDEA \uD83C\uDDEA\uD83C\uDDFA. Open to all with fun in new. \\r\\nServer in Deutschland. Offen für alle mit Spaß an Neuen.", + "usage": { + "users": { + "active_month": 125 + } + }, + "thumbnail": { + "url": "https://loma.ml/ad/friendica-banner.jpg" + }, + "languages": [ + "de" + ], + "configuration": { + "statuses": { + "max_characters": $friendicaMaximum + }, + "media_attachments": { + "supported_mime_types": { + "image/jpeg": "jpg", + "image/jpg": "jpg", + "image/png": "png", + "image/gif": "gif" + }, + "image_size_limit": 10485760 + } + }, + "registrations": { + "enabled": true, + "approval_required": false + }, + "contact": { + "email": "anony@miz.ed", + "account": { + "id": "9632", + "username": "webm", + "acct": "webm", + "display_name": "web m \uD83C\uDDEA\uD83C\uDDFA", + "locked": false, + "bot": false, + "discoverable": true, + "group": false, + "created_at": "2018-05-21T11:24:55.000Z", + "note": "\uD83C\uDDE9\uD83C\uDDEA Über diesen Account werden Änderungen oder geplante Beeinträchtigungen angekündigt. Wenn du einen Account auf Loma.ml besitzt, dann solltest du dich mit mir verbinden.\uD83C\uDDEA\uD83C\uDDFA Changes or planned impairments are announced via this account. If you have an account on Loma.ml, you should connect to me.\uD83C\uDD98 Fallbackaccount @webm@joinfriendica.de", + "url": "https://loma.ml/profile/webm", + "avatar": "https://loma.ml/photo/contact/320/373ebf56355ac895a09cb99264485383?ts=1686417730", + "avatar_static": "https://loma.ml/photo/contact/320/373ebf56355ac895a09cb99264485383?ts=1686417730&static=1", + "header": "https://loma.ml/photo/header/373ebf56355ac895a09cb99264485383?ts=1686417730", + "header_static": "https://loma.ml/photo/header/373ebf56355ac895a09cb99264485383?ts=1686417730&static=1", + "followers_count": 23, + "following_count": 25, + "statuses_count": 15, + "last_status_at": "2023-09-19T00:00:00.000Z", + "emojis": [], + "fields": [] + } + }, + "rules": [], + "friendica": { + "version": "2023.09-rc", + "codename": "Giant Rhubarb", + "db_version": 1539 + } + } + """.trimIndent() + } }