mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Implement feed editing
This commit is contained in:
parent
5a8a044a92
commit
8eebb75b18
4 changed files with 86 additions and 2 deletions
|
@ -32,7 +32,7 @@ Miniflux version 2.0.27 is emulated, though not all features are implemented
|
||||||
- Various error codes and messages differ due to significant implementation differences
|
- Various error codes and messages differ due to significant implementation differences
|
||||||
- `PUT` requests which return a body respond with `200 OK` rather than `201 Created`
|
- `PUT` requests which return a body respond with `200 OK` rather than `201 Created`
|
||||||
- The "All" category is treated specially (see below for details)
|
- The "All" category is treated specially (see below for details)
|
||||||
- Category names consisting only of whitespace are rejected along with the empty string
|
- Feed and category titles consisting only of whitespace are rejected along with the empty string
|
||||||
- Filtering rules may not function identically (see below for details)
|
- Filtering rules may not function identically (see below for details)
|
||||||
- The `checked_at` field of feeds indicates when the feed was last updated rather than when it was last checked
|
- The `checked_at` field of feeds indicates when the feed was last updated rather than when it was last checked
|
||||||
- Creating a feed with the `scrape` property set to `true` might not return scraped content for the initial synchronization
|
- Creating a feed with the `scrape` property set to `true` might not return scraped content for the initial synchronization
|
||||||
|
|
|
@ -66,6 +66,34 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
'entry_swipe' => ["swipe", true],
|
'entry_swipe' => ["swipe", true],
|
||||||
'stylesheet' => ["stylesheet", ""],
|
'stylesheet' => ["stylesheet", ""],
|
||||||
];
|
];
|
||||||
|
/** A map between Miniflux's input properties and our input properties when modifiying feeds
|
||||||
|
*
|
||||||
|
* Miniflux also allows changing the following properties:
|
||||||
|
*
|
||||||
|
* - feed_url
|
||||||
|
* - username
|
||||||
|
* - password
|
||||||
|
* - user_agent
|
||||||
|
* - scraper_rules
|
||||||
|
* - rewrite_rules
|
||||||
|
* - disabled
|
||||||
|
* - ignore_http_cache
|
||||||
|
* - fetch_via_proxy
|
||||||
|
*
|
||||||
|
* These either do not apply because we have no cache or proxy,
|
||||||
|
* or cannot be changed because feeds are deduplicated and changing
|
||||||
|
* how they are fetched is not practical with our implementation.
|
||||||
|
* The properties are still checked for type and syntactic validity
|
||||||
|
* where practical, on the assumption Miniflux would also reject
|
||||||
|
* invalid values.
|
||||||
|
*/
|
||||||
|
protected const FEED_META_MAP = [
|
||||||
|
'title' => "title",
|
||||||
|
'category_id' => "folder",
|
||||||
|
'crawler' => "scrape",
|
||||||
|
'keeplist_rules' => "keep_rule",
|
||||||
|
'blocklist_rules' => "block_rule",
|
||||||
|
];
|
||||||
protected const CALLS = [ // handler method Admin Path Body Query Required fields
|
protected const CALLS = [ // handler method Admin Path Body Query Required fields
|
||||||
'/categories' => [
|
'/categories' => [
|
||||||
'GET' => ["getCategories", false, false, false, false, []],
|
'GET' => ["getCategories", false, false, false, false, []],
|
||||||
|
@ -745,6 +773,32 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
return new Response(['feed_id' => $id], 201);
|
return new Response(['feed_id' => $id], 201);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function updateFeed(array $path, array $data): ResponseInterface {
|
||||||
|
$in = [];
|
||||||
|
foreach (self::FEED_META_MAP as $from => $to) {
|
||||||
|
if (isset($data[$from])) {
|
||||||
|
$in[$to] = $data[$from];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($in['folder'])) {
|
||||||
|
$in['folder'] -= 1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, (int) $path[1], $in);
|
||||||
|
return $this->getFeed($path);
|
||||||
|
} catch (ExceptionInput $e) {
|
||||||
|
switch ($e->getCode()) {
|
||||||
|
case 10231:
|
||||||
|
case 10232:
|
||||||
|
return new ErrorResponse("InvalidTitle", 422);
|
||||||
|
case 10235:
|
||||||
|
return new ErrorResponse("MissingCategory", 422);
|
||||||
|
case 10239:
|
||||||
|
return new ErrorResponse("404", 404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function tokenGenerate(string $user, string $label): string {
|
public static function tokenGenerate(string $user, string $label): string {
|
||||||
// Miniflux produces tokens in base64url alphabet
|
// Miniflux produces tokens in base64url alphabet
|
||||||
$t = str_replace(["+", "/"], ["-", "_"], base64_encode(random_bytes(self::TOKEN_LENGTH)));
|
$t = str_replace(["+", "/"], ["-", "_"], base64_encode(random_bytes(self::TOKEN_LENGTH)));
|
||||||
|
|
|
@ -21,11 +21,12 @@ return [
|
||||||
'API.Miniflux.Error.FetchOther' => 'Unable to fetch this resource',
|
'API.Miniflux.Error.FetchOther' => 'Unable to fetch this resource',
|
||||||
'API.Miniflux.Error.FetchFormat' => 'Unsupported feed format',
|
'API.Miniflux.Error.FetchFormat' => 'Unsupported feed format',
|
||||||
'API.Miniflux.Error.DuplicateCategory' => 'This category already exists.',
|
'API.Miniflux.Error.DuplicateCategory' => 'This category already exists.',
|
||||||
'API.Miniflux.Error.InvalidCategory' => 'Invalid category title "{title}"',
|
'API.Miniflux.Error.InvalidCategory' => 'Invalid category title',
|
||||||
'API.Miniflux.Error.MissingCategory' => 'This category does not exist or does not belong to this user.',
|
'API.Miniflux.Error.MissingCategory' => 'This category does not exist or does not belong to this user.',
|
||||||
'API.Miniflux.Error.InvalidElevation' => 'Only administrators can change permissions of standard users',
|
'API.Miniflux.Error.InvalidElevation' => 'Only administrators can change permissions of standard users',
|
||||||
'API.Miniflux.Error.DuplicateUser' => 'The user name "{user}" already exists',
|
'API.Miniflux.Error.DuplicateUser' => 'The user name "{user}" already exists',
|
||||||
'API.Miniflux.Error.DuplicateFeed' => 'This feed already exists.',
|
'API.Miniflux.Error.DuplicateFeed' => 'This feed already exists.',
|
||||||
|
'API.Miniflux.Error.InvalidTitle' => 'Invalid feed title',
|
||||||
|
|
||||||
'API.TTRSS.Category.Uncategorized' => 'Uncategorized',
|
'API.TTRSS.Category.Uncategorized' => 'Uncategorized',
|
||||||
'API.TTRSS.Category.Special' => 'Special',
|
'API.TTRSS.Category.Special' => 'Special',
|
||||||
|
|
|
@ -683,4 +683,33 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
[['feed_url' => "http://example.com/", 'category_id' => 1, 'blocklist_rules' => "A"], 2112, 44, true, true, new Response(['feed_id' => 44], 201)],
|
[['feed_url' => "http://example.com/", 'category_id' => 1, 'blocklist_rules' => "A"], 2112, 44, true, true, new Response(['feed_id' => 44], 201)],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideFeedModifications */
|
||||||
|
public function testModifyAFeed(array $in, array $data, $out, ResponseInterface $exp): void {
|
||||||
|
$this->h = \Phake::partialMock(V1::class);
|
||||||
|
\Phake::when($this->h)->getFeed->thenReturn(new Response($this->feedsOut[0]));
|
||||||
|
if ($out instanceof \Exception) {
|
||||||
|
\Phake::when(Arsse::$db)->subscriptionPropertiesSet->thenThrow($out);
|
||||||
|
} else {
|
||||||
|
\Phake::when(Arsse::$db)->subscriptionPropertiesSet->thenReturn($out);
|
||||||
|
}
|
||||||
|
$this->assertMessage($exp, $this->req("PUT", "/feeds/2112"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideFeedModifications(): iterable {
|
||||||
|
self::clearData();
|
||||||
|
$success = new Response($this->feedsOut[0]);
|
||||||
|
return [
|
||||||
|
[[], [], true, $success],
|
||||||
|
[[], [], new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)],
|
||||||
|
[['title' => ""], ['title' => ""], new ExceptionInput("missing"), new ErrorResponse("InvalidTitle", 422)],
|
||||||
|
[['title' => " "], ['title' => " "], new ExceptionInput("whitespace"), new ErrorResponse("InvalidTitle", 422)],
|
||||||
|
[['title' => " "], ['title' => " "], new ExceptionInput("whitespace"), new ErrorResponse("InvalidTitle", 422)],
|
||||||
|
[['category_id' => 47], ['folder' => 46], new ExceptionInput("idMissing"), new ErrorResponse("MissingCategory", 422)],
|
||||||
|
[['crawler' => false], ['scrape' => false], true, $success],
|
||||||
|
[['keeplist_rules' => ""], ['keep_rule' => ""], true, $success],
|
||||||
|
[['blocklist_rules' => "ook"], ['block_rule' => "ook"], true, $success],
|
||||||
|
[['title' => "Ook!", 'crawler' => true], ['title' => "Ook!", 'scrape' => true], true, $success]
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue