mirror of https://github.com/kiwix/libkiwix.git
Serving /catalog/v2/entries
/catalog/v2/entries is intended to play the combined role of /catalog/root.xml and /catalog/search of the old OPDS API. Currently, the latter role is not yet implemented. Implementation note: instead of tweaking and reusing `OPDSDumper::dumpOPDSFeed()`, the generation of the OPDS feed is done via `mustache` and a new template `static/catalog_v2_entries.xml`.
This commit is contained in:
parent
92c2de8d46
commit
19b59fd72f
|
@ -723,6 +723,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2(const RequestContext
|
||||||
|
|
||||||
if (url == "root.xml") {
|
if (url == "root.xml") {
|
||||||
return handle_catalog_v2_root(request);
|
return handle_catalog_v2_root(request);
|
||||||
|
} else if (url == "entries") {
|
||||||
|
return handle_catalog_v2_entries(request);
|
||||||
} else if (url == "categories") {
|
} else if (url == "categories") {
|
||||||
return handle_catalog_v2_categories(request);
|
return handle_catalog_v2_categories(request);
|
||||||
} else {
|
} else {
|
||||||
|
@ -747,6 +749,50 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_root(const RequestCo
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Response> InternalServer::handle_catalog_v2_entries(const RequestContext& request)
|
||||||
|
{
|
||||||
|
const std::string root_url = normalizeRootUrl(m_root);
|
||||||
|
|
||||||
|
const auto now = gen_date_str();
|
||||||
|
kainjow::mustache::list bookData;
|
||||||
|
for ( const auto& bookId : mp_library->getBooksIds() ) {
|
||||||
|
const Book& book = mp_library->getBookById(bookId);
|
||||||
|
const MustacheData bookUrl = book.getUrl().empty()
|
||||||
|
? MustacheData(false)
|
||||||
|
: MustacheData(book.getUrl());
|
||||||
|
bookData.push_back(kainjow::mustache::object{
|
||||||
|
{"id", "urn:uuid:"+book.getId()},
|
||||||
|
{"name", book.getName()},
|
||||||
|
{"title", book.getTitle()},
|
||||||
|
{"description", book.getDescription()},
|
||||||
|
{"language", book.getLanguage()},
|
||||||
|
{"content_id", book.getHumanReadableIdFromPath()},
|
||||||
|
{"updated", book.getDate() + "T00:00:00Z"},
|
||||||
|
{"category", book.getCategory()},
|
||||||
|
{"flavour", book.getFlavour()},
|
||||||
|
{"tags", book.getTags()},
|
||||||
|
{"article_count", to_string(book.getArticleCount())},
|
||||||
|
{"media_count", to_string(book.getMediaCount())},
|
||||||
|
{"author_name", book.getCreator()},
|
||||||
|
{"publisher_name", book.getPublisher()},
|
||||||
|
{"url", bookUrl},
|
||||||
|
{"size", to_string(book.getSize())},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContentResponse::build(
|
||||||
|
*this,
|
||||||
|
RESOURCE::catalog_v2_entries_xml,
|
||||||
|
kainjow::mustache::object{
|
||||||
|
{"date", now},
|
||||||
|
{"endpoint_root", root_url + "/catalog/v2"},
|
||||||
|
{"feed_id", gen_uuid(m_library_id + "/entries")},
|
||||||
|
{"books", bookData }
|
||||||
|
},
|
||||||
|
"application/atom+xml;profile=opds-catalog;kind=acquisition"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Response> InternalServer::handle_catalog_v2_categories(const RequestContext& request)
|
std::unique_ptr<Response> InternalServer::handle_catalog_v2_categories(const RequestContext& request)
|
||||||
{
|
{
|
||||||
const std::string root_url = normalizeRootUrl(m_root);
|
const std::string root_url = normalizeRootUrl(m_root);
|
||||||
|
|
|
@ -75,6 +75,7 @@ class InternalServer {
|
||||||
std::unique_ptr<Response> handle_catalog(const RequestContext& request);
|
std::unique_ptr<Response> handle_catalog(const RequestContext& request);
|
||||||
std::unique_ptr<Response> handle_catalog_v2(const RequestContext& request);
|
std::unique_ptr<Response> handle_catalog_v2(const RequestContext& request);
|
||||||
std::unique_ptr<Response> handle_catalog_v2_root(const RequestContext& request);
|
std::unique_ptr<Response> handle_catalog_v2_root(const RequestContext& request);
|
||||||
|
std::unique_ptr<Response> handle_catalog_v2_entries(const RequestContext& request);
|
||||||
std::unique_ptr<Response> handle_catalog_v2_categories(const RequestContext& request);
|
std::unique_ptr<Response> handle_catalog_v2_categories(const RequestContext& request);
|
||||||
std::unique_ptr<Response> handle_meta(const RequestContext& request);
|
std::unique_ptr<Response> handle_meta(const RequestContext& request);
|
||||||
std::unique_ptr<Response> handle_search(const RequestContext& request);
|
std::unique_ptr<Response> handle_search(const RequestContext& request);
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom"
|
||||||
|
xmlns:opds="https://specs.opds.io/opds-1.2">
|
||||||
|
<id>{{feed_id}}</id>
|
||||||
|
|
||||||
|
<link rel="self"
|
||||||
|
href="{{endpoint_root}}/entries"
|
||||||
|
type="application/atom+xml;profile=opds-catalog;kind=acquisition"/>
|
||||||
|
<link rel="start"
|
||||||
|
href="{{endpoint_root}}/root.xml"
|
||||||
|
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
||||||
|
<link rel="up"
|
||||||
|
href="{{endpoint_root}}/root.xml"
|
||||||
|
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
||||||
|
|
||||||
|
<title>All Entries</title>
|
||||||
|
<updated>{{date}}</updated>
|
||||||
|
|
||||||
|
{{#books}}
|
||||||
|
<entry>
|
||||||
|
<id>{{id}}</id>
|
||||||
|
<title>{{title}}</title>
|
||||||
|
<summary>{{description}}</summary>
|
||||||
|
<language>{{language}}</language>
|
||||||
|
<updated>{{updated}}</updated>
|
||||||
|
<name>{{name}}</name>
|
||||||
|
<flavour>{{flavour}}</flavour>
|
||||||
|
<category>{{category}}</category>
|
||||||
|
<tags>{{tags}}</tags>
|
||||||
|
<articleCount>{{article_count}}</articleCount>
|
||||||
|
<mediaCount>{{media_count}}</mediaCount>
|
||||||
|
<icon>/meta?name=favicon&content={{{content_id}}}</icon>
|
||||||
|
<link type="text/html" href="/{{{content_id}}}" />
|
||||||
|
<author>
|
||||||
|
<name>{{author_name}}</name>
|
||||||
|
</author>
|
||||||
|
<publisher>
|
||||||
|
<name>{{publisher_name}}</name>
|
||||||
|
</publisher>
|
||||||
|
{{#url}}
|
||||||
|
<link rel="http://opds-spec.org/acquisition/open-access" type="application/x-zim" href="{{{url}}}" length="{{{size}}}" />
|
||||||
|
{{/url}}
|
||||||
|
</entry>
|
||||||
|
{{/books}}
|
||||||
|
</feed>
|
|
@ -38,4 +38,5 @@ templates/external_blocker_part.html
|
||||||
templates/captured_external.html
|
templates/captured_external.html
|
||||||
opensearchdescription.xml
|
opensearchdescription.xml
|
||||||
catalog_v2_root.xml
|
catalog_v2_root.xml
|
||||||
|
catalog_v2_entries.xml
|
||||||
catalog_v2_categories.xml
|
catalog_v2_categories.xml
|
||||||
|
|
|
@ -981,3 +981,33 @@ TEST_F(LibraryServerTest, catalog_v2_categories)
|
||||||
)";
|
)";
|
||||||
EXPECT_EQ(maskVariableOPDSFeedData(r->body), expected_output);
|
EXPECT_EQ(maskVariableOPDSFeedData(r->body), expected_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(LibraryServerTest, catalog_v2_entries)
|
||||||
|
{
|
||||||
|
const auto r = zfs1_->GET("/catalog/v2/entries");
|
||||||
|
EXPECT_EQ(r->status, 200);
|
||||||
|
EXPECT_EQ(maskVariableOPDSFeedData(r->body),
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
"<feed xmlns=\"http://www.w3.org/2005/Atom\"\n"
|
||||||
|
" xmlns:opds=\"https://specs.opds.io/opds-1.2\">\n"
|
||||||
|
" <id>12345678-90ab-cdef-1234-567890abcdef</id>\n"
|
||||||
|
"\n"
|
||||||
|
" <link rel=\"self\"\n"
|
||||||
|
" href=\"/catalog/v2/entries\"\n"
|
||||||
|
" type=\"application/atom+xml;profile=opds-catalog;kind=acquisition\"/>\n"
|
||||||
|
" <link rel=\"start\"\n"
|
||||||
|
" href=\"/catalog/v2/root.xml\"\n"
|
||||||
|
" type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>\n"
|
||||||
|
" <link rel=\"up\"\n"
|
||||||
|
" href=\"/catalog/v2/root.xml\"\n"
|
||||||
|
" type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>\n"
|
||||||
|
"\n"
|
||||||
|
" <title>All Entries</title>\n"
|
||||||
|
" <updated>YYYY-MM-DDThh:mm:ssZ</updated>\n"
|
||||||
|
"\n"
|
||||||
|
CHARLES_RAY_CATALOG_ENTRY
|
||||||
|
RAY_CHARLES_CATALOG_ENTRY
|
||||||
|
UNCATEGORIZED_RAY_CHARLES_CATALOG_ENTRY
|
||||||
|
"</feed>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue