Fallback to current date time if it is missing for RSS1 and ATOM items

This commit is contained in:
Shinokuni 2020-10-02 18:53:52 +02:00
parent 10a7b99e59
commit 5c4cc81628
4 changed files with 43 additions and 37 deletions

View File

@ -5,8 +5,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import com.readrops.api.utils.DateUtils import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.ParseException import com.readrops.api.utils.ParseException
import junit.framework.TestCase.assertEquals import junit.framework.TestCase.*
import junit.framework.TestCase.assertNotNull
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -35,24 +34,28 @@ class ATOMItemsAdapterTest {
assertNotNull(item.content) assertNotNull(item.content)
} }
@Test
fun noDateTest() {
val stream = context.resources.assets.open("localfeed/atom/atom_items_no_date.xml")
val item = adapter.fromXml(stream).first()
assertNotNull(item.pubDate)
}
@Test @Test
fun noTitleTest() { fun noTitleTest() {
val stream = context.resources.assets.open("localfeed/atom/atom_items_no_title.xml") val stream = context.resources.assets.open("localfeed/atom/atom_items_no_title.xml")
Assert.assertThrows("Item title is required", ParseException::class.java) { adapter.fromXml(stream) } val exception = Assert.assertThrows(ParseException::class.java) { adapter.fromXml(stream) }
assertTrue(exception.message!!.contains("Item title is required"))
} }
@Test @Test
fun noLinkTest() { fun noLinkTest() {
val stream = context.resources.assets.open("localfeed/atom/atom_items_no_link.xml") val stream = context.resources.assets.open("localfeed/atom/atom_items_no_link.xml")
Assert.assertThrows("Item link is required", ParseException::class.java) { adapter.fromXml(stream) } val exception = Assert.assertThrows(ParseException::class.java) { adapter.fromXml(stream) }
assertTrue(exception.message!!.contains("Item link is required"))
} }
@Test
fun noDateTest() {
val stream = context.resources.assets.open("localfeed/atom/atom_items_no_date.xml")
Assert.assertThrows("Item date is required", ParseException::class.java) { adapter.fromXml(stream) }
}
} }

View File

@ -5,8 +5,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import com.readrops.api.utils.DateUtils import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.ParseException import com.readrops.api.utils.ParseException
import junit.framework.TestCase.assertEquals import junit.framework.TestCase.*
import junit.framework.TestCase.assertNotNull
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -48,24 +47,27 @@ class RSS1ItemsAdapterTest {
"that-told-time-now-tells-the-time-remaining?utm_source=rss1.0mainlinkanon&utm_medium=feed") "that-told-time-now-tells-the-time-remaining?utm_source=rss1.0mainlinkanon&utm_medium=feed")
} }
@Test
fun nullDateTest() {
val stream = context.resources.assets.open("localfeed/rss1/rss1_items_no_date.xml")
val item = adapter.fromXml(stream).first()
assertNotNull(item.pubDate)
}
@Test @Test
fun nullTitleTest() { fun nullTitleTest() {
val stream = context.resources.assets.open("localfeed/rss1/rss1_items_no_title.xml") val stream = context.resources.assets.open("localfeed/rss1/rss1_items_no_title.xml")
Assert.assertThrows(ParseException::class.java) { adapter.fromXml(stream) } val exception = Assert.assertThrows(ParseException::class.java) { adapter.fromXml(stream) }
assertTrue(exception.message!!.contains("Item title is required"))
} }
@Test @Test
fun nullLinkTest() { fun nullLinkTest() {
val stream = context.resources.assets.open("localfeed/rss1/rss1_items_no_link.xml") val stream = context.resources.assets.open("localfeed/rss1/rss1_items_no_link.xml")
Assert.assertThrows(ParseException::class.java) { adapter.fromXml(stream) } val exception = Assert.assertThrows(ParseException::class.java) { adapter.fromXml(stream) }
} assertTrue(exception.message!!.contains("RSS1 link or about element is required"))
@Test
fun nullDateTest() {
val stream = context.resources.assets.open("localfeed/rss1/rss1_items_no_date.xml")
Assert.assertThrows(ParseException::class.java) { adapter.fromXml(stream) }
} }
} }

View File

@ -7,50 +7,53 @@ import com.gitlab.mvysny.konsumexml.konsumeXml
import com.readrops.api.localfeed.XmlAdapter import com.readrops.api.localfeed.XmlAdapter
import com.readrops.api.utils.* import com.readrops.api.utils.*
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import org.joda.time.LocalDateTime
import java.io.InputStream import java.io.InputStream
class ATOMItemsAdapter : XmlAdapter<List<Item>> { class ATOMItemsAdapter : XmlAdapter<List<Item>> {
override fun fromXml(inputStream: InputStream): List<Item> { override fun fromXml(inputStream: InputStream): List<Item> {
val konsume = inputStream.konsumeXml() val konsumer = inputStream.konsumeXml()
val items = arrayListOf<Item>() val items = arrayListOf<Item>()
return try { return try {
konsume.child("feed") { konsumer.child("feed") {
allChildrenAutoIgnore("entry") { allChildrenAutoIgnore("entry") {
val item = Item().apply { val item = Item().apply {
allChildrenAutoIgnore(names) { allChildrenAutoIgnore(names) {
when (tagName) { when (tagName) {
"title" -> title = nonNullText() "title" -> title = nonNullText()
"id" -> guid = nullableText() "id" -> guid = nullableText()
"updated" -> pubDate = DateUtils.parse(nonNullText()) "updated" -> pubDate = DateUtils.parse(nullableText())
"link" -> parseLink(this, this@apply) "link" -> parseLink(this, this@apply)
"author" -> allChildrenAutoIgnore("name") { author = nullableText() } "author" -> allChildrenAutoIgnore("name") { author = nullableText() }
"summary" -> description = nullableTextRecursively() "summary" -> description = nullableTextRecursively()
"content" -> content = nullableTextRecursively() "content" -> content = nullableTextRecursively()
else -> skipContents()
} }
} }
} }
validateItem(item) validateItem(item)
if (item.pubDate == null) item.pubDate = LocalDateTime.now()
if (item.guid == null) item.guid = item.link if (item.guid == null) item.guid = item.link
items += item items += item
} }
} }
konsume.close() konsumer.close()
items items
} catch (e: Exception) { } catch (e: Exception) {
throw ParseException(e.message) throw ParseException(e.message)
} }
} }
private fun parseLink(konsume: Konsumer, item: Item) { private fun parseLink(konsumer: Konsumer, item: Item) {
konsume.apply { konsumer.apply {
if (attributes.getValueOpt("rel") == null || if (attributes.getValueOpt("rel") == null ||
attributes["rel"] == "alternate") attributes["rel"] == "alternate")
item.link = attributes["href"] item.link = attributes.getValueOpt("href")
} }
} }
@ -59,7 +62,6 @@ class ATOMItemsAdapter : XmlAdapter<List<Item>> {
when { when {
item.title == null -> throw ParseException("Item title is required") item.title == null -> throw ParseException("Item title is required")
item.link == null -> throw ParseException("Item link is required") item.link == null -> throw ParseException("Item link is required")
item.pubDate == null -> throw ParseException("Item date id required")
} }
} }

View File

@ -7,16 +7,17 @@ import com.readrops.api.localfeed.XmlAdapter
import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX
import com.readrops.api.utils.* import com.readrops.api.utils.*
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import org.joda.time.LocalDateTime
import java.io.InputStream import java.io.InputStream
class RSS1ItemsAdapter : XmlAdapter<List<Item>> { class RSS1ItemsAdapter : XmlAdapter<List<Item>> {
override fun fromXml(inputStream: InputStream): List<Item> { override fun fromXml(inputStream: InputStream): List<Item> {
val konsume = inputStream.konsumeXml() val konsumer = inputStream.konsumeXml()
val items = arrayListOf<Item>() val items = arrayListOf<Item>()
return try { return try {
konsume.child("RDF") { konsumer.child("RDF") {
allChildrenAutoIgnore("item") { allChildrenAutoIgnore("item") {
val authors = arrayListOf<String?>() val authors = arrayListOf<String?>()
val about = attributes.getValueOpt("about", val about = attributes.getValueOpt("about",
@ -27,7 +28,7 @@ class RSS1ItemsAdapter : XmlAdapter<List<Item>> {
when (tagName) { when (tagName) {
"title" -> title = nonNullText() "title" -> title = nonNullText()
"link" -> link = nullableText() "link" -> link = nullableText()
"dc:date" -> pubDate = DateUtils.parse(nonNullText()) "dc:date" -> pubDate = DateUtils.parse(nullableText())
"dc:creator" -> authors += nullableText() "dc:creator" -> authors += nullableText()
"description" -> description = nullableTextRecursively() "description" -> description = nullableTextRecursively()
"content:encoded" -> content = nullableTextRecursively() "content:encoded" -> content = nullableTextRecursively()
@ -36,6 +37,7 @@ class RSS1ItemsAdapter : XmlAdapter<List<Item>> {
} }
} }
if (item.pubDate == null) item.pubDate = LocalDateTime.now()
if (item.link == null) item.link = about if (item.link == null) item.link = about
?: throw ParseException("RSS1 link or about element is required") ?: throw ParseException("RSS1 link or about element is required")
item.guid = item.link item.guid = item.link
@ -49,7 +51,7 @@ class RSS1ItemsAdapter : XmlAdapter<List<Item>> {
} }
} }
konsume.close() konsumer.close()
items items
} catch (e: Exception) { } catch (e: Exception) {
throw ParseException(e.message) throw ParseException(e.message)
@ -57,10 +59,7 @@ class RSS1ItemsAdapter : XmlAdapter<List<Item>> {
} }
private fun validateItem(item: Item) { private fun validateItem(item: Item) {
when { if (item.title == null) throw ParseException("Item title is required")
item.title == null -> throw ParseException("Item title is required")
item.pubDate == null -> throw ParseException("Item date is required")
}
} }
companion object { companion object {