From b5c1a57a39ad2feacc397e8a79322ac5d86622d6 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Wed, 19 Mar 2025 08:20:44 +0100 Subject: [PATCH] fix loading posts with invalid `published_at` in preview card (#4993) closes #4992 --- .../keylesspalace/tusky/di/NetworkModule.kt | 2 +- .../keylesspalace/tusky/entity/PreviewCard.kt | 4 +- .../tusky/json/GuardedAdapter.kt | 10 ++-- .../tusky/json/GuardedAdapterTest.kt | 53 +++++++++++++++++++ 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt index 2a81d55eb..d7d16fcf2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt @@ -82,8 +82,8 @@ object NetworkModule { @Provides @Singleton fun providesMoshi(): Moshi = Moshi.Builder() - .add(Date::class.java, Rfc3339DateJsonAdapter()) .add(GuardedAdapter.ANNOTATION_FACTORY) + .add(Date::class.java, Rfc3339DateJsonAdapter()) // Enum types with fallback value .add( Attachment.Type::class.java, diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/PreviewCard.kt b/app/src/main/java/com/keylesspalace/tusky/entity/PreviewCard.kt index c90b025f2..5168f7421 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/PreviewCard.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/PreviewCard.kt @@ -15,6 +15,7 @@ package com.keylesspalace.tusky.entity +import com.keylesspalace.tusky.json.Guarded import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import java.util.Date @@ -27,7 +28,8 @@ data class PreviewCard( val authors: List = emptyList(), @Json(name = "author_name") val authorName: String? = null, @Json(name = "provider_name") val providerName: String? = null, - @Json(name = "published_at") val publishedAt: Date?, + // sometimes this date is invalid https://github.com/tuskyapp/Tusky/issues/4992 + @Json(name = "published_at") @Guarded val publishedAt: Date?, val image: String? = null, val type: String, val width: Int = 0, diff --git a/app/src/main/java/com/keylesspalace/tusky/json/GuardedAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/json/GuardedAdapter.kt index 11cb1f3af..579d58d12 100644 --- a/app/src/main/java/com/keylesspalace/tusky/json/GuardedAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/json/GuardedAdapter.kt @@ -17,8 +17,8 @@ package com.keylesspalace.tusky.json +import android.util.Log import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.JsonDataException import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonWriter import com.squareup.moshi.Moshi @@ -35,10 +35,12 @@ class GuardedAdapter private constructor( override fun fromJson(reader: JsonReader): T? { return try { - delegate.fromJson(reader) - } catch (e: JsonDataException) { - reader.skipValue() + reader.peekJson().use { delegate.fromJson(it) } + } catch (e: Exception) { + Log.w("GuardedAdapter", "failed to read json", e) null + } finally { + reader.skipValue() } } diff --git a/app/src/test/java/com/keylesspalace/tusky/json/GuardedAdapterTest.kt b/app/src/test/java/com/keylesspalace/tusky/json/GuardedAdapterTest.kt index 87ae8e5f5..5dfaef991 100644 --- a/app/src/test/java/com/keylesspalace/tusky/json/GuardedAdapterTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/json/GuardedAdapterTest.kt @@ -1,8 +1,11 @@ package com.keylesspalace.tusky.json +import com.keylesspalace.tusky.entity.PreviewCard import com.keylesspalace.tusky.entity.Relationship import com.squareup.moshi.Moshi import com.squareup.moshi.adapter +import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter +import java.util.Date import org.junit.Assert.assertEquals import org.junit.Test @@ -11,6 +14,7 @@ class GuardedAdapterTest { private val moshi = Moshi.Builder() .add(GuardedAdapter.ANNOTATION_FACTORY) + .add(Date::class.java, Rfc3339DateJsonAdapter()) .build() @Test @@ -131,4 +135,53 @@ class GuardedAdapterTest { moshi.adapter().fromJson(jsonInput) ) } + + @Test + fun `should deserialize PreviewCard when attribute 'published_at' is invalid`() { + // https://github.com/tuskyapp/Tusky/issues/4992 + val jsonInput = """{ + + "url": "https://www.cbc.ca/amp/1.7484477", + "title": "Canada reconsidering F-35 purchase amid tensions with Washington, says minister", + "description": "Canada is looking at cancelling a major portion of its purchase of U.S.-built F-35 stealth fighters and plans on opening talks with rival aircraft makers, Defence Minister Bill Blair said.", + "language": "en", + "type": "link", + "author_name": "Murray Brewster", + "author_url": "", + "provider_name": "CBC", + "provider_url": "", + "html": "", + "width": 0, + "height": 0, + "image": "https://files.mastodon.social/cache/preview_cards/images/137/231/445/original/0f63297db3ac7362.jpg", + "image_description": "", + "embed_url": "", + "blurhash": "U74#eeXoK9nLrVWZS+nfXVaenKkXTOjErobx", + "published_at": "57171-08-04T06:31:30.000Z", + "authors": [ + { + "name": "Murray Brewster", + "url": "", + "account": null + } + ] + + }""" + assertEquals( + PreviewCard( + url = "https://www.cbc.ca/amp/1.7484477", + title = "Canada reconsidering F-35 purchase amid tensions with Washington, says minister", + description = "Canada is looking at cancelling a major portion of its purchase of U.S.-built F-35 stealth fighters and plans on opening talks with rival aircraft makers, Defence Minister Bill Blair said.", + type = "link", + authorName = "Murray Brewster", + providerName = "CBC", + image = "https://files.mastodon.social/cache/preview_cards/images/137/231/445/original/0f63297db3ac7362.jpg", + width = 0, + height = 0, + blurhash = "U74#eeXoK9nLrVWZS+nfXVaenKkXTOjErobx", + publishedAt = null, + ), + moshi.adapter().fromJson(jsonInput) + ) + } }