diff --git a/lib/REST/Miniflux/V1.php b/lib/REST/Miniflux/V1.php index be093d48..47decbfa 100644 --- a/lib/REST/Miniflux/V1.php +++ b/lib/REST/Miniflux/V1.php @@ -1032,6 +1032,21 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { } return ['total' => $count, 'entries' => $out]; } + + protected function findEntry(int $id, Context $c = null): array { + $c = ($c ?? new Context)->article($id); + $tr = Arsse::$db->begin(); + $meta = $this->userMeta(Arsse::$user->id); + // find the entry we want + $entry = Arsse::$db->articleList(Arsse::$user->id, $c, self::ARTICLE_COLUMNS)->getRow(); + if (!$entry) { + throw new ExceptionInput("idMissing"); + } + $out = $this->transformEntry($entry, $meta['num'], $meta['tz']); + // next transform the parent feed of the entry + $out['feed'] = $this->transformFeed(Arsse::$db->subscriptionPropertiesGet(Arsse::$user->id, $out['feed_id']), $meta['num'], $meta['root'], $meta['tz']); + return $out; + } protected function getEntries(array $query): ResponseInterface { try { @@ -1059,6 +1074,37 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { return new ErrorResponse("404", 404); } } + + protected function getEntry(array $path): ResponseInterface { + try { + return new Response($this->findEntry((int) $path[1])); + } catch (ExceptionInput $e) { + return new ErrorResponse("404", 404); + } + } + + protected function getFeedEntry(array $path): ResponseInterface { + $c = (new Context)->subscription((int) $path[1]); + try { + return new Response($this->findEntry((int) $path[3], $c)); + } catch (ExceptionInput $e) { + return new ErrorResponse("404", 404); + } + } + + protected function getCategoryEntry(array $path): ResponseInterface { + $c = new Context; + if ($path[1] === "1") { + $c->folderShallow(0); + } else { + $c->folder((int) $path[1] - 1); + } + try { + return new Response($this->findEntry((int) $path[3], $c)); + } catch (ExceptionInput $e) { + return new ErrorResponse("404", 404); + } + } public static function tokenGenerate(string $user, string $label): string { // Miniflux produces tokenss in base64url alphabet diff --git a/tests/cases/REST/Miniflux/TestV1.php b/tests/cases/REST/Miniflux/TestV1.php index 02387dc1..a2c6aa82 100644 --- a/tests/cases/REST/Miniflux/TestV1.php +++ b/tests/cases/REST/Miniflux/TestV1.php @@ -721,7 +721,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { } /** @dataProvider provideEntryQueries */ - public function testGetEntries(string $url, ?Context $c, ?array $order, $out, bool $count, ResponseInterface $exp) { + public function testGetEntries(string $url, ?Context $c, ?array $order, $out, bool $count, ResponseInterface $exp): void { \Phake::when(Arsse::$db)->subscriptionList->thenReturn(new Result($this->v(self::FEEDS))); \Phake::when(Arsse::$db)->articleCount->thenReturn(2112); if ($out instanceof \Exception) { @@ -808,4 +808,42 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { ["/categories/2112/entries", (clone $c)->folder(2111), $o, new ExceptionInput("idMissing"), false, new ErrorResponse("404", 404)], ]; } + + /** @dataProvider provideSingleEntryQueries */ + public function testGetASingleEntry(string $url, Context $c, $out, ResponseInterface $exp): void { + \Phake::when(Arsse::$db)->subscriptionPropertiesGet->thenReturn($this->v(self::FEEDS[1])); + if ($out instanceof \Exception) { + \Phake::when(Arsse::$db)->articleList->thenThrow($out); + } else { + \Phake::when(Arsse::$db)->articleList->thenReturn(new Result($this->v($out))); + } + $this->assertMessage($exp, $this->req("GET", $url)); + if ($c) { + \Phake::verify(Arsse::$db)->articleList(Arsse::$user->id, $c, array_keys(self::ENTRIES[0])); + } else { + \Phake::verify(Arsse::$db, \Phake::times(0))->articleList; + } + if ($out && is_array($out)) { + \Phake::verify(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 55); + } else { + \Phake::verify(Arsse::$db, \Phake::times(0))->subscriptionList; + } + } + + public function provideSingleEntryQueries(): iterable { + $c = new Context; + return [ + ["/entries/42", (clone $c)->article(42), [self::ENTRIES[1]], new Response(self::ENTRIES_OUT[1])], + ["/entries/2112", (clone $c)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)], + ["/feeds/47/entries/42", (clone $c)->subscription(47)->article(42), [self::ENTRIES[1]], new Response(self::ENTRIES_OUT[1])], + ["/feeds/47/entries/44", (clone $c)->subscription(47)->article(44), [], new ErrorResponse("404", 404)], + ["/feeds/47/entries/2112", (clone $c)->subscription(47)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)], + ["/feeds/2112/entries/47", (clone $c)->subscription(2112)->article(47), new ExceptionInput("idMissing"), new ErrorResponse("404", 404)], + ["/categories/47/entries/42", (clone $c)->folder(46)->article(42), [self::ENTRIES[1]], new Response(self::ENTRIES_OUT[1])], + ["/categories/47/entries/44", (clone $c)->folder(46)->article(44), [], new ErrorResponse("404", 404)], + ["/categories/47/entries/2112", (clone $c)->folder(46)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)], + ["/categories/2112/entries/47", (clone $c)->folder(2111)->article(47), new ExceptionInput("idMissing"), new ErrorResponse("404", 404)], + ["/categories/1/entries/42", (clone $c)->folderShallow(0)->article(42), [self::ENTRIES[1]], new Response(self::ENTRIES_OUT[1])], + ]; + } }