mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
CLI command for exporting OPML and sundry cleanup
This commit is contained in:
parent
deea294f8a
commit
77efaa7b41
2 changed files with 69 additions and 34 deletions
53
lib/CLI.php
53
lib/CLI.php
|
@ -7,6 +7,7 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse;
|
namespace JKingWeb\Arsse;
|
||||||
|
|
||||||
use JKingWeb\Arsse\REST\Fever\User as Fever;
|
use JKingWeb\Arsse\REST\Fever\User as Fever;
|
||||||
|
use JKingWeb\Arsse\ImportExport\OPML;
|
||||||
|
|
||||||
class CLI {
|
class CLI {
|
||||||
const USAGE = <<<USAGE_TEXT
|
const USAGE = <<<USAGE_TEXT
|
||||||
|
@ -23,6 +24,7 @@ Usage:
|
||||||
arsse.php user unset-pass <username>
|
arsse.php user unset-pass <username>
|
||||||
[--oldpass=<pass>] [--fever]
|
[--oldpass=<pass>] [--fever]
|
||||||
arsse.php user auth <username> <password> [--fever]
|
arsse.php user auth <username> <password> [--fever]
|
||||||
|
arsse.php export <username> [<file>] [-f | --flat]
|
||||||
arsse.php --version
|
arsse.php --version
|
||||||
arsse.php --help | -h
|
arsse.php --help | -h
|
||||||
|
|
||||||
|
@ -54,6 +56,12 @@ USAGE_TEXT;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function resolveFile($file, string $mode): string {
|
||||||
|
// TODO: checking read/write permissions on the provided path may be useful
|
||||||
|
$stdinOrStdout = in_array($mode, ["r", "r+"]) ? "php://input" : "php://output";
|
||||||
|
return ($file === "-" ? null : $file) ?? $stdinOrStdout;
|
||||||
|
}
|
||||||
|
|
||||||
public function dispatch(array $argv = null) {
|
public function dispatch(array $argv = null) {
|
||||||
$argv = $argv ?? $_SERVER['argv'];
|
$argv = $argv ?? $_SERVER['argv'];
|
||||||
$argv0 = array_shift($argv);
|
$argv0 = array_shift($argv);
|
||||||
|
@ -62,7 +70,12 @@ USAGE_TEXT;
|
||||||
'help' => false,
|
'help' => false,
|
||||||
]);
|
]);
|
||||||
try {
|
try {
|
||||||
switch ($this->command(["--help", "--version", "daemon", "feed refresh", "feed refresh-all", "conf save-defaults", "user"], $args)) {
|
$cmd = $this->command(["--help", "--version", "daemon", "feed refresh", "feed refresh-all", "conf save-defaults", "user", "export"], $args);
|
||||||
|
if ($cmd && !in_array($cmd, ["--help", "--version", "conf save-defaults"])) {
|
||||||
|
// only certain commands don't require configuration to be loaded
|
||||||
|
$this->loadConf();
|
||||||
|
}
|
||||||
|
switch ($cmd) {
|
||||||
case "--help":
|
case "--help":
|
||||||
echo $this->usage($argv0).\PHP_EOL;
|
echo $this->usage($argv0).\PHP_EOL;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -70,23 +83,22 @@ USAGE_TEXT;
|
||||||
echo Arsse::VERSION.\PHP_EOL;
|
echo Arsse::VERSION.\PHP_EOL;
|
||||||
return 0;
|
return 0;
|
||||||
case "daemon":
|
case "daemon":
|
||||||
$this->loadConf();
|
$this->getInstance(Service::class)->watch(true);
|
||||||
$this->getService()->watch(true);
|
|
||||||
return 0;
|
return 0;
|
||||||
case "feed refresh":
|
case "feed refresh":
|
||||||
$this->loadConf();
|
|
||||||
return (int) !Arsse::$db->feedUpdate((int) $args['<n>'], true);
|
return (int) !Arsse::$db->feedUpdate((int) $args['<n>'], true);
|
||||||
case "feed refresh-all":
|
case "feed refresh-all":
|
||||||
$this->loadConf();
|
$this->getInstance(Service::class)->watch(false);
|
||||||
$this->getService()->watch(false);
|
|
||||||
return 0;
|
return 0;
|
||||||
case "conf save-defaults":
|
case "conf save-defaults":
|
||||||
$file = $args['<file>'];
|
$file = $this->resolveFile($args['<file>'], "w");
|
||||||
$file = ($file === "-" ? null : $file) ?? "php://output";
|
return (int) !$this->getInstance(Conf::class)->exportFile($file, true);
|
||||||
return (int) !($this->getConf())->exportFile($file, true);
|
|
||||||
case "user":
|
case "user":
|
||||||
$this->loadConf();
|
|
||||||
return $this->userManage($args);
|
return $this->userManage($args);
|
||||||
|
case "export":
|
||||||
|
$u = $args['<username>'];
|
||||||
|
$file = $this->resolveFile($args['<file>'], "w");
|
||||||
|
return (int) !$this->getInstance(OPML::class)->exportFile($file, $u, $args['--flat']);
|
||||||
}
|
}
|
||||||
} catch (AbstractException $e) {
|
} catch (AbstractException $e) {
|
||||||
$this->logError($e->getMessage());
|
$this->logError($e->getMessage());
|
||||||
|
@ -99,19 +111,8 @@ USAGE_TEXT;
|
||||||
fwrite(STDERR, $msg.\PHP_EOL);
|
fwrite(STDERR, $msg.\PHP_EOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @codeCoverageIgnore */
|
protected function getInstance(string $class) {
|
||||||
protected function getService(): Service {
|
return new $class;
|
||||||
return new Service;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
protected function getConf(): Conf {
|
|
||||||
return new Conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
protected function getFever(): Fever {
|
|
||||||
return new Fever;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function userManage($args): int {
|
protected function userManage($args): int {
|
||||||
|
@ -120,7 +121,7 @@ USAGE_TEXT;
|
||||||
return $this->userAddOrSetPassword("add", $args["<username>"], $args["<password>"]);
|
return $this->userAddOrSetPassword("add", $args["<username>"], $args["<password>"]);
|
||||||
case "set-pass":
|
case "set-pass":
|
||||||
if ($args['--fever']) {
|
if ($args['--fever']) {
|
||||||
$passwd = $this->getFever()->register($args["<username>"], $args["<password>"]);
|
$passwd = $this->getInstance(Fever::class)->register($args["<username>"], $args["<password>"]);
|
||||||
if (is_null($args["<password>"])) {
|
if (is_null($args["<password>"])) {
|
||||||
echo $passwd.\PHP_EOL;
|
echo $passwd.\PHP_EOL;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +131,7 @@ USAGE_TEXT;
|
||||||
}
|
}
|
||||||
case "unset-pass":
|
case "unset-pass":
|
||||||
if ($args['--fever']) {
|
if ($args['--fever']) {
|
||||||
$this->getFever()->unregister($args["<username>"]);
|
$this->getInstance(Fever::class)->unregister($args["<username>"]);
|
||||||
} else {
|
} else {
|
||||||
Arsse::$user->passwordUnset($args["<username>"], $args["--oldpass"]);
|
Arsse::$user->passwordUnset($args["<username>"], $args["--oldpass"]);
|
||||||
}
|
}
|
||||||
|
@ -162,7 +163,7 @@ USAGE_TEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function userAuthenticate(string $user, string $password, bool $fever = false): int {
|
protected function userAuthenticate(string $user, string $password, bool $fever = false): int {
|
||||||
$result = $fever ? $this->getFever()->authenticate($user, $password) : Arsse::$user->auth($user, $password);
|
$result = $fever ? $this->getInstance(Fever::class)->authenticate($user, $password) : Arsse::$user->auth($user, $password);
|
||||||
if ($result) {
|
if ($result) {
|
||||||
echo Arsse::$lang->msg("CLI.Auth.Success").\PHP_EOL;
|
echo Arsse::$lang->msg("CLI.Auth.Success").\PHP_EOL;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -13,6 +13,7 @@ use JKingWeb\Arsse\Database;
|
||||||
use JKingWeb\Arsse\Service;
|
use JKingWeb\Arsse\Service;
|
||||||
use JKingWeb\Arsse\CLI;
|
use JKingWeb\Arsse\CLI;
|
||||||
use JKingWeb\Arsse\REST\Fever\User as FeverUser;
|
use JKingWeb\Arsse\REST\Fever\User as FeverUser;
|
||||||
|
use JKingWeb\Arsse\ImportExport\OPML;
|
||||||
use Phake;
|
use Phake;
|
||||||
|
|
||||||
/** @covers \JKingWeb\Arsse\CLI */
|
/** @covers \JKingWeb\Arsse\CLI */
|
||||||
|
@ -68,21 +69,21 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
public function testStartTheDaemon() {
|
public function testStartTheDaemon() {
|
||||||
$srv = Phake::mock(Service::class);
|
$srv = Phake::mock(Service::class);
|
||||||
Phake::when($srv)->watch->thenReturn(new \DateTimeImmutable);
|
Phake::when($srv)->watch->thenReturn(new \DateTimeImmutable);
|
||||||
Phake::when($this->cli)->getService->thenReturn($srv);
|
Phake::when($this->cli)->getInstance(Service::class)->thenReturn($srv);
|
||||||
$this->assertConsole($this->cli, "arsse.php daemon", 0);
|
$this->assertConsole($this->cli, "arsse.php daemon", 0);
|
||||||
$this->assertLoaded(true);
|
$this->assertLoaded(true);
|
||||||
Phake::verify($srv)->watch(true);
|
Phake::verify($srv)->watch(true);
|
||||||
Phake::verify($this->cli)->getService;
|
Phake::verify($this->cli)->getInstance(Service::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRefreshAllFeeds() {
|
public function testRefreshAllFeeds() {
|
||||||
$srv = Phake::mock(Service::class);
|
$srv = Phake::mock(Service::class);
|
||||||
Phake::when($srv)->watch->thenReturn(new \DateTimeImmutable);
|
Phake::when($srv)->watch->thenReturn(new \DateTimeImmutable);
|
||||||
Phake::when($this->cli)->getService->thenReturn($srv);
|
Phake::when($this->cli)->getInstance(Service::class)->thenReturn($srv);
|
||||||
$this->assertConsole($this->cli, "arsse.php feed refresh-all", 0);
|
$this->assertConsole($this->cli, "arsse.php feed refresh-all", 0);
|
||||||
$this->assertLoaded(true);
|
$this->assertLoaded(true);
|
||||||
Phake::verify($srv)->watch(false);
|
Phake::verify($srv)->watch(false);
|
||||||
Phake::verify($this->cli)->getService;
|
Phake::verify($this->cli)->getInstance(Service::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @dataProvider provideFeedUpdates */
|
/** @dataProvider provideFeedUpdates */
|
||||||
|
@ -108,7 +109,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
Phake::when($conf)->exportFile("php://output", true)->thenReturn(true);
|
Phake::when($conf)->exportFile("php://output", true)->thenReturn(true);
|
||||||
Phake::when($conf)->exportFile("good.conf", true)->thenReturn(true);
|
Phake::when($conf)->exportFile("good.conf", true)->thenReturn(true);
|
||||||
Phake::when($conf)->exportFile("bad.conf", true)->thenThrow(new \JKingWeb\Arsse\Conf\Exception("fileUnwritable"));
|
Phake::when($conf)->exportFile("bad.conf", true)->thenThrow(new \JKingWeb\Arsse\Conf\Exception("fileUnwritable"));
|
||||||
Phake::when($this->cli)->getConf->thenReturn($conf);
|
Phake::when($this->cli)->getInstance(Conf::class)->thenReturn($conf);
|
||||||
$this->assertConsole($this->cli, $cmd, $exitStatus);
|
$this->assertConsole($this->cli, $cmd, $exitStatus);
|
||||||
$this->assertLoaded(false);
|
$this->assertLoaded(false);
|
||||||
Phake::verify($conf)->exportFile($file, true);
|
Phake::verify($conf)->exportFile($file, true);
|
||||||
|
@ -179,7 +180,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
\Phake::when($fever)->authenticate->thenReturn(false);
|
\Phake::when($fever)->authenticate->thenReturn(false);
|
||||||
\Phake::when($fever)->authenticate("john.doe@example.com", "ashalla")->thenReturn(true);
|
\Phake::when($fever)->authenticate("john.doe@example.com", "ashalla")->thenReturn(true);
|
||||||
\Phake::when($fever)->authenticate("jane.doe@example.com", "thx1138")->thenReturn(true);
|
\Phake::when($fever)->authenticate("jane.doe@example.com", "thx1138")->thenReturn(true);
|
||||||
\Phake::when($this->cli)->getFever->thenReturn($fever);
|
\Phake::when($this->cli)->getInstance(FeverUser::class)->thenReturn($fever);
|
||||||
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
|
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +235,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
Arsse::$user->method("passwordSet")->will($this->returnCallback($passwordChange));
|
Arsse::$user->method("passwordSet")->will($this->returnCallback($passwordChange));
|
||||||
$fever = \Phake::mock(FeverUser::class);
|
$fever = \Phake::mock(FeverUser::class);
|
||||||
\Phake::when($fever)->register->thenReturnCallback($passwordChange);
|
\Phake::when($fever)->register->thenReturnCallback($passwordChange);
|
||||||
\Phake::when($this->cli)->getFever->thenReturn($fever);
|
\Phake::when($this->cli)->getInstance(FeverUser::class)->thenReturn($fever);
|
||||||
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
|
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +265,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
Arsse::$user->method("passwordUnset")->will($this->returnCallback($passwordClear));
|
Arsse::$user->method("passwordUnset")->will($this->returnCallback($passwordClear));
|
||||||
$fever = \Phake::mock(FeverUser::class);
|
$fever = \Phake::mock(FeverUser::class);
|
||||||
\Phake::when($fever)->unregister->thenReturnCallback($passwordClear);
|
\Phake::when($fever)->unregister->thenReturnCallback($passwordClear);
|
||||||
\Phake::when($this->cli)->getFever->thenReturn($fever);
|
\Phake::when($this->cli)->getInstance(FeverUser::class)->thenReturn($fever);
|
||||||
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
|
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,4 +277,37 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
["arsse.php user unset-pass jane.doe@example.com --fever", 10402, ""],
|
["arsse.php user unset-pass jane.doe@example.com --fever", 10402, ""],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @dataProvider provideOpmlExports */
|
||||||
|
public function testExportToOpml(string $cmd, int $exitStatus, string $file, string $user, bool $flat) {
|
||||||
|
$opml = Phake::mock(OPML::class);
|
||||||
|
Phake::when($opml)->exportFile("php://output", $user, $flat)->thenReturn(true);
|
||||||
|
Phake::when($opml)->exportFile("good.opml", $user, $flat)->thenReturn(true);
|
||||||
|
Phake::when($opml)->exportFile("bad.opml", $user, $flat)->thenThrow(new \JKingWeb\Arsse\ImportExport\Exception("fileUnwritable"));
|
||||||
|
Phake::when($this->cli)->getInstance(OPML::class)->thenReturn($opml);
|
||||||
|
$this->assertConsole($this->cli, $cmd, $exitStatus);
|
||||||
|
$this->assertLoaded(true);
|
||||||
|
Phake::verify($opml)->exportFile($file, $user, $flat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideOpmlExports() {
|
||||||
|
return [
|
||||||
|
["arsse.php export john.doe@example.com", 0, "php://output", "john.doe@example.com", false],
|
||||||
|
["arsse.php export john.doe@example.com -", 0, "php://output", "john.doe@example.com", false],
|
||||||
|
["arsse.php export john.doe@example.com good.opml", 0, "good.opml", "john.doe@example.com", false],
|
||||||
|
["arsse.php export john.doe@example.com bad.opml", 10604, "bad.opml", "john.doe@example.com", false],
|
||||||
|
["arsse.php export john.doe@example.com --flat", 0, "php://output", "john.doe@example.com", true],
|
||||||
|
["arsse.php export john.doe@example.com - --flat", 0, "php://output", "john.doe@example.com", true],
|
||||||
|
["arsse.php export --flat john.doe@example.com good.opml", 0, "good.opml", "john.doe@example.com", true],
|
||||||
|
["arsse.php export john.doe@example.com bad.opml --flat", 10604, "bad.opml", "john.doe@example.com", true],
|
||||||
|
["arsse.php export jane.doe@example.com", 0, "php://output", "jane.doe@example.com", false],
|
||||||
|
["arsse.php export jane.doe@example.com -", 0, "php://output", "jane.doe@example.com", false],
|
||||||
|
["arsse.php export jane.doe@example.com good.opml", 0, "good.opml", "jane.doe@example.com", false],
|
||||||
|
["arsse.php export jane.doe@example.com bad.opml", 10604, "bad.opml", "jane.doe@example.com", false],
|
||||||
|
["arsse.php export jane.doe@example.com --flat", 0, "php://output", "jane.doe@example.com", true],
|
||||||
|
["arsse.php export jane.doe@example.com - --flat", 0, "php://output", "jane.doe@example.com", true],
|
||||||
|
["arsse.php export --flat jane.doe@example.com good.opml", 0, "good.opml", "jane.doe@example.com", true],
|
||||||
|
["arsse.php export jane.doe@example.com bad.opml --flat", 10604, "bad.opml", "jane.doe@example.com", true],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue