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); +}