From 3b8461b1ca4ba0e5cac8195493052cca94f92874 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Thu, 28 Feb 2019 16:22:04 -0500 Subject: [PATCH] Add searching to TTRSS handler --- CHANGELOG | 6 ++++++ README.md | 8 +++++++- lib/REST/TinyTinyRSS/API.php | 13 ++++++++++--- tests/cases/REST/TinyTinyRSS/TestAPI.php | 5 +++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 10832aae..af5159f3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +Version 0.7.0 (2019-??-??) +========================== + +New features: +- Support for basic freeform searching in Tiny Tiny RSS + Version 0.6.1 (2019-01-23) ========================== diff --git a/README.md b/README.md index 2cec044d..d4fca7f3 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,6 @@ We are not aware of any other extensions to the TTRSS protocol. If you know of a - The `getPref` operation is not implemented; it returns `UNKNOWN_METHOD` - The `shareToPublished` operation is not implemented; it returns `UNKNOWN_METHOD` - Setting an article's "published" flag with the `updateArticle` operation is not implemented and will gracefully fail -- The `search` parameter of the `getHeadlines` operation is not implemented; the operation will proceed as if no search string were specified - The `sanitize`, `force_update`, and `has_sandbox` parameters of the `getHeadlines` operation are ignored - String `feed_id` values for the `getCompactHeadlines` operation are not supported and will yield an `INCORRECT_USAGE` error - Articles are limited to a single attachment rather than multiple attachments @@ -141,6 +140,13 @@ We are not aware of any other extensions to the TTRSS protocol. If you know of a - Feed, category, and label names are normally unrestricted; The Arsse rejects empty strings, as well as strings composed solely of whitespace - Discovering multiple feeds during `subscribeToFeed` processing normally produces an error; The Arsse instead chooses the first feed it finds - Providing the `setArticleLabel` operation with an invalid label normally silently fails; The Arsse returns an `INVALID_USAGE` error instead +- Processing of the `search` parameter of the `getHeadlines` operation differs in the following ways: + - Values other than `"true"` or `"false"` for the `unread`, `star`, and `pub` special keywords treat the entire token as a search term rather than as `"false"` + - Limits are placed on the number of search terms: ten each for `title`, `author`, and `note`, and twenty for content searching; exceeding the limits will yield a non-standard `TOO_MANY_SEARCH_TERMS` error + - Invalid dates are ignored rather than assumed to be `"1970-01-01"` + - Only a single negative date is allowed (this is a known bug rather than intentional) + - Dates are always relative to UTC + - Full-text search is not yet employed with any database, including PostgreSQL - Article hashes are normally SHA1; The Arsse uses SHA256 hashes - Article attachments normally have unique IDs; The Arsse always gives attachments an ID of `"0"` - The default sort order of the `getHeadlines` operation normally uses custom sorting for "special" feeds; The Arsse's default sort order is equivalent to `feed_dates` for all feeds diff --git a/lib/REST/TinyTinyRSS/API.php b/lib/REST/TinyTinyRSS/API.php index f126324c..a3572ba3 100644 --- a/lib/REST/TinyTinyRSS/API.php +++ b/lib/REST/TinyTinyRSS/API.php @@ -49,7 +49,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { 'sid' => ValueInfo::T_STRING, // session ID 'seq' => ValueInfo::T_INT, // request number from client 'user' => ValueInfo::T_STRING | ValueInfo::M_STRICT, // user name for `login` - 'password' => ValueInfo::T_STRING | ValueInfo::M_STRICT, // password for `login` and `subscribeToFeed` + 'password' => ValueInfo::T_STRING | ValueInfo::M_STRICT, // password for `login` or remote password for `subscribeToFeed` 'include_empty' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to include empty items in `getFeedTree` and `getCategories` 'unread_only' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to exclude items without unread articles in `getCategories` and `getFeeds` 'enable_nested' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to NOT show subcategories in `getCategories @@ -76,7 +76,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { 'since_id' => ValueInfo::T_INT, // cut-off article ID for `getHeadlines` and `getCompactHeadlines; returns only higher article IDs when specified 'order_by' => ValueInfo::T_STRING, // sort order for `getHeadlines` 'include_header' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to attach a header to the results of `getHeadlines` - 'search' => ValueInfo::T_STRING, // search string for `getHeadlines` (not yet implemented) + 'search' => ValueInfo::T_STRING, // search string for `getHeadlines` 'field' => ValueInfo::T_INT, // which state to change in `updateArticle` 'mode' => ValueInfo::T_INT, // whether to set, clear, or toggle the selected state in `updateArticle` 'data' => ValueInfo::T_STRING, // note text in `updateArticle` if setting a note @@ -1478,7 +1478,14 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { default: throw new \JKingWeb\Arsse\Exception("constantUnknown", $viewMode); // @codeCoverageIgnore } - // TODO: implement searching + // handle the search string, if any + if (isset($data['search'])) { + $c = Search::parse($data['search'], $c); + if (!$c) { + // the search string inherently returns an empty result, either directly or interacting with other input + return new ResultEmpty; + } + } // handle sorting switch ($data['order_by']) { case "date_reverse": diff --git a/tests/cases/REST/TinyTinyRSS/TestAPI.php b/tests/cases/REST/TinyTinyRSS/TestAPI.php index 4b497a7f..91b370cf 100644 --- a/tests/cases/REST/TinyTinyRSS/TestAPI.php +++ b/tests/cases/REST/TinyTinyRSS/TestAPI.php @@ -1809,6 +1809,8 @@ LONG_STRING; ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'view_mode' => "published"], ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -6, 'view_mode' => "unread"], ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 2112], + ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'view_mode' => "unread", 'search' => "unread:false"], + ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'search' => "pub:true"], ]; $in2 = [ // simple context tests @@ -1833,6 +1835,7 @@ LONG_STRING; ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'is_cat' => true, 'include_nested' => true], ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'order_by' => "feed_dates"], ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'order_by' => "date_reverse"], + ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'search' => "interesting"], ]; $in3 = [ // time-based context tests @@ -1868,6 +1871,7 @@ LONG_STRING; Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->folderShallow(42), $this->anything())->thenReturn($this->generateHeadlines(14)); Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->folder(42), $this->anything())->thenReturn($this->generateHeadlines(15)); Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->reverse(false), $this->anything())->thenReturn($this->generateHeadlines(16)); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->searchTerms(["interesting"]), $this->anything())->thenReturn($this->generateHeadlines(17)); $out2 = [ $this->respErr("INCORRECT_USAGE"), $this->outputHeadlines(11), @@ -1890,6 +1894,7 @@ LONG_STRING; $this->outputHeadlines(15), $this->outputHeadlines(11), // defaulting sorting is not fully implemented $this->outputHeadlines(16), + $this->outputHeadlines(17), ]; $out3 = [ $this->outputHeadlines(1001),