Call RSS1ItemAdapter in RSS1FeedAdapter

This commit is contained in:
Shinokuni 2021-09-04 16:59:08 +02:00
parent 7a417e83de
commit d50d65384c
9 changed files with 179 additions and 198 deletions

View File

@ -1,10 +1,6 @@
package com.readrops.api.localfeed
import com.gitlab.mvysny.konsumexml.Konsumer
import com.readrops.api.localfeed.atom.ATOMFeedAdapter
import com.readrops.api.localfeed.atom.ATOMItemAdapter
import com.readrops.api.localfeed.rss1.RSS1FeedAdapter
import com.readrops.api.localfeed.rss1.RSS1ItemsAdapter
import com.readrops.api.localfeed.rss2.RSS2FeedAdapter
import com.readrops.api.localfeed.rss2.RSS2ItemsAdapter
import com.readrops.db.entities.Feed
@ -16,7 +12,7 @@ interface XmlAdapter<T> {
companion object {
fun xmlFeedAdapterFactory(type: LocalRSSHelper.RSSType): XmlAdapter<Feed> = when (type) {
LocalRSSHelper.RSSType.RSS_1 -> RSS1FeedAdapter()
//LocalRSSHelper.RSSType.RSS_1 -> RSS1FeedAdapter()
LocalRSSHelper.RSSType.RSS_2 -> RSS2FeedAdapter()
//LocalRSSHelper.RSSType.ATOM -> ATOMFeedAdapter()
else -> throw IllegalArgumentException("Unknown RSS type : $type")
@ -24,7 +20,7 @@ interface XmlAdapter<T> {
fun xmlItemsAdapterFactory(type: LocalRSSHelper.RSSType): XmlAdapter<List<Item>> =
when (type) {
LocalRSSHelper.RSSType.RSS_1 -> RSS1ItemsAdapter()
//LocalRSSHelper.RSSType.RSS_1 -> RSS1ItemsAdapter()
LocalRSSHelper.RSSType.RSS_2 -> RSS2ItemsAdapter()
//LocalRSSHelper.RSSType.ATOM -> ATOMItemAdapter()
else -> throw IllegalArgumentException("Unknown RSS type : $type")

View File

@ -37,7 +37,6 @@ class ATOMItemAdapter : XmlAdapter<Item> {
if (item.pubDate == null) item.pubDate = LocalDateTime.now()
if (item.guid == null) item.guid = item.link
konsumer.close()
item
} catch (e: Exception) {
throw ParseException(e.message)

View File

@ -10,38 +10,49 @@ import com.readrops.api.utils.extensions.checkElement
import com.readrops.api.utils.extensions.nonNullText
import com.readrops.api.utils.extensions.nullableText
import com.readrops.db.entities.Feed
import com.readrops.db.entities.Item
class RSS1FeedAdapter : XmlAdapter<Feed> {
class RSS1FeedAdapter : XmlAdapter<Pair<Feed, List<Item>>> {
override fun fromXml(konsumer: Konsumer): Feed {
override fun fromXml(konsumer: Konsumer): Pair<Feed, List<Item>> {
val feed = Feed()
val items = arrayListOf<Item>()
val itemAdapter = RSS1ItemAdapter()
return try {
konsumer.checkElement(LocalRSSHelper.RSS_1_ROOT_NAME) {
it.allChildrenAutoIgnore("channel") {
feed.url = attributes.getValueOpt("about",
namespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
allChildrenAutoIgnore(names) {
with(feed) {
when (tagName) {
"title" -> name = nonNullText()
"link" -> siteUrl = nonNullText()
"description" -> description = nullableText()
}
}
it.allChildrenAutoIgnore(Names.of("channel", "item")) {
when (tagName) {
"channel" -> parseChannel(this, feed)
"item" -> items += itemAdapter.fromXml(this)
}
}
}
konsumer.close()
feed
Pair(feed, items)
} catch (e: Exception) {
throw ParseException(e.message)
}
}
private fun parseChannel(konsumer: Konsumer, feed: Feed) = with(konsumer) {
feed.url = attributes.getValueOrNull("about",
namespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
allChildrenAutoIgnore(names) {
with(feed) {
when (tagName) {
"title" -> name = nonNullText()
"link" -> siteUrl = nonNullText()
"description" -> description = nullableText()
}
}
}
}
companion object {
val names = Names.of("title", "link", "description")
}

View File

@ -0,0 +1,62 @@
package com.readrops.api.localfeed.rss1
import com.gitlab.mvysny.konsumexml.Konsumer
import com.gitlab.mvysny.konsumexml.Names
import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore
import com.readrops.api.localfeed.XmlAdapter
import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX
import com.readrops.api.utils.*
import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nonNullText
import com.readrops.api.utils.extensions.nullableText
import com.readrops.api.utils.extensions.nullableTextRecursively
import com.readrops.db.entities.Item
import org.joda.time.LocalDateTime
class RSS1ItemAdapter : XmlAdapter<Item> {
override fun fromXml(konsumer: Konsumer): Item {
val item= Item()
return try {
val authors = arrayListOf<String?>()
val about = konsumer.attributes.getValueOrNull("about",
namespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
item.apply {
konsumer.allChildrenAutoIgnore(names) {
when (tagName) {
"title" -> title = nonNullText()
"link" -> link = nullableText()
"dc:date" -> pubDate = DateUtils.parse(nullableText())
"dc:creator" -> authors += nullableText()
"description" -> description = nullableTextRecursively()
"content:encoded" -> content = nullableTextRecursively()
else -> skipContents()
}
}
}
if (item.pubDate == null) item.pubDate = LocalDateTime.now()
if (item.link == null) item.link = about
?: throw ParseException("RSS1 link or about element is required")
item.guid = item.link
if (authors.filterNotNull().isNotEmpty()) item.author = authors.filterNotNull()
.joinToString(limit = AUTHORS_MAX)
validateItem(item)
item
} catch (e: Exception) {
throw ParseException(e.message)
}
}
private fun validateItem(item: Item) {
if (item.title == null) throw ParseException("Item title is required")
}
companion object {
val names = Names.of("title", "description", "date", "link", "creator", "encoded")
}
}

View File

@ -1,70 +0,0 @@
package com.readrops.api.localfeed.rss1
import com.gitlab.mvysny.konsumexml.Konsumer
import com.gitlab.mvysny.konsumexml.Names
import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore
import com.readrops.api.localfeed.XmlAdapter
import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX
import com.readrops.api.utils.*
import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nonNullText
import com.readrops.api.utils.extensions.nullableText
import com.readrops.api.utils.extensions.nullableTextRecursively
import com.readrops.db.entities.Item
import org.joda.time.LocalDateTime
class RSS1ItemsAdapter : XmlAdapter<List<Item>> {
override fun fromXml(konsumer: Konsumer): List<Item> {
val items = arrayListOf<Item>()
return try {
konsumer.child("RDF") {
allChildrenAutoIgnore("item") {
val authors = arrayListOf<String?>()
val about = attributes.getValueOpt("about",
namespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
val item = Item().apply {
allChildrenAutoIgnore(names) {
when (tagName) {
"title" -> title = nonNullText()
"link" -> link = nullableText()
"dc:date" -> pubDate = DateUtils.parse(nullableText())
"dc:creator" -> authors += nullableText()
"description" -> description = nullableTextRecursively()
"content:encoded" -> content = nullableTextRecursively()
else -> skipContents()
}
}
}
if (item.pubDate == null) item.pubDate = LocalDateTime.now()
if (item.link == null) item.link = about
?: throw ParseException("RSS1 link or about element is required")
item.guid = item.link
if (authors.filterNotNull().isNotEmpty()) item.author = authors.filterNotNull()
.joinToString(limit = AUTHORS_MAX)
validateItem(item)
items += item
}
}
konsumer.close()
items
} catch (e: Exception) {
throw ParseException(e.message)
}
}
private fun validateItem(item: Item) {
if (item.title == null) throw ParseException("Item title is required")
}
companion object {
val names = Names.of("title", "description", "date", "link", "creator", "encoded")
}
}

View File

@ -1,9 +1,5 @@
package com.readrops.api.localfeed
import com.readrops.api.localfeed.atom.ATOMFeedAdapter
import com.readrops.api.localfeed.atom.ATOMItemAdapter
import com.readrops.api.localfeed.rss1.RSS1FeedAdapter
import com.readrops.api.localfeed.rss1.RSS1ItemsAdapter
import com.readrops.api.localfeed.rss2.RSS2FeedAdapter
import com.readrops.api.localfeed.rss2.RSS2ItemsAdapter
import junit.framework.TestCase.assertTrue
@ -18,7 +14,7 @@ class XmlAdapterTest {
@Test
fun xmlFeedAdapterFactoryTest() {
assertTrue(XmlAdapter.xmlFeedAdapterFactory(LocalRSSHelper.RSSType.RSS_1) is RSS1FeedAdapter)
//assertTrue(XmlAdapter.xmlFeedAdapterFactory(LocalRSSHelper.RSSType.RSS_1) is RSS1FeedAdapter)
assertTrue(XmlAdapter.xmlFeedAdapterFactory(LocalRSSHelper.RSSType.RSS_2) is RSS2FeedAdapter)
//assertTrue(XmlAdapter.xmlFeedAdapterFactory(LocalRSSHelper.RSSType.ATOM) is ATOMFeedAdapter)
@ -28,7 +24,7 @@ class XmlAdapterTest {
@Test
fun xmlItemsAdapterFactoryTest() {
assertTrue(XmlAdapter.xmlItemsAdapterFactory(LocalRSSHelper.RSSType.RSS_1) is RSS1ItemsAdapter)
//assertTrue(XmlAdapter.xmlItemsAdapterFactory(LocalRSSHelper.RSSType.RSS_1) is RSS1ItemsAdapter)
assertTrue(XmlAdapter.xmlItemsAdapterFactory(LocalRSSHelper.RSSType.RSS_2) is RSS2ItemsAdapter)
//assertTrue(XmlAdapter.xmlItemsAdapterFactory(LocalRSSHelper.RSSType.ATOM) is ATOMItemAdapter)

View File

@ -0,0 +1,87 @@
package com.readrops.api.localfeed.rss1
import com.gitlab.mvysny.konsumexml.konsumeXml
import com.readrops.api.TestUtils
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNotNull
import junit.framework.TestCase
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Test
class RSS1AdapterTest {
private val adapter = RSS1FeedAdapter()
@Test
fun normalCaseTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_feed.xml")
val pair = adapter.fromXml(stream.konsumeXml())
val feed = pair.first
val items = pair.second
with(feed) {
assertEquals(name, "Slashdot")
assertEquals(url, "https://slashdot.org/")
assertEquals(siteUrl, "https://slashdot.org/")
assertEquals(description, "News for nerds, stuff that matters")
}
with(items[0]) {
assertEquals(items.size, 4)
assertEquals(title, "Google Expands its Flutter Development Kit To Windows Apps")
assertEquals(link!!.trim(), "https://developers.slashdot.org/story/20/09/23/1616231/google-expands-" +
"its-flutter-development-kit-to-windows-apps?utm_source=rss1.0mainlinkanon&utm_medium=feed")
assertEquals(guid!!.trim(), "https://developers.slashdot.org/story/20/09/23/1616231/google-expands-" +
"its-flutter-development-kit-to-windows-apps?utm_source=rss1.0mainlinkanon&utm_medium=feed")
assertEquals(pubDate, DateUtils.parse("2020-09-23T16:15:00+00:00"))
assertEquals(author, "msmash")
assertNotNull(description)
assertEquals(content, "content:encoded")
}
}
@Test
fun specialCasesTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_items_special_cases.xml")
val item = adapter.fromXml(stream.konsumeXml()).second[0]
TestCase.assertEquals(item.author, "msmash, creator 2, creator 3, creator 4, ...")
TestCase.assertEquals(item.link, "https://news.slashdot.org/story/20/09/23/1420240/a-new-york-clock-" +
"that-told-time-now-tells-the-time-remaining?utm_source=rss1.0mainlinkanon&utm_medium=feed")
}
@Test
fun nullDateTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_items_no_date.xml")
val item = adapter.fromXml(stream.konsumeXml()).second[0]
TestCase.assertNotNull(item.pubDate)
}
@Test
fun nullTitleTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_items_no_title.xml")
val exception = assertThrows(ParseException::class.java) {
adapter.fromXml(stream.konsumeXml())
}
assertTrue(exception.message!!.contains("Item title is required"))
}
@Test
fun nullLinkTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_items_no_link.xml")
val exception = assertThrows(ParseException::class.java) {
adapter.fromXml(stream.konsumeXml())
}
assertTrue(exception.message!!.contains("RSS1 link or about element is required"))
}
}

View File

@ -1,23 +0,0 @@
package com.readrops.api.localfeed.rss1
import com.gitlab.mvysny.konsumexml.konsumeXml
import com.readrops.api.TestUtils
import junit.framework.Assert.assertEquals
import org.junit.Test
class RSS1FeedAdapterTest {
private val adapter = RSS1FeedAdapter()
@Test
fun normalCaseTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_feed.xml")
val feed = adapter.fromXml(stream.konsumeXml())
assertEquals(feed.name, "Slashdot")
assertEquals(feed.url, "https://slashdot.org/")
assertEquals(feed.siteUrl, "https://slashdot.org/")
assertEquals(feed.description, "News for nerds, stuff that matters")
}
}

View File

@ -1,77 +0,0 @@
package com.readrops.api.localfeed.rss1
import com.gitlab.mvysny.konsumexml.konsumeXml
import com.readrops.api.TestUtils
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
class RSS1ItemsAdapterTest {
private val adapter = RSS1ItemsAdapter()
@get:Rule
val expectedException: ExpectedException = ExpectedException.none()
@Test
fun normalCasesTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_feed.xml")
val items = adapter.fromXml(stream.konsumeXml())
val item = items.first()
assertEquals(items.size, 4)
assertEquals(item.title, "Google Expands its Flutter Development Kit To Windows Apps")
assertEquals(item.link!!.trim(), "https://developers.slashdot.org/story/20/09/23/1616231/google-expands-" +
"its-flutter-development-kit-to-windows-apps?utm_source=rss1.0mainlinkanon&utm_medium=feed")
assertEquals(item.guid!!.trim(), "https://developers.slashdot.org/story/20/09/23/1616231/google-expands-" +
"its-flutter-development-kit-to-windows-apps?utm_source=rss1.0mainlinkanon&utm_medium=feed")
assertEquals(item.pubDate, DateUtils.parse("2020-09-23T16:15:00+00:00"))
assertEquals(item.author, "msmash")
assertNotNull(item.description)
assertEquals(item.content, "content:encoded")
}
@Test
fun specialCasesTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_items_special_cases.xml")
val item = adapter.fromXml(stream.konsumeXml()).first()
assertEquals(item.author, "msmash, creator 2, creator 3, creator 4, ...")
assertEquals(item.link, "https://news.slashdot.org/story/20/09/23/1420240/a-new-york-clock-" +
"that-told-time-now-tells-the-time-remaining?utm_source=rss1.0mainlinkanon&utm_medium=feed")
}
@Test
fun nullDateTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_items_no_date.xml")
val item = adapter.fromXml(stream.konsumeXml()).first()
assertNotNull(item.pubDate)
}
@Test
fun nullTitleTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_items_no_title.xml")
expectedException.expect(ParseException::class.java)
expectedException.expectMessage("Item title is required")
adapter.fromXml(stream.konsumeXml())
}
@Test
fun nullLinkTest() {
val stream = TestUtils.loadResource("localfeed/rss1/rss1_items_no_link.xml")
expectedException.expect(ParseException::class.java)
expectedException.expectMessage("RSS1 link or about element is required")
adapter.fromXml(stream.konsumeXml())
}
}