mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Finished last of feedUpdate tests; implemented NCN API functions
- Fixes #26 - Fixes #27
This commit is contained in:
parent
0514321a76
commit
dd24246f49
4 changed files with 104 additions and 16 deletions
|
@ -479,7 +479,7 @@ class Database {
|
|||
public function feedUpdate(int $feedID, bool $throwError = false): bool {
|
||||
$tr = $this->db->begin();
|
||||
// check to make sure the feed exists
|
||||
$f = $this->db->prepare('SELECT url, username, password, DATEFORMAT("http", modified) AS lastmodified, etag, err_count FROM arsse_feeds where id is ?', "int")->run($feedID)->getRow();
|
||||
$f = $this->db->prepare("SELECT url, username, password, DATEFORMAT('http', modified) AS lastmodified, etag, err_count FROM arsse_feeds where id is ?", "int")->run($feedID)->getRow();
|
||||
if(!$f) throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $feedID]);
|
||||
// the Feed object throws an exception when there are problems, but that isn't ideal
|
||||
// here. When an exception is thrown it should update the database with the
|
||||
|
@ -488,7 +488,7 @@ class Database {
|
|||
$feed = new Feed($feedID, $f['url'], (string)$f['lastmodified'], $f['etag'], $f['username'], $f['password']);
|
||||
if(!$feed->modified) {
|
||||
// if the feed hasn't changed, just compute the next fetch time and record it
|
||||
$this->db->prepare('UPDATE arsse_feeds SET updated = CURRENT_TIMESTAMP, next_fetch = ? WHERE id is ?', 'datetime', 'int')->run($feed->nextFetch, $feedID);
|
||||
$this->db->prepare("UPDATE arsse_feeds SET updated = CURRENT_TIMESTAMP, next_fetch = ? WHERE id is ?", 'datetime', 'int')->run($feed->nextFetch, $feedID);
|
||||
$tr->commit();
|
||||
return false;
|
||||
}
|
||||
|
@ -510,16 +510,16 @@ class Database {
|
|||
}
|
||||
if(sizeof($feed->newItems)) {
|
||||
$qInsertArticle = $this->db->prepare(
|
||||
'INSERT INTO arsse_articles(url,title,author,published,edited,guid,content,url_title_hash,url_content_hash,title_content_hash,feed) values(?,?,?,?,?,?,?,?,?,?,?)',
|
||||
"INSERT INTO arsse_articles(url,title,author,published,edited,guid,content,url_title_hash,url_content_hash,title_content_hash,feed) values(?,?,?,?,?,?,?,?,?,?,?)",
|
||||
'str', 'str', 'str', 'datetime', 'datetime', 'str', 'str', 'str', 'str', 'str', 'int'
|
||||
);
|
||||
}
|
||||
if(sizeof($feed->changedItems)) {
|
||||
$qDeleteEnclosures = $this->db->prepare('DELETE FROM arsse_enclosures WHERE article is ?', 'int');
|
||||
$qDeleteCategories = $this->db->prepare('DELETE FROM arsse_categories WHERE article is ?', 'int');
|
||||
$qClearReadMarks = $this->db->prepare('UPDATE arsse_marks SET read = 0, modified = CURRENT_TIMESTAMP WHERE article is ? and read is 1', 'int');
|
||||
$qDeleteEnclosures = $this->db->prepare("DELETE FROM arsse_enclosures WHERE article is ?", 'int');
|
||||
$qDeleteCategories = $this->db->prepare("DELETE FROM arsse_categories WHERE article is ?", 'int');
|
||||
$qClearReadMarks = $this->db->prepare("UPDATE arsse_marks SET read = 0, modified = CURRENT_TIMESTAMP WHERE article is ? and read is 1", 'int');
|
||||
$qUpdateArticle = $this->db->prepare(
|
||||
'UPDATE arsse_articles SET url = ?, title = ?, author = ?, published = ?, edited = ?, modified = CURRENT_TIMESTAMP, guid = ?, content = ?, url_title_hash = ?, url_content_hash = ?, title_content_hash = ? WHERE id is ?',
|
||||
"UPDATE arsse_articles SET url = ?, title = ?, author = ?, published = ?, edited = ?, modified = CURRENT_TIMESTAMP, guid = ?, content = ?, url_title_hash = ?, url_content_hash = ?, title_content_hash = ? WHERE id is ?",
|
||||
'str', 'str', 'str', 'datetime', 'datetime', 'str', 'str', 'str', 'str', 'str', 'int'
|
||||
);
|
||||
}
|
||||
|
@ -573,7 +573,7 @@ class Database {
|
|||
}
|
||||
// lastly update the feed database itself with updated information.
|
||||
$this->db->prepare(
|
||||
'UPDATE arsse_feeds SET url = ?, title = ?, favicon = ?, source = ?, updated = CURRENT_TIMESTAMP, modified = ?, etag = ?, err_count = 0, err_msg = "", next_fetch = ? WHERE id is ?',
|
||||
"UPDATE arsse_feeds SET url = ?, title = ?, favicon = ?, source = ?, updated = CURRENT_TIMESTAMP, modified = ?, etag = ?, err_count = 0, err_msg = '', next_fetch = ? WHERE id is ?",
|
||||
'str', 'str', 'str', 'str', 'datetime', 'str', 'datetime', 'int'
|
||||
)->run(
|
||||
$feed->data->feedUrl,
|
||||
|
@ -591,21 +591,20 @@ class Database {
|
|||
|
||||
public function feedMatchLatest(int $feedID, int $count): Db\Result {
|
||||
return $this->db->prepare(
|
||||
'SELECT id, DATEFORMAT("unix", edited) AS edited_date, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed is ? ORDER BY modified desc, id desc limit ?',
|
||||
"SELECT id, DATEFORMAT('unix', edited) AS edited_date, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed is ? ORDER BY modified desc, id desc limit ?",
|
||||
'int', 'int'
|
||||
)->run($feedID, $count);
|
||||
}
|
||||
|
||||
public function feedMatchIds(int $feedID, array $ids = [], array $hashesUT = [], array $hashesUC = [], array $hashesTC = []): Db\Result {
|
||||
// compile SQL IN() clauses and necessary type bindings for the four identifier lists
|
||||
list($cId, $tId) = $this->generateIn($ids, "str");
|
||||
list($cId, $tId) = $this->generateIn($ids, "str");
|
||||
list($cHashUT, $tHashUT) = $this->generateIn($hashesUT, "str");
|
||||
list($cHashUC, $tHashUC) = $this->generateIn($hashesUC, "str");
|
||||
list($cHashTC, $tHashTC) = $this->generateIn($hashesTC, "str");
|
||||
// perform the query
|
||||
return $articles = $this->db->prepare(
|
||||
'SELECT id, DATEFORMAT("unix", edited) AS edited_date, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles '.
|
||||
'WHERE feed is ? and (guid in($cId) or url_title_hash in($cHashUT) or url_content_hash in($cHashUC) or title_content_hash in($cHashTC))',
|
||||
"SELECT id, DATEFORMAT('unix', edited) AS edited_date, guid, url_title_hash, url_content_hash, title_content_hash FROM arsse_articles WHERE feed is ? and (guid in($cId) or url_title_hash in($cHashUT) or url_content_hash in($cHashUC) or title_content_hash in($cHashTC))",
|
||||
'int', $tId, $tHashUT, $tHashUC, $tHashTC
|
||||
)->run($feedID, $ids, $hashesUT, $hashesUC, $hashesTC);
|
||||
}
|
||||
|
|
|
@ -11,12 +11,14 @@ use JKingWeb\Arsse\REST\Exception501;
|
|||
use JKingWeb\Arsse\REST\Exception405;
|
||||
|
||||
class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||
const REALM = "NextCloud News API v1-2";
|
||||
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
|
||||
// try to authenticate
|
||||
if(!Data::$user->authHTTP()) return new Response(401, "", "", ['WWW-Authenticate: Basic realm="NextCloud News API v1-2"']);
|
||||
if(!Data::$user->authHTTP()) return new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.self::REALM.'"']);
|
||||
// only accept GET, POST, PUT, or DELETE
|
||||
if(!in_array($req->method, ["GET", "POST", "PUT", "DELETE"])) return new Response(405, "", "", ['Allow: GET, POST, PUT, DELETE']);
|
||||
// normalize the input
|
||||
|
@ -229,6 +231,8 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
|||
|
||||
// refresh a feed
|
||||
protected function feedUpdate(array $url, array $data): Response {
|
||||
// function requires admin rights per spec
|
||||
if(Data::$user->rightsGet(Data::$user->id)==User::RIGHTS_NONE) return new Response(403);
|
||||
// perform an update of a single feed
|
||||
if(!array_key_exists("feedId", $data)) return new Response(422);
|
||||
if(!$this->validateId($data['feedId'])) return new Response(404);
|
||||
|
@ -237,7 +241,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
|||
} catch(ExceptionInput $e) {
|
||||
return new Response(404);
|
||||
}
|
||||
return new Response(200);
|
||||
return new Response(204);
|
||||
}
|
||||
|
||||
// add a new feed
|
||||
|
|
|
@ -81,6 +81,7 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
|
|||
// create a mock user manager
|
||||
Data::$user = Phake::mock(User::class);
|
||||
Phake::when(Data::$user)->authHTTP->thenReturn(true);
|
||||
Phake::when(Data::$user)->rightsGet->thenReturn(100);
|
||||
Data::$user->id = "john.doe@example.com";
|
||||
// create a mock database interface
|
||||
Data::$db = Phake::mock(Database::Class);
|
||||
|
@ -137,6 +138,12 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
function testReceiveAuthenticationChallenge() {
|
||||
Phake::when(Data::$user)->authHTTP->thenReturn(false);
|
||||
$exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.REST\NextCloudNews\V1_2::REALM.'"']);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/")));
|
||||
}
|
||||
|
||||
function testListFolders() {
|
||||
$list = [
|
||||
['id' => 1, 'name' => "Software", 'parent' => null],
|
||||
|
@ -348,4 +355,47 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
|
|||
$exp = new Response(404);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/rename", json_encode($in[4]), 'application/json')));
|
||||
}
|
||||
|
||||
function testListStaleFeeds() {
|
||||
$out = [
|
||||
[
|
||||
'id' => 42,
|
||||
'userId' => "",
|
||||
],
|
||||
[
|
||||
'id' => 2112,
|
||||
'userId' => "",
|
||||
],
|
||||
];
|
||||
Phake::when(Data::$db)->feedListStale->thenReturn(array_column($out,"id"));
|
||||
$exp = new Response(200, ['feeds' => $out]);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/all")));
|
||||
// retrieving the list when not an admin fails
|
||||
Phake::when(Data::$user)->rightsGet->thenReturn(0);
|
||||
$exp = new Response(403);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/all")));
|
||||
}
|
||||
|
||||
function testUpdateAFeed() {
|
||||
$in = [
|
||||
['feedId' => 42], // valid
|
||||
['feedId' => 2112], // feed does not exist
|
||||
['feedId' => "ook"], // invalid ID
|
||||
['feed' => 42], // invalid input
|
||||
];
|
||||
Phake::when(Data::$db)->feedUpdate( 42)->thenReturn(true);
|
||||
Phake::when(Data::$db)->feedUpdate(2112)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing"));
|
||||
$exp = new Response(204);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
|
||||
$exp = new Response(404);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[1]), 'application/json')));
|
||||
$exp = new Response(404);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[2]), 'application/json')));
|
||||
$exp = new Response(422);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[3]), 'application/json')));
|
||||
// retrieving the list when not an admin fails
|
||||
Phake::when(Data::$user)->rightsGet->thenReturn(0);
|
||||
$exp = new Response(403);
|
||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
|
||||
}
|
||||
}
|
|
@ -9,6 +9,25 @@ use JKingWeb\Arsse\Feed\Exception as FeedException;
|
|||
use Phake;
|
||||
|
||||
trait SeriesFeed {
|
||||
protected $matches = [
|
||||
[
|
||||
'id' => 4,
|
||||
'edited_date' => 946944000, // 2000-01-04T00:00:00Z
|
||||
'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180',
|
||||
'url_title_hash' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8',
|
||||
'url_content_hash' => 'f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3',
|
||||
'title_content_hash' => 'ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9',
|
||||
],
|
||||
[
|
||||
'id' => 5,
|
||||
'edited_date' => 947030400, // 2000-01-05T00:00:00Z
|
||||
'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41',
|
||||
'url_title_hash' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022',
|
||||
'url_content_hash' => '834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900',
|
||||
'title_content_hash' => '43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba',
|
||||
],
|
||||
];
|
||||
|
||||
function setUpSeries() {
|
||||
$past = gmdate("Y-m-d H:i:s",strtotime("now - 1 minute"));
|
||||
$future = gmdate("Y-m-d H:i:s",strtotime("now + 1 minute"));
|
||||
|
@ -54,6 +73,7 @@ trait SeriesFeed {
|
|||
[3,1,'http://example.com/3','Article title 3','','2000-01-03 00:00:00','2000-01-03 00:00:00','<p>Article content 3</p>','31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92','f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b','b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406','ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b',$past],
|
||||
[4,1,'http://example.com/4','Article title 4','','2000-01-04 00:00:00','2000-01-04 00:00:00','<p>Article content 4</p>','804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180','f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8','f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3','ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9',$past],
|
||||
[5,1,'http://example.com/5','Article title 5','','2000-01-05 00:00:00','2000-01-05 00:00:00','<p>Article content 5</p>','db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41','d40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022','834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900','43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba',$past],
|
||||
[6,2,'http://example.com/1','Article title 1','','2000-01-01 00:00:00','2000-01-01 00:00:00','<p>Article content 1</p>','e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda','f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6','fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4','18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207',$past],
|
||||
]
|
||||
],
|
||||
'arsse_editions' => [
|
||||
|
@ -112,6 +132,21 @@ trait SeriesFeed {
|
|||
$this->user = "john.doe@example.com";
|
||||
}
|
||||
|
||||
function testListLatestItems() {
|
||||
$this->assertResult($this->matches, Data::$db->feedMatchLatest(1,2));
|
||||
}
|
||||
|
||||
function testMatchItemsById() {
|
||||
$this->assertResult($this->matches, Data::$db->feedMatchIds(1, ['804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180','db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41']));
|
||||
foreach($this->matches as $m) {
|
||||
$exp = [$m];
|
||||
$this->assertResult($exp, Data::$db->feedMatchIds(1, [], [$m['url_title_hash']]));
|
||||
$this->assertResult($exp, Data::$db->feedMatchIds(1, [], [], [$m['url_content_hash']]));
|
||||
$this->assertResult($exp, Data::$db->feedMatchIds(1, [], [], [], [$m['title_content_hash']]));
|
||||
}
|
||||
$this->assertResult([['id' => 1]], Data::$db->feedMatchIds(1, ['e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda'])); // this ID appears in both feed 1 and feed 2; only one result should be returned
|
||||
}
|
||||
|
||||
function testUpdateAFeed() {
|
||||
// update a valid feed with both new and changed items
|
||||
Data::$db->feedUpdate(1);
|
||||
|
@ -123,9 +158,9 @@ trait SeriesFeed {
|
|||
]);
|
||||
$state['arsse_articles']['rows'][2] = [3,1,'http://example.com/3','Article title 3 (updated)','','2000-01-03 00:00:00','2000-01-03 00:00:00','<p>Article content 3</p>','31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92','6cc99be662ef3486fef35a890123f18d74c29a32d714802d743c5b4ef713315a','b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406','d5faccc13bf8267850a1e8e61f95950a0f34167df2c8c58011c0aaa6367026ac',$now];
|
||||
$state['arsse_articles']['rows'][3] = [4,1,'http://example.com/4','Article title 4','','2000-01-04 00:00:00','2000-01-04 00:00:01','<p>Article content 4</p>','804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180','f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8','f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3','ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9',$now];
|
||||
$state['arsse_articles']['rows'][5] = [6,1,'http://example.com/6','Article title 6','','2000-01-06 00:00:00','2000-01-06 00:00:00','<p>Article content 6</p>','b3461ab8e8759eeb1d65a818c65051ec00c1dfbbb32a3c8f6999434e3e3b76ab','91d051a8e6749d014506848acd45e959af50bf876427c4f0e3a1ec0f04777b51','211d78b1a040d40d17e747a363cc283f58767b2e502630d8de9b8f1d5e941d18','5ed68ccb64243b8c1931241d2c9276274c3b1d87f223634aa7a1ab0141292ca7',$now];
|
||||
$state['arsse_articles']['rows'][] = [7,1,'http://example.com/6','Article title 6','','2000-01-06 00:00:00','2000-01-06 00:00:00','<p>Article content 6</p>','b3461ab8e8759eeb1d65a818c65051ec00c1dfbbb32a3c8f6999434e3e3b76ab','91d051a8e6749d014506848acd45e959af50bf876427c4f0e3a1ec0f04777b51','211d78b1a040d40d17e747a363cc283f58767b2e502630d8de9b8f1d5e941d18','5ed68ccb64243b8c1931241d2c9276274c3b1d87f223634aa7a1ab0141292ca7',$now];
|
||||
$state['arsse_editions']['rows'] = array_merge($state['arsse_editions']['rows'], [
|
||||
[6,6,$now],
|
||||
[6,7,$now],
|
||||
[7,3,$now],
|
||||
[8,4,$now],
|
||||
]);
|
||||
|
|
Loading…
Reference in a new issue