From 027854e4f4cddc0d0cf4bfcd0f1859dc081f9fcc Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 1 Sep 2021 21:52:22 +0400 Subject: [PATCH 1/7] Extracted getSingleBookData() in opds_dumper.cpp --- src/opds_dumper.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/opds_dumper.cpp b/src/opds_dumper.cpp index bfbe61c84..a36666e7b 100644 --- a/src/opds_dumper.cpp +++ b/src/opds_dumper.cpp @@ -69,15 +69,12 @@ IllustrationInfo getBookIllustrationInfo(const Book& book) return illustrations; } -BookData getBookData(const Library* library, const std::vector& bookIds) +kainjow::mustache::object getSingleBookData(const Book& book) { - BookData bookData; - for ( const auto& bookId : bookIds ) { - const Book& book = library->getBookById(bookId); const MustacheData bookUrl = book.getUrl().empty() ? MustacheData(false) : MustacheData(book.getUrl()); - bookData.push_back(kainjow::mustache::object{ + return kainjow::mustache::object{ {"id", "urn:uuid:"+book.getId()}, {"name", book.getName()}, {"title", book.getTitle()}, @@ -95,7 +92,15 @@ BookData getBookData(const Library* library, const std::vector& boo {"url", bookUrl}, {"size", to_string(book.getSize())}, {"icons", getBookIllustrationInfo(book)}, - }); + }; +} + +BookData getBookData(const Library* library, const std::vector& bookIds) +{ + BookData bookData; + for ( const auto& bookId : bookIds ) { + const Book& book = library->getBookById(bookId); + bookData.push_back(getSingleBookData(book)); } return bookData; From 12d9b698061238a5835d694195d973040dc556c7 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 1 Sep 2021 21:54:59 +0400 Subject: [PATCH 2/7] OPDSDumper::dumpOPDSCompleteEntry() --- include/opds_dumper.h | 8 +++++ src/opds_dumper.cpp | 7 +++++ static/resources_list.txt | 1 + .../templates/catalog_v2_complete_entry.xml | 29 +++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 static/templates/catalog_v2_complete_entry.xml diff --git a/include/opds_dumper.h b/include/opds_dumper.h index 69c74e753..bdea0f8ac 100644 --- a/include/opds_dumper.h +++ b/include/opds_dumper.h @@ -63,6 +63,14 @@ class OPDSDumper */ std::string dumpOPDSFeedV2(const std::vector& bookIds, const std::string& query) const; + /** + * Dump the OPDS complete entry document. + * + * @param bookId the id of the book + * @return The OPDS complete entry document. + */ + std::string dumpOPDSCompleteEntry(const std::string& bookId) const; + /** * Dump the categories OPDS feed. * diff --git a/src/opds_dumper.cpp b/src/opds_dumper.cpp index a36666e7b..61e93b266 100644 --- a/src/opds_dumper.cpp +++ b/src/opds_dumper.cpp @@ -153,6 +153,13 @@ string OPDSDumper::dumpOPDSFeedV2(const std::vector& bookIds, const return render_template(RESOURCE::templates::catalog_v2_entries_xml, template_data); } +std::string OPDSDumper::dumpOPDSCompleteEntry(const std::string& bookId) const +{ + const auto bookData = getSingleBookData(library->getBookById(bookId)); + + return render_template(RESOURCE::templates::catalog_v2_complete_entry_xml, bookData); +} + std::string OPDSDumper::categoriesOPDSFeed() const { const auto now = gen_date_str(); diff --git a/static/resources_list.txt b/static/resources_list.txt index 6e92fbaac..9ace25b1b 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -45,6 +45,7 @@ templates/captured_external.html templates/catalog_entries.xml templates/catalog_v2_root.xml templates/catalog_v2_entries.xml +templates/catalog_v2_complete_entry.xml templates/catalog_v2_categories.xml templates/catalog_v2_languages.xml opensearchdescription.xml diff --git a/static/templates/catalog_v2_complete_entry.xml b/static/templates/catalog_v2_complete_entry.xml new file mode 100644 index 000000000..cd4abe5f2 --- /dev/null +++ b/static/templates/catalog_v2_complete_entry.xml @@ -0,0 +1,29 @@ + + + urn:uuid:{{id}} + {{title}} + {{description}} + {{language}} + {{updated}} + {{name}} + {{flavour}} + {{category}} + {{tags}} + {{article_count}} + {{media_count}} + {{#icons}} + + {{/icons}} + + + {{author_name}} + + + {{publisher_name}} + + {{#url}} + + {{/url}} + From e15a0f433899e7c3133c9bfc031c642e6c128023 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 1 Sep 2021 21:59:48 +0400 Subject: [PATCH 3/7] /catalog/v2/entry/ OPDS API endpoint --- src/opds_dumper.cpp | 2 +- src/server/internalServer.h | 1 + src/server/internalServer_catalog_v2.cpp | 22 ++++++++++++++++++++++ static/templates/catalog_entries.xml | 2 +- static/templates/catalog_v2_entries.xml | 2 +- test/server.cpp | 15 ++++++++++++++- 6 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/opds_dumper.cpp b/src/opds_dumper.cpp index 61e93b266..b5f5bb078 100644 --- a/src/opds_dumper.cpp +++ b/src/opds_dumper.cpp @@ -75,7 +75,7 @@ kainjow::mustache::object getSingleBookData(const Book& book) ? MustacheData(false) : MustacheData(book.getUrl()); return kainjow::mustache::object{ - {"id", "urn:uuid:"+book.getId()}, + {"id", book.getId()}, {"name", book.getName()}, {"title", book.getTitle()}, {"description", book.getDescription()}, diff --git a/src/server/internalServer.h b/src/server/internalServer.h index f1c813a81..3c9b6d89c 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -76,6 +76,7 @@ class InternalServer { std::unique_ptr handle_catalog_v2(const RequestContext& request); std::unique_ptr handle_catalog_v2_root(const RequestContext& request); std::unique_ptr handle_catalog_v2_entries(const RequestContext& request); + std::unique_ptr handle_catalog_v2_complete_entry(const RequestContext& request, const std::string& entryId); std::unique_ptr handle_catalog_v2_categories(const RequestContext& request); std::unique_ptr handle_catalog_v2_languages(const RequestContext& request); std::unique_ptr handle_meta(const RequestContext& request); diff --git a/src/server/internalServer_catalog_v2.cpp b/src/server/internalServer_catalog_v2.cpp index 9dc88b49c..79960b42d 100644 --- a/src/server/internalServer_catalog_v2.cpp +++ b/src/server/internalServer_catalog_v2.cpp @@ -55,6 +55,9 @@ std::unique_ptr InternalServer::handle_catalog_v2(const RequestContext kainjow::mustache::object({{"endpoint_root", endpoint_root}}), "application/opensearchdescription+xml" ); + } else if (url == "entry") { + const std::string entryId = request.get_url_part(3); + return handle_catalog_v2_complete_entry(request, entryId); } else if (url == "entries") { return handle_catalog_v2_entries(request); } else if (url == "categories") { @@ -97,6 +100,25 @@ std::unique_ptr InternalServer::handle_catalog_v2_entries(const Reques ); } +std::unique_ptr InternalServer::handle_catalog_v2_complete_entry(const RequestContext& request, const std::string& entryId) +{ + try { + mp_library->getBookById(entryId); + } catch (const std::out_of_range&) { + return Response::build_404(*this, request, "", ""); + } + + OPDSDumper opdsDumper(mp_library); + opdsDumper.setRootLocation(m_root); + opdsDumper.setLibraryId(m_library_id); + const auto opdsFeed = opdsDumper.dumpOPDSCompleteEntry(entryId); + return ContentResponse::build( + *this, + opdsFeed, + "application/atom+xml;type=entry;profile=opds-catalog" + ); +} + std::unique_ptr InternalServer::handle_catalog_v2_categories(const RequestContext& request) { OPDSDumper opdsDumper(mp_library); diff --git a/static/templates/catalog_entries.xml b/static/templates/catalog_entries.xml index 0a6a05dde..a0e6cf090 100644 --- a/static/templates/catalog_entries.xml +++ b/static/templates/catalog_entries.xml @@ -11,7 +11,7 @@ {{#books}} - {{id}} + urn:uuid:{{id}} {{title}} {{description}} {{language}} diff --git a/static/templates/catalog_v2_entries.xml b/static/templates/catalog_v2_entries.xml index 098744d86..356d3d7b1 100644 --- a/static/templates/catalog_v2_entries.xml +++ b/static/templates/catalog_v2_entries.xml @@ -23,7 +23,7 @@ {{/filter}} {{#books}} - {{id}} + urn:uuid:{{id}} {{title}} {{description}} {{language}} diff --git a/test/server.cpp b/test/server.cpp index f039df253..6094e5646 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -1239,4 +1239,17 @@ TEST_F(LibraryServerTest, suggestions_in_range) int currCount = std::count(body.begin(), body.end(), '{') - 1; ASSERT_EQ(currCount, 0); } -} \ No newline at end of file +} + +TEST_F(LibraryServerTest, catalog_v2_individual_entry_access) +{ + const auto r = zfs1_->GET("/catalog/v2/entry/raycharles"); + EXPECT_EQ(r->status, 200); + EXPECT_EQ(maskVariableOPDSFeedData(r->body), + "\n" + RAY_CHARLES_CATALOG_ENTRY + ); + + const auto r1 = zfs1_->GET("/catalog/v2/entry/non-existent-entry"); + EXPECT_EQ(r1->status, 404); +} From e773a29f291b5202ea0ac9046ae2a78bf45c9820 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 1 Sep 2021 22:12:17 +0400 Subject: [PATCH 4/7] Rearranged elements in OPDS entry XML --- static/templates/catalog_entries.xml | 2 +- static/templates/catalog_v2_complete_entry.xml | 2 +- static/templates/catalog_v2_entries.xml | 2 +- test/server.cpp | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/static/templates/catalog_entries.xml b/static/templates/catalog_entries.xml index a0e6cf090..144f68bf8 100644 --- a/static/templates/catalog_entries.xml +++ b/static/templates/catalog_entries.xml @@ -13,9 +13,9 @@ urn:uuid:{{id}} {{title}} + {{updated}} {{description}} {{language}} - {{updated}} {{name}} {{flavour}} {{category}} diff --git a/static/templates/catalog_v2_complete_entry.xml b/static/templates/catalog_v2_complete_entry.xml index cd4abe5f2..76335a3f6 100644 --- a/static/templates/catalog_v2_complete_entry.xml +++ b/static/templates/catalog_v2_complete_entry.xml @@ -2,9 +2,9 @@ urn:uuid:{{id}} {{title}} + {{updated}} {{description}} {{language}} - {{updated}} {{name}} {{flavour}} {{category}} diff --git a/static/templates/catalog_v2_entries.xml b/static/templates/catalog_v2_entries.xml index 356d3d7b1..451e3b342 100644 --- a/static/templates/catalog_v2_entries.xml +++ b/static/templates/catalog_v2_entries.xml @@ -25,9 +25,9 @@ urn:uuid:{{id}} {{title}} + {{updated}} {{description}} {{language}} - {{updated}} {{name}} {{flavour}} {{category}} diff --git a/test/server.cpp b/test/server.cpp index 6094e5646..eebae7338 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -618,9 +618,9 @@ std::string maskVariableOPDSFeedData(std::string s) " \n" \ " urn:uuid:charlesray\n" \ " Charles, Ray\n" \ + " YYYY-MM-DDThh:mm:ssZ\n" \ " Wikipedia articles about Ray Charles\n" \ " fra\n" \ - " YYYY-MM-DDThh:mm:ssZ\n" \ " wikipedia_fr_ray_charles\n" \ " \n" \ " jazz\n" \ @@ -644,9 +644,9 @@ std::string maskVariableOPDSFeedData(std::string s) " \n" \ " urn:uuid:raycharles\n" \ " Ray Charles\n" \ + " YYYY-MM-DDThh:mm:ssZ\n" \ " Wikipedia articles about Ray Charles\n" \ " eng\n" \ - " YYYY-MM-DDThh:mm:ssZ\n" \ " wikipedia_en_ray_charles\n" \ " \n" \ " wikipedia\n" \ @@ -670,9 +670,9 @@ std::string maskVariableOPDSFeedData(std::string s) " \n" \ " urn:uuid:raycharles_uncategorized\n" \ " Ray (uncategorized) Charles\n" \ + " YYYY-MM-DDThh:mm:ssZ\n" \ " No category is assigned to this library entry.\n" \ " rus\n" \ - " YYYY-MM-DDThh:mm:ssZ\n" \ " wikipedia_ru_ray_charles\n" \ " \n" \ " \n" \ From 4c657c082e7009e63d7d60c0d8da6827b47aa102 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 1 Sep 2021 22:15:49 +0400 Subject: [PATCH 5/7] /catalog/v2/partial_entries OPDS API endpoint --- include/opds_dumper.h | 3 +- src/opds_dumper.cpp | 8 +++-- src/server/internalServer.h | 2 +- src/server/internalServer_catalog_v2.cpp | 8 +++-- static/templates/catalog_v2_entries.xml | 9 +++-- test/server.cpp | 46 ++++++++++++++++++++++-- 6 files changed, 64 insertions(+), 12 deletions(-) diff --git a/include/opds_dumper.h b/include/opds_dumper.h index bdea0f8ac..3c4ce4227 100644 --- a/include/opds_dumper.h +++ b/include/opds_dumper.h @@ -59,9 +59,10 @@ class OPDSDumper * * @param bookIds the ids of the books to include in the feed * @param query the query used to obtain the list of book ids + * @param partial whether the feed should include partial or complete entries * @return The OPDS feed. */ - std::string dumpOPDSFeedV2(const std::vector& bookIds, const std::string& query) const; + std::string dumpOPDSFeedV2(const std::vector& bookIds, const std::string& query, bool partial) const; /** * Dump the OPDS complete entry document. diff --git a/src/opds_dumper.cpp b/src/opds_dumper.cpp index b5f5bb078..ddf2e83de 100644 --- a/src/opds_dumper.cpp +++ b/src/opds_dumper.cpp @@ -134,20 +134,22 @@ string OPDSDumper::dumpOPDSFeed(const std::vector& bookIds, const s return render_template(RESOURCE::templates::catalog_entries_xml, template_data); } -string OPDSDumper::dumpOPDSFeedV2(const std::vector& bookIds, const std::string& query) const +string OPDSDumper::dumpOPDSFeedV2(const std::vector& bookIds, const std::string& query, bool partial) const { const auto bookData = getBookData(library, bookIds); + const char* const endpoint = partial ? "/partial_entries" : "/entries"; const kainjow::mustache::object template_data{ {"date", gen_date_str()}, {"endpoint_root", rootLocation + "/catalog/v2"}, - {"feed_id", gen_uuid(libraryId + "/entries?"+query)}, + {"feed_id", gen_uuid(libraryId + endpoint + "?" + query)}, {"filter", query.empty() ? MustacheData(false) : MustacheData(query)}, {"query", query.empty() ? "" : "?" + urlEncode(query)}, {"totalResults", to_string(m_totalResults)}, {"startIndex", to_string(m_startIndex)}, {"itemsPerPage", to_string(m_count)}, - {"books", bookData } + {"books", bookData }, + {"dump_partial_entries", MustacheData(partial)} }; return render_template(RESOURCE::templates::catalog_v2_entries_xml, template_data); diff --git a/src/server/internalServer.h b/src/server/internalServer.h index 3c9b6d89c..cfaceb9ec 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -75,7 +75,7 @@ class InternalServer { std::unique_ptr handle_catalog(const RequestContext& request); std::unique_ptr handle_catalog_v2(const RequestContext& request); std::unique_ptr handle_catalog_v2_root(const RequestContext& request); - std::unique_ptr handle_catalog_v2_entries(const RequestContext& request); + std::unique_ptr handle_catalog_v2_entries(const RequestContext& request, bool partial); std::unique_ptr handle_catalog_v2_complete_entry(const RequestContext& request, const std::string& entryId); std::unique_ptr handle_catalog_v2_categories(const RequestContext& request); std::unique_ptr handle_catalog_v2_languages(const RequestContext& request); diff --git a/src/server/internalServer_catalog_v2.cpp b/src/server/internalServer_catalog_v2.cpp index 79960b42d..88a3f64ef 100644 --- a/src/server/internalServer_catalog_v2.cpp +++ b/src/server/internalServer_catalog_v2.cpp @@ -59,7 +59,9 @@ std::unique_ptr InternalServer::handle_catalog_v2(const RequestContext const std::string entryId = request.get_url_part(3); return handle_catalog_v2_complete_entry(request, entryId); } else if (url == "entries") { - return handle_catalog_v2_entries(request); + return handle_catalog_v2_entries(request, /*partial=*/false); + } else if (url == "partial_entries") { + return handle_catalog_v2_entries(request, /*partial=*/true); } else if (url == "categories") { return handle_catalog_v2_categories(request); } else if (url == "languages") { @@ -86,13 +88,13 @@ std::unique_ptr InternalServer::handle_catalog_v2_root(const RequestCo ); } -std::unique_ptr InternalServer::handle_catalog_v2_entries(const RequestContext& request) +std::unique_ptr InternalServer::handle_catalog_v2_entries(const RequestContext& request, bool partial) { OPDSDumper opdsDumper(mp_library); opdsDumper.setRootLocation(m_root); opdsDumper.setLibraryId(m_library_id); const auto bookIds = search_catalog(request, opdsDumper); - const auto opdsFeed = opdsDumper.dumpOPDSFeedV2(bookIds, request.get_query()); + const auto opdsFeed = opdsDumper.dumpOPDSFeedV2(bookIds, request.get_query(), partial); return ContentResponse::build( *this, opdsFeed, diff --git a/static/templates/catalog_v2_entries.xml b/static/templates/catalog_v2_entries.xml index 451e3b342..36e59a850 100644 --- a/static/templates/catalog_v2_entries.xml +++ b/static/templates/catalog_v2_entries.xml @@ -5,7 +5,7 @@ {{feed_id}} urn:uuid:{{id}} {{title}} {{updated}} - {{description}} +{{#dump_partial_entries}} + +{{/dump_partial_entries}}{{^dump_partial_entries}} {{description}} {{language}} {{name}} {{flavour}} @@ -49,6 +53,7 @@ {{#url}} {{/url}} +{{/dump_partial_entries}} {{/books}} diff --git a/test/server.cpp b/test/server.cpp index eebae7338..34ff1b6b8 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -1075,7 +1075,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages) EXPECT_EQ(maskVariableOPDSFeedData(r->body), expected_output); } -#define CATALOG_V2_ENTRIES_PREAMBLE(q) \ +#define CATALOG_V2_ENTRIES_PREAMBLE0(x) \ "\n" \ "12345678-90ab-cdef-1234-567890abcdef\n" \ "\n" \ " \n" \ " \n" \ "\n" \ +#define CATALOG_V2_ENTRIES_PREAMBLE(q) \ + CATALOG_V2_ENTRIES_PREAMBLE0("entries" q) + +#define CATALOG_V2_PARTIAL_ENTRIES_PREAMBLE(q) \ + CATALOG_V2_ENTRIES_PREAMBLE0("partial_entries" q) TEST_F(LibraryServerTest, catalog_v2_entries) { @@ -1253,3 +1258,40 @@ TEST_F(LibraryServerTest, catalog_v2_individual_entry_access) const auto r1 = zfs1_->GET("/catalog/v2/entry/non-existent-entry"); EXPECT_EQ(r1->status, 404); } + +TEST_F(LibraryServerTest, catalog_v2_partial_entries) +{ + const auto r = zfs1_->GET("/catalog/v2/partial_entries"); + EXPECT_EQ(r->status, 200); + EXPECT_EQ(maskVariableOPDSFeedData(r->body), + CATALOG_V2_PARTIAL_ENTRIES_PREAMBLE("") + " All Entries\n" + " YYYY-MM-DDThh:mm:ssZ\n" + "\n" + " \n" + " urn:uuid:charlesray\n" + " Charles, Ray\n" + " YYYY-MM-DDThh:mm:ssZ\n" + " \n" + " \n" + " \n" + " urn:uuid:raycharles\n" + " Ray Charles\n" + " YYYY-MM-DDThh:mm:ssZ\n" + " \n" + " \n" + " \n" + " urn:uuid:raycharles_uncategorized\n" + " Ray (uncategorized) Charles\n" + " YYYY-MM-DDThh:mm:ssZ\n" + " \n" + " \n" + "\n" + ); +} From b3f7556096ea235be1b127e49504e67b49dfda9f Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 1 Sep 2021 22:16:29 +0400 Subject: [PATCH 6/7] Added partial entries feed to the OPDS root feed --- src/server/internalServer_catalog_v2.cpp | 1 + static/templates/catalog_v2_root.xml | 9 +++++++++ test/server.cpp | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/src/server/internalServer_catalog_v2.cpp b/src/server/internalServer_catalog_v2.cpp index 88a3f64ef..7e49f76ea 100644 --- a/src/server/internalServer_catalog_v2.cpp +++ b/src/server/internalServer_catalog_v2.cpp @@ -81,6 +81,7 @@ std::unique_ptr InternalServer::handle_catalog_v2_root(const RequestCo {"endpoint_root", m_root + "/catalog/v2"}, {"feed_id", gen_uuid(m_library_id)}, {"all_entries_feed_id", gen_uuid(m_library_id + "/entries")}, + {"partial_entries_feed_id", gen_uuid(m_library_id + "/partial_entries")}, {"category_list_feed_id", gen_uuid(m_library_id + "/categories")}, {"language_list_feed_id", gen_uuid(m_library_id + "/languages")} }, diff --git a/static/templates/catalog_v2_root.xml b/static/templates/catalog_v2_root.xml index 9aec1cbad..b10c8d836 100644 --- a/static/templates/catalog_v2_root.xml +++ b/static/templates/catalog_v2_root.xml @@ -23,6 +23,15 @@ {{all_entries_feed_id}} All entries from this catalog. + + All entries (partial) + + {{date}} + {{partial_entries_feed_id}} + All entries from this catalog in partial format. + List of categories 12345678-90ab-cdef-1234-567890abcdef All entries from this catalog. + + All entries (partial) + + YYYY-MM-DDThh:mm:ssZ + 12345678-90ab-cdef-1234-567890abcdef + All entries from this catalog in partial format. + List of categories Date: Tue, 7 Sep 2021 22:22:49 +0400 Subject: [PATCH 7/7] Removed duplication across two mustache templates Deduplicated the mustache templates static/templates/catalog_v2_entries.xml and static/templates/catalog_v2_complete_entry.xml (the latter was renamed to static/templates/catalog_v2_entry.xml). --- src/opds_dumper.cpp | 36 +++++++++++------- static/resources_list.txt | 2 +- static/templates/catalog_entries.xml | 32 +--------------- static/templates/catalog_v2_entries.xml | 37 +------------------ ...omplete_entry.xml => catalog_v2_entry.xml} | 11 ++++-- test/server.cpp | 1 - 6 files changed, 34 insertions(+), 85 deletions(-) rename static/templates/{catalog_v2_complete_entry.xml => catalog_v2_entry.xml} (72%) diff --git a/src/opds_dumper.cpp b/src/opds_dumper.cpp index ddf2e83de..6897b3142 100644 --- a/src/opds_dumper.cpp +++ b/src/opds_dumper.cpp @@ -51,7 +51,7 @@ namespace { typedef kainjow::mustache::data MustacheData; -typedef kainjow::mustache::list BookData; +typedef kainjow::mustache::list BooksData; typedef kainjow::mustache::list IllustrationInfo; IllustrationInfo getBookIllustrationInfo(const Book& book) @@ -95,15 +95,26 @@ kainjow::mustache::object getSingleBookData(const Book& book) }; } -BookData getBookData(const Library* library, const std::vector& bookIds) +std::string getSingleBookEntryXML(const Book& book, bool withXMLHeader, const std::string& endpointRoot, bool partial) { - BookData bookData; + auto data = getSingleBookData(book); + data["with_xml_header"] = MustacheData(withXMLHeader); + data["dump_partial_entries"] = MustacheData(partial); + data["endpoint_root"] = endpointRoot; + return render_template(RESOURCE::templates::catalog_v2_entry_xml, data); +} + +BooksData getBooksData(const Library* library, const std::vector& bookIds, const std::string& endpointRoot, bool partial) +{ + BooksData booksData; for ( const auto& bookId : bookIds ) { const Book& book = library->getBookById(bookId); - bookData.push_back(getSingleBookData(book)); + booksData.push_back(kainjow::mustache::object{ + {"entry", getSingleBookEntryXML(book, false, endpointRoot, partial)} + }); } - return bookData; + return booksData; } std::string getLanguageSelfName(const std::string& lang) { @@ -119,7 +130,7 @@ std::string getLanguageSelfName(const std::string& lang) { string OPDSDumper::dumpOPDSFeed(const std::vector& bookIds, const std::string& query) const { - const auto bookData = getBookData(library, bookIds); + const auto booksData = getBooksData(library, bookIds, "", false); const kainjow::mustache::object template_data{ {"date", gen_date_str()}, {"root", rootLocation}, @@ -128,7 +139,7 @@ string OPDSDumper::dumpOPDSFeed(const std::vector& bookIds, const s {"totalResults", to_string(m_totalResults)}, {"startIndex", to_string(m_startIndex)}, {"itemsPerPage", to_string(m_count)}, - {"books", bookData } + {"books", booksData } }; return render_template(RESOURCE::templates::catalog_entries_xml, template_data); @@ -136,19 +147,20 @@ string OPDSDumper::dumpOPDSFeed(const std::vector& bookIds, const s string OPDSDumper::dumpOPDSFeedV2(const std::vector& bookIds, const std::string& query, bool partial) const { - const auto bookData = getBookData(library, bookIds); + const auto endpointRoot = rootLocation + "/catalog/v2"; + const auto booksData = getBooksData(library, bookIds, endpointRoot, partial); const char* const endpoint = partial ? "/partial_entries" : "/entries"; const kainjow::mustache::object template_data{ {"date", gen_date_str()}, - {"endpoint_root", rootLocation + "/catalog/v2"}, + {"endpoint_root", endpointRoot}, {"feed_id", gen_uuid(libraryId + endpoint + "?" + query)}, {"filter", query.empty() ? MustacheData(false) : MustacheData(query)}, {"query", query.empty() ? "" : "?" + urlEncode(query)}, {"totalResults", to_string(m_totalResults)}, {"startIndex", to_string(m_startIndex)}, {"itemsPerPage", to_string(m_count)}, - {"books", bookData }, + {"books", booksData }, {"dump_partial_entries", MustacheData(partial)} }; @@ -157,9 +169,7 @@ string OPDSDumper::dumpOPDSFeedV2(const std::vector& bookIds, const std::string OPDSDumper::dumpOPDSCompleteEntry(const std::string& bookId) const { - const auto bookData = getSingleBookData(library->getBookById(bookId)); - - return render_template(RESOURCE::templates::catalog_v2_complete_entry_xml, bookData); + return getSingleBookEntryXML(library->getBookById(bookId), true, "", false); } std::string OPDSDumper::categoriesOPDSFeed() const diff --git a/static/resources_list.txt b/static/resources_list.txt index 9ace25b1b..eb65b9dbe 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -45,7 +45,7 @@ templates/captured_external.html templates/catalog_entries.xml templates/catalog_v2_root.xml templates/catalog_v2_entries.xml -templates/catalog_v2_complete_entry.xml +templates/catalog_v2_entry.xml templates/catalog_v2_categories.xml templates/catalog_v2_languages.xml opensearchdescription.xml diff --git a/static/templates/catalog_entries.xml b/static/templates/catalog_entries.xml index 144f68bf8..40cbfccd7 100644 --- a/static/templates/catalog_entries.xml +++ b/static/templates/catalog_entries.xml @@ -9,34 +9,4 @@ {{/filter}} - {{#books}} - - urn:uuid:{{id}} - {{title}} - {{updated}} - {{description}} - {{language}} - {{name}} - {{flavour}} - {{category}} - {{tags}} - {{article_count}} - {{media_count}} - {{#icons}} - - {{/icons}} - - - {{author_name}} - - - {{publisher_name}} - - {{#url}} - - {{/url}} - - {{/books}} - +{{#books}}{{{entry}}}{{/books}} diff --git a/static/templates/catalog_v2_entries.xml b/static/templates/catalog_v2_entries.xml index 36e59a850..2df9523d8 100644 --- a/static/templates/catalog_v2_entries.xml +++ b/static/templates/catalog_v2_entries.xml @@ -21,39 +21,4 @@ {{startIndex}} {{itemsPerPage}} {{/filter}} - {{#books}} - - urn:uuid:{{id}} - {{title}} - {{updated}} -{{#dump_partial_entries}} - -{{/dump_partial_entries}}{{^dump_partial_entries}} {{description}} - {{language}} - {{name}} - {{flavour}} - {{category}} - {{tags}} - {{article_count}} - {{media_count}} - {{#icons}} - - {{/icons}} - - - {{author_name}} - - - {{publisher_name}} - - {{#url}} - - {{/url}} -{{/dump_partial_entries}} - - {{/books}} - +{{#books}}{{{entry}}}{{/books}} diff --git a/static/templates/catalog_v2_complete_entry.xml b/static/templates/catalog_v2_entry.xml similarity index 72% rename from static/templates/catalog_v2_complete_entry.xml rename to static/templates/catalog_v2_entry.xml index 76335a3f6..33abd56d6 100644 --- a/static/templates/catalog_v2_complete_entry.xml +++ b/static/templates/catalog_v2_entry.xml @@ -1,9 +1,13 @@ - - +{{#with_xml_header}} +{{/with_xml_header}} urn:uuid:{{id}} {{title}} {{updated}} - {{description}} +{{#dump_partial_entries}} + +{{/dump_partial_entries}}{{^dump_partial_entries}} {{description}} {{language}} {{name}} {{flavour}} @@ -26,4 +30,5 @@ {{#url}} {{/url}} +{{/dump_partial_entries}} diff --git a/test/server.cpp b/test/server.cpp index ad5438531..85f553ce0 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -905,7 +905,6 @@ TEST_F(LibraryServerTest, catalog_search_results_pagination) " 100\n" " 0\n" CATALOG_LINK_TAGS - " \n" "\n" ); }