diff --git a/lib/Database.php b/lib/Database.php index a7fea0e..ca98b3d 100644 --- a/lib/Database.php +++ b/lib/Database.php @@ -524,6 +524,24 @@ class Database { return $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues()); } + public function subscriptionCount(string $user, $folder = null): int { + if (!Arsse::$user->authorize($user, __FUNCTION__)) { + throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + } + // validate inputs + $folder = $this->folderValidateId($user, $folder)['id']; + // create a complex query + $q = new Query("SELECT count(*) from arsse_subscriptions"); + $q->setWhere("owner is ?", "str", $user); + if ($folder) { + // if it does exist, add a common table expression to list it and its children so that we select from the entire subtree + $q->setCTE("folders(folder)", "SELECT ? union select id from arsse_folders join folders on parent is folder", "int", $folder); + // add a suitable WHERE condition + $q->setWhere("folder in (select folder from folders)"); + } + return $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getValue(); + } + public function subscriptionRemove(string $user, $id): bool { if (!Arsse::$user->authorize($user, __FUNCTION__)) { throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); diff --git a/lib/REST/TinyTinyRSS/API.php b/lib/REST/TinyTinyRSS/API.php index 43c8f2c..6e1a71d 100644 --- a/lib/REST/TinyTinyRSS/API.php +++ b/lib/REST/TinyTinyRSS/API.php @@ -361,4 +361,22 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { } return null; } + + public function opGetUnread(array $data): array { + // simply sum the unread count of each subscription + $out = 0; + foreach (Arsse::$db->subscriptionList(Arsse::$user->id) as $sub) { + $out += $sub['unread']; + } + return ['unread' => $out]; + } + + public function opGetConfig(array $data): array { + return [ + 'icons_dir' => "feed-icons", + 'icons_url' => "feed-icons", + 'daemon_is_running' => Service::hasCheckedIn(), + 'num_feeds' => Arsse::$db->subscriptionCount(Arsse::$user->id), + ]; + } } diff --git a/tests/REST/TinyTinyRSS/TestTinyTinyAPI.php b/tests/REST/TinyTinyRSS/TestTinyTinyAPI.php index 3b4b152..467e359 100644 --- a/tests/REST/TinyTinyRSS/TestTinyTinyAPI.php +++ b/tests/REST/TinyTinyRSS/TestTinyTinyAPI.php @@ -583,4 +583,30 @@ class TestTinyTinyAPI extends Test\AbstractTest { $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in[8])))); Phake::verify(Arsse::$db, Phake::times(3))->subscriptionPropertiesSet(Arsse::$user->id, $this->anything(), $this->anything()); } + + public function testRetrieveTheGlobalUnreadCount() { + $in = ['op' => "getUnread", 'sid' => "PriestsOfSyrinx"]; + Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([ + ['id' => 1, 'unread' => 2112], + ['id' => 2, 'unread' => 42], + ['id' => 3, 'unread' => 47], + ])); + $exp = $this->respGood(['unread' => 2112 + 42 + 47]); + $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in)))); + } + + public function testRetrieveTheServerConfiguration () { + $in = ['op' => "getConfig", 'sid' => "PriestsOfSyrinx"]; + $interval = Service::interval(); + $valid = (new \DateTimeImmutable("now", new \DateTimezone("UTC")))->sub($interval); + $invalid = $valid->sub($interval)->sub($interval); + Phake::when(Arsse::$db)->metaGet("service_last_checkin")->thenReturn(Date::transform($valid, "sql"))->thenReturn(Date::transform($invalid, "sql")); + Phake::when(Arsse::$db)->subscriptionCount(Arsse::$user->id)->thenReturn(12)->thenReturn(2); + $exp = [ + ['icons_dir' => "feed-icons", 'icons_url' => "feed-icons", 'daemon_is_running' => true, 'num_feeds' => 12], + ['icons_dir' => "feed-icons", 'icons_url' => "feed-icons", 'daemon_is_running' => false, 'num_feeds' => 2], + ]; + $this->assertEquals($this->respGood($exp[0]), $this->h->dispatch(new Request("POST", "", json_encode($in)))); + $this->assertEquals($this->respGood($exp[1]), $this->h->dispatch(new Request("POST", "", json_encode($in)))); + } } diff --git a/tests/lib/Database/SeriesSubscription.php b/tests/lib/Database/SeriesSubscription.php index a40e509..4a1c841 100644 --- a/tests/lib/Database/SeriesSubscription.php +++ b/tests/lib/Database/SeriesSubscription.php @@ -282,6 +282,22 @@ trait SeriesSubscription { Arsse::$db->subscriptionList($this->user); } + public function testCountSubscriptions() { + $this->assertSame(2, Arsse::$db->subscriptionCount($this->user)); + $this->assertSame(1, Arsse::$db->subscriptionCount($this->user, 2)); + } + + public function testCountSubscriptionsInAMissingFolder() { + $this->assertException("idMissing", "Db", "ExceptionInput"); + Arsse::$db->subscriptionCount($this->user, 4); + } + + public function testCountSubscriptionsWithoutAuthority() { + Phake::when(Arsse::$user)->authorize->thenReturn(false); + $this->assertException("notAuthorized", "User", "ExceptionAuthz"); + Arsse::$db->subscriptionCount($this->user); + } + public function testGetThePropertiesOfAMissingSubscription() { $this->assertException("subjectMissing", "Db", "ExceptionInput"); Arsse::$db->subscriptionPropertiesGet($this->user, 2112);