Skip to content

Commit

Permalink
Some Nextcloud News item fields can be null
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinokuni committed Dec 1, 2024
1 parent 6e1dbc7 commit 7701ceb
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package com.readrops.api.services.nextcloudnews.adapters
import android.annotation.SuppressLint
import com.readrops.api.utils.ApiUtils
import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nextNonEmptyString
import com.readrops.api.utils.extensions.nextNullableLong
import com.readrops.api.utils.extensions.nextNullableString
import com.readrops.db.entities.Item
import com.readrops.db.util.DateUtils
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import java.time.LocalDateTime

class NextcloudNewsItemsAdapter : JsonAdapter<List<Item>>() {

Expand Down Expand Up @@ -39,9 +40,18 @@ class NextcloudNewsItemsAdapter : JsonAdapter<List<Item>>() {
when (reader.selectName(NAMES)) {
0 -> remoteId = reader.nextInt().toString()
1 -> link = reader.nextNullableString()
2 -> title = reader.nextNonEmptyString()
2 -> title = reader.nextNullableString()
3 -> author = reader.nextNullableString()
4 -> pubDate = DateUtils.fromEpochSeconds(reader.nextLong())
4 -> {
val value = reader.nextNullableLong()

pubDate = if (value != null) {
DateUtils.fromEpochSeconds(value)
} else {
LocalDateTime.now()
}
}

5 -> content = reader.nextNullableString()
6 -> enclosureMime = reader.nextNullableString()
7 -> enclosureLink = reader.nextNullableString()
Expand All @@ -53,10 +63,14 @@ class NextcloudNewsItemsAdapter : JsonAdapter<List<Item>>() {
}
}

if (enclosureMime != null && ApiUtils.isMimeImage(enclosureMime!!))
if (enclosureMime != null && ApiUtils.isMimeImage(enclosureMime!!)) {
item.imageLink = enclosureLink
}

if (item.title != null) {
items += item
}

items += item
reader.endObject()
}

Expand All @@ -70,7 +84,9 @@ class NextcloudNewsItemsAdapter : JsonAdapter<List<Item>>() {
}

companion object {
val NAMES: JsonReader.Options = JsonReader.Options.of("id", "url", "title", "author",
"pubDate", "body", "enclosureMime", "enclosureLink", "feedId", "unread", "starred")
val NAMES: JsonReader.Options = JsonReader.Options.of(
"id", "url", "title", "author",
"pubDate", "body", "enclosureMime", "enclosureLink", "feedId", "unread", "starred"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ fun JsonReader.nextNonEmptyString(): String {
fun JsonReader.nextNullableInt(): Int? =
if (peek() != JsonReader.Token.NULL) nextInt() else nextNull()

fun JsonReader.nextNullableLong(): Long? =
if (peek() != JsonReader.Token.NULL) nextLong() else nextNull()

fun JsonReader.skipField() {
skipName()
skipValue()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ import com.readrops.api.TestUtils
import com.readrops.api.apiModule
import com.readrops.api.enqueueOK
import com.readrops.api.enqueueStream
import com.readrops.api.okResponseWithBody
import com.readrops.api.services.SyncType
import com.readrops.db.entities.account.Account
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import kotlinx.coroutines.test.runTest
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import org.junit.After
import org.junit.Before
import org.junit.Rule
Expand Down Expand Up @@ -93,12 +98,18 @@ class NextcloudNewsDataSourceTest : KoinTest {
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/items.json")
mockServer.enqueueStream(stream)

val items = nextcloudNewsDataSource.getItems(NextcloudNewsDataSource.ItemQueryType.ALL.value, false, 10)
val type = NextcloudNewsDataSource.ItemQueryType.ALL.value

val items = nextcloudNewsDataSource.getItems(
type = type,
read = false,
batchSize = 10
)
val request = mockServer.takeRequest()

assertTrue { items.size == 3 }
assertTrue { items.size == 2 }
with(request.requestUrl!!) {
assertEquals("3", queryParameter("type"))
assertEquals("$type", queryParameter("type"))
assertEquals("false", queryParameter("getRead"))
assertEquals("10", queryParameter("batchSize"))
}
Expand All @@ -113,7 +124,7 @@ class NextcloudNewsDataSourceTest : KoinTest {
nextcloudNewsDataSource.getNewItems(1512, NextcloudNewsDataSource.ItemQueryType.ALL)
val request = mockServer.takeRequest()

assertTrue { items.size == 3 }
assertTrue { items.size == 2 }
with(request.requestUrl!!) {
assertEquals("1512", queryParameter("lastModified"))
assertEquals("3", queryParameter("type"))
Expand All @@ -125,13 +136,13 @@ class NextcloudNewsDataSourceTest : KoinTest {
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/feeds.json")
mockServer.enqueueStream(stream)

val feeds = nextcloudNewsDataSource.createFeed("https://news.ycombinator.com/rss", null)
val feeds = nextcloudNewsDataSource.createFeed("https://news.ycombinator.com/rss", 100)
val request = mockServer.takeRequest()

assertTrue { feeds.isNotEmpty() }
with(request.requestUrl!!) {
assertEquals("https://news.ycombinator.com/rss", queryParameter("url"))
assertEquals(null, queryParameter("folderId"))
assertEquals("100", queryParameter("folderId"))
}
}

Expand All @@ -152,12 +163,11 @@ class NextcloudNewsDataSourceTest : KoinTest {
nextcloudNewsDataSource.changeFeedFolder(15, 18)
val request = mockServer.takeRequest()

val type =
Types.newParameterizedType(
Map::class.java,
String::class.java,
Int::class.javaObjectType
)
val type = Types.newParameterizedType(
Map::class.java,
String::class.java,
Int::class.javaObjectType
)
val adapter = moshi.adapter<Map<String, Int>>(type)
val body = adapter.fromJson(request.body)!!

Expand Down Expand Up @@ -267,12 +277,11 @@ class NextcloudNewsDataSourceTest : KoinTest {
val starRequest = mockServer.takeRequest()
val unstarRequest = mockServer.takeRequest()

val type =
Types.newParameterizedType(
Map::class.java,
String::class.java,
Types.newParameterizedType(List::class.java, Int::class.javaObjectType)
)
val type = Types.newParameterizedType(
Map::class.java,
String::class.java,
Types.newParameterizedType(List::class.java, Int::class.javaObjectType)
)
val adapter = moshi.adapter<Map<String, List<Int>>>(type)

val starBody = adapter.fromJson(starRequest.body)!!
Expand All @@ -281,4 +290,100 @@ class NextcloudNewsDataSourceTest : KoinTest {
assertEquals(data.starredIds, starBody["itemIds"])
assertEquals(data.unstarredIds, unstarBody["itemIds"])
}

@Test
fun initialSyncTest() = runTest {
mockServer.dispatcher = object : Dispatcher() {

override fun dispatch(request: RecordedRequest): MockResponse {
with(request.path!!) {
return when {
this == "/folders" -> {
MockResponse.okResponseWithBody(TestUtils.loadResource("services/nextcloudnews/adapters/valid_folder.json"))
}

this == "/feeds" -> {
MockResponse.okResponseWithBody(TestUtils.loadResource("services/nextcloudnews/adapters/feeds.json"))
}

contains("/items") -> {

MockResponse.okResponseWithBody(TestUtils.loadResource("services/nextcloudnews/adapters/items.json"))
}

else -> MockResponse().setResponseCode(404)
}
}
}
}

val result =
nextcloudNewsDataSource.synchronize(SyncType.INITIAL_SYNC, NextcloudNewsSyncData())

with(result) {
assertEquals(1, folders.size)
assertEquals(3, feeds.size)
assertEquals(2, items.size)
assertEquals(2, starredItems.size)
}
}

@Test
fun classicSyncTest() = runTest {
var setItemState = 0
val lastModified = 10L
val ids = listOf(1, 2, 3, 4)

mockServer.dispatcher = object : Dispatcher() {

override fun dispatch(request: RecordedRequest): MockResponse {
with(request.path!!) {
// important, otherwise test fails and I don't know why
println("request: ${request.path}")
return when {
this == "/folders" -> {
MockResponse.okResponseWithBody(TestUtils.loadResource("services/nextcloudnews/adapters/valid_folder.json"))
}

this == "/feeds" -> {
MockResponse.okResponseWithBody(TestUtils.loadResource("services/nextcloudnews/adapters/feeds.json"))
}

contains("/items/updated") -> {
assertEquals(
"$lastModified",
request.requestUrl!!.queryParameter("lastModified")
)
MockResponse.okResponseWithBody(TestUtils.loadResource("services/nextcloudnews/adapters/items.json"))
}

this.matches(Regex("/items/(read|unread|star|unstar)/multiple")) -> {
setItemState++
MockResponse().setResponseCode(200)
}

else -> MockResponse().setResponseCode(404)
}
}
}
}

val result = nextcloudNewsDataSource.synchronize(
SyncType.CLASSIC_SYNC,
NextcloudNewsSyncData(
lastModified = lastModified,
readIds = ids,
unreadIds = ids,
starredIds = ids,
unstarredIds = ids
)
)

with(result) {
assertEquals(4, setItemState)
assertEquals(1, folders.size)
assertEquals(3, feeds.size)
assertEquals(2, items.size)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ class NextcloudNewsItemsAdapterTest {
val stream = TestUtils.loadResource("services/nextcloudnews/adapters/items.json")

val items = adapter.fromJson(Buffer().readFrom(stream))!!
val item = items[0]
val item = items.first()

assertEquals(2, items.size)

with(item) {
assertEquals(remoteId, "3443")
Expand All @@ -33,11 +35,11 @@ class NextcloudNewsItemsAdapterTest {
assertEquals(isRead, false)
assertEquals(isStarred, false)
assertEquals(pubDate, DateUtils.fromEpochSeconds(1367270544))
assertEquals(imageLink, null)
assertEquals(imageLink, "https://test.org/image.jpg")
}

with(items[1]) {
assertEquals(imageLink, "https://test.org/image.jpg")
assertEquals(imageLink, null)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.readrops.api.utils

import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nextNonEmptyString
import com.readrops.api.utils.extensions.nextNullableLong
import com.readrops.api.utils.extensions.nextNullableString
import com.squareup.moshi.JsonReader
import junit.framework.TestCase.assertEquals
Expand Down Expand Up @@ -85,4 +86,33 @@ class JsonReaderExtensionsTest {
reader.nextNonEmptyString()
}

@Test
fun nextNullableLongNormalCaseTest() {
val reader = JsonReader.of(Buffer().readFrom("""
{
"field": "5555555555555555555"
}
""".trimIndent().byteInputStream()))

reader.beginObject()
reader.nextName()

assertEquals(5555555555555555555L, reader.nextNullableLong())
reader.endObject()
}

@Test
fun nextNullableLongNullCaseTest() {
val reader = JsonReader.of(Buffer().readFrom("""
{
"field": null
}
""".trimIndent().byteInputStream()))

reader.beginObject()
reader.nextName()

assertNull(reader.nextNullableLong())
reader.endObject()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"author": "Jan Grulich (grulja)",
"pubDate": 1367270544,
"body": "<p>At first I have to say...</p>",
"enclosureMime": null,
"enclosureLink": null,
"enclosureMime": "image",
"enclosureLink": "https://test.org/image.jpg",
"mediaThumbnail": null,
"mediaDescription": null,
"feedId": 67,
Expand All @@ -25,12 +25,12 @@
"guid": "http://grulja.wordpress.com/?p=76",
"guidHash": "3059047a572cd9cd5d0bf645faffd077",
"url": "http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/",
"title": "Plasma-nm after the solid sprint",
"title": "",
"author": "Jan Grulich (grulja)",
"pubDate": 1367270544,
"body": "<p>At first I have to say...</p>",
"enclosureMime": "image",
"enclosureLink": "https://test.org/image.jpg",
"enclosureMime": null,
"enclosureLink": null,
"mediaThumbnail": null,
"mediaDescription": null,
"feedId": 67,
Expand Down

0 comments on commit 7701ceb

Please sign in to comment.