From 6f2aad79c0cb1a146e5eeb004c7cc94ed0e81ca9 Mon Sep 17 00:00:00 2001 From: Shinokuni Date: Sat, 4 Sep 2021 17:24:13 +0200 Subject: [PATCH] Call RSS2ItemAdapter in RSS2FeedAdapter --- .../com/readrops/api/localfeed/XmlAdapter.kt | 6 +- .../api/localfeed/rss2/RSS2FeedAdapter.kt | 17 ++- ...RSS2ItemsAdapter.kt => RSS2ItemAdapter.kt} | 71 +++++------ .../readrops/api/localfeed/XmlAdapterTest.kt | 7 +- .../api/localfeed/rss2/RSS2AdapterTest.kt | 118 ++++++++++++++++++ .../api/localfeed/rss2/RSS2FeedAdapterTest.kt | 32 ----- .../localfeed/rss2/RSS2ItemsAdapterTest.kt | 99 --------------- api/src/test/resources/localfeed/rss_feed.xml | 4 +- 8 files changed, 167 insertions(+), 187 deletions(-) rename api/src/main/java/com/readrops/api/localfeed/rss2/{RSS2ItemsAdapter.kt => RSS2ItemAdapter.kt} (50%) create mode 100644 api/src/test/java/com/readrops/api/localfeed/rss2/RSS2AdapterTest.kt delete mode 100644 api/src/test/java/com/readrops/api/localfeed/rss2/RSS2FeedAdapterTest.kt delete mode 100644 api/src/test/java/com/readrops/api/localfeed/rss2/RSS2ItemsAdapterTest.kt diff --git a/api/src/main/java/com/readrops/api/localfeed/XmlAdapter.kt b/api/src/main/java/com/readrops/api/localfeed/XmlAdapter.kt index 18846159..4efbd1a4 100644 --- a/api/src/main/java/com/readrops/api/localfeed/XmlAdapter.kt +++ b/api/src/main/java/com/readrops/api/localfeed/XmlAdapter.kt @@ -1,8 +1,6 @@ package com.readrops.api.localfeed import com.gitlab.mvysny.konsumexml.Konsumer -import com.readrops.api.localfeed.rss2.RSS2FeedAdapter -import com.readrops.api.localfeed.rss2.RSS2ItemsAdapter import com.readrops.db.entities.Feed import com.readrops.db.entities.Item @@ -13,7 +11,7 @@ interface XmlAdapter { companion object { fun xmlFeedAdapterFactory(type: LocalRSSHelper.RSSType): XmlAdapter = when (type) { //LocalRSSHelper.RSSType.RSS_1 -> RSS1FeedAdapter() - LocalRSSHelper.RSSType.RSS_2 -> RSS2FeedAdapter() + //LocalRSSHelper.RSSType.RSS_2 -> RSS2FeedAdapter() //LocalRSSHelper.RSSType.ATOM -> ATOMFeedAdapter() else -> throw IllegalArgumentException("Unknown RSS type : $type") } @@ -21,7 +19,7 @@ interface XmlAdapter { fun xmlItemsAdapterFactory(type: LocalRSSHelper.RSSType): XmlAdapter> = when (type) { //LocalRSSHelper.RSSType.RSS_1 -> RSS1ItemsAdapter() - LocalRSSHelper.RSSType.RSS_2 -> RSS2ItemsAdapter() + //LocalRSSHelper.RSSType.RSS_2 -> RSS2ItemsAdapter() //LocalRSSHelper.RSSType.ATOM -> ATOMItemAdapter() else -> throw IllegalArgumentException("Unknown RSS type : $type") } diff --git a/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2FeedAdapter.kt b/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2FeedAdapter.kt index 3bcd1748..f6845bf9 100644 --- a/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2FeedAdapter.kt +++ b/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2FeedAdapter.kt @@ -10,13 +10,17 @@ 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 import org.jsoup.Jsoup -class RSS2FeedAdapter : XmlAdapter { +class RSS2FeedAdapter : XmlAdapter>> { - override fun fromXml(konsumer: Konsumer): Feed { + override fun fromXml(konsumer: Konsumer): Pair> { val feed = Feed() + val items = arrayListOf() + val itemAdapter = RSS2ItemAdapter() + return try { konsumer.checkElement(LocalRSSHelper.RSS_2_ROOT_NAME) { it.child("channel") { @@ -27,9 +31,10 @@ class RSS2FeedAdapter : XmlAdapter { "description" -> description = nullableText() "link" -> siteUrl = nullableText() "atom:link" -> { - if (attributes.getValueOpt("rel") == "self") - url = attributes.getValueOpt("href") + if (attributes.getValueOrNull("rel") == "self") + url = attributes.getValueOrNull("href") } + "item" -> items += itemAdapter.fromXml(this@allChildrenAutoIgnore) else -> skipContents() } } @@ -38,13 +43,13 @@ class RSS2FeedAdapter : XmlAdapter { } konsumer.close() - feed + Pair(feed, items) } catch (e: Exception) { throw ParseException(e.message) } } companion object { - val names = Names.of("title", "description", "link") + val names = Names.of("title", "description", "link", "item") } } \ No newline at end of file diff --git a/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemsAdapter.kt b/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemAdapter.kt similarity index 50% rename from api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemsAdapter.kt rename to api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemAdapter.kt index d8756ca9..fc36d3ba 100644 --- a/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemsAdapter.kt +++ b/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemAdapter.kt @@ -11,74 +11,65 @@ import com.readrops.api.utils.extensions.nullableTextRecursively import com.readrops.db.entities.Item import org.joda.time.LocalDateTime -class RSS2ItemsAdapter : XmlAdapter> { +class RSS2ItemAdapter : XmlAdapter { - override fun fromXml(konsumer: Konsumer): List { - val items = mutableListOf() + override fun fromXml(konsumer: Konsumer): Item { + val item = Item() return try { - konsumer.child("rss") { - child("channel") { - allChildrenAutoIgnore("item") { - val creators = arrayListOf() + //konsumer.checkCurrent("item") + val creators = arrayListOf() - val item = Item().apply { - allChildrenAutoIgnore(names) { - when (tagName) { - "title" -> title = ApiUtils.cleanText(nonNullText()) - "link" -> link = nonNullText() - "author" -> author = nullableText() - "dc:creator" -> creators += nullableText() - "pubDate" -> pubDate = DateUtils.parse(nullableText()) - "dc:date" -> pubDate = DateUtils.parse(nullableText()) - "guid" -> guid = nullableText() - "description" -> description = nullableTextRecursively() - "content:encoded" -> content = nullableTextRecursively() - "enclosure" -> parseEnclosure(this, item = this@apply) - "media:content" -> parseMediaContent(this, item = this@apply) - "media:group" -> parseMediaGroup(this, item = this@apply) - else -> skipContents() // for example media:description - } - } - } - - finalizeItem(item, creators) - - items += item + item.apply { + konsumer.allChildrenAutoIgnore(names) { + when (tagName) { + "title" -> title = ApiUtils.cleanText(nonNullText()) + "link" -> link = nonNullText() + "author" -> author = nullableText() + "dc:creator" -> creators += nullableText() + "pubDate" -> pubDate = DateUtils.parse(nullableText()) + "dc:date" -> pubDate = DateUtils.parse(nullableText()) + "guid" -> guid = nullableText() + "description" -> description = nullableTextRecursively() + "content:encoded" -> content = nullableTextRecursively() + "enclosure" -> parseEnclosure(this, item = this@apply) + "media:content" -> parseMediaContent(this, item = this@apply) + "media:group" -> parseMediaGroup(this, item = this@apply) + else -> skipContents() // for example media:description } } } - konsumer.close() - items + finalizeItem(item, creators) + item } catch (e: KonsumerException) { throw ParseException(e.message) } } private fun parseEnclosure(konsumer: Konsumer, item: Item) = with(konsumer) { - if (attributes.getValueOpt("type") != null + if (attributes.getValueOrNull("type") != null && ApiUtils.isMimeImage(attributes["type"]) && item.imageLink == null) - item.imageLink = attributes.getValueOpt("url") + item.imageLink = attributes.getValueOrNull("url") } private fun isMediumImage(konsumer: Konsumer) = with(konsumer) { - attributes.getValueOpt("medium") != null && ApiUtils.isMimeImage(attributes["medium"]) + attributes.getValueOrNull("medium") != null && ApiUtils.isMimeImage(attributes["medium"]) } private fun isTypeImage(konsumer: Konsumer) = with(konsumer) { - attributes.getValueOpt("type") != null && ApiUtils.isMimeImage(attributes["type"]) + attributes.getValueOrNull("type") != null && ApiUtils.isMimeImage(attributes["type"]) } - private fun parseMediaContent(konsumer: Konsumer, item: Item) { + private fun parseMediaContent(konsumer: Konsumer, item: Item) = with(konsumer) { if ((isMediumImage(konsumer) || isTypeImage(konsumer)) && item.imageLink == null) - item.imageLink = konsumer.attributes.getValueOpt("url") + item.imageLink = konsumer.attributes.getValueOrNull("url") konsumer.skipContents() // ignore media content sub elements } - private fun parseMediaGroup(konsumer: Konsumer, item: Item) { - konsumer.allChildrenAutoIgnore("content") { + private fun parseMediaGroup(konsumer: Konsumer, item: Item) = with(konsumer) { + allChildrenAutoIgnore("content") { when (tagName) { "media:content" -> parseMediaContent(this, item) else -> skipContents() diff --git a/api/src/test/java/com/readrops/api/localfeed/XmlAdapterTest.kt b/api/src/test/java/com/readrops/api/localfeed/XmlAdapterTest.kt index 4e4173b6..ad5409e8 100644 --- a/api/src/test/java/com/readrops/api/localfeed/XmlAdapterTest.kt +++ b/api/src/test/java/com/readrops/api/localfeed/XmlAdapterTest.kt @@ -1,8 +1,5 @@ package com.readrops.api.localfeed -import com.readrops.api.localfeed.rss2.RSS2FeedAdapter -import com.readrops.api.localfeed.rss2.RSS2ItemsAdapter -import junit.framework.TestCase.assertTrue import org.junit.Rule import org.junit.Test import org.junit.rules.ExpectedException @@ -15,7 +12,7 @@ class XmlAdapterTest { @Test fun xmlFeedAdapterFactoryTest() { //assertTrue(XmlAdapter.xmlFeedAdapterFactory(LocalRSSHelper.RSSType.RSS_1) is RSS1FeedAdapter) - assertTrue(XmlAdapter.xmlFeedAdapterFactory(LocalRSSHelper.RSSType.RSS_2) is RSS2FeedAdapter) + //assertTrue(XmlAdapter.xmlFeedAdapterFactory(LocalRSSHelper.RSSType.RSS_2) is RSS2FeedAdapter) //assertTrue(XmlAdapter.xmlFeedAdapterFactory(LocalRSSHelper.RSSType.ATOM) is ATOMFeedAdapter) expectedException.expect(IllegalArgumentException::class.java) @@ -25,7 +22,7 @@ class XmlAdapterTest { @Test fun xmlItemsAdapterFactoryTest() { //assertTrue(XmlAdapter.xmlItemsAdapterFactory(LocalRSSHelper.RSSType.RSS_1) is RSS1ItemsAdapter) - assertTrue(XmlAdapter.xmlItemsAdapterFactory(LocalRSSHelper.RSSType.RSS_2) is RSS2ItemsAdapter) + //assertTrue(XmlAdapter.xmlItemsAdapterFactory(LocalRSSHelper.RSSType.RSS_2) is RSS2ItemsAdapter) //assertTrue(XmlAdapter.xmlItemsAdapterFactory(LocalRSSHelper.RSSType.ATOM) is ATOMItemAdapter) expectedException.expect(IllegalArgumentException::class.java) diff --git a/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2AdapterTest.kt b/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2AdapterTest.kt new file mode 100644 index 00000000..ee9bf990 --- /dev/null +++ b/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2AdapterTest.kt @@ -0,0 +1,118 @@ +package com.readrops.api.localfeed.rss2 + +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 +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertTrue +import org.junit.Assert.assertThrows +import org.junit.Test + +class RSS2AdapterTest { + + private val adapter = RSS2FeedAdapter() + + @Test + fun normalCasesTest() { + val stream = TestUtils.loadResource("localfeed/rss_feed.xml") + + val pair = adapter.fromXml(stream.konsumeXml()) + val feed = pair.first + val items = pair.second + + with(feed) { + assertEquals(name, "Hacker News") + assertEquals(url, "https://news.ycombinator.com/feed/") + assertEquals(siteUrl, "https://news.ycombinator.com/") + assertEquals(description, "Links for the intellectually curious, ranked by readers.") + } + + with(items[0]) { + assertEquals(items.size, 7) + assertEquals(title, "Africa declared free of wild polio") + assertEquals(link, "https://www.bbc.com/news/world-africa-53887947") + assertEquals(pubDate, DateUtils.parse("Tue, 25 Aug 2020 17:15:49 +0000")) + assertEquals(author, "Author 1") + assertEquals(description, "Comments") + assertEquals(guid, "https://www.bbc.com/news/world-africa-53887947") + } + } + + + @Test + fun nullTitleTest() { + val stream = TestUtils.loadResource("localfeed/rss2/rss_feed_special_cases.xml") + + assertThrows(ParseException::class.java) { + adapter.fromXml(stream.konsumeXml()) + } + } + + @Test + fun otherNamespacesTest() { + val stream = TestUtils.loadResource("localfeed/rss2/rss_items_other_namespaces.xml") + val item = adapter.fromXml(stream.konsumeXml()).second[0] + + assertEquals(item.guid, "guid") + assertEquals(item.author, "creator 1, creator 2, creator 3, creator 4") + assertEquals(item.pubDate, DateUtils.parse("2020-08-05T14:03:48Z")) + assertEquals(item.content, "content:encoded") + } + + @Test + fun noDateTest() { + val stream = TestUtils.loadResource("localfeed/rss2/rss_items_no_date.xml") + val item = adapter.fromXml(stream.konsumeXml()).second[0] + + TestCase.assertNotNull(item.pubDate) + } + + @Test + fun noTitleTest() { + val stream = TestUtils.loadResource("localfeed/rss2/rss_items_no_title.xml") + + val exception = assertThrows(ParseException::class.java) { + adapter.fromXml(stream.konsumeXml()) + } + + assertTrue(exception.message!!.contains("Item title is required")) + } + + @Test + fun noLinkTest() { + val stream = TestUtils.loadResource("localfeed/rss2/rss_items_no_link.xml") + + val exception = assertThrows(ParseException::class.java) { + adapter.fromXml(stream.konsumeXml()) + } + + assertTrue(exception.message!!.contains("Item link is required")) + } + + @Test + fun enclosureTest() { + val stream = TestUtils.loadResource("localfeed/rss2/rss_items_enclosure.xml") + val item = adapter.fromXml(stream.konsumeXml()).second[0] + + assertEquals(item.imageLink, "https://image1.jpg") + } + + @Test + fun mediaContentTest() { + val stream = TestUtils.loadResource("localfeed/rss2/rss_items_media_content.xml") + val items = adapter.fromXml(stream.konsumeXml()) + + assertEquals(items.second[0].imageLink, "https://image1.jpg") + assertEquals(items.second[1].imageLink, "https://image2.jpg") + } + + @Test + fun mediaGroupTest() { + val stream = TestUtils.loadResource("localfeed/rss2/rss_items_media_group.xml") + val item = adapter.fromXml(stream.konsumeXml()).second[0] + + assertEquals(item.imageLink, "https://image1.jpg") + } +} \ No newline at end of file diff --git a/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2FeedAdapterTest.kt b/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2FeedAdapterTest.kt deleted file mode 100644 index 6f09d7e2..00000000 --- a/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2FeedAdapterTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.readrops.api.localfeed.rss2 - -import com.gitlab.mvysny.konsumexml.konsumeXml -import com.readrops.api.TestUtils -import com.readrops.api.utils.exceptions.ParseException -import junit.framework.TestCase.assertEquals -import org.junit.Test - -class RSS2FeedAdapterTest { - - - private val adapter = RSS2FeedAdapter() - - @Test - fun normalCasesTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_full_feed.xml") - - val feed = adapter.fromXml(stream.konsumeXml()) - - assertEquals(feed.name, "Hacker News") - assertEquals(feed.url, "https://news.ycombinator.com/feed/") - assertEquals(feed.siteUrl, "https://news.ycombinator.com/") - assertEquals(feed.description, "Links for the intellectually curious, ranked by readers.") - } - - - @Test(expected = ParseException::class) - fun nullTitleTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_feed_special_cases.xml") - adapter.fromXml(stream.konsumeXml()) - } -} \ No newline at end of file diff --git a/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2ItemsAdapterTest.kt b/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2ItemsAdapterTest.kt deleted file mode 100644 index df0d0095..00000000 --- a/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2ItemsAdapterTest.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.readrops.api.localfeed.rss2 - -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 RSS2ItemsAdapterTest { - - private val adapter = RSS2ItemsAdapter() - - @get:Rule - val expectedException: ExpectedException = ExpectedException.none() - - @Test - fun normalCasesTest() { - val stream = TestUtils.loadResource("localfeed/rss_feed.xml") - - val items = adapter.fromXml(stream.konsumeXml()) - val item = items.first() - - assertEquals(items.size, 7) - assertEquals(item.title, "Africa declared free of wild polio") - assertEquals(item.link, "https://www.bbc.com/news/world-africa-53887947") - assertEquals(item.pubDate, DateUtils.parse("Tue, 25 Aug 2020 17:15:49 +0000")) - assertEquals(item.author, "Author 1") - assertEquals(item.description, "Comments") - assertEquals(item.guid, "https://www.bbc.com/news/world-africa-53887947") - } - - @Test - fun otherNamespacesTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_items_other_namespaces.xml") - val item = adapter.fromXml(stream.konsumeXml()).first() - - assertEquals(item.guid, "guid") - assertEquals(item.author, "creator 1, creator 2, creator 3, creator 4") - assertEquals(item.pubDate, DateUtils.parse("2020-08-05T14:03:48Z")) - assertEquals(item.content, "content:encoded") - } - - @Test - fun noDateTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_items_no_date.xml") - val item = adapter.fromXml(stream.konsumeXml()).first() - - assertNotNull(item.pubDate) - } - - @Test - fun noTitleTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_items_no_title.xml") - - expectedException.expect(ParseException::class.java) - expectedException.expectMessage("Item title is required") - - adapter.fromXml(stream.konsumeXml()) - } - - @Test - fun noLinkTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_items_no_link.xml") - - expectedException.expect(ParseException::class.java) - expectedException.expectMessage("Item link is required") - - adapter.fromXml(stream.konsumeXml()) - } - - @Test - fun enclosureTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_items_enclosure.xml") - val item = adapter.fromXml(stream.konsumeXml()).first() - - assertEquals(item.imageLink, "https://image1.jpg") - } - - @Test - fun mediaContentTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_items_media_content.xml") - val items = adapter.fromXml(stream.konsumeXml()) - - assertEquals(items.first().imageLink, "https://image1.jpg") - assertEquals(items[1].imageLink, "https://image2.jpg") - } - - @Test - fun mediaGroupTest() { - val stream = TestUtils.loadResource("localfeed/rss2/rss_items_media_group.xml") - val item = adapter.fromXml(stream.konsumeXml()).first() - - assertEquals(item.imageLink, "https://image1.jpg") - } -} \ No newline at end of file diff --git a/api/src/test/resources/localfeed/rss_feed.xml b/api/src/test/resources/localfeed/rss_feed.xml index 79d3b7a1..c4702aae 100644 --- a/api/src/test/resources/localfeed/rss_feed.xml +++ b/api/src/test/resources/localfeed/rss_feed.xml @@ -1,7 +1,9 @@ - + Hacker News + https://news.ycombinator.com/ Links for the intellectually curious, ranked by readers.