From 9123decf99b80f305c0d68ba8e9bc48e227f9426 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 10:16:18 -0800 Subject: [PATCH 01/16] fix warnings --- analysis_options.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 0323d68..3d2d6cd 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,7 +1,4 @@ analyzer: - language: - enablePreviewDart2: true - strong-mode: true errors: unused_import: error unused_local_variable: error From 374f4f59b85ec584393f54b54661c5c9f17282b0 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 10:18:27 -0800 Subject: [PATCH 02/16] update client --- example/main.dart | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/example/main.dart b/example/main.dart index 81a0888..39647c6 100644 --- a/example/main.dart +++ b/example/main.dart @@ -1,24 +1,16 @@ import 'package:http/http.dart' as http; import 'package:webfeed/webfeed.dart'; -void main() { +void main() async { var client = new http.Client(); // RSS feed - client.get("https://developer.apple.com/news/releases/rss/releases.rss").then((response) { - return response.body; - }).then((bodyString) { - var channel = new RssFeed.parse(bodyString); - print(channel); - return channel; - }); + var rssresp = await client.get("https://developer.apple.com/news/releases/rss/releases.rss"); + var channel = new RssFeed.parse(rssresp.body); + print(channel); // Atom feed - client.get("https://www.theverge.com/rss/index.xml").then((response) { - return response.body; - }).then((bodyString) { - var feed = new AtomFeed.parse(bodyString); - print(feed); - return feed; - }); + var atomresp = await client.get("https://www.theverge.com/rss/index.xml"); + var feed = new AtomFeed.parse(atomresp.body); + print(feed); } From ca3c4095e6bdbcd34c978b10113e8e5927a61736 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 10:37:47 -0800 Subject: [PATCH 03/16] testing support for RFC 5005 --- test/atom_test.dart | 40 ++++++++++++++++++++++++++++++++++++---- test/xml/Atom-Page1.xml | 20 ++++++++++++++++++++ test/xml/Atom-Page2.xml | 20 ++++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 test/xml/Atom-Page1.xml create mode 100644 test/xml/Atom-Page2.xml diff --git a/test/atom_test.dart b/test/atom_test.dart index e7ba377..eaf887a 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -88,7 +88,7 @@ void main() { expect(item.content, "This is content 1"); expect(item.rights, "This is rights 1"); }); - test("parse Atom-Media.xml", (){ + test("parse Atom-Media.xml", () { var xmlString = new File("test/xml/Atom-Media.xml").readAsStringSync(); var feed = new AtomFeed.parse(xmlString); @@ -97,7 +97,7 @@ void main() { expect(feed.updated, "2018-04-06T13:02:46Z"); expect(feed.items.length, 1); - + var item = feed.items.first; expect(item.media.group.contents.length, 5); expect(item.media.group.credits.length, 2); @@ -135,9 +135,9 @@ void main() { expect(item.media.description.type, "plain"); expect(item.media.description.value, "This was some really bizarre band I listened to as a young lad."); - + expect(item.media.keywords, "kitty, cat, big dog, yarn, fluffy"); - + expect(item.media.thumbnails.length, 2); var mediaThumbnail = item.media.thumbnails.first; expect(mediaThumbnail.url, "http://www.foo.com/keyframe1.jpg"); @@ -253,4 +253,36 @@ void main() { expect(item.content, null); expect(item.rights, null); }); + + // RFC 5005: Feed Paging and Archiving + test("parse Atom-Page1.xml", () { + var xmlString = File("test/xml/Atom-Page1.xml").readAsStringSync(); + + var feed = AtomFeed.parse(xmlString); + var firstPage = feed.links.firstWhere((l) => l.rel == 'first', orElse: () => null); + var previousPage = feed.links.firstWhere((l) => l.rel == 'previous', orElse: () => null); + var nextPage = feed.links.firstWhere((l) => l.rel == 'next', orElse: () => null); + var lastPage = feed.links.firstWhere((l) => l.rel == 'last', orElse: () => null); + + expect(firstPage.href, 'http://example.org/index.atom'); + expect(previousPage, null); + expect(nextPage.href, 'http://example.org/index.atom?page=2'); + expect(lastPage.href, 'http://example.org/index.atom?page=2'); + }); + + // RFC 5005: Feed Paging and Archiving + test("parse Atom-Page2.xml", () { + var xmlString = File("test/xml/Atom-Page2.xml").readAsStringSync(); + + var feed = AtomFeed.parse(xmlString); + var firstPage = feed.links.firstWhere((l) => l.rel == 'first', orElse: () => null); + var previousPage = feed.links.firstWhere((l) => l.rel == 'previous', orElse: () => null); + var nextPage = feed.links.firstWhere((l) => l.rel == 'next', orElse: () => null); + var lastPage = feed.links.firstWhere((l) => l.rel == 'last', orElse: () => null); + + expect(firstPage.href, 'http://example.org/index.atom'); + expect(previousPage.href, 'http://example.org/index.atom?page=2'); + expect(nextPage, null); + expect(lastPage.href, 'http://example.org/index.atom?page=2'); + }); } diff --git a/test/xml/Atom-Page1.xml b/test/xml/Atom-Page1.xml new file mode 100644 index 0000000..e8e0460 --- /dev/null +++ b/test/xml/Atom-Page1.xml @@ -0,0 +1,20 @@ + + + Example Feed + + + + + 2003-12-13T18:30:02Z + + John Doe + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + \ No newline at end of file diff --git a/test/xml/Atom-Page2.xml b/test/xml/Atom-Page2.xml new file mode 100644 index 0000000..f25af48 --- /dev/null +++ b/test/xml/Atom-Page2.xml @@ -0,0 +1,20 @@ + + + Example Feed + + + + + 2003-12-13T18:30:02Z + + John Doe + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + \ No newline at end of file From 140f88856602d60bfbb9f4de6a018565279ff071 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 10:59:40 -0800 Subject: [PATCH 04/16] generate empty ATOM feed --- lib/domain/atom_feed.dart | 8 ++++++-- test/atom_test.dart | 24 +++++++++--------------- test/xml/Atom-Empty.xml | 5 +---- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/lib/domain/atom_feed.dart b/lib/domain/atom_feed.dart index 92f2cd6..2723389 100644 --- a/lib/domain/atom_feed.dart +++ b/lib/domain/atom_feed.dart @@ -66,12 +66,16 @@ class AtomFeed { categories: feedElement.findElements("category").map((element) { return AtomCategory.parse(element); }).toList(), - generator: - AtomGenerator.parse(findElementOrNull(feedElement, "generator")), + generator: AtomGenerator.parse(findElementOrNull(feedElement, "generator")), icon: findElementOrNull(feedElement, "icon")?.text, logo: findElementOrNull(feedElement, "logo")?.text, rights: findElementOrNull(feedElement, "rights")?.text, subtitle: findElementOrNull(feedElement, "subtitle")?.text, ); } + + XmlDocument toXml() { + var doc = parse(''); + return doc; + } } diff --git a/test/atom_test.dart b/test/atom_test.dart index eaf887a..a21c067 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -237,21 +237,7 @@ void main() { expect(feed.logo, null); expect(feed.subtitle, null); - expect(feed.items.length, 1); - var item = feed.items.first; - - expect(item.authors.length, 0); - - expect(item.links.length, 0); - - expect(item.categories.length, 0); - - expect(item.contributors.length, 0); - - expect(item.published, null); - expect(item.summary, null); - expect(item.content, null); - expect(item.rights, null); + expect(feed.items.length, 0); }); // RFC 5005: Feed Paging and Archiving @@ -285,4 +271,12 @@ void main() { expect(nextPage, null); expect(lastPage.href, 'http://example.org/index.atom?page=2'); }); + + test("generate Atom-Empty.xml", () { + var xmlString = File("test/xml/Atom-Empty.xml").readAsStringSync(); + var feed = AtomFeed(); + var xml = feed.toXml(); + var xmlString2 = xml.toXmlString(pretty: true, indent: ' '); + expect(xmlString2, xmlString); + }); } diff --git a/test/xml/Atom-Empty.xml b/test/xml/Atom-Empty.xml index 638fb04..54b00c2 100644 --- a/test/xml/Atom-Empty.xml +++ b/test/xml/Atom-Empty.xml @@ -1,5 +1,2 @@ - - - - \ No newline at end of file + \ No newline at end of file From e2c227cebc9a6532a1629b7f4e10fb5c0ede2920 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 11:50:42 -0800 Subject: [PATCH 05/16] updated for minimum validate empty ATOM feed --- lib/domain/atom_feed.dart | 15 +++++++++++++-- test/atom_test.dart | 8 ++++---- test/xml/Atom-Empty.xml | 6 +++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/domain/atom_feed.dart b/lib/domain/atom_feed.dart index 2723389..ec75b6d 100644 --- a/lib/domain/atom_feed.dart +++ b/lib/domain/atom_feed.dart @@ -9,7 +9,7 @@ import 'package:xml/xml.dart'; class AtomFeed { final String id; final String title; - final String updated; + final DateTime updated; final List items; final List links; @@ -47,10 +47,12 @@ class AtomFeed { throw new ArgumentError("feed not found"); } + var updated = findElementOrNull(feedElement, "updated")?.text; + return AtomFeed( id: findElementOrNull(feedElement, "id")?.text, title: findElementOrNull(feedElement, "title")?.text, - updated: findElementOrNull(feedElement, "updated")?.text, + updated: updated == null ? null : DateTime.parse(updated), items: feedElement.findElements("entry").map((element) { return AtomItem.parse(element); }).toList(), @@ -76,6 +78,15 @@ class AtomFeed { XmlDocument toXml() { var doc = parse(''); + + var b = XmlBuilder(); + b.element('title'); + b.element('id', nest: () => b.text(id)); + b.element('updated', nest: () => b.text(updated.toUtc().toIso8601String())); + + var feed = doc.findAllElements('feed').first; + b.build().children.forEach((c) => feed.children.add(c.copy())); + return doc; } } diff --git a/test/atom_test.dart b/test/atom_test.dart index a21c067..697ec0b 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -225,9 +225,9 @@ void main() { var feed = AtomFeed.parse(xmlString); - expect(feed.id, null); - expect(feed.title, null); - expect(feed.updated, null); + expect(feed.id, 'https://example.com'); + expect(feed.title, ''); + expect(feed.updated, DateTime.parse('1970-01-01T00:00:00-00:00')); expect(feed.links.length, 0); expect(feed.authors.length, 0); expect(feed.contributors.length, 0); @@ -274,7 +274,7 @@ void main() { test("generate Atom-Empty.xml", () { var xmlString = File("test/xml/Atom-Empty.xml").readAsStringSync(); - var feed = AtomFeed(); + var feed = AtomFeed(id: 'https://example.com', updated: DateTime.parse('1970-01-01T00:00:00-00:00')); var xml = feed.toXml(); var xmlString2 = xml.toXmlString(pretty: true, indent: ' '); expect(xmlString2, xmlString); diff --git a/test/xml/Atom-Empty.xml b/test/xml/Atom-Empty.xml index 54b00c2..8f251ae 100644 --- a/test/xml/Atom-Empty.xml +++ b/test/xml/Atom-Empty.xml @@ -1,2 +1,6 @@ - \ No newline at end of file + + + <id>https://example.com</id> + <updated>1970-01-01T00:00:00.000Z</updated> +</feed> \ No newline at end of file From ea3ba30f814f11c723bbb410f94e42bbf48f6838 Mon Sep 17 00:00:00 2001 From: Chris Sells <csells@sellsbrothers.com> Date: Fri, 29 Nov 2019 12:03:35 -0800 Subject: [PATCH 06/16] updated from String to Date; WIP: generator Atom.xml --- lib/domain/atom_feed.dart | 2 +- test/atom_test.dart | 79 +++++++++++++++++++++++++++++++++++++-- test/xml/Atom-Empty.xml | 2 +- test/xml/Atom.xml | 4 +- 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/lib/domain/atom_feed.dart b/lib/domain/atom_feed.dart index ec75b6d..a27f449 100644 --- a/lib/domain/atom_feed.dart +++ b/lib/domain/atom_feed.dart @@ -80,8 +80,8 @@ class AtomFeed { var doc = parse('<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom"></feed>'); var b = XmlBuilder(); - b.element('title'); b.element('id', nest: () => b.text(id)); + b.element('title', nest: () => title == null ? null : b.text(title)); b.element('updated', nest: () => b.text(updated.toUtc().toIso8601String())); var feed = doc.findAllElements('feed').first; diff --git a/test/atom_test.dart b/test/atom_test.dart index 697ec0b..1f381a8 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -21,7 +21,7 @@ void main() { expect(feed.id, "foo-bar-id"); expect(feed.title, "Foo bar news"); - expect(feed.updated, "2018-04-06T13:02:46Z"); + expect(feed.updated, DateTime.parse("2018-04-06T13:02:46Z")); expect(feed.links.length, 2); expect(feed.links.first.rel, "foo"); @@ -94,7 +94,7 @@ void main() { var feed = new AtomFeed.parse(xmlString); expect(feed.id, "foo-bar-id"); expect(feed.title, "Foo bar news"); - expect(feed.updated, "2018-04-06T13:02:46Z"); + expect(feed.updated, DateTime.parse("2018-04-06T13:02:46Z")); expect(feed.items.length, 1); @@ -275,8 +275,79 @@ void main() { test("generate Atom-Empty.xml", () { var xmlString = File("test/xml/Atom-Empty.xml").readAsStringSync(); var feed = AtomFeed(id: 'https://example.com', updated: DateTime.parse('1970-01-01T00:00:00-00:00')); - var xml = feed.toXml(); - var xmlString2 = xml.toXmlString(pretty: true, indent: ' '); + var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); expect(xmlString2, xmlString); }); + + test("generate Atom.xml", () { + var xmlString = File("test/xml/Atom.xml").readAsStringSync(); + var feed = AtomFeed(id: 'foo-bar-id', title: 'Foo bar news', updated: DateTime.parse('2018-04-06T13:02:46Z')); + var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); + expect(xmlString2, xmlString); + + // expect(feed.links.length, 2); + // expect(feed.links.first.rel, "foo"); + // expect(feed.links.first.type, "text/html"); + // expect(feed.links.first.hreflang, "en"); + // expect(feed.links.first.href, "http://foo.bar.news/"); + // expect(feed.links.first.title, "Foo bar news html"); + // expect(feed.links.first.length, 1000); + + // expect(feed.authors.length, 2); + // expect(feed.authors.first.name, "Alice"); + // expect(feed.authors.first.uri, "http://foo.bar.news/people/alice"); + // expect(feed.authors.first.email, "alice@foo.bar.news"); + + // expect(feed.contributors.length, 2); + // expect(feed.contributors.first.name, "Charlie"); + // expect(feed.contributors.first.uri, "http://foo.bar.news/people/charlie"); + // expect(feed.contributors.first.email, "charlie@foo.bar.news"); + + // expect(feed.categories.length, 2); + // expect(feed.categories.first.term, "foo category"); + // expect(feed.categories.first.scheme, "this-is-foo-scheme"); + // expect(feed.categories.first.label, "this is foo label"); + + // expect(feed.generator.uri, "http://foo.bar.news/generator"); + // expect(feed.generator.version, "1.0"); + // expect(feed.generator.value, "Foo bar generator"); + + // expect(feed.icon, "http://foo.bar.news/icon.png"); + // expect(feed.logo, "http://foo.bar.news/logo.png"); + // expect(feed.subtitle, "This is subtitle"); + + // expect(feed.items.length, 2); + // var item = feed.items.first; + // expect(item.id, "foo-bar-entry-id-1"); + // expect(item.title, "Foo bar item 1"); + // expect(item.updated, "2018-04-06T13:02:47Z"); + + // expect(item.authors.length, 2); + // expect(item.authors.first.name, "Ellie"); + // expect(item.authors.first.uri, "http://foo.bar.news/people/ellie"); + // expect(item.authors.first.email, "ellie@foo.bar.news"); + + // expect(item.links.length, 2); + // expect(item.links.first.rel, "foo entry"); + // expect(item.links.first.type, "text/html"); + // expect(item.links.first.hreflang, "en"); + // expect(item.links.first.href, "http://foo.bar.news/entry"); + // expect(item.links.first.title, "Foo bar news html"); + // expect(item.links.first.length, 1000); + + // expect(item.categories.length, 2); + // expect(item.categories.first.term, "foo entry category"); + // expect(item.categories.first.scheme, "this-is-foo-entry-scheme"); + // expect(item.categories.first.label, "this is foo entry label"); + + // expect(item.contributors.length, 2); + // expect(item.contributors.first.name, "Gin"); + // expect(item.contributors.first.uri, "http://foo.bar.news/people/gin"); + // expect(item.contributors.first.email, "gin@foo.bar.news"); + + // expect(item.published, "2018-04-06T13:02:49Z"); + // expect(item.summary, "This is summary 1"); + // expect(item.content, "This is content 1"); + // expect(item.rights, "This is rights 1"); + }); } diff --git a/test/xml/Atom-Empty.xml b/test/xml/Atom-Empty.xml index 8f251ae..9008a38 100644 --- a/test/xml/Atom-Empty.xml +++ b/test/xml/Atom-Empty.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> - <title/> <id>https://example.com</id> + <title/> <updated>1970-01-01T00:00:00.000Z</updated> </feed> \ No newline at end of file diff --git a/test/xml/Atom.xml b/test/xml/Atom.xml index e88eb12..5a475cb 100644 --- a/test/xml/Atom.xml +++ b/test/xml/Atom.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <id>foo-bar-id</id> - <title type="text">Foo bar news - 2018-04-06T13:02:46Z + Foo bar news + 2018-04-06T13:02:46.000Z From af8264ae94928f37f52a467bd296d0200ca4e1a6 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 15:31:27 -0800 Subject: [PATCH 07/16] Use URI throughout; WIP: generatoring ATOM. --- lib/domain/atom_category.dart | 13 ++-- lib/domain/atom_feed.dart | 77 ++++++++++++--------- lib/domain/atom_generator.dart | 20 +++--- lib/domain/atom_link.dart | 23 +++---- lib/domain/atom_person.dart | 14 ++-- lib/domain/media/content.dart | 7 +- lib/domain/media/copyright.dart | 11 ++- lib/domain/media/embed.dart | 15 ++--- lib/domain/media/license.dart | 11 ++- lib/domain/media/media.dart | 114 ++++++++------------------------ lib/domain/media/peer_link.dart | 11 ++- lib/domain/media/player.dart | 11 ++- lib/domain/media/price.dart | 7 +- lib/domain/media/thumbnail.dart | 7 +- lib/util/helpers.dart | 32 ++++++--- test/atom_test.dart | 95 +++++++++++++------------- test/xml/Atom.xml | 3 +- 17 files changed, 218 insertions(+), 253 deletions(-) diff --git a/lib/domain/atom_category.dart b/lib/domain/atom_category.dart index 22c6a97..be6d377 100644 --- a/lib/domain/atom_category.dart +++ b/lib/domain/atom_category.dart @@ -5,12 +5,11 @@ class AtomCategory { final String scheme; final String label; - AtomCategory(this.term, this.scheme, this.label); + AtomCategory({this.term, this.scheme, this.label}); - factory AtomCategory.parse(XmlElement element) { - var term = element.getAttribute("term"); - var scheme = element.getAttribute("scheme"); - var label = element.getAttribute("label"); - return AtomCategory(term, scheme, label); - } + factory AtomCategory.parse(XmlElement element) => AtomCategory( + term: element.getAttribute("term"), + scheme: element.getAttribute("scheme"), + label: element.getAttribute("label"), + ); } diff --git a/lib/domain/atom_feed.dart b/lib/domain/atom_feed.dart index a27f449..2766e87 100644 --- a/lib/domain/atom_feed.dart +++ b/lib/domain/atom_feed.dart @@ -7,25 +7,24 @@ import 'package:webfeed/util/helpers.dart'; import 'package:xml/xml.dart'; class AtomFeed { - final String id; + final Uri id; final String title; final DateTime updated; final List items; - final List links; final List authors; final List contributors; final List categories; final AtomGenerator generator; - final String icon; - final String logo; + final Uri icon; + final Uri logo; final String rights; final String subtitle; AtomFeed({ this.id, this.title, - this.updated, + updated, this.items, this.links, this.authors, @@ -36,7 +35,7 @@ class AtomFeed { this.logo, this.rights, this.subtitle, - }); + }) : this.updated = updated ?? DateTime.now(); // default value factory AtomFeed.parse(String xmlString) { var document = parse(xmlString); @@ -47,43 +46,59 @@ class AtomFeed { throw new ArgumentError("feed not found"); } - var updated = findElementOrNull(feedElement, "updated")?.text; - return AtomFeed( - id: findElementOrNull(feedElement, "id")?.text, - title: findElementOrNull(feedElement, "title")?.text, - updated: updated == null ? null : DateTime.parse(updated), - items: feedElement.findElements("entry").map((element) { - return AtomItem.parse(element); - }).toList(), - links: feedElement.findElements("link").map((element) { - return AtomLink.parse(element); - }).toList(), - authors: feedElement.findElements("author").map((element) { - return AtomPerson.parse(element); - }).toList(), - contributors: feedElement.findElements("contributor").map((element) { - return AtomPerson.parse(element); - }).toList(), - categories: feedElement.findElements("category").map((element) { - return AtomCategory.parse(element); - }).toList(), + id: parseUriLiteral(feedElement, "id"), + title: parseTextLiteral(feedElement, "title"), + updated: parseDateTimeLiteral(feedElement, "updated"), + items: feedElement.findElements("entry").map((e) => AtomItem.parse(e)).toList(), + links: feedElement.findElements("link").map((e) => AtomLink.parse(e)).toList(), + authors: feedElement.findElements("author").map((e) => AtomPerson.parse(e)).toList(), + contributors: feedElement.findElements("contributor").map((e) => AtomPerson.parse(e)).toList(), + categories: feedElement.findElements("category").map((e) => AtomCategory.parse(e)).toList(), generator: AtomGenerator.parse(findElementOrNull(feedElement, "generator")), - icon: findElementOrNull(feedElement, "icon")?.text, - logo: findElementOrNull(feedElement, "logo")?.text, - rights: findElementOrNull(feedElement, "rights")?.text, - subtitle: findElementOrNull(feedElement, "subtitle")?.text, + icon: parseUriLiteral(feedElement, "icon"), + logo: parseUriLiteral(feedElement, "logo"), + rights: parseTextLiteral(feedElement, "rights"), + subtitle: parseTextLiteral(feedElement, "subtitle"), ); } XmlDocument toXml() { + if (id == null) throw Exception('must have an id'); var doc = parse(''); var b = XmlBuilder(); b.element('id', nest: () => b.text(id)); - b.element('title', nest: () => title == null ? null : b.text(title)); + b.element('title', nest: () => title == null ? '' : b.text(title)); b.element('updated', nest: () => b.text(updated.toUtc().toIso8601String())); + for (var link in links ?? List()) { + b.element('link', nest: () { + if (link.rel != null) b.attribute('rel', link.rel); + if (link.type != null) b.attribute('type', link.type); + if (link.hreflang != null) b.attribute('hreflang', link.hreflang); + if (link.href != null) b.attribute('href', link.href); + if (link.title != null) b.attribute('title', link.title); + if (link.length != null) b.attribute('length', link.length); + }); + } + + for (var author in authors ?? List()) { + b.element('author', nest: () { + if (author.name != null) b.element('name', nest: () => b.text(author.name)); + if (author.uri != null) b.element('uri', nest: () => b.text(author.uri)); + if (author.email != null) b.element('email', nest: () => b.text(author.email)); + }); + } + + for (var contributor in contributors ?? List()) { + b.element('contributor', nest: () { + if (contributor.name != null) b.element('name', nest: () => b.text(contributor.name)); + if (contributor.uri != null) b.element('uri', nest: () => b.text(contributor.uri)); + if (contributor.email != null) b.element('email', nest: () => b.text(contributor.email)); + }); + } + var feed = doc.findAllElements('feed').first; b.build().children.forEach((c) => feed.children.add(c.copy())); diff --git a/lib/domain/atom_generator.dart b/lib/domain/atom_generator.dart index 7fd8579..368e36a 100644 --- a/lib/domain/atom_generator.dart +++ b/lib/domain/atom_generator.dart @@ -1,19 +1,23 @@ import 'package:xml/xml.dart'; class AtomGenerator { - final String uri; + final Uri uri; final String version; final String value; - AtomGenerator(this.uri, this.version, this.value); + AtomGenerator({ + this.uri, + this.version, + this.value, + }); factory AtomGenerator.parse(XmlElement element) { - if (element == null) { - return null; - } + if (element == null) return null; var uri = element.getAttribute("uri"); - var version = element.getAttribute("version"); - var value = element.text; - return new AtomGenerator(uri, version, value); + return AtomGenerator( + uri: uri == null ? null : Uri.parse(uri), + version: element.getAttribute("version"), + value: element.text, + ); } } diff --git a/lib/domain/atom_link.dart b/lib/domain/atom_link.dart index 3b570f2..5018ed8 100644 --- a/lib/domain/atom_link.dart +++ b/lib/domain/atom_link.dart @@ -1,32 +1,31 @@ import 'package:xml/xml.dart'; class AtomLink { - final String href; + final Uri href; final String rel; final String type; final String hreflang; final String title; final int length; - AtomLink( + AtomLink({ this.href, this.rel, this.type, this.hreflang, this.title, this.length, - ); + }); factory AtomLink.parse(XmlElement element) { var href = element.getAttribute("href"); - var rel = element.getAttribute("rel"); - var type = element.getAttribute("type"); - var title = element.getAttribute("title"); - var hreflang = element.getAttribute("hreflang"); - var length = 0; - if (element.getAttribute("length") != null) { - length = int.parse(element.getAttribute("length")); - } - return AtomLink(href, rel, type, hreflang, title, length); + return AtomLink( + href: href == null ? null : Uri.parse(href), + rel: element.getAttribute("rel"), + type: element.getAttribute("type"), + hreflang: element.getAttribute("hreflang"), + title: element.getAttribute("title"), + length: int.parse(element.getAttribute("length") ?? "0"), + ); } } diff --git a/lib/domain/atom_person.dart b/lib/domain/atom_person.dart index 3eb1e4a..bd48d2a 100644 --- a/lib/domain/atom_person.dart +++ b/lib/domain/atom_person.dart @@ -3,15 +3,17 @@ import 'package:xml/xml.dart'; class AtomPerson { final String name; - final String uri; + final Uri uri; final String email; - AtomPerson(this.name, this.uri, this.email); + AtomPerson({this.name, this.uri, this.email}); factory AtomPerson.parse(XmlElement element) { - var name = findElementOrNull(element, "name")?.text; - var uri = findElementOrNull(element, "uri")?.text; - var email = findElementOrNull(element, "email")?.text; - return AtomPerson(name, uri, email); + var uri = findElementOrNull(element, "uri"); + return AtomPerson( + name: findElementOrNull(element, "name")?.text, + uri: uri == null ? null : Uri.parse(uri.text), + email: findElementOrNull(element, "email")?.text, + ); } } diff --git a/lib/domain/media/content.dart b/lib/domain/media/content.dart index 3a4ebaa..6236025 100644 --- a/lib/domain/media/content.dart +++ b/lib/domain/media/content.dart @@ -1,7 +1,7 @@ import 'package:xml/xml.dart'; class Content { - final String url; + final Uri url; final String type; final int fileSize; final String medium; @@ -34,8 +34,9 @@ class Content { }); factory Content.parse(XmlElement element) { - return new Content( - url: element.getAttribute("url"), + var url = element.getAttribute("url"); + return Content( + url: url == null ? null : Uri.parse(url), type: element.getAttribute("type"), fileSize: int.tryParse(element.getAttribute("fileSize") ?? "0"), medium: element.getAttribute("medium"), diff --git a/lib/domain/media/copyright.dart b/lib/domain/media/copyright.dart index 1cad059..87b909f 100644 --- a/lib/domain/media/copyright.dart +++ b/lib/domain/media/copyright.dart @@ -1,7 +1,7 @@ import 'package:xml/xml.dart'; class Copyright { - final String url; + final Uri url; final String value; Copyright({ @@ -10,11 +10,10 @@ class Copyright { }); factory Copyright.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Copyright( - url: element.getAttribute("url"), + if (element == null) return null; + var url = element.getAttribute("url"); + return Copyright( + url: url == null ? null : Uri.parse(url), value: element.text, ); } diff --git a/lib/domain/media/embed.dart b/lib/domain/media/embed.dart index 1cca476..305eeba 100644 --- a/lib/domain/media/embed.dart +++ b/lib/domain/media/embed.dart @@ -2,7 +2,7 @@ import 'package:webfeed/domain/media/param.dart'; import 'package:xml/xml.dart'; class Embed { - final String url; + final Uri url; final int width; final int height; final List params; @@ -15,16 +15,13 @@ class Embed { }); factory Embed.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Embed( - url: element.getAttribute("url"), + if (element == null) return null; + var url = element.getAttribute("url"); + return Embed( + url: url == null ? null : Uri.parse(url), width: int.tryParse(element.getAttribute("width") ?? "0"), height: int.tryParse(element.getAttribute("height") ?? "0"), - params: element.findElements("media:param").map((e) { - return new Param.parse(e); - }).toList(), + params: element.findElements("media:param").map((e) => Param.parse(e)).toList(), ); } } diff --git a/lib/domain/media/license.dart b/lib/domain/media/license.dart index c52a8c6..622b5d8 100644 --- a/lib/domain/media/license.dart +++ b/lib/domain/media/license.dart @@ -2,7 +2,7 @@ import 'package:xml/xml.dart'; class License { final String type; - final String href; + final Uri href; final String value; License({ @@ -12,12 +12,11 @@ class License { }); factory License.parse(XmlElement element) { - if (element == null) { - return null; - } - return new License( + if (element == null) return null; + var href = element.getAttribute("href"); + return License( type: element.getAttribute("type"), - href: element.getAttribute("href"), + href: href == null ? null : Uri.parse(href), value: element.text, ); } diff --git a/lib/domain/media/media.dart b/lib/domain/media/media.dart index 2f685c1..1af634d 100644 --- a/lib/domain/media/media.dart +++ b/lib/domain/media/media.dart @@ -41,7 +41,7 @@ class Media { final List comments; final Embed embed; final List responses; - final List backLinks; + final List backLinks; final Status status; final List prices; final License license; @@ -78,92 +78,32 @@ class Media { }); factory Media.parse(XmlElement element) { - return new Media( - group: new Group.parse( - findElementOrNull(element, "media:group"), - ), - contents: element.findElements("media:content").map((e) { - return new Content.parse(e); - }).toList(), - credits: element.findElements("media:credit").map((e) { - return new Credit.parse(e); - }).toList(), - category: new Category.parse( - findElementOrNull(element, "media:category"), - ), - rating: new Rating.parse( - findElementOrNull(element, "media:rating"), - ), - title: new Title.parse( - findElementOrNull(element, "media:title"), - ), - description: new Description.parse( - findElementOrNull(element, "media:description"), - ), - keywords: findElementOrNull(element, "media:keywords")?.text, - thumbnails: element.findElements("media:thumbnail").map((e) { - return new Thumbnail.parse(e); - }).toList(), - hash: new Hash.parse( - findElementOrNull(element, "media:hash"), - ), - player: new Player.parse( - findElementOrNull(element, "media:player"), - ), - copyright: new Copyright.parse( - findElementOrNull(element, "media:copyright"), - ), - text: new Text.parse( - findElementOrNull(element, "media:text"), - ), - restriction: new Restriction.parse( - findElementOrNull(element, "media:restriction"), - ), - community: new Community.parse( - findElementOrNull(element, "media:community"), - ), - comments: findElementOrNull(element, "media:comments") - ?.findElements("media:comment") - ?.map((e) { - return e.text; - })?.toList() ?? - [], - embed: new Embed.parse( - findElementOrNull(element, "media:embed"), - ), - responses: findElementOrNull(element, "media:responses") - ?.findElements("media:response") - ?.map((e) { - return e.text; - })?.toList() ?? - [], - backLinks: findElementOrNull(element, "media:backLinks") - ?.findElements("media:backLink") - ?.map((e) { - return e.text; - })?.toList() ?? - [], - status: new Status.parse( - findElementOrNull(element, "media:status"), - ), - prices: element.findElements("media:price").map((e) { - return new Price.parse(e); - }).toList(), - license: new License.parse( - findElementOrNull(element, "media:license"), - ), - peerLink: new PeerLink.parse( - findElementOrNull(element, "media:peerLink"), - ), - rights: new Rights.parse( - findElementOrNull(element, "media:rights"), - ), - scenes: findElementOrNull(element, "media:scenes") - ?.findElements("media:scene") - ?.map((e) { - return new Scene.parse(e); - })?.toList() ?? - [], + return Media( + group: Group.parse(findElementOrNull(element, "media:group")), + contents: element.findElements("media:content").map((e) => Content.parse(e)).toList(), + credits: element.findElements("media:credit").map((e) => Credit.parse(e)).toList(), + category: Category.parse(findElementOrNull(element, "media:category")), + rating: Rating.parse(findElementOrNull(element, "media:rating")), + title: Title.parse(findElementOrNull(element, "media:title")), + description: Description.parse(findElementOrNull(element, "media:description")), + keywords: parseTextLiteral(element, "media:keywords"), + thumbnails: element.findElements("media:thumbnail").map((e) => Thumbnail.parse(e)).toList(), + hash: Hash.parse(findElementOrNull(element, "media:hash")), + player: Player.parse(findElementOrNull(element, "media:player")), + copyright: Copyright.parse(findElementOrNull(element, "media:copyright")), + text: Text.parse(findElementOrNull(element, "media:text")), + restriction: Restriction.parse(findElementOrNull(element, "media:restriction")), + community: Community.parse(findElementOrNull(element, "media:community")), + comments: findElementOrNull(element, "media:comments")?.findElements("media:comment")?.map((e) => e.text)?.toList() ?? [], + embed: Embed.parse(findElementOrNull(element, "media:embed")), + responses: findElementOrNull(element, "media:responses")?.findElements("media:response")?.map((e) => e.text)?.toList() ?? [], + backLinks: findElementOrNull(element, "media:backLinks")?.findElements("media:backLink")?.map((e) => Uri.parse(e.text))?.toList() ?? [], + status: Status.parse(findElementOrNull(element, "media:status")), + prices: element.findElements("media:price").map((e) => Price.parse(e)).toList(), + license: License.parse(findElementOrNull(element, "media:license")), + peerLink: PeerLink.parse(findElementOrNull(element, "media:peerLink")), + rights: Rights.parse(findElementOrNull(element, "media:rights")), + scenes: findElementOrNull(element, "media:scenes")?.findElements("media:scene")?.map((e) => Scene.parse(e))?.toList() ?? [], ); } } diff --git a/lib/domain/media/peer_link.dart b/lib/domain/media/peer_link.dart index 6c51a17..efd2be8 100644 --- a/lib/domain/media/peer_link.dart +++ b/lib/domain/media/peer_link.dart @@ -2,7 +2,7 @@ import 'package:xml/xml.dart'; class PeerLink { final String type; - final String href; + final Uri href; final String value; PeerLink({ @@ -12,12 +12,11 @@ class PeerLink { }); factory PeerLink.parse(XmlElement element) { - if (element == null) { - return null; - } - return new PeerLink( + if (element == null) return null; + var href = element.getAttribute("href"); + return PeerLink( type: element.getAttribute("type"), - href: element.getAttribute("href"), + href: href == null ? null : Uri.parse(href), value: element.text, ); } diff --git a/lib/domain/media/player.dart b/lib/domain/media/player.dart index 23d5e7a..a765a7f 100644 --- a/lib/domain/media/player.dart +++ b/lib/domain/media/player.dart @@ -1,7 +1,7 @@ import 'package:xml/xml.dart'; class Player { - final String url; + final Uri url; final int width; final int height; final String value; @@ -14,11 +14,10 @@ class Player { }); factory Player.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Player( - url: element.getAttribute("url"), + if (element == null) return null; + var url = element.getAttribute("url"); + return Player( + url: url == null ? null : Uri.parse(url), width: int.tryParse(element.getAttribute("width") ?? "0"), height: int.tryParse(element.getAttribute("height") ?? "0"), value: element.text, diff --git a/lib/domain/media/price.dart b/lib/domain/media/price.dart index 6da075b..fbb3be9 100644 --- a/lib/domain/media/price.dart +++ b/lib/domain/media/price.dart @@ -3,7 +3,7 @@ import 'package:xml/xml.dart'; class Price { final double price; final String type; - final String info; + final Uri info; final String currency; Price({ @@ -14,10 +14,11 @@ class Price { }); factory Price.parse(XmlElement element) { - return new Price( + var info = element.getAttribute("info"); + return Price( price: double.tryParse(element.getAttribute("price") ?? "0"), type: element.getAttribute("type"), - info: element.getAttribute("info"), + info: info == null ? null : Uri.parse(info), currency: element.getAttribute("currency"), ); } diff --git a/lib/domain/media/thumbnail.dart b/lib/domain/media/thumbnail.dart index 8adce3b..eb177be 100644 --- a/lib/domain/media/thumbnail.dart +++ b/lib/domain/media/thumbnail.dart @@ -1,7 +1,7 @@ import 'package:xml/xml.dart'; class Thumbnail { - final String url; + final Uri url; final String width; final String height; final String time; @@ -14,8 +14,9 @@ class Thumbnail { }); factory Thumbnail.parse(XmlElement element) { - return new Thumbnail( - url: element.getAttribute("url"), + var url = element.getAttribute("url"); + return Thumbnail( + url: url == null ? null : Uri.parse(url), width: element.getAttribute("width"), height: element.getAttribute("height"), time: element.getAttribute("time"), diff --git a/lib/util/helpers.dart b/lib/util/helpers.dart index 179926c..5169ed0 100644 --- a/lib/util/helpers.dart +++ b/lib/util/helpers.dart @@ -2,8 +2,7 @@ import 'dart:core'; import 'package:xml/xml.dart'; -XmlElement findElementOrNull(XmlElement element, String name, - {String namespace}) { +XmlElement findElementOrNull(XmlElement element, String name, {String namespace}) { try { return element.findAllElements(name, namespace: namespace).first; } on StateError { @@ -11,8 +10,7 @@ XmlElement findElementOrNull(XmlElement element, String name, } } -List findAllDirectElementsOrNull(XmlElement element, String name, - {String namespace}) { +List findAllDirectElementsOrNull(XmlElement element, String name, {String namespace}) { try { return element.findElements(name, namespace: namespace).toList(); } on StateError { @@ -20,9 +18,27 @@ List findAllDirectElementsOrNull(XmlElement element, String name, } } -bool parseBoolLiteral(XmlElement element, String tagName) { - var v = findElementOrNull(element, tagName)?.text?.toLowerCase()?.trim(); - if (v == null) return null; - return ["yes", "true"].contains(v); +String parseTextLiteral(XmlElement element, String name, {String namespace}) { + var s = findElementOrNull(element, name, namespace: namespace)?.text; + return s == null || s.isEmpty ? null : s; } +bool parseBoolLiteral(XmlElement element, String name, {String namespace}) { + var s = parseTextLiteral(element, name, namespace: namespace)?.toLowerCase()?.trim(); + return s == null + ? null + : [ + "yes", + "true" + ].contains(s); +} + +Uri parseUriLiteral(XmlElement element, String name, {String namespace}) { + var s = parseTextLiteral(element, name, namespace: namespace); + return s == null ? null : Uri.parse(s); +} + +DateTime parseDateTimeLiteral(XmlElement element, String name, {String namespace}) { + var s = parseTextLiteral(element, name, namespace: namespace); + return s == null ? null : DateTime.parse(s); +} diff --git a/test/atom_test.dart b/test/atom_test.dart index 1f381a8..34a9e16 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -19,7 +19,7 @@ void main() { var feed = new AtomFeed.parse(xmlString); - expect(feed.id, "foo-bar-id"); + expect(feed.id, Uri.parse("foo-bar-id")); expect(feed.title, "Foo bar news"); expect(feed.updated, DateTime.parse("2018-04-06T13:02:46Z")); @@ -27,18 +27,18 @@ void main() { expect(feed.links.first.rel, "foo"); expect(feed.links.first.type, "text/html"); expect(feed.links.first.hreflang, "en"); - expect(feed.links.first.href, "http://foo.bar.news/"); + expect(feed.links.first.href, Uri.parse("http://foo.bar.news/")); expect(feed.links.first.title, "Foo bar news html"); expect(feed.links.first.length, 1000); expect(feed.authors.length, 2); expect(feed.authors.first.name, "Alice"); - expect(feed.authors.first.uri, "http://foo.bar.news/people/alice"); + expect(feed.authors.first.uri, Uri.parse("http://foo.bar.news/people/alice")); expect(feed.authors.first.email, "alice@foo.bar.news"); expect(feed.contributors.length, 2); expect(feed.contributors.first.name, "Charlie"); - expect(feed.contributors.first.uri, "http://foo.bar.news/people/charlie"); + expect(feed.contributors.first.uri, Uri.parse("http://foo.bar.news/people/charlie")); expect(feed.contributors.first.email, "charlie@foo.bar.news"); expect(feed.categories.length, 2); @@ -46,12 +46,12 @@ void main() { expect(feed.categories.first.scheme, "this-is-foo-scheme"); expect(feed.categories.first.label, "this is foo label"); - expect(feed.generator.uri, "http://foo.bar.news/generator"); + expect(feed.generator.uri, Uri.parse("http://foo.bar.news/generator")); expect(feed.generator.version, "1.0"); expect(feed.generator.value, "Foo bar generator"); - expect(feed.icon, "http://foo.bar.news/icon.png"); - expect(feed.logo, "http://foo.bar.news/logo.png"); + expect(feed.icon, Uri.parse("http://foo.bar.news/icon.png")); + expect(feed.logo, Uri.parse("http://foo.bar.news/logo.png")); expect(feed.subtitle, "This is subtitle"); expect(feed.items.length, 2); @@ -62,14 +62,14 @@ void main() { expect(item.authors.length, 2); expect(item.authors.first.name, "Ellie"); - expect(item.authors.first.uri, "http://foo.bar.news/people/ellie"); + expect(item.authors.first.uri, Uri.parse("http://foo.bar.news/people/ellie")); expect(item.authors.first.email, "ellie@foo.bar.news"); expect(item.links.length, 2); expect(item.links.first.rel, "foo entry"); expect(item.links.first.type, "text/html"); expect(item.links.first.hreflang, "en"); - expect(item.links.first.href, "http://foo.bar.news/entry"); + expect(item.links.first.href, Uri.parse("http://foo.bar.news/entry")); expect(item.links.first.title, "Foo bar news html"); expect(item.links.first.length, 1000); @@ -80,7 +80,7 @@ void main() { expect(item.contributors.length, 2); expect(item.contributors.first.name, "Gin"); - expect(item.contributors.first.uri, "http://foo.bar.news/people/gin"); + expect(item.contributors.first.uri, Uri.parse("http://foo.bar.news/people/gin")); expect(item.contributors.first.email, "gin@foo.bar.news"); expect(item.published, "2018-04-06T13:02:49Z"); @@ -88,11 +88,12 @@ void main() { expect(item.content, "This is content 1"); expect(item.rights, "This is rights 1"); }); + test("parse Atom-Media.xml", () { var xmlString = new File("test/xml/Atom-Media.xml").readAsStringSync(); - var feed = new AtomFeed.parse(xmlString); - expect(feed.id, "foo-bar-id"); + var feed = AtomFeed.parse(xmlString); + expect(feed.id, Uri.parse("foo-bar-id")); expect(feed.title, "Foo bar news"); expect(feed.updated, DateTime.parse("2018-04-06T13:02:46Z")); @@ -106,7 +107,7 @@ void main() { expect(item.media.contents.length, 2); var mediaContent = item.media.contents.first; - expect(mediaContent.url, "http://www.foo.com/video.mov"); + expect(mediaContent.url, Uri.parse("http://www.foo.com/video.mov")); expect(mediaContent.type, "video/quicktime"); expect(mediaContent.fileSize, 2000); expect(mediaContent.medium, "video"); @@ -140,7 +141,7 @@ void main() { expect(item.media.thumbnails.length, 2); var mediaThumbnail = item.media.thumbnails.first; - expect(mediaThumbnail.url, "http://www.foo.com/keyframe1.jpg"); + expect(mediaThumbnail.url, Uri.parse("http://www.foo.com/keyframe1.jpg")); expect(mediaThumbnail.width, "75"); expect(mediaThumbnail.height, "50"); expect(mediaThumbnail.time, "12:05:01.123"); @@ -148,12 +149,12 @@ void main() { expect(item.media.hash.algo, "md5"); expect(item.media.hash.value, "dfdec888b72151965a34b4b59031290a"); - expect(item.media.player.url, "http://www.foo.com/player?id=1111"); + expect(item.media.player.url, Uri.parse("http://www.foo.com/player?id=1111")); expect(item.media.player.width, 400); expect(item.media.player.height, 200); expect(item.media.player.value, ""); - expect(item.media.copyright.url, "http://blah.com/additional-info.html"); + expect(item.media.copyright.url, Uri.parse("http://blah.com/additional-info.html")); expect(item.media.copyright.value, "2005 FooBar Media"); expect(item.media.text.type, "plain"); @@ -179,7 +180,7 @@ void main() { expect(item.media.comments.first, "comment1"); expect(item.media.comments.last, "comment2"); - expect(item.media.embed.url, "http://www.foo.com/player.swf"); + expect(item.media.embed.url, Uri.parse("http://www.foo.com/player.swf")); expect(item.media.embed.width, 512); expect(item.media.embed.height, 323); expect(item.media.embed.params.length, 5); @@ -191,8 +192,8 @@ void main() { expect(item.media.responses.last, "http://www.response2.com"); expect(item.media.backLinks.length, 2); - expect(item.media.backLinks.first, "http://www.backlink1.com"); - expect(item.media.backLinks.last, "http://www.backlink2.com"); + expect(item.media.backLinks.first, Uri.parse("http://www.backlink1.com")); + expect(item.media.backLinks.last, Uri.parse("http://www.backlink2.com")); expect(item.media.status.state, "active"); expect(item.media.status.reason, null); @@ -200,15 +201,15 @@ void main() { expect(item.media.prices.length, 2); expect(item.media.prices.first.price, 19.99); expect(item.media.prices.first.type, "rent"); - expect(item.media.prices.first.info, "http://www.dummy.jp/package_info.html"); + expect(item.media.prices.first.info, Uri.parse("http://www.dummy.jp/package_info.html")); expect(item.media.prices.first.currency, "EUR"); expect(item.media.license.type, "text/html"); - expect(item.media.license.href, "http://www.licensehost.com/license"); + expect(item.media.license.href, Uri.parse("http://www.licensehost.com/license")); expect(item.media.license.value, " Sample license for a video"); expect(item.media.peerLink.type, "application/x-bittorrent"); - expect(item.media.peerLink.href, "http://www.foo.org/sampleFile.torrent"); + expect(item.media.peerLink.href, Uri.parse("http://www.foo.org/sampleFile.torrent")); expect(item.media.peerLink.value, ""); expect(item.media.rights.status, "official"); @@ -225,8 +226,8 @@ void main() { var feed = AtomFeed.parse(xmlString); - expect(feed.id, 'https://example.com'); - expect(feed.title, ''); + expect(feed.id, Uri.parse('https://example.com')); + expect(feed.title, null); expect(feed.updated, DateTime.parse('1970-01-01T00:00:00-00:00')); expect(feed.links.length, 0); expect(feed.authors.length, 0); @@ -250,10 +251,10 @@ void main() { var nextPage = feed.links.firstWhere((l) => l.rel == 'next', orElse: () => null); var lastPage = feed.links.firstWhere((l) => l.rel == 'last', orElse: () => null); - expect(firstPage.href, 'http://example.org/index.atom'); + expect(firstPage.href, Uri.parse('http://example.org/index.atom')); expect(previousPage, null); - expect(nextPage.href, 'http://example.org/index.atom?page=2'); - expect(lastPage.href, 'http://example.org/index.atom?page=2'); + expect(nextPage.href, Uri.parse('http://example.org/index.atom?page=2')); + expect(lastPage.href, Uri.parse('http://example.org/index.atom?page=2')); }); // RFC 5005: Feed Paging and Archiving @@ -266,43 +267,37 @@ void main() { var nextPage = feed.links.firstWhere((l) => l.rel == 'next', orElse: () => null); var lastPage = feed.links.firstWhere((l) => l.rel == 'last', orElse: () => null); - expect(firstPage.href, 'http://example.org/index.atom'); - expect(previousPage.href, 'http://example.org/index.atom?page=2'); + expect(firstPage.href, Uri.parse('http://example.org/index.atom')); + expect(previousPage.href, Uri.parse('http://example.org/index.atom?page=2')); expect(nextPage, null); - expect(lastPage.href, 'http://example.org/index.atom?page=2'); + expect(lastPage.href, Uri.parse('http://example.org/index.atom?page=2')); }); test("generate Atom-Empty.xml", () { var xmlString = File("test/xml/Atom-Empty.xml").readAsStringSync(); - var feed = AtomFeed(id: 'https://example.com', updated: DateTime.parse('1970-01-01T00:00:00-00:00')); + var feed = AtomFeed(id: Uri.parse('https://example.com'), updated: DateTime.parse('1970-01-01T00:00:00-00:00')); var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); expect(xmlString2, xmlString); }); test("generate Atom.xml", () { var xmlString = File("test/xml/Atom.xml").readAsStringSync(); - var feed = AtomFeed(id: 'foo-bar-id', title: 'Foo bar news', updated: DateTime.parse('2018-04-06T13:02:46Z')); + var feed = AtomFeed(id: Uri.parse('foo-bar-id'), title: 'Foo bar news', updated: DateTime.parse('2018-04-06T13:02:46Z'), links: [ + AtomLink(rel: 'foo', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/'), title: 'Foo bar news html', length: 1000), + AtomLink(rel: 'bar', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/feed.atom'), title: 'Foo bar news atom', length: 100), + ], authors: [ + AtomPerson(name: 'Alice', uri: Uri.parse('http://foo.bar.news/people/alice'), email: 'alice@foo.bar.news'), + AtomPerson(name: 'Bob', uri: Uri.parse('http://foo.bar.news/people/bob'), email: 'bob@foo.bar.news'), + ], contributors: [ + AtomPerson(name: 'Charlie', uri: Uri.parse('http://foo.bar.news/people/charlie'), email: 'charlie@foo.bar.news'), + AtomPerson(name: 'David', uri: Uri.parse('http://foo.bar.news/people/david'), email: 'david@foo.bar.news'), + ], categories: [ + AtomCategory(), + ]); + var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); expect(xmlString2, xmlString); - // expect(feed.links.length, 2); - // expect(feed.links.first.rel, "foo"); - // expect(feed.links.first.type, "text/html"); - // expect(feed.links.first.hreflang, "en"); - // expect(feed.links.first.href, "http://foo.bar.news/"); - // expect(feed.links.first.title, "Foo bar news html"); - // expect(feed.links.first.length, 1000); - - // expect(feed.authors.length, 2); - // expect(feed.authors.first.name, "Alice"); - // expect(feed.authors.first.uri, "http://foo.bar.news/people/alice"); - // expect(feed.authors.first.email, "alice@foo.bar.news"); - - // expect(feed.contributors.length, 2); - // expect(feed.contributors.first.name, "Charlie"); - // expect(feed.contributors.first.uri, "http://foo.bar.news/people/charlie"); - // expect(feed.contributors.first.email, "charlie@foo.bar.news"); - // expect(feed.categories.length, 2); // expect(feed.categories.first.term, "foo category"); // expect(feed.categories.first.scheme, "this-is-foo-scheme"); diff --git a/test/xml/Atom.xml b/test/xml/Atom.xml index 5a475cb..f94ce3b 100644 --- a/test/xml/Atom.xml +++ b/test/xml/Atom.xml @@ -4,8 +4,7 @@ Foo bar news 2018-04-06T13:02:46.000Z - + Alice http://foo.bar.news/people/alice From e329c75ef64ab21173a26d878de45c1eec1746b7 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 16:01:09 -0800 Subject: [PATCH 08/16] refactor and more work on generating ATOM --- lib/domain/atom_category.dart | 14 ++- lib/domain/atom_feed.dart | 54 ++++------- lib/domain/atom_generator.dart | 9 ++ lib/domain/atom_link.dart | 11 +++ lib/domain/atom_person.dart | 8 ++ test/atom_test.dart | 170 ++++++++++++++++++++++----------- 6 files changed, 173 insertions(+), 93 deletions(-) diff --git a/lib/domain/atom_category.dart b/lib/domain/atom_category.dart index be6d377..9ffb589 100644 --- a/lib/domain/atom_category.dart +++ b/lib/domain/atom_category.dart @@ -5,11 +5,23 @@ class AtomCategory { final String scheme; final String label; - AtomCategory({this.term, this.scheme, this.label}); + AtomCategory({ + this.term, + this.scheme, + this.label, + }); factory AtomCategory.parse(XmlElement element) => AtomCategory( term: element.getAttribute("term"), scheme: element.getAttribute("scheme"), label: element.getAttribute("label"), ); + + void build(XmlBuilder b) { + b.element('category', nest: () { + if (term != null) b.attribute('term', term); + if (scheme != null) b.attribute('scheme', scheme); + if (label != null) b.attribute('label', label); + }); + } } diff --git a/lib/domain/atom_feed.dart b/lib/domain/atom_feed.dart index 2766e87..dc7c87f 100644 --- a/lib/domain/atom_feed.dart +++ b/lib/domain/atom_feed.dart @@ -38,12 +38,13 @@ class AtomFeed { }) : this.updated = updated ?? DateTime.now(); // default value factory AtomFeed.parse(String xmlString) { - var document = parse(xmlString); XmlElement feedElement; + try { + var document = parse(xmlString); feedElement = document.findElements("feed").first; } on StateError { - throw new ArgumentError("feed not found"); + throw ArgumentError("feed not found"); } return AtomFeed( @@ -64,44 +65,29 @@ class AtomFeed { } XmlDocument toXml() { - if (id == null) throw Exception('must have an id'); var doc = parse(''); - var b = XmlBuilder(); + build(b); + var feed = doc.findAllElements('feed').first; + b.build().children.forEach((c) => feed.children.add(c.copy())); + return doc; + } + + void build(XmlBuilder b) { + if (id == null) throw Exception('must have an id'); + b.element('id', nest: () => b.text(id)); b.element('title', nest: () => title == null ? '' : b.text(title)); b.element('updated', nest: () => b.text(updated.toUtc().toIso8601String())); - for (var link in links ?? List()) { - b.element('link', nest: () { - if (link.rel != null) b.attribute('rel', link.rel); - if (link.type != null) b.attribute('type', link.type); - if (link.hreflang != null) b.attribute('hreflang', link.hreflang); - if (link.href != null) b.attribute('href', link.href); - if (link.title != null) b.attribute('title', link.title); - if (link.length != null) b.attribute('length', link.length); - }); - } - - for (var author in authors ?? List()) { - b.element('author', nest: () { - if (author.name != null) b.element('name', nest: () => b.text(author.name)); - if (author.uri != null) b.element('uri', nest: () => b.text(author.uri)); - if (author.email != null) b.element('email', nest: () => b.text(author.email)); - }); - } - - for (var contributor in contributors ?? List()) { - b.element('contributor', nest: () { - if (contributor.name != null) b.element('name', nest: () => b.text(contributor.name)); - if (contributor.uri != null) b.element('uri', nest: () => b.text(contributor.uri)); - if (contributor.email != null) b.element('email', nest: () => b.text(contributor.email)); - }); - } + if (links != null) links.forEach((l) => l.build(b)); + if (authors != null) authors.forEach((a) => a.build(b, 'author')); + if (contributors != null) contributors.forEach((c) => c.build(b, 'contributor')); + if (categories != null) categories.forEach((c) => c.build(b)); + if (generator != null) generator.build(b); - var feed = doc.findAllElements('feed').first; - b.build().children.forEach((c) => feed.children.add(c.copy())); - - return doc; + if (icon != null) b.element('icon', nest: () => b.text(icon)); + if (logo != null) b.element('logo', nest: () => b.text(logo)); + if (subtitle != null) b.element('subtitle', nest: () => b.text(subtitle)); } } diff --git a/lib/domain/atom_generator.dart b/lib/domain/atom_generator.dart index 368e36a..254b29e 100644 --- a/lib/domain/atom_generator.dart +++ b/lib/domain/atom_generator.dart @@ -20,4 +20,13 @@ class AtomGenerator { value: element.text, ); } + + void build(XmlBuilder b) { + // Foo bar generator + b.element('generator', nest: () { + if (uri != null) b.attribute('uri', uri); + if (version != null) b.attribute('version', version); + if (value != null) b.text(value); + }); + } } diff --git a/lib/domain/atom_link.dart b/lib/domain/atom_link.dart index 5018ed8..7bdd213 100644 --- a/lib/domain/atom_link.dart +++ b/lib/domain/atom_link.dart @@ -28,4 +28,15 @@ class AtomLink { length: int.parse(element.getAttribute("length") ?? "0"), ); } + + void build(XmlBuilder b) { + b.element('link', nest: () { + if (rel != null) b.attribute('rel', rel); + if (type != null) b.attribute('type', type); + if (hreflang != null) b.attribute('hreflang', hreflang); + if (href != null) b.attribute('href', href); + if (title != null) b.attribute('title', title); + if (length != null) b.attribute('length', length); + }); + } } diff --git a/lib/domain/atom_person.dart b/lib/domain/atom_person.dart index bd48d2a..7ea14f7 100644 --- a/lib/domain/atom_person.dart +++ b/lib/domain/atom_person.dart @@ -16,4 +16,12 @@ class AtomPerson { email: findElementOrNull(element, "email")?.text, ); } + + void build(XmlBuilder b, String type) { + b.element(type, nest: () { + if (name != null) b.element('name', nest: () => b.text(name)); + if (uri != null) b.element('uri', nest: () => b.text(uri)); + if (email != null) b.element('email', nest: () => b.text(email)); + }); + } } diff --git a/test/atom_test.dart b/test/atom_test.dart index 34a9e16..aa1d984 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -282,67 +282,121 @@ void main() { test("generate Atom.xml", () { var xmlString = File("test/xml/Atom.xml").readAsStringSync(); - var feed = AtomFeed(id: Uri.parse('foo-bar-id'), title: 'Foo bar news', updated: DateTime.parse('2018-04-06T13:02:46Z'), links: [ - AtomLink(rel: 'foo', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/'), title: 'Foo bar news html', length: 1000), - AtomLink(rel: 'bar', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/feed.atom'), title: 'Foo bar news atom', length: 100), - ], authors: [ - AtomPerson(name: 'Alice', uri: Uri.parse('http://foo.bar.news/people/alice'), email: 'alice@foo.bar.news'), - AtomPerson(name: 'Bob', uri: Uri.parse('http://foo.bar.news/people/bob'), email: 'bob@foo.bar.news'), - ], contributors: [ - AtomPerson(name: 'Charlie', uri: Uri.parse('http://foo.bar.news/people/charlie'), email: 'charlie@foo.bar.news'), - AtomPerson(name: 'David', uri: Uri.parse('http://foo.bar.news/people/david'), email: 'david@foo.bar.news'), - ], categories: [ - AtomCategory(), - ]); + var feed = AtomFeed( + id: Uri.parse('foo-bar-id'), + title: 'Foo bar news', + updated: DateTime.parse('2018-04-06T13:02:46Z'), + links: [ + AtomLink(rel: 'foo', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/'), title: 'Foo bar news html', length: 1000), + AtomLink(rel: 'bar', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/feed.atom'), title: 'Foo bar news atom', length: 100), + ], + authors: [ + AtomPerson(name: 'Alice', uri: Uri.parse('http://foo.bar.news/people/alice'), email: 'alice@foo.bar.news'), + AtomPerson(name: 'Bob', uri: Uri.parse('http://foo.bar.news/people/bob'), email: 'bob@foo.bar.news'), + ], + contributors: [ + AtomPerson(name: 'Charlie', uri: Uri.parse('http://foo.bar.news/people/charlie'), email: 'charlie@foo.bar.news'), + AtomPerson(name: 'David', uri: Uri.parse('http://foo.bar.news/people/david'), email: 'david@foo.bar.news'), + ], + categories: [ + AtomCategory(term: 'foo category', scheme: 'this-is-foo-scheme', label: 'this is foo label'), + AtomCategory(term: 'bar category', scheme: 'this-is-bar-scheme', label: 'this is bar label'), + ], + generator: AtomGenerator(uri: Uri.parse('http://foo.bar.news/generator'), version: '1.0', value: 'Foo bar generator'), + icon: Uri.parse('http://foo.bar.news/icon.png'), + logo: Uri.parse('http://foo.bar.news/logo.png'), + subtitle: 'This is subtitle', + ); var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); expect(xmlString2, xmlString); - // expect(feed.categories.length, 2); - // expect(feed.categories.first.term, "foo category"); - // expect(feed.categories.first.scheme, "this-is-foo-scheme"); - // expect(feed.categories.first.label, "this is foo label"); - - // expect(feed.generator.uri, "http://foo.bar.news/generator"); - // expect(feed.generator.version, "1.0"); - // expect(feed.generator.value, "Foo bar generator"); - - // expect(feed.icon, "http://foo.bar.news/icon.png"); - // expect(feed.logo, "http://foo.bar.news/logo.png"); - // expect(feed.subtitle, "This is subtitle"); - - // expect(feed.items.length, 2); - // var item = feed.items.first; - // expect(item.id, "foo-bar-entry-id-1"); - // expect(item.title, "Foo bar item 1"); - // expect(item.updated, "2018-04-06T13:02:47Z"); - - // expect(item.authors.length, 2); - // expect(item.authors.first.name, "Ellie"); - // expect(item.authors.first.uri, "http://foo.bar.news/people/ellie"); - // expect(item.authors.first.email, "ellie@foo.bar.news"); - - // expect(item.links.length, 2); - // expect(item.links.first.rel, "foo entry"); - // expect(item.links.first.type, "text/html"); - // expect(item.links.first.hreflang, "en"); - // expect(item.links.first.href, "http://foo.bar.news/entry"); - // expect(item.links.first.title, "Foo bar news html"); - // expect(item.links.first.length, 1000); - - // expect(item.categories.length, 2); - // expect(item.categories.first.term, "foo entry category"); - // expect(item.categories.first.scheme, "this-is-foo-entry-scheme"); - // expect(item.categories.first.label, "this is foo entry label"); - - // expect(item.contributors.length, 2); - // expect(item.contributors.first.name, "Gin"); - // expect(item.contributors.first.uri, "http://foo.bar.news/people/gin"); - // expect(item.contributors.first.email, "gin@foo.bar.news"); - - // expect(item.published, "2018-04-06T13:02:49Z"); - // expect(item.summary, "This is summary 1"); - // expect(item.content, "This is content 1"); - // expect(item.rights, "This is rights 1"); + /* + http://foo.bar.news/icon.png + http://foo.bar.news/logo.png + This is subtitle + + foo-bar-entry-id-1 + Foo bar item 1 + 2018-04-06T13:02:47Z + + Ellie + http://foo.bar.news/people/ellie + ellie@foo.bar.news + + + Franz + http://foo.bar.news/people/franz + franz@foo.bar.news + + + + + + + Gin + http://foo.bar.news/people/gin + gin@foo.bar.news + + + Hanz + http://foo.bar.news/people/hanz + hanz@foo.bar.news + + + http://foo.bar.news/source + Foo bar source + 2018-04-06T13:02:48Z + + + 2018-04-06T13:02:49Z + This is summary 1 + This is content 1 + This is rights 1 + + + foo-bar-entry-id-2 + Foo bar item 2 + 2018-04-06T13:02:50Z + + Iris + http://foo.bar.news/people/iris + iris@foo.bar.news + + + Jhon + http://foo.bar.news/people/jhon + jhon@foo.bar.news + + + + + + + Kevin + http://foo.bar.news/people/kevin + kevin@foo.bar.news + + + Lucy + http://foo.bar.news/people/lucy + lucy@foo.bar.news + + + http://foo.bar.news/source + Foo bar source + 2018-04-06T13:02:51Z + + + 2018-04-06T13:02:52Z + This is summary 2 + This is content 2 + This is rights 2 + + */ }); } From 14c2e3b9d909fa6e26bb8bae16924cf55a47093b Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 16:42:26 -0800 Subject: [PATCH 09/16] ATOM generation at top level --- test/atom_test.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/atom_test.dart b/test/atom_test.dart index aa1d984..a75f5b4 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -312,9 +312,6 @@ void main() { expect(xmlString2, xmlString); /* - http://foo.bar.news/icon.png - http://foo.bar.news/logo.png - This is subtitle foo-bar-entry-id-1 Foo bar item 1 From becff46feec3bbf75aebecb3eb0f49d6762a9c7c Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 17:25:49 -0800 Subject: [PATCH 10/16] WIP: writing ATOM entries --- example/main.dart | 6 +-- lib/domain/atom_feed.dart | 2 + lib/domain/atom_item.dart | 68 ++++++++++++++++++------------- lib/domain/atom_source.dart | 32 ++++++++++----- test/atom_test.dart | 81 +++++++++++++++++-------------------- test/xml/Atom.xml | 15 +++---- 6 files changed, 108 insertions(+), 96 deletions(-) diff --git a/example/main.dart b/example/main.dart index 39647c6..7b182a5 100644 --- a/example/main.dart +++ b/example/main.dart @@ -6,11 +6,11 @@ void main() async { // RSS feed var rssresp = await client.get("https://developer.apple.com/news/releases/rss/releases.rss"); - var channel = new RssFeed.parse(rssresp.body); + var channel = RssFeed.parse(rssresp.body); print(channel); // Atom feed var atomresp = await client.get("https://www.theverge.com/rss/index.xml"); - var feed = new AtomFeed.parse(atomresp.body); - print(feed); + var feed = AtomFeed.parse(atomresp.body); + print(feed.toXml().toXmlString(pretty: true, indent: ' ')); } diff --git a/lib/domain/atom_feed.dart b/lib/domain/atom_feed.dart index dc7c87f..ab39fff 100644 --- a/lib/domain/atom_feed.dart +++ b/lib/domain/atom_feed.dart @@ -89,5 +89,7 @@ class AtomFeed { if (icon != null) b.element('icon', nest: () => b.text(icon)); if (logo != null) b.element('logo', nest: () => b.text(logo)); if (subtitle != null) b.element('subtitle', nest: () => b.text(subtitle)); + + if (items != null) items.forEach((i) => i.build(b)); } } diff --git a/lib/domain/atom_item.dart b/lib/domain/atom_item.dart index 618015b..a07f84a 100644 --- a/lib/domain/atom_item.dart +++ b/lib/domain/atom_item.dart @@ -9,14 +9,14 @@ import 'package:xml/xml.dart'; class AtomItem { final String id; final String title; - final String updated; + final DateTime updated; final List authors; final List links; final List categories; final List contributors; final AtomSource source; - final String published; + final DateTime published; final String content; final String summary; final String rights; @@ -25,42 +25,52 @@ class AtomItem { AtomItem({ this.id, this.title, - this.updated, + updated, this.authors, this.links, this.categories, this.contributors, this.source, - this.published, + published, this.content, this.summary, this.rights, this.media, - }); + }) : this.updated = updated ?? DateTime.now(), + this.published = published ?? DateTime.now(); - factory AtomItem.parse(XmlElement element) { - return AtomItem( - id: findElementOrNull(element, "id")?.text, - title: findElementOrNull(element, "title")?.text, - updated: findElementOrNull(element, "updated")?.text, - authors: element.findElements("author").map((element) { - return AtomPerson.parse(element); - }).toList(), - links: element.findElements("link").map((element) { - return AtomLink.parse(element); - }).toList(), - categories: element.findElements("category").map((element) { - return AtomCategory.parse(element); - }).toList(), - contributors: element.findElements("contributor").map((element) { - return AtomPerson.parse(element); - }).toList(), - source: AtomSource.parse(findElementOrNull(element, "source")), - published: findElementOrNull(element, "published")?.text, - content: findElementOrNull(element, "content")?.text, - summary: findElementOrNull(element, "summary")?.text, - rights: findElementOrNull(element, "rights")?.text, - media: Media.parse(element), - ); + factory AtomItem.parse(XmlElement element) => AtomItem( + id: parseTextLiteral(element, "id"), + title: parseTextLiteral(element, "title"), + updated: parseDateTimeLiteral(element, "updated"), + authors: element.findElements("author").map((e) => AtomPerson.parse(e)).toList(), + links: element.findElements("link").map((e) => AtomLink.parse(e)).toList(), + categories: element.findElements("category").map((e) => AtomCategory.parse(e)).toList(), + contributors: element.findElements("contributor").map((e) => AtomPerson.parse(e)).toList(), + source: AtomSource.parse(findElementOrNull(element, "source")), + published: parseDateTimeLiteral(element, "published"), + content: parseTextLiteral(element, "content"), + summary: parseTextLiteral(element, "summary"), + rights: parseTextLiteral(element, "rights"), + media: Media.parse(element), + ); + + void build(XmlBuilder b) { + if (id == null || id.isEmpty) throw Exception('must have an id'); + b.element('entry', nest: () { + b.element('id', nest: () => b.text(id)); + if (title != null) b.element('title', nest: () => b.text(title)); + if (updated != null) b.element('updated', nest: () => b.text(updated.toUtc().toIso8601String())); + if (authors != null) authors.forEach((a) => a.build(b, 'author')); + if (links != null) links.forEach((l) => l.build(b)); + if (categories != null) categories.forEach((c) => c.build(b)); + if (contributors != null) contributors.forEach((c) => c.build(b, 'contributor')); + if (source != null) source.build(b); + if (published != null) b.element('published', nest: () => b.text(published.toUtc().toIso8601String())); + if (summary != null) b.element('summary', nest: () => b.text(summary)); + if (content != null) b.element('content', nest: () => b.text(content)); + if (rights != null) b.element('rights', nest: () => b.text(rights)); + //if (media != null) media.build(b); // TODO + }); } } diff --git a/lib/domain/atom_source.dart b/lib/domain/atom_source.dart index 37fb619..228afe0 100644 --- a/lib/domain/atom_source.dart +++ b/lib/domain/atom_source.dart @@ -2,20 +2,32 @@ import 'package:webfeed/util/helpers.dart'; import 'package:xml/xml.dart'; class AtomSource { - final String id; + final Uri id; final String title; - final String updated; + final DateTime updated; - AtomSource(this.id, this.title, this.updated); + AtomSource({ + this.id, + this.title, + updated, + }) : this.updated = updated ?? DateTime.now(); factory AtomSource.parse(XmlElement element) { - if (element == null) { - return null; - } - var id = findElementOrNull(element, "id")?.text; - var title = findElementOrNull(element, "title")?.text; - var updated = findElementOrNull(element, "updated")?.text; + if (element == null) return null; + var id = parseTextLiteral(element, 'id'); + return AtomSource( + id: id == null ? null : Uri.parse(id), + title: parseTextLiteral(element, 'title'), + updated: parseDateTimeLiteral(element, 'updated'), + ); + } - return AtomSource(id, title, updated); + void build(XmlBuilder b) { + if (id == null) throw Exception('must have id'); + b.element('source', nest: () { + b.element('id', nest: () => b.text(id)); + if (title != null) b.element('title', nest: () => b.text(title)); + if (updated != null) b.element('updated', nest: () => b.text(updated.toUtc().toIso8601String())); + }); } } diff --git a/test/atom_test.dart b/test/atom_test.dart index a75f5b4..b07bbdd 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -58,7 +58,7 @@ void main() { var item = feed.items.first; expect(item.id, "foo-bar-entry-id-1"); expect(item.title, "Foo bar item 1"); - expect(item.updated, "2018-04-06T13:02:47Z"); + expect(item.updated, DateTime.parse("2018-04-06T13:02:47Z")); expect(item.authors.length, 2); expect(item.authors.first.name, "Ellie"); @@ -286,6 +286,9 @@ void main() { id: Uri.parse('foo-bar-id'), title: 'Foo bar news', updated: DateTime.parse('2018-04-06T13:02:46Z'), + icon: Uri.parse('http://foo.bar.news/icon.png'), + logo: Uri.parse('http://foo.bar.news/logo.png'), + subtitle: 'This is subtitle', links: [ AtomLink(rel: 'foo', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/'), title: 'Foo bar news html', length: 1000), AtomLink(rel: 'bar', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/feed.atom'), title: 'Foo bar news atom', length: 100), @@ -303,56 +306,44 @@ void main() { AtomCategory(term: 'bar category', scheme: 'this-is-bar-scheme', label: 'this is bar label'), ], generator: AtomGenerator(uri: Uri.parse('http://foo.bar.news/generator'), version: '1.0', value: 'Foo bar generator'), - icon: Uri.parse('http://foo.bar.news/icon.png'), - logo: Uri.parse('http://foo.bar.news/logo.png'), - subtitle: 'This is subtitle', + items: [ + AtomItem( + id: 'foo-bar-entry-id-1', + title: 'Foo bar item 1', + updated: DateTime.parse('2018-04-06T13:02:47Z'), + published: DateTime.parse('2018-04-06T13:02:49Z'), + summary: 'This is summary 1', + content: 'This is content 1', + rights: 'This is rights 1', + authors: [ + AtomPerson(name: 'Ellie', uri: Uri.parse('http://foo.bar.news/people/ellie'), email: 'ellie@foo.bar.news'), + AtomPerson(name: 'Franz', uri: Uri.parse('http://foo.bar.news/people/franz'), email: 'franz@foo.bar.news'), + ], + links: [ + AtomLink(rel: 'foo entry', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/entry'), title: 'Foo bar news html', length: 1000), + AtomLink(rel: 'bar entry', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/entry/feed.atom'), title: 'Foo bar entry atom', length: 100), + ], + categories: [ + AtomCategory(term: 'foo entry category', scheme: 'this-is-foo-entry-scheme', label: 'this is foo entry label'), + AtomCategory(term: 'bar entry category', scheme: 'this-is-bar-entry-scheme', label: 'this is bar entry label'), + ], + contributors: [ + AtomPerson(name: 'Gin', uri: Uri.parse('http://foo.bar.news/people/gin'), email: 'gin@foo.bar.news'), + AtomPerson(name: 'Hanz', uri: Uri.parse('http://foo.bar.news/people/hanz'), email: 'hanz@foo.bar.news'), + ], + source: AtomSource( + id: Uri.parse('http://foo.bar.news/source'), + title: 'Foo bar source', + updated: DateTime.parse('2018-04-06T13:02:48Z'), + ), + ), + ], ); var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); expect(xmlString2, xmlString); /* - - foo-bar-entry-id-1 - Foo bar item 1 - 2018-04-06T13:02:47Z - - Ellie - http://foo.bar.news/people/ellie - ellie@foo.bar.news - - - Franz - http://foo.bar.news/people/franz - franz@foo.bar.news - - - - - - - Gin - http://foo.bar.news/people/gin - gin@foo.bar.news - - - Hanz - http://foo.bar.news/people/hanz - hanz@foo.bar.news - - - http://foo.bar.news/source - Foo bar source - 2018-04-06T13:02:48Z - - - 2018-04-06T13:02:49Z - This is summary 1 - This is content 1 - This is rights 1 - foo-bar-entry-id-2 Foo bar item 2 diff --git a/test/xml/Atom.xml b/test/xml/Atom.xml index f94ce3b..16e78ed 100644 --- a/test/xml/Atom.xml +++ b/test/xml/Atom.xml @@ -34,7 +34,7 @@ foo-bar-entry-id-1 Foo bar item 1 - 2018-04-06T13:02:47Z + 2018-04-06T13:02:47.000Z Ellie http://foo.bar.news/people/ellie @@ -45,10 +45,8 @@ http://foo.bar.news/people/franz franz@foo.bar.news - - + + @@ -64,11 +62,10 @@ http://foo.bar.news/source Foo bar source - 2018-04-06T13:02:48Z + 2018-04-06T13:02:48.000Z - - 2018-04-06T13:02:49Z - This is summary 1 + 2018-04-06T13:02:49.000Z + This is summary 1 This is content 1 This is rights 1 From 96f73ba4f121ed0d5eb53461f57761f6bcff9d9b Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 17:36:39 -0800 Subject: [PATCH 11/16] generate core ATOM --- test/atom_test.dart | 84 +++++++++++++++++++++------------------------ test/xml/Atom.xml | 15 ++++---- 2 files changed, 45 insertions(+), 54 deletions(-) diff --git a/test/atom_test.dart b/test/atom_test.dart index b07bbdd..d0b9415 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -83,7 +83,7 @@ void main() { expect(item.contributors.first.uri, Uri.parse("http://foo.bar.news/people/gin")); expect(item.contributors.first.email, "gin@foo.bar.news"); - expect(item.published, "2018-04-06T13:02:49Z"); + expect(item.published, DateTime.parse("2018-04-06T13:02:49Z")); expect(item.summary, "This is summary 1"); expect(item.content, "This is content 1"); expect(item.rights, "This is rights 1"); @@ -337,54 +337,48 @@ void main() { updated: DateTime.parse('2018-04-06T13:02:48Z'), ), ), + AtomItem( + id: 'foo-bar-entry-id-2', + title: 'Foo bar item 2', + updated: DateTime.parse('2018-04-06T13:02:50Z'), + published: DateTime.parse('2018-04-06T13:02:52Z'), + summary: 'This is summary 2', + content: 'This is content 2', + rights: 'This is rights 2', + authors: [ + AtomPerson( + name: 'Iris', + uri: Uri.parse('http://foo.bar.news/people/iris'), + email: 'iris@foo.bar.news', + ), + AtomPerson( + name: 'Jhon', + uri: Uri.parse('http://foo.bar.news/people/jhon'), + email: 'jhon@foo.bar.news', + ), + ], + links: [ + AtomLink(rel: 'foo entry', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/entry'), title: 'Foo bar news html', length: 1000), + AtomLink(rel: 'bar entry', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/entry/feed.atom'), title: 'Foo bar entry atom', length: 100), + ], + categories: [ + AtomCategory(term: 'foo entry category'), + AtomCategory(term: 'bar entry category'), + ], + contributors: [ + AtomPerson(name: 'Kevin', uri: Uri.parse('http://foo.bar.news/people/kevin'), email: 'kevin@foo.bar.news'), + AtomPerson(name: 'Lucy', uri: Uri.parse('http://foo.bar.news/people/lucy'), email: 'lucy@foo.bar.news'), + ], + source: AtomSource( + id: Uri.parse('http://foo.bar.news/source'), + title: 'Foo bar source', + updated: DateTime.parse('2018-04-06T13:02:51Z'), + ), + ), ], ); var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); expect(xmlString2, xmlString); - - /* - - foo-bar-entry-id-2 - Foo bar item 2 - 2018-04-06T13:02:50Z - - Iris - http://foo.bar.news/people/iris - iris@foo.bar.news - - - Jhon - http://foo.bar.news/people/jhon - jhon@foo.bar.news - - - - - - - Kevin - http://foo.bar.news/people/kevin - kevin@foo.bar.news - - - Lucy - http://foo.bar.news/people/lucy - lucy@foo.bar.news - - - http://foo.bar.news/source - Foo bar source - 2018-04-06T13:02:51Z - - - 2018-04-06T13:02:52Z - This is summary 2 - This is content 2 - This is rights 2 - - */ }); } diff --git a/test/xml/Atom.xml b/test/xml/Atom.xml index 16e78ed..8a181fe 100644 --- a/test/xml/Atom.xml +++ b/test/xml/Atom.xml @@ -72,7 +72,7 @@ foo-bar-entry-id-2 Foo bar item 2 - 2018-04-06T13:02:50Z + 2018-04-06T13:02:50.000Z Iris http://foo.bar.news/people/iris @@ -83,10 +83,8 @@ http://foo.bar.news/people/jhon jhon@foo.bar.news - - + + @@ -102,11 +100,10 @@ http://foo.bar.news/source Foo bar source - 2018-04-06T13:02:51Z + 2018-04-06T13:02:51.000Z - - 2018-04-06T13:02:52Z - This is summary 2 + 2018-04-06T13:02:52.000Z + This is summary 2 This is content 2 This is rights 2 From 2109551f653ea7bbfd6a8292a30ddb9c866390c2 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 18:05:00 -0800 Subject: [PATCH 12/16] testing RFC 5005; updated README --- README.md | 37 +++++++++++++++------ lib/domain/atom_item.dart | 5 ++- pubspec.yaml | 4 +-- test/atom_test.dart | 70 ++++++++++++++++++++++++++++++++++++++- test/xml/Atom-Page1.xml | 22 ++++++------ test/xml/Atom-Page2.xml | 24 +++++++------- 6 files changed, 123 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 0e83d16..c90ef0b 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,28 @@ [![Build Status](https://travis-ci.org/witochandra/webfeed.svg?branch=master)](https://travis-ci.org/witochandra/webfeed) [![Pub](https://img.shields.io/pub/v/webfeed.svg)](https://pub.dartlang.org/packages/webfeed) -A dart package for parsing RSS and Atom feed. +A dart package for parsing and generating RSS and Atom feeds. ### Features -- [x] RSS -- [x] Atom -- [x] Namespaces - - [x] Media RSS - - [x] Dublin Core +- [x] Parsing + - [x] RSS + - [x] Atom + - [x] Namespaces + - [x] Media RSS + - [x] Dublin Core +- [ ] Generating + - [ ] RSS + - [x] Atom + - [ ] Namespaces + - [ ] Media RSS + - [ ] Dublin Core ### Installing Add this line into your `pubspec.yaml` ``` -webfeed: ^0.4.2 +webfeed: ^0.5.0 ``` Import the package into your dart code using: @@ -27,10 +34,16 @@ import 'package:webfeed/webfeed.dart'; ### Example -To parse string into `RssFeed` object use: +To parse string into an object use: +```dart +var rssFeed = RssFeed.parse(xmlString); // for parsing RSS feed +var atomFeed = AtomFeed.parse(xmlString); // for parsing Atom feed ``` -var rssFeed = new RssFeed.parse(xmlString); // for parsing RSS feed -var atomFeed = new AtomFeed.parse(xmlString); // for parsing Atom feed + +To generate string from an object use: +```dart +var atomFeed = AtomFeed(id: Uri.parse('urn:42'), title: 'a title', ...); // for creating Atom feed +var xmlString = atomFeed.toXml().toXmlString(); // for creating XML string for Atom feed ``` ### Preview @@ -105,6 +118,10 @@ item.rights item.media ``` +## Contributors +- Wito Chandra (author) +- Chris Sells (provided XML generation from Atom OM) + ## License WebFeed is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details diff --git a/lib/domain/atom_item.dart b/lib/domain/atom_item.dart index a07f84a..fb8c399 100644 --- a/lib/domain/atom_item.dart +++ b/lib/domain/atom_item.dart @@ -31,13 +31,12 @@ class AtomItem { this.categories, this.contributors, this.source, - published, + this.published, this.content, this.summary, this.rights, this.media, - }) : this.updated = updated ?? DateTime.now(), - this.published = published ?? DateTime.now(); + }) : this.updated = updated ?? DateTime.now(); factory AtomItem.parse(XmlElement element) => AtomItem( id: parseTextLiteral(element, "id"), diff --git a/pubspec.yaml b/pubspec.yaml index b7bb403..067869e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: webfeed -version: 0.4.2 -description: webfeed is a dart package for parsing RSS and Atom feeds. Media & DublinCore namespaces are also supported. +version: 0.5.0 +description: webfeed is a dart package for parsing and generating RSS and Atom feeds. Media & DublinCore namespaces are also supported. author: Wito Chandra homepage: https://github.com/witochandra/webfeed environment: diff --git a/test/atom_test.dart b/test/atom_test.dart index d0b9415..c0dacf4 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -268,7 +268,7 @@ void main() { var lastPage = feed.links.firstWhere((l) => l.rel == 'last', orElse: () => null); expect(firstPage.href, Uri.parse('http://example.org/index.atom')); - expect(previousPage.href, Uri.parse('http://example.org/index.atom?page=2')); + expect(previousPage.href, Uri.parse('http://example.org/index.atom')); expect(nextPage, null); expect(lastPage.href, Uri.parse('http://example.org/index.atom?page=2')); }); @@ -381,4 +381,72 @@ void main() { var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); expect(xmlString2, xmlString); }); + + // RFC 5005: Feed Paging and Archiving + test("generate Atom-Page1.xml", () { + var xmlString = File("test/xml/Atom-Page1.xml").readAsStringSync(); + + var feed = AtomFeed( + title: 'Example Feed', + links: [ + AtomLink(href: Uri.parse('http://example.org/')), + AtomLink(rel: 'first', href: Uri.parse('http://example.org/index.atom')), + AtomLink(rel: 'next', href: Uri.parse('http://example.org/index.atom?page=2')), + AtomLink(rel: 'last', href: Uri.parse('http://example.org/index.atom?page=2')), + ], + updated: DateTime.parse('2003-12-13T18:30:02Z'), + authors: [ + AtomPerson(name: 'John Doe'), + ], + id: Uri.parse('urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6'), + items: [ + AtomItem( + title: 'Atom-Powered Robots Run Amok', + links: [ + AtomLink(href: Uri.parse('http://example.org/2003/12/13/atom03')), + ], + id: 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', + updated: DateTime.parse('2003-12-13T18:30:02Z'), + summary: 'Some text.', + ), + ], + ); + + var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); + expect(xmlString2, xmlString); + }); + + // RFC 5005: Feed Paging and Archiving + test("generate Atom-Page2.xml", () { + var xmlString = File("test/xml/Atom-Page2.xml").readAsStringSync(); + + var feed = AtomFeed( + title: 'Example Feed', + links: [ + AtomLink(href: Uri.parse('http://example.org/')), + AtomLink(rel: 'first', href: Uri.parse('http://example.org/index.atom')), + AtomLink(rel: 'previous', href: Uri.parse('http://example.org/index.atom')), + AtomLink(rel: 'last', href: Uri.parse('http://example.org/index.atom?page=2')), + ], + updated: DateTime.parse('2003-12-13T18:30:02Z'), + authors: [ + AtomPerson(name: 'John Doe'), + ], + id: Uri.parse('urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6'), + items: [ + AtomItem( + title: 'Atom-Powered Robots Run Amok', + links: [ + AtomLink(href: Uri.parse('http://example.org/2003/12/13/atom03')), + ], + id: 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', + updated: DateTime.parse('2003-12-13T18:30:02Z'), + summary: 'Some text.', + ), + ], + ); + + var xmlString2 = feed.toXml().toXmlString(pretty: true, indent: ' '); + expect(xmlString2, xmlString); + }); } diff --git a/test/xml/Atom-Page1.xml b/test/xml/Atom-Page1.xml index e8e0460..cc817f4 100644 --- a/test/xml/Atom-Page1.xml +++ b/test/xml/Atom-Page1.xml @@ -1,20 +1,20 @@ - - + + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 Example Feed + 2003-12-13T18:30:02.000Z - 2003-12-13T18:30:02Z - John Doe + John Doe - urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 - Atom-Powered Robots Run Amok - - urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a - 2003-12-13T18:30:02Z - Some text. + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + Atom-Powered Robots Run Amok + 2003-12-13T18:30:02.000Z + + Some text. - \ No newline at end of file + \ No newline at end of file diff --git a/test/xml/Atom-Page2.xml b/test/xml/Atom-Page2.xml index f25af48..3b56a3a 100644 --- a/test/xml/Atom-Page2.xml +++ b/test/xml/Atom-Page2.xml @@ -1,20 +1,20 @@ - - + + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 Example Feed + 2003-12-13T18:30:02.000Z - + - 2003-12-13T18:30:02Z - John Doe + John Doe - urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 - Atom-Powered Robots Run Amok - - urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a - 2003-12-13T18:30:02Z - Some text. + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + Atom-Powered Robots Run Amok + 2003-12-13T18:30:02.000Z + + Some text. - \ No newline at end of file + \ No newline at end of file From 50a6ce0434b0c44784d8108d8261f02cae3b9aa8 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 18:31:14 -0800 Subject: [PATCH 13/16] capture content type; some style changes --- example/main.dart | 2 +- lib/domain/atom_content.dart | 21 +++++++++++++++++++++ lib/domain/atom_item.dart | 8 ++++---- lib/domain/atom_person.dart | 8 ++++---- lib/domain/media/category.dart | 6 ++---- lib/domain/media/community.dart | 18 +++++------------- lib/domain/media/credit.dart | 12 +++++------- lib/domain/media/description.dart | 6 ++---- lib/domain/media/group.dart | 23 +++++++---------------- lib/domain/media/hash.dart | 6 ++---- lib/domain/media/param.dart | 6 ++---- lib/domain/media/rating.dart | 6 ++---- lib/domain/media/restriction.dart | 6 ++---- lib/domain/media/rights.dart | 6 ++---- lib/domain/media/scene.dart | 14 ++++++-------- lib/domain/media/star_rating.dart | 14 ++++++-------- lib/domain/media/statistics.dart | 10 ++++------ lib/domain/media/status.dart | 6 ++---- lib/domain/media/tags.dart | 6 ++---- lib/domain/media/text.dart | 6 ++---- lib/domain/media/title.dart | 6 ++---- test/atom_test.dart | 15 ++++++++------- 22 files changed, 93 insertions(+), 118 deletions(-) create mode 100644 lib/domain/atom_content.dart diff --git a/example/main.dart b/example/main.dart index 7b182a5..3ae2492 100644 --- a/example/main.dart +++ b/example/main.dart @@ -2,7 +2,7 @@ import 'package:http/http.dart' as http; import 'package:webfeed/webfeed.dart'; void main() async { - var client = new http.Client(); + var client = http.Client(); // RSS feed var rssresp = await client.get("https://developer.apple.com/news/releases/rss/releases.rss"); diff --git a/lib/domain/atom_content.dart b/lib/domain/atom_content.dart new file mode 100644 index 0000000..448fda3 --- /dev/null +++ b/lib/domain/atom_content.dart @@ -0,0 +1,21 @@ +import 'package:xml/xml.dart'; + +class AtomContent { + String type; + String text; + + AtomContent({ + this.type, + this.text, + }); + + factory AtomContent.parse(XmlElement element) => AtomContent( + type: element.getAttribute('type'), + text: element.text, + ); + + void build(XmlBuilder b) => b.element('content', nest: () { + if (type != null) b.attribute('type', type); + if (text != null) b.text(text); + }); +} diff --git a/lib/domain/atom_item.dart b/lib/domain/atom_item.dart index fb8c399..a5197ea 100644 --- a/lib/domain/atom_item.dart +++ b/lib/domain/atom_item.dart @@ -1,4 +1,5 @@ import 'package:webfeed/domain/atom_category.dart'; +import 'package:webfeed/domain/atom_content.dart'; import 'package:webfeed/domain/atom_link.dart'; import 'package:webfeed/domain/atom_person.dart'; import 'package:webfeed/domain/atom_source.dart'; @@ -10,14 +11,13 @@ class AtomItem { final String id; final String title; final DateTime updated; - final List authors; final List links; final List categories; final List contributors; final AtomSource source; final DateTime published; - final String content; + final AtomContent content; final String summary; final String rights; final Media media; @@ -48,7 +48,7 @@ class AtomItem { contributors: element.findElements("contributor").map((e) => AtomPerson.parse(e)).toList(), source: AtomSource.parse(findElementOrNull(element, "source")), published: parseDateTimeLiteral(element, "published"), - content: parseTextLiteral(element, "content"), + content: AtomContent.parse(findElementOrNull(element, "content")), summary: parseTextLiteral(element, "summary"), rights: parseTextLiteral(element, "rights"), media: Media.parse(element), @@ -67,7 +67,7 @@ class AtomItem { if (source != null) source.build(b); if (published != null) b.element('published', nest: () => b.text(published.toUtc().toIso8601String())); if (summary != null) b.element('summary', nest: () => b.text(summary)); - if (content != null) b.element('content', nest: () => b.text(content)); + if (content != null) content.build(b); if (rights != null) b.element('rights', nest: () => b.text(rights)); //if (media != null) media.build(b); // TODO }); diff --git a/lib/domain/atom_person.dart b/lib/domain/atom_person.dart index 7ea14f7..54d34e6 100644 --- a/lib/domain/atom_person.dart +++ b/lib/domain/atom_person.dart @@ -9,11 +9,11 @@ class AtomPerson { AtomPerson({this.name, this.uri, this.email}); factory AtomPerson.parse(XmlElement element) { - var uri = findElementOrNull(element, "uri"); + var uri = parseTextLiteral(element, "uri"); return AtomPerson( - name: findElementOrNull(element, "name")?.text, - uri: uri == null ? null : Uri.parse(uri.text), - email: findElementOrNull(element, "email")?.text, + name: parseTextLiteral(element, "name"), + uri: uri == null ? null : Uri.parse(uri), + email: parseTextLiteral(element, "email"), ); } diff --git a/lib/domain/media/category.dart b/lib/domain/media/category.dart index 4796cd7..22bcc6a 100644 --- a/lib/domain/media/category.dart +++ b/lib/domain/media/category.dart @@ -12,10 +12,8 @@ class Category { }); factory Category.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Category( + if (element == null) return null; + return Category( scheme: element.getAttribute("scheme"), label: element.getAttribute("label"), value: element.text, diff --git a/lib/domain/media/community.dart b/lib/domain/media/community.dart index b301050..7076447 100644 --- a/lib/domain/media/community.dart +++ b/lib/domain/media/community.dart @@ -16,19 +16,11 @@ class Community { }); factory Community.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Community( - starRating: new StarRating.parse( - findElementOrNull(element, "media:starRating"), - ), - statistics: new Statistics.parse( - findElementOrNull(element, "media:statistics"), - ), - tags: new Tags.parse( - findElementOrNull(element, "media:tags"), - ), + if (element == null) return null; + return Community( + starRating: StarRating.parse(findElementOrNull(element, "media:starRating")), + statistics: Statistics.parse(findElementOrNull(element, "media:statistics")), + tags: Tags.parse(findElementOrNull(element, "media:tags")), ); } } diff --git a/lib/domain/media/credit.dart b/lib/domain/media/credit.dart index 70a5e50..ccd3534 100644 --- a/lib/domain/media/credit.dart +++ b/lib/domain/media/credit.dart @@ -11,11 +11,9 @@ class Credit { this.value, }); - factory Credit.parse(XmlElement element) { - return new Credit( - role: element.getAttribute("role"), - scheme: element.getAttribute("scheme"), - value: element.text, - ); - } + factory Credit.parse(XmlElement element) => Credit( + role: element.getAttribute("role"), + scheme: element.getAttribute("scheme"), + value: element.text, + ); } diff --git a/lib/domain/media/description.dart b/lib/domain/media/description.dart index ba6f0e0..9e04ae1 100644 --- a/lib/domain/media/description.dart +++ b/lib/domain/media/description.dart @@ -10,10 +10,8 @@ class Description { }); factory Description.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Description( + if (element == null) return null; + return Description( type: element.getAttribute("type"), value: element.text, ); diff --git a/lib/domain/media/group.dart b/lib/domain/media/group.dart index 1a9a1cb..b0ae70f 100644 --- a/lib/domain/media/group.dart +++ b/lib/domain/media/group.dart @@ -19,22 +19,13 @@ class Group { }); factory Group.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Group( - contents: element.findElements("media:content").map((e) { - return new Content.parse(e); - }).toList(), - credits: element.findElements("media:credit").map((e) { - return new Credit.parse(e); - }).toList(), - category: new Category.parse( - findElementOrNull(element, "media:category"), - ), - rating: new Rating.parse( - findElementOrNull(element, "media:rating"), - ), + if (element == null) return null; + + return Group( + contents: element.findElements("media:content").map((e) => Content.parse(e)).toList(), + credits: element.findElements("media:credit").map((e) => Credit.parse(e)).toList(), + category: Category.parse(findElementOrNull(element, "media:category")), + rating: Rating.parse(findElementOrNull(element, "media:rating")), ); } } diff --git a/lib/domain/media/hash.dart b/lib/domain/media/hash.dart index 72ce859..adc5ac9 100644 --- a/lib/domain/media/hash.dart +++ b/lib/domain/media/hash.dart @@ -10,10 +10,8 @@ class Hash { }); factory Hash.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Hash( + if (element == null) return null; + return Hash( algo: element.getAttribute("algo"), value: element.text, ); diff --git a/lib/domain/media/param.dart b/lib/domain/media/param.dart index 175e6b8..93e0952 100644 --- a/lib/domain/media/param.dart +++ b/lib/domain/media/param.dart @@ -10,10 +10,8 @@ class Param { }); factory Param.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Param( + if (element == null) return null; + return Param( name: element.getAttribute("name"), value: element.text, ); diff --git a/lib/domain/media/rating.dart b/lib/domain/media/rating.dart index 77c2b12..cab9641 100644 --- a/lib/domain/media/rating.dart +++ b/lib/domain/media/rating.dart @@ -10,10 +10,8 @@ class Rating { }); factory Rating.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Rating( + if (element == null) return null; + return Rating( scheme: element.getAttribute("scheme"), value: element.text, ); diff --git a/lib/domain/media/restriction.dart b/lib/domain/media/restriction.dart index 4aa56bd..d527120 100644 --- a/lib/domain/media/restriction.dart +++ b/lib/domain/media/restriction.dart @@ -12,10 +12,8 @@ class Restriction { }); factory Restriction.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Restriction( + if (element == null) return null; + return Restriction( relationship: element.getAttribute("relationship"), type: element.getAttribute("type"), value: element.text, diff --git a/lib/domain/media/rights.dart b/lib/domain/media/rights.dart index eb00d4d..9831cde 100644 --- a/lib/domain/media/rights.dart +++ b/lib/domain/media/rights.dart @@ -8,10 +8,8 @@ class Rights { }); factory Rights.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Rights( + if (element == null) return null; + return Rights( status: element.getAttribute("status"), ); } diff --git a/lib/domain/media/scene.dart b/lib/domain/media/scene.dart index 9eab8d0..29e15dd 100644 --- a/lib/domain/media/scene.dart +++ b/lib/domain/media/scene.dart @@ -15,14 +15,12 @@ class Scene { }); factory Scene.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Scene( - title: findElementOrNull(element, "sceneTitle")?.text, - description: findElementOrNull(element, "sceneDescription")?.text, - startTime: findElementOrNull(element, "sceneStartTime")?.text, - endTime: findElementOrNull(element, "sceneEndTime")?.text, + if (element == null) return null; + return Scene( + title: parseTextLiteral(element, "sceneTitle"), + description: parseTextLiteral(element, "sceneDescription"), + startTime: parseTextLiteral(element, "sceneStartTime"), + endTime: parseTextLiteral(element, "sceneEndTime"), ); } } diff --git a/lib/domain/media/star_rating.dart b/lib/domain/media/star_rating.dart index ae4c400..7a68ee5 100644 --- a/lib/domain/media/star_rating.dart +++ b/lib/domain/media/star_rating.dart @@ -13,12 +13,10 @@ class StarRating { this.max, }); - factory StarRating.parse(XmlElement element) { - return new StarRating( - average: double.tryParse(element.getAttribute("average") ?? "0"), - count: int.tryParse(element.getAttribute("count") ?? "0"), - min: int.tryParse(element.getAttribute("min") ?? "0"), - max: int.tryParse(element.getAttribute("max") ?? "0"), - ); - } + factory StarRating.parse(XmlElement element) => StarRating( + average: double.tryParse(element.getAttribute("average") ?? "0"), + count: int.tryParse(element.getAttribute("count") ?? "0"), + min: int.tryParse(element.getAttribute("min") ?? "0"), + max: int.tryParse(element.getAttribute("max") ?? "0"), + ); } diff --git a/lib/domain/media/statistics.dart b/lib/domain/media/statistics.dart index d93461f..5756f20 100644 --- a/lib/domain/media/statistics.dart +++ b/lib/domain/media/statistics.dart @@ -9,10 +9,8 @@ class Statistics { this.favorites, }); - factory Statistics.parse(XmlElement element) { - return new Statistics( - views: int.tryParse(element.getAttribute("views") ?? "0"), - favorites: int.tryParse(element.getAttribute("favorites") ?? "0"), - ); - } + factory Statistics.parse(XmlElement element) => Statistics( + views: int.tryParse(element.getAttribute("views") ?? "0"), + favorites: int.tryParse(element.getAttribute("favorites") ?? "0"), + ); } diff --git a/lib/domain/media/status.dart b/lib/domain/media/status.dart index 3071a09..aef25c2 100644 --- a/lib/domain/media/status.dart +++ b/lib/domain/media/status.dart @@ -10,10 +10,8 @@ class Status { }); factory Status.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Status( + if (element == null) return null; + return Status( state: element.getAttribute("state"), reason: element.getAttribute("reason"), ); diff --git a/lib/domain/media/tags.dart b/lib/domain/media/tags.dart index c7001a7..2e1b4d2 100644 --- a/lib/domain/media/tags.dart +++ b/lib/domain/media/tags.dart @@ -10,10 +10,8 @@ class Tags { }); factory Tags.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Tags( + if (element == null) return null; + return Tags( tags: element.text, weight: int.tryParse(element.getAttribute("weight") ?? "1"), ); diff --git a/lib/domain/media/text.dart b/lib/domain/media/text.dart index 1ff886b..452abf8 100644 --- a/lib/domain/media/text.dart +++ b/lib/domain/media/text.dart @@ -16,10 +16,8 @@ class Text { }); factory Text.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Text( + if (element == null) return null; + return Text( type: element.getAttribute("type"), lang: element.getAttribute("lang"), start: element.getAttribute("start"), diff --git a/lib/domain/media/title.dart b/lib/domain/media/title.dart index 8c875a6..982cf98 100644 --- a/lib/domain/media/title.dart +++ b/lib/domain/media/title.dart @@ -10,10 +10,8 @@ class Title { }); factory Title.parse(XmlElement element) { - if (element == null) { - return null; - } - return new Title( + if (element == null) return null; + return Title( type: element.getAttribute("type"), value: element.text, ); diff --git a/test/atom_test.dart b/test/atom_test.dart index c0dacf4..b690673 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -2,22 +2,23 @@ import 'dart:core'; import 'dart:io'; import 'package:test/test.dart'; +import 'package:webfeed/domain/atom_content.dart'; import 'package:webfeed/webfeed.dart'; void main() { test("parse Invalid.xml", () { - var xmlString = new File("test/xml/Invalid.xml").readAsStringSync(); + var xmlString = File("test/xml/Invalid.xml").readAsStringSync(); try { - new AtomFeed.parse(xmlString); + AtomFeed.parse(xmlString); fail("Should throw Argument Error"); } on ArgumentError {} }); test("parse Atom.xml", () { - var xmlString = new File("test/xml/Atom.xml").readAsStringSync(); + var xmlString = File("test/xml/Atom.xml").readAsStringSync(); - var feed = new AtomFeed.parse(xmlString); + var feed = AtomFeed.parse(xmlString); expect(feed.id, Uri.parse("foo-bar-id")); expect(feed.title, "Foo bar news"); @@ -90,7 +91,7 @@ void main() { }); test("parse Atom-Media.xml", () { - var xmlString = new File("test/xml/Atom-Media.xml").readAsStringSync(); + var xmlString = File("test/xml/Atom-Media.xml").readAsStringSync(); var feed = AtomFeed.parse(xmlString); expect(feed.id, Uri.parse("foo-bar-id")); @@ -313,7 +314,7 @@ void main() { updated: DateTime.parse('2018-04-06T13:02:47Z'), published: DateTime.parse('2018-04-06T13:02:49Z'), summary: 'This is summary 1', - content: 'This is content 1', + content: AtomContent(text: 'This is content 1'), rights: 'This is rights 1', authors: [ AtomPerson(name: 'Ellie', uri: Uri.parse('http://foo.bar.news/people/ellie'), email: 'ellie@foo.bar.news'), @@ -343,7 +344,7 @@ void main() { updated: DateTime.parse('2018-04-06T13:02:50Z'), published: DateTime.parse('2018-04-06T13:02:52Z'), summary: 'This is summary 2', - content: 'This is content 2', + content: AtomContent(text: 'This is content 2'), rights: 'This is rights 2', authors: [ AtomPerson( From 71e0eb06de57d5d5ad910d18fde9e7f0532959d4 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 18:48:23 -0800 Subject: [PATCH 14/16] backing out RSS, Media and Dublin Core API changes --- lib/domain/atom_content.dart | 11 +++--- lib/domain/media/content.dart | 39 +++++++++----------- lib/domain/media/copyright.dart | 5 ++- lib/domain/media/embed.dart | 5 ++- lib/domain/media/license.dart | 5 ++- lib/domain/media/media.dart | 4 +-- lib/domain/media/peer_link.dart | 5 ++- lib/domain/media/player.dart | 5 ++- lib/domain/media/price.dart | 17 ++++----- lib/domain/media/thumbnail.dart | 17 ++++----- lib/webfeed.dart | 1 + test/atom_test.dart | 23 ++++++------ test/rss_test.dart | 63 +++++++++++++++------------------ 13 files changed, 90 insertions(+), 110 deletions(-) diff --git a/lib/domain/atom_content.dart b/lib/domain/atom_content.dart index 448fda3..153e202 100644 --- a/lib/domain/atom_content.dart +++ b/lib/domain/atom_content.dart @@ -9,10 +9,13 @@ class AtomContent { this.text, }); - factory AtomContent.parse(XmlElement element) => AtomContent( - type: element.getAttribute('type'), - text: element.text, - ); + factory AtomContent.parse(XmlElement element) { + if (element == null) return null; + return AtomContent( + type: element.getAttribute('type'), + text: element.text, + ); + } void build(XmlBuilder b) => b.element('content', nest: () { if (type != null) b.attribute('type', type); diff --git a/lib/domain/media/content.dart b/lib/domain/media/content.dart index 6236025..b44d493 100644 --- a/lib/domain/media/content.dart +++ b/lib/domain/media/content.dart @@ -1,7 +1,7 @@ import 'package:xml/xml.dart'; class Content { - final Uri url; + final String url; final String type; final int fileSize; final String medium; @@ -33,25 +33,20 @@ class Content { this.lang, }); - factory Content.parse(XmlElement element) { - var url = element.getAttribute("url"); - return Content( - url: url == null ? null : Uri.parse(url), - type: element.getAttribute("type"), - fileSize: int.tryParse(element.getAttribute("fileSize") ?? "0"), - medium: element.getAttribute("medium"), - isDefault: element.getAttribute("isDefault") == "true", - expression: element.getAttribute("expression"), - bitrate: int.tryParse(element.getAttribute("bitrate") ?? "0"), - framerate: double.tryParse(element.getAttribute("framerate") ?? "0"), - samplingrate: double.tryParse( - element.getAttribute("samplingrate") ?? "0", - ), - channels: int.tryParse(element.getAttribute("channels") ?? "0"), - duration: int.tryParse(element.getAttribute("duration") ?? "0"), - height: int.tryParse(element.getAttribute("height") ?? "0"), - width: int.tryParse(element.getAttribute("width") ?? "0"), - lang: element.getAttribute("lang"), - ); - } + factory Content.parse(XmlElement element) => Content( + url: element.getAttribute("url"), + type: element.getAttribute("type"), + fileSize: int.tryParse(element.getAttribute("fileSize") ?? "0"), + medium: element.getAttribute("medium"), + isDefault: element.getAttribute("isDefault") == "true", + expression: element.getAttribute("expression"), + bitrate: int.tryParse(element.getAttribute("bitrate") ?? "0"), + framerate: double.tryParse(element.getAttribute("framerate") ?? "0"), + samplingrate: double.tryParse(element.getAttribute("samplingrate") ?? "0"), + channels: int.tryParse(element.getAttribute("channels") ?? "0"), + duration: int.tryParse(element.getAttribute("duration") ?? "0"), + height: int.tryParse(element.getAttribute("height") ?? "0"), + width: int.tryParse(element.getAttribute("width") ?? "0"), + lang: element.getAttribute("lang"), + ); } diff --git a/lib/domain/media/copyright.dart b/lib/domain/media/copyright.dart index 87b909f..7c5493a 100644 --- a/lib/domain/media/copyright.dart +++ b/lib/domain/media/copyright.dart @@ -1,7 +1,7 @@ import 'package:xml/xml.dart'; class Copyright { - final Uri url; + final String url; final String value; Copyright({ @@ -11,9 +11,8 @@ class Copyright { factory Copyright.parse(XmlElement element) { if (element == null) return null; - var url = element.getAttribute("url"); return Copyright( - url: url == null ? null : Uri.parse(url), + url: element.getAttribute("url"), value: element.text, ); } diff --git a/lib/domain/media/embed.dart b/lib/domain/media/embed.dart index 305eeba..8f4932f 100644 --- a/lib/domain/media/embed.dart +++ b/lib/domain/media/embed.dart @@ -2,7 +2,7 @@ import 'package:webfeed/domain/media/param.dart'; import 'package:xml/xml.dart'; class Embed { - final Uri url; + final String url; final int width; final int height; final List params; @@ -16,9 +16,8 @@ class Embed { factory Embed.parse(XmlElement element) { if (element == null) return null; - var url = element.getAttribute("url"); return Embed( - url: url == null ? null : Uri.parse(url), + url: element.getAttribute("url"), width: int.tryParse(element.getAttribute("width") ?? "0"), height: int.tryParse(element.getAttribute("height") ?? "0"), params: element.findElements("media:param").map((e) => Param.parse(e)).toList(), diff --git a/lib/domain/media/license.dart b/lib/domain/media/license.dart index 622b5d8..d4882f3 100644 --- a/lib/domain/media/license.dart +++ b/lib/domain/media/license.dart @@ -2,7 +2,7 @@ import 'package:xml/xml.dart'; class License { final String type; - final Uri href; + final String href; final String value; License({ @@ -13,10 +13,9 @@ class License { factory License.parse(XmlElement element) { if (element == null) return null; - var href = element.getAttribute("href"); return License( type: element.getAttribute("type"), - href: href == null ? null : Uri.parse(href), + href: element.getAttribute("href"), value: element.text, ); } diff --git a/lib/domain/media/media.dart b/lib/domain/media/media.dart index 1af634d..6c5c63f 100644 --- a/lib/domain/media/media.dart +++ b/lib/domain/media/media.dart @@ -41,7 +41,7 @@ class Media { final List comments; final Embed embed; final List responses; - final List backLinks; + final List backLinks; final Status status; final List prices; final License license; @@ -97,7 +97,7 @@ class Media { comments: findElementOrNull(element, "media:comments")?.findElements("media:comment")?.map((e) => e.text)?.toList() ?? [], embed: Embed.parse(findElementOrNull(element, "media:embed")), responses: findElementOrNull(element, "media:responses")?.findElements("media:response")?.map((e) => e.text)?.toList() ?? [], - backLinks: findElementOrNull(element, "media:backLinks")?.findElements("media:backLink")?.map((e) => Uri.parse(e.text))?.toList() ?? [], + backLinks: findElementOrNull(element, "media:backLinks")?.findElements("media:backLink")?.map((e) => e.text)?.toList() ?? [], status: Status.parse(findElementOrNull(element, "media:status")), prices: element.findElements("media:price").map((e) => Price.parse(e)).toList(), license: License.parse(findElementOrNull(element, "media:license")), diff --git a/lib/domain/media/peer_link.dart b/lib/domain/media/peer_link.dart index efd2be8..cba2cf7 100644 --- a/lib/domain/media/peer_link.dart +++ b/lib/domain/media/peer_link.dart @@ -2,7 +2,7 @@ import 'package:xml/xml.dart'; class PeerLink { final String type; - final Uri href; + final String href; final String value; PeerLink({ @@ -13,10 +13,9 @@ class PeerLink { factory PeerLink.parse(XmlElement element) { if (element == null) return null; - var href = element.getAttribute("href"); return PeerLink( type: element.getAttribute("type"), - href: href == null ? null : Uri.parse(href), + href: element.getAttribute("href"), value: element.text, ); } diff --git a/lib/domain/media/player.dart b/lib/domain/media/player.dart index a765a7f..c7464bf 100644 --- a/lib/domain/media/player.dart +++ b/lib/domain/media/player.dart @@ -1,7 +1,7 @@ import 'package:xml/xml.dart'; class Player { - final Uri url; + final String url; final int width; final int height; final String value; @@ -15,9 +15,8 @@ class Player { factory Player.parse(XmlElement element) { if (element == null) return null; - var url = element.getAttribute("url"); return Player( - url: url == null ? null : Uri.parse(url), + url: element.getAttribute("url"), width: int.tryParse(element.getAttribute("width") ?? "0"), height: int.tryParse(element.getAttribute("height") ?? "0"), value: element.text, diff --git a/lib/domain/media/price.dart b/lib/domain/media/price.dart index fbb3be9..6b4313a 100644 --- a/lib/domain/media/price.dart +++ b/lib/domain/media/price.dart @@ -3,7 +3,7 @@ import 'package:xml/xml.dart'; class Price { final double price; final String type; - final Uri info; + final String info; final String currency; Price({ @@ -13,13 +13,10 @@ class Price { this.currency, }); - factory Price.parse(XmlElement element) { - var info = element.getAttribute("info"); - return Price( - price: double.tryParse(element.getAttribute("price") ?? "0"), - type: element.getAttribute("type"), - info: info == null ? null : Uri.parse(info), - currency: element.getAttribute("currency"), - ); - } + factory Price.parse(XmlElement element) => Price( + price: double.tryParse(element.getAttribute("price") ?? "0"), + type: element.getAttribute("type"), + info: element.getAttribute("info"), + currency: element.getAttribute("currency"), + ); } diff --git a/lib/domain/media/thumbnail.dart b/lib/domain/media/thumbnail.dart index eb177be..9da9322 100644 --- a/lib/domain/media/thumbnail.dart +++ b/lib/domain/media/thumbnail.dart @@ -1,7 +1,7 @@ import 'package:xml/xml.dart'; class Thumbnail { - final Uri url; + final String url; final String width; final String height; final String time; @@ -13,13 +13,10 @@ class Thumbnail { this.time, }); - factory Thumbnail.parse(XmlElement element) { - var url = element.getAttribute("url"); - return Thumbnail( - url: url == null ? null : Uri.parse(url), - width: element.getAttribute("width"), - height: element.getAttribute("height"), - time: element.getAttribute("time"), - ); - } + factory Thumbnail.parse(XmlElement element) => Thumbnail( + url: element.getAttribute("url"), + width: element.getAttribute("width"), + height: element.getAttribute("height"), + time: element.getAttribute("time"), + ); } diff --git a/lib/webfeed.dart b/lib/webfeed.dart index 94c2632..a2f693e 100644 --- a/lib/webfeed.dart +++ b/lib/webfeed.dart @@ -1,4 +1,5 @@ export 'domain/atom_category.dart'; +export 'domain/atom_content.dart'; export 'domain/atom_feed.dart'; export 'domain/atom_generator.dart'; export 'domain/atom_item.dart'; diff --git a/test/atom_test.dart b/test/atom_test.dart index b690673..58ce3ee 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -2,7 +2,6 @@ import 'dart:core'; import 'dart:io'; import 'package:test/test.dart'; -import 'package:webfeed/domain/atom_content.dart'; import 'package:webfeed/webfeed.dart'; void main() { @@ -86,7 +85,7 @@ void main() { expect(item.published, DateTime.parse("2018-04-06T13:02:49Z")); expect(item.summary, "This is summary 1"); - expect(item.content, "This is content 1"); + expect(item.content.text, "This is content 1"); expect(item.rights, "This is rights 1"); }); @@ -108,7 +107,7 @@ void main() { expect(item.media.contents.length, 2); var mediaContent = item.media.contents.first; - expect(mediaContent.url, Uri.parse("http://www.foo.com/video.mov")); + expect(mediaContent.url, "http://www.foo.com/video.mov"); expect(mediaContent.type, "video/quicktime"); expect(mediaContent.fileSize, 2000); expect(mediaContent.medium, "video"); @@ -142,7 +141,7 @@ void main() { expect(item.media.thumbnails.length, 2); var mediaThumbnail = item.media.thumbnails.first; - expect(mediaThumbnail.url, Uri.parse("http://www.foo.com/keyframe1.jpg")); + expect(mediaThumbnail.url, "http://www.foo.com/keyframe1.jpg"); expect(mediaThumbnail.width, "75"); expect(mediaThumbnail.height, "50"); expect(mediaThumbnail.time, "12:05:01.123"); @@ -150,12 +149,12 @@ void main() { expect(item.media.hash.algo, "md5"); expect(item.media.hash.value, "dfdec888b72151965a34b4b59031290a"); - expect(item.media.player.url, Uri.parse("http://www.foo.com/player?id=1111")); + expect(item.media.player.url, "http://www.foo.com/player?id=1111"); expect(item.media.player.width, 400); expect(item.media.player.height, 200); expect(item.media.player.value, ""); - expect(item.media.copyright.url, Uri.parse("http://blah.com/additional-info.html")); + expect(item.media.copyright.url, "http://blah.com/additional-info.html"); expect(item.media.copyright.value, "2005 FooBar Media"); expect(item.media.text.type, "plain"); @@ -181,7 +180,7 @@ void main() { expect(item.media.comments.first, "comment1"); expect(item.media.comments.last, "comment2"); - expect(item.media.embed.url, Uri.parse("http://www.foo.com/player.swf")); + expect(item.media.embed.url, "http://www.foo.com/player.swf"); expect(item.media.embed.width, 512); expect(item.media.embed.height, 323); expect(item.media.embed.params.length, 5); @@ -193,8 +192,8 @@ void main() { expect(item.media.responses.last, "http://www.response2.com"); expect(item.media.backLinks.length, 2); - expect(item.media.backLinks.first, Uri.parse("http://www.backlink1.com")); - expect(item.media.backLinks.last, Uri.parse("http://www.backlink2.com")); + expect(item.media.backLinks.first, "http://www.backlink1.com"); + expect(item.media.backLinks.last, "http://www.backlink2.com"); expect(item.media.status.state, "active"); expect(item.media.status.reason, null); @@ -202,15 +201,15 @@ void main() { expect(item.media.prices.length, 2); expect(item.media.prices.first.price, 19.99); expect(item.media.prices.first.type, "rent"); - expect(item.media.prices.first.info, Uri.parse("http://www.dummy.jp/package_info.html")); + expect(item.media.prices.first.info, "http://www.dummy.jp/package_info.html"); expect(item.media.prices.first.currency, "EUR"); expect(item.media.license.type, "text/html"); - expect(item.media.license.href, Uri.parse("http://www.licensehost.com/license")); + expect(item.media.license.href, "http://www.licensehost.com/license"); expect(item.media.license.value, " Sample license for a video"); expect(item.media.peerLink.type, "application/x-bittorrent"); - expect(item.media.peerLink.href, Uri.parse("http://www.foo.org/sampleFile.torrent")); + expect(item.media.peerLink.href, "http://www.foo.org/sampleFile.torrent"); expect(item.media.peerLink.value, ""); expect(item.media.rights.status, "official"); diff --git a/test/rss_test.dart b/test/rss_test.dart index 5b14dfc..4c9ac13 100644 --- a/test/rss_test.dart +++ b/test/rss_test.dart @@ -22,8 +22,7 @@ void main() { var feed = new RssFeed.parse(xmlString); expect(feed.title, "News - Foo bar News"); - expect(feed.description, - "Foo bar News and Updates feed provided by Foo bar, Inc."); + expect(feed.description, "Foo bar News and Updates feed provided by Foo bar, Inc."); expect(feed.link, "https://foo.bar.news/"); expect(feed.author, "hello@world.net"); expect(feed.language, "en-US"); @@ -66,10 +65,8 @@ void main() { expect(feed.items.length, 2); - expect(feed.items.first.title, - "The standard Lorem Ipsum passage, used since the 1500s"); - expect(feed.items.first.description, - "Lorem ipsum dolor sit amet, consectetur adipiscing elit"); + expect(feed.items.first.title, "The standard Lorem Ipsum passage, used since the 1500s"); + expect(feed.items.first.description, "Lorem ipsum dolor sit amet, consectetur adipiscing elit"); expect(feed.items.first.link, "https://foo.bar.news/1"); expect(feed.items.first.guid, "https://foo.bar.news/1?guid"); expect(feed.items.first.pubDate, "Mon, 26 Mar 2018 14:00:00 PDT"); @@ -79,23 +76,19 @@ void main() { expect(feed.items.first.source.url, "https://foo.bar.news/1?source"); expect(feed.items.first.source.value, "Foo Bar"); expect(feed.items.first.comments, "https://foo.bar.news/1/comments"); - expect(feed.items.first.enclosure.url, - "http://www.scripting.com/mp3s/weatherReportSuite.mp3"); + expect(feed.items.first.enclosure.url, "http://www.scripting.com/mp3s/weatherReportSuite.mp3"); expect(feed.items.first.enclosure.length, 12216320); expect(feed.items.first.enclosure.type, "audio/mpeg"); - expect(feed.items.first.content.value, - " Test content
"); - expect( - feed.items.first.content.images.first, "https://test.com/image_link"); + expect(feed.items.first.content.value, " Test content
"); + expect(feed.items.first.content.images.first, "https://test.com/image_link"); }); test("parse RSS-Media.xml", () { var xmlString = new File("test/xml/RSS-Media.xml").readAsStringSync(); var feed = new RssFeed.parse(xmlString); expect(feed.title, "Song Site"); - expect( - feed.description, "Media RSS example with new fields added in v1.5.0"); + expect(feed.description, "Media RSS example with new fields added in v1.5.0"); expect(feed.items.length, 1); @@ -128,8 +121,7 @@ void main() { expect(mediaCredit.scheme, "urn:yvs"); expect(mediaCredit.value, "copyright holder of the entity"); - expect(item.media.category.scheme, - "http://search.yahoo.com/mrss/category_ schema"); + expect(item.media.category.scheme, "http://search.yahoo.com/mrss/category_ schema"); expect(item.media.category.label, "Music"); expect(item.media.category.value, "music/artist/album/song"); @@ -140,8 +132,7 @@ void main() { expect(item.media.title.value, "The Judy's -- The Moo Song"); expect(item.media.description.type, "plain"); - expect(item.media.description.value, - "This was some really bizarre band I listened to as a young lad."); + expect(item.media.description.value, "This was some really bizarre band I listened to as a young lad."); expect(item.media.keywords, "kitty, cat, big dog, yarn, fluffy"); @@ -191,8 +182,7 @@ void main() { expect(item.media.embed.height, 323); expect(item.media.embed.params.length, 5); expect(item.media.embed.params.first.name, "type"); - expect( - item.media.embed.params.first.value, "application/x-shockwave-flash"); + expect(item.media.embed.params.first.value, "application/x-shockwave-flash"); expect(item.media.responses.length, 2); expect(item.media.responses.first, "http://www.response1.com"); @@ -208,8 +198,7 @@ void main() { expect(item.media.prices.length, 2); expect(item.media.prices.first.price, 19.99); expect(item.media.prices.first.type, "rent"); - expect( - item.media.prices.first.info, "http://www.dummy.jp/package_info.html"); + expect(item.media.prices.first.info, "http://www.dummy.jp/package_info.html"); expect(item.media.prices.first.currency, "EUR"); expect(item.media.license.type, "text/html"); @@ -318,10 +307,8 @@ void main() { expect(feed.itunes.author, "Changelog Media"); expect(feed.itunes.summary, "Foo"); expect(feed.itunes.explicit, false); - expect(feed.itunes.image.href, - "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357"); - expect(feed.itunes.keywords, - "go,golang,open source,software,development".split(",")); + expect(feed.itunes.image.href, "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357"); + expect(feed.itunes.keywords, "go,golang,open source,software,development".split(",")); expect(feed.itunes.owner.name, "Changelog Media"); expect(feed.itunes.owner.email, "editors@changelog.com"); expect( @@ -329,14 +316,23 @@ void main() { feed.itunes.categories[0].category, feed.itunes.categories[1].category ]), - ["Technology", "Foo"]); + [ + "Technology", + "Foo" + ]); for (var category in feed.itunes.categories) { switch (category.category) { case "Foo": - expect(category.subCategories, ["Bar", "Baz"]); + expect(category.subCategories, [ + "Bar", + "Baz" + ]); break; case "Technology": - expect(category.subCategories, ["Software How-To", "Tech News"]); + expect(category.subCategories, [ + "Software How-To", + "Tech News" + ]); break; } } @@ -350,16 +346,13 @@ void main() { expect(item.itunes.episodeType, RssItunesEpisodeType.full); expect(item.itunes.episode, 1); expect(item.itunes.season, 1); - expect(item.itunes.image.href, - "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357"); + expect(item.itunes.image.href, "https://cdn.changelog.com/uploads/covers/go-time-original.png?v=63725770357"); expect(item.itunes.duration, Duration(minutes: 32, seconds: 30)); expect(item.itunes.explicit, false); - expect(item.itunes.keywords, - "go,golang,open source,software,development".split(",")); + expect(item.itunes.keywords, "go,golang,open source,software,development".split(",")); expect(item.itunes.subtitle, "with Erik, Carlisia, and Brian"); expect(item.itunes.summary, "Foo"); - expect(item.itunes.author, - "Erik St. Martin, Carlisia Pinto, and Brian Ketelsen"); + expect(item.itunes.author, "Erik St. Martin, Carlisia Pinto, and Brian Ketelsen"); expect(item.itunes.explicit, false); expect(item.itunes.title, "awesome title"); expect(item.itunes.block, false); From 1ac18eb23cec1080f1b5212cd9b121f34bbcc645 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Fri, 29 Nov 2019 19:02:48 -0800 Subject: [PATCH 15/16] enable typed summari as well as content --- lib/domain/atom_content.dart | 2 +- lib/domain/atom_item.dart | 8 ++++---- lib/domain/atom_person.dart | 4 ++-- pubspec.yaml | 2 +- test/atom_test.dart | 10 +++++----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/domain/atom_content.dart b/lib/domain/atom_content.dart index 153e202..5860b84 100644 --- a/lib/domain/atom_content.dart +++ b/lib/domain/atom_content.dart @@ -17,7 +17,7 @@ class AtomContent { ); } - void build(XmlBuilder b) => b.element('content', nest: () { + void build(XmlBuilder b, String kind) => b.element(kind, nest: () { if (type != null) b.attribute('type', type); if (text != null) b.text(text); }); diff --git a/lib/domain/atom_item.dart b/lib/domain/atom_item.dart index a5197ea..06a1e42 100644 --- a/lib/domain/atom_item.dart +++ b/lib/domain/atom_item.dart @@ -18,7 +18,7 @@ class AtomItem { final AtomSource source; final DateTime published; final AtomContent content; - final String summary; + final AtomContent summary; final String rights; final Media media; @@ -49,7 +49,7 @@ class AtomItem { source: AtomSource.parse(findElementOrNull(element, "source")), published: parseDateTimeLiteral(element, "published"), content: AtomContent.parse(findElementOrNull(element, "content")), - summary: parseTextLiteral(element, "summary"), + summary: AtomContent.parse(findElementOrNull(element, "summary")), rights: parseTextLiteral(element, "rights"), media: Media.parse(element), ); @@ -66,8 +66,8 @@ class AtomItem { if (contributors != null) contributors.forEach((c) => c.build(b, 'contributor')); if (source != null) source.build(b); if (published != null) b.element('published', nest: () => b.text(published.toUtc().toIso8601String())); - if (summary != null) b.element('summary', nest: () => b.text(summary)); - if (content != null) content.build(b); + if (summary != null) summary.build(b, 'summary'); + if (content != null) content.build(b, 'content'); if (rights != null) b.element('rights', nest: () => b.text(rights)); //if (media != null) media.build(b); // TODO }); diff --git a/lib/domain/atom_person.dart b/lib/domain/atom_person.dart index 54d34e6..7b15cce 100644 --- a/lib/domain/atom_person.dart +++ b/lib/domain/atom_person.dart @@ -17,8 +17,8 @@ class AtomPerson { ); } - void build(XmlBuilder b, String type) { - b.element(type, nest: () { + void build(XmlBuilder b, String kind) { + b.element(kind, nest: () { if (name != null) b.element('name', nest: () => b.text(name)); if (uri != null) b.element('uri', nest: () => b.text(uri)); if (email != null) b.element('email', nest: () => b.text(email)); diff --git a/pubspec.yaml b/pubspec.yaml index 067869e..99470d9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: webfeed -version: 0.5.0 +version: 0.5.1 description: webfeed is a dart package for parsing and generating RSS and Atom feeds. Media & DublinCore namespaces are also supported. author: Wito Chandra homepage: https://github.com/witochandra/webfeed diff --git a/test/atom_test.dart b/test/atom_test.dart index 58ce3ee..bd52330 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -84,7 +84,7 @@ void main() { expect(item.contributors.first.email, "gin@foo.bar.news"); expect(item.published, DateTime.parse("2018-04-06T13:02:49Z")); - expect(item.summary, "This is summary 1"); + expect(item.summary.text, "This is summary 1"); expect(item.content.text, "This is content 1"); expect(item.rights, "This is rights 1"); }); @@ -312,7 +312,7 @@ void main() { title: 'Foo bar item 1', updated: DateTime.parse('2018-04-06T13:02:47Z'), published: DateTime.parse('2018-04-06T13:02:49Z'), - summary: 'This is summary 1', + summary: AtomContent(text: 'This is summary 1'), content: AtomContent(text: 'This is content 1'), rights: 'This is rights 1', authors: [ @@ -342,7 +342,7 @@ void main() { title: 'Foo bar item 2', updated: DateTime.parse('2018-04-06T13:02:50Z'), published: DateTime.parse('2018-04-06T13:02:52Z'), - summary: 'This is summary 2', + summary: AtomContent(text: 'This is summary 2'), content: AtomContent(text: 'This is content 2'), rights: 'This is rights 2', authors: [ @@ -407,7 +407,7 @@ void main() { ], id: 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', updated: DateTime.parse('2003-12-13T18:30:02Z'), - summary: 'Some text.', + summary: AtomContent(text: 'Some text.'), ), ], ); @@ -441,7 +441,7 @@ void main() { ], id: 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', updated: DateTime.parse('2003-12-13T18:30:02Z'), - summary: 'Some text.', + summary: AtomContent(text: 'Some text.'), ), ], ); From e07c6ac17cf79c70e4b1fbc15fcd94086a1398f2 Mon Sep 17 00:00:00 2001 From: Chris Sells Date: Sat, 30 Nov 2019 12:14:01 -0800 Subject: [PATCH 16/16] updated item.id to URI according to Atom spec --- lib/domain/atom_item.dart | 8 ++++---- pubspec.yaml | 2 +- test/atom_test.dart | 43 +++++++++++++++++++-------------------- test/xml/Atom.xml | 26 +++++++++++------------ 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/lib/domain/atom_item.dart b/lib/domain/atom_item.dart index 06a1e42..1fba17a 100644 --- a/lib/domain/atom_item.dart +++ b/lib/domain/atom_item.dart @@ -8,7 +8,7 @@ import 'package:webfeed/util/helpers.dart'; import 'package:xml/xml.dart'; class AtomItem { - final String id; + final Uri id; final String title; final DateTime updated; final List authors; @@ -39,7 +39,7 @@ class AtomItem { }) : this.updated = updated ?? DateTime.now(); factory AtomItem.parse(XmlElement element) => AtomItem( - id: parseTextLiteral(element, "id"), + id: parseUriLiteral(element, "id"), title: parseTextLiteral(element, "title"), updated: parseDateTimeLiteral(element, "updated"), authors: element.findElements("author").map((e) => AtomPerson.parse(e)).toList(), @@ -55,7 +55,7 @@ class AtomItem { ); void build(XmlBuilder b) { - if (id == null || id.isEmpty) throw Exception('must have an id'); + if (id == null) throw Exception('must have an id'); b.element('entry', nest: () { b.element('id', nest: () => b.text(id)); if (title != null) b.element('title', nest: () => b.text(title)); @@ -69,7 +69,7 @@ class AtomItem { if (summary != null) summary.build(b, 'summary'); if (content != null) content.build(b, 'content'); if (rights != null) b.element('rights', nest: () => b.text(rights)); - //if (media != null) media.build(b); // TODO + //if (media != null) media.build(b); }); } } diff --git a/pubspec.yaml b/pubspec.yaml index 99470d9..1015ad0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: webfeed -version: 0.5.1 +version: 0.5.2 description: webfeed is a dart package for parsing and generating RSS and Atom feeds. Media & DublinCore namespaces are also supported. author: Wito Chandra homepage: https://github.com/witochandra/webfeed diff --git a/test/atom_test.dart b/test/atom_test.dart index bd52330..fa99211 100644 --- a/test/atom_test.dart +++ b/test/atom_test.dart @@ -18,13 +18,12 @@ void main() { var xmlString = File("test/xml/Atom.xml").readAsStringSync(); var feed = AtomFeed.parse(xmlString); - - expect(feed.id, Uri.parse("foo-bar-id")); + expect(feed.id, Uri.parse("urn:foo:foo-bar-id")); expect(feed.title, "Foo bar news"); expect(feed.updated, DateTime.parse("2018-04-06T13:02:46Z")); expect(feed.links.length, 2); - expect(feed.links.first.rel, "foo"); + expect(feed.links.first.rel, "related"); expect(feed.links.first.type, "text/html"); expect(feed.links.first.hreflang, "en"); expect(feed.links.first.href, Uri.parse("http://foo.bar.news/")); @@ -43,7 +42,7 @@ void main() { expect(feed.categories.length, 2); expect(feed.categories.first.term, "foo category"); - expect(feed.categories.first.scheme, "this-is-foo-scheme"); + expect(feed.categories.first.scheme, "https://foo.com/this-is-foo-scheme"); expect(feed.categories.first.label, "this is foo label"); expect(feed.generator.uri, Uri.parse("http://foo.bar.news/generator")); @@ -56,7 +55,7 @@ void main() { expect(feed.items.length, 2); var item = feed.items.first; - expect(item.id, "foo-bar-entry-id-1"); + expect(item.id, Uri.parse("urn:foo:foo-bar-entry-id-1")); expect(item.title, "Foo bar item 1"); expect(item.updated, DateTime.parse("2018-04-06T13:02:47Z")); @@ -66,7 +65,7 @@ void main() { expect(item.authors.first.email, "ellie@foo.bar.news"); expect(item.links.length, 2); - expect(item.links.first.rel, "foo entry"); + expect(item.links.first.rel, "related"); expect(item.links.first.type, "text/html"); expect(item.links.first.hreflang, "en"); expect(item.links.first.href, Uri.parse("http://foo.bar.news/entry")); @@ -75,7 +74,7 @@ void main() { expect(item.categories.length, 2); expect(item.categories.first.term, "foo entry category"); - expect(item.categories.first.scheme, "this-is-foo-entry-scheme"); + expect(item.categories.first.scheme, "https://foo.com/this-is-foo-entry-scheme"); expect(item.categories.first.label, "this is foo entry label"); expect(item.contributors.length, 2); @@ -283,15 +282,15 @@ void main() { test("generate Atom.xml", () { var xmlString = File("test/xml/Atom.xml").readAsStringSync(); var feed = AtomFeed( - id: Uri.parse('foo-bar-id'), + id: Uri.parse('urn:foo:foo-bar-id'), title: 'Foo bar news', updated: DateTime.parse('2018-04-06T13:02:46Z'), icon: Uri.parse('http://foo.bar.news/icon.png'), logo: Uri.parse('http://foo.bar.news/logo.png'), subtitle: 'This is subtitle', links: [ - AtomLink(rel: 'foo', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/'), title: 'Foo bar news html', length: 1000), - AtomLink(rel: 'bar', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/feed.atom'), title: 'Foo bar news atom', length: 100), + AtomLink(rel: 'related', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/'), title: 'Foo bar news html', length: 1000), + AtomLink(rel: 'related', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/feed.atom'), title: 'Foo bar news atom', length: 100), ], authors: [ AtomPerson(name: 'Alice', uri: Uri.parse('http://foo.bar.news/people/alice'), email: 'alice@foo.bar.news'), @@ -302,13 +301,13 @@ void main() { AtomPerson(name: 'David', uri: Uri.parse('http://foo.bar.news/people/david'), email: 'david@foo.bar.news'), ], categories: [ - AtomCategory(term: 'foo category', scheme: 'this-is-foo-scheme', label: 'this is foo label'), - AtomCategory(term: 'bar category', scheme: 'this-is-bar-scheme', label: 'this is bar label'), + AtomCategory(term: 'foo category', scheme: 'https://foo.com/this-is-foo-scheme', label: 'this is foo label'), + AtomCategory(term: 'bar category', scheme: 'https://foo.com/this-is-bar-scheme', label: 'this is bar label'), ], generator: AtomGenerator(uri: Uri.parse('http://foo.bar.news/generator'), version: '1.0', value: 'Foo bar generator'), items: [ AtomItem( - id: 'foo-bar-entry-id-1', + id: Uri.parse('urn:foo:foo-bar-entry-id-1'), title: 'Foo bar item 1', updated: DateTime.parse('2018-04-06T13:02:47Z'), published: DateTime.parse('2018-04-06T13:02:49Z'), @@ -320,12 +319,12 @@ void main() { AtomPerson(name: 'Franz', uri: Uri.parse('http://foo.bar.news/people/franz'), email: 'franz@foo.bar.news'), ], links: [ - AtomLink(rel: 'foo entry', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/entry'), title: 'Foo bar news html', length: 1000), - AtomLink(rel: 'bar entry', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/entry/feed.atom'), title: 'Foo bar entry atom', length: 100), + AtomLink(rel: 'related', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/entry'), title: 'Foo bar news html', length: 1000), + AtomLink(rel: 'related', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/entry/feed.atom'), title: 'Foo bar entry atom', length: 100), ], categories: [ - AtomCategory(term: 'foo entry category', scheme: 'this-is-foo-entry-scheme', label: 'this is foo entry label'), - AtomCategory(term: 'bar entry category', scheme: 'this-is-bar-entry-scheme', label: 'this is bar entry label'), + AtomCategory(term: 'foo entry category', scheme: 'https://foo.com/this-is-foo-entry-scheme', label: 'this is foo entry label'), + AtomCategory(term: 'bar entry category', scheme: 'https://foo.com/this-is-bar-entry-scheme', label: 'this is bar entry label'), ], contributors: [ AtomPerson(name: 'Gin', uri: Uri.parse('http://foo.bar.news/people/gin'), email: 'gin@foo.bar.news'), @@ -338,7 +337,7 @@ void main() { ), ), AtomItem( - id: 'foo-bar-entry-id-2', + id: Uri.parse('urn:foo:foo-bar-entry-id-2'), title: 'Foo bar item 2', updated: DateTime.parse('2018-04-06T13:02:50Z'), published: DateTime.parse('2018-04-06T13:02:52Z'), @@ -358,8 +357,8 @@ void main() { ), ], links: [ - AtomLink(rel: 'foo entry', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/entry'), title: 'Foo bar news html', length: 1000), - AtomLink(rel: 'bar entry', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/entry/feed.atom'), title: 'Foo bar entry atom', length: 100), + AtomLink(rel: 'related', type: 'text/html', hreflang: 'en', href: Uri.parse('http://foo.bar.news/entry'), title: 'Foo bar news html', length: 1000), + AtomLink(rel: 'related', type: 'application/atom+xml', hreflang: 'pt', href: Uri.parse('http://foo.bar.news/entry/feed.atom'), title: 'Foo bar entry atom', length: 100), ], categories: [ AtomCategory(term: 'foo entry category'), @@ -405,7 +404,7 @@ void main() { links: [ AtomLink(href: Uri.parse('http://example.org/2003/12/13/atom03')), ], - id: 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', + id: Uri.parse('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'), updated: DateTime.parse('2003-12-13T18:30:02Z'), summary: AtomContent(text: 'Some text.'), ), @@ -439,7 +438,7 @@ void main() { links: [ AtomLink(href: Uri.parse('http://example.org/2003/12/13/atom03')), ], - id: 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', + id: Uri.parse('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'), updated: DateTime.parse('2003-12-13T18:30:02Z'), summary: AtomContent(text: 'Some text.'), ), diff --git a/test/xml/Atom.xml b/test/xml/Atom.xml index 8a181fe..989517c 100644 --- a/test/xml/Atom.xml +++ b/test/xml/Atom.xml @@ -1,10 +1,10 @@ - foo-bar-id + urn:foo:foo-bar-id Foo bar news 2018-04-06T13:02:46.000Z - - + + Alice http://foo.bar.news/people/alice @@ -25,14 +25,14 @@ http://foo.bar.news/people/david david@foo.bar.news - - + + Foo bar generator http://foo.bar.news/icon.png http://foo.bar.news/logo.png This is subtitle - foo-bar-entry-id-1 + urn:foo:foo-bar-entry-id-1 Foo bar item 1 2018-04-06T13:02:47.000Z @@ -45,10 +45,10 @@ http://foo.bar.news/people/franz franz@foo.bar.news - - - - + + + + Gin http://foo.bar.news/people/gin @@ -70,7 +70,7 @@ This is rights 1 - foo-bar-entry-id-2 + urn:foo:foo-bar-entry-id-2 Foo bar item 2 2018-04-06T13:02:50.000Z @@ -83,8 +83,8 @@ http://foo.bar.news/people/jhon jhon@foo.bar.news - - + +