Fix deserialization of the response from friendica on api/v2/instance (#4103)

Fixes #4100

---------

Co-authored-by: Konrad Pozniak <connyduck@users.noreply.github.com>
This commit is contained in:
Levi Bard 2023-11-13 10:05:28 +01:00 committed by GitHub
parent ff39f9b3c2
commit 21a4308fef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 18 deletions

View File

@ -64,17 +64,17 @@ class InstanceInfoRepository @Inject constructor(
{ instance -> { instance ->
val instanceEntity = InstanceInfoEntity( val instanceEntity = InstanceInfoEntity(
instance = instanceName, instance = instanceName,
maximumTootCharacters = instance.configuration.statuses.maxCharacters, maximumTootCharacters = instance.configuration.statuses?.maxCharacters ?: DEFAULT_CHARACTER_LIMIT,
maxPollOptions = instance.configuration.polls.maxOptions, maxPollOptions = instance.configuration.polls?.maxOptions ?: DEFAULT_MAX_OPTION_COUNT,
maxPollOptionLength = instance.configuration.polls.maxCharactersPerOption, maxPollOptionLength = instance.configuration.polls?.maxCharactersPerOption ?: DEFAULT_MAX_OPTION_LENGTH,
minPollDuration = instance.configuration.polls.minExpirationSeconds, minPollDuration = instance.configuration.polls?.minExpirationSeconds ?: DEFAULT_MIN_POLL_DURATION,
maxPollDuration = instance.configuration.polls.maxExpirationSeconds, maxPollDuration = instance.configuration.polls?.maxExpirationSeconds ?: DEFAULT_MAX_POLL_DURATION,
charactersReservedPerUrl = instance.configuration.statuses.charactersReservedPerUrl, charactersReservedPerUrl = instance.configuration.statuses?.charactersReservedPerUrl ?: DEFAULT_CHARACTERS_RESERVED_PER_URL,
version = instance.version, version = instance.version,
videoSizeLimit = instance.configuration.mediaAttachments.videoSizeLimitBytes.toInt(), videoSizeLimit = instance.configuration.mediaAttachments?.videoSizeLimitBytes?.toInt() ?: DEFAULT_VIDEO_SIZE_LIMIT,
imageSizeLimit = instance.configuration.mediaAttachments.imageSizeLimitBytes.toInt(), imageSizeLimit = instance.configuration.mediaAttachments?.imageSizeLimitBytes?.toInt() ?: DEFAULT_IMAGE_SIZE_LIMIT,
imageMatrixLimit = instance.configuration.mediaAttachments.imagePixelCountLimit.toInt(), imageMatrixLimit = instance.configuration.mediaAttachments?.imagePixelCountLimit?.toInt() ?: DEFAULT_IMAGE_MATRIX_LIMIT,
maxMediaAttachments = instance.configuration.statuses.maxMediaAttachments, maxMediaAttachments = instance.configuration.statuses?.maxMediaAttachments ?: DEFAULT_MAX_MEDIA_ATTACHMENTS,
maxFields = instance.pleroma?.metadata?.fieldLimits?.maxFields, maxFields = instance.pleroma?.metadata?.fieldLimits?.maxFields,
maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength, maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength,
maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength, maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength,

View File

@ -31,12 +31,12 @@ data class Instance(
) )
} }
data class Configuration( data class Configuration(
val urls: Urls, val urls: Urls?,
val accounts: Accounts, val accounts: Accounts?,
val statuses: Statuses, val statuses: Statuses?,
@SerializedName("media_attachments") val mediaAttachments: MediaAttachments, @SerializedName("media_attachments") val mediaAttachments: MediaAttachments?,
val polls: Polls, val polls: Polls?,
val translation: Translation, val translation: Translation?,
) { ) {
data class Urls(@SerializedName("streaming_api") val streamingApi: String) data class Urls(@SerializedName("streaming_api") val streamingApi: String)
data class Accounts(@SerializedName("max_featured_tags") val maxFeaturedTags: Int) 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, @SerializedName("characters_reserved_per_url") val charactersReservedPerUrl: Int,
) )
data class MediaAttachments( data class MediaAttachments(
@SerializedName("supported_mime_types") val supportedMimeTypes: List<String>, // Warning: This is an array in mastodon and a dictionary in friendica
// @SerializedName("supported_mime_types") val supportedMimeTypes: List<String>,
@SerializedName("image_size_limit") val imageSizeLimitBytes: Long, @SerializedName("image_size_limit") val imageSizeLimitBytes: Long,
@SerializedName("image_matrix_limit") val imagePixelCountLimit: Long, @SerializedName("image_matrix_limit") val imagePixelCountLimit: Long,
@SerializedName("video_size_limit") val videoSizeLimitBytes: Long, @SerializedName("video_size_limit") val videoSizeLimitBytes: Long,

View File

@ -22,6 +22,7 @@ import android.os.Looper.getMainLooper
import android.widget.EditText import android.widget.EditText
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import at.connyduck.calladapter.networkresult.NetworkResult import at.connyduck.calladapter.networkresult.NetworkResult
import com.google.gson.Gson
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.compose.ComposeActivity import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.compose.ComposeViewModel import com.keylesspalace.tusky.components.compose.ComposeViewModel
@ -95,6 +96,7 @@ class ComposeActivityTest {
private var instanceV1ResponseCallback: (() -> InstanceV1)? = null private var instanceV1ResponseCallback: (() -> InstanceV1)? = null
private var instanceResponseCallback: (() -> Instance)? = null private var instanceResponseCallback: (() -> Instance)? = null
private var composeOptions: ComposeActivity.ComposeOptions? = null private var composeOptions: ComposeActivity.ComposeOptions? = null
private val gson = Gson()
@Before @Before
fun setupActivity() { fun setupActivity() {
@ -528,6 +530,15 @@ class ComposeActivityTest {
assertEquals(language, activity.selectedLanguage) 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() { private fun clickUp() {
val menuItem = RoboMenuItem(android.R.id.home) val menuItem = RoboMenuItem(android.R.id.home)
activity.onOptionsItemSelected(menuItem) activity.onOptionsItemSelected(menuItem)
@ -560,7 +571,7 @@ class ComposeActivityTest {
InstanceInfoRepository.DEFAULT_MAX_MEDIA_ATTACHMENTS, InstanceInfoRepository.DEFAULT_MAX_MEDIA_ATTACHMENTS,
charactersReservedPerUrl ?: InstanceInfoRepository.DEFAULT_CHARACTERS_RESERVED_PER_URL 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.Polls(0, 0, 0, 0),
Instance.Configuration.Translation(false), Instance.Configuration.Translation(false),
) )
@ -591,4 +602,83 @@ class ComposeActivityTest {
polls = null 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()
}
} }