Browse Source

Complete implementations of server status and user status REST calls

- Fixes #30
- Fixes #31
- Avatars are not yet supported by the data model; blocked by issue #52
microsub
J. King 7 years ago
parent
commit
4cded011ff
  1. 18
      lib/REST/NextCloudNews/V1_2.php
  2. 24
      lib/Service.php
  3. 2
      lib/User.php
  4. 21
      tests/REST/NextCloudNews/TestNCNV1_2.php
  5. 51
      tests/Service/TestService.php
  6. 4
      tests/phpunit.xml

18
lib/REST/NextCloudNews/V1_2.php

@ -595,13 +595,22 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
} }
protected function userStatus(array $url, array $data): Response { protected function userStatus(array $url, array $data): Response {
// FIXME: stub $data = Arsse::$user::propertiesGet(Arsse::$user->id, true);
$data = Arsse::$db->userPropertiesGet(Arsse::$user->id); // construct the avatar structure, if an image is available
if(isset($data['avatar'])) {
$avatar = [
'data' => base64_encode($data['avatar']['data']),
'mime' => $data['avatar']['type'],
];
} else {
$avatar = null;
}
// construct the rest of the structure
$out = [ $out = [
'userId' => Arsse::$user->id, 'userId' => Arsse::$user->id,
'displayName' => $data['name'] ?? Arsse::$user->id, 'displayName' => $data['name'] ?? Arsse::$user->id,
'lastLoginTimestamp' => time(), 'lastLoginTimestamp' => time(),
'avatar' => null, 'avatar' => $avatar,
]; ];
return new Response(200, $out); return new Response(200, $out);
} }
@ -629,12 +638,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
} }
protected function serverStatus(array $url, array $data): Response { protected function serverStatus(array $url, array $data): Response {
// FIXME: stub
return new Response(200, [ return new Response(200, [
'version' => self::VERSION, 'version' => self::VERSION,
'arsse_version' => \JKingWeb\Arsse\VERSION, 'arsse_version' => \JKingWeb\Arsse\VERSION,
'warnings' => [ 'warnings' => [
'improperlyConfiguredCron' => false, 'improperlyConfiguredCron' => !\JKingWeb\Arsse\Service::hasCheckedIn(),
] ]
]); ]);
} }

24
lib/Service.php

@ -22,8 +22,12 @@ class Service {
return $classes; return $classes;
} }
protected static function interval(): \DateInterval { public static function interval(): \DateInterval {
return new \DateInterval(Arsse::$conf->serviceFrequency); // FIXME: this needs to fall back in case of incorrect input try{
return new \DateInterval(Arsse::$conf->serviceFrequency);
} catch(\Exception $e) {
return new \DateInterval("PT2M");
}
} }
function __construct() { function __construct() {
@ -32,14 +36,13 @@ class Service {
$this->interval = static::interval(); $this->interval = static::interval();
} }
function watch(bool $loop = true) { function watch(bool $loop = true): \DateTimeInterface {
$t = new \DateTime(); $t = new \DateTime();
do { do {
$this->checkIn(); $this->checkIn();
static::cleanupPre(); static::cleanupPre();
$list = Arsse::$db->feedListStale(); $list = Arsse::$db->feedListStale();
if($list) { if($list) {
echo date("H:i:s")." Updating feeds ".json_encode($list)."\n";
$this->drv->queue(...$list); $this->drv->queue(...$list);
$this->drv->exec(); $this->drv->exec();
$this->drv->clean(); $this->drv->clean();
@ -47,10 +50,13 @@ class Service {
unset($list); unset($list);
} }
$t->add($this->interval); $t->add($this->interval);
do { if($loop) {
@time_sleep_until($t->getTimestamp()); do {
} while($t->getTimestamp() > time()); @time_sleep_until($t->getTimestamp());
} while($t->getTimestamp() > time());
}
} while($loop); } while($loop);
return $t;
} }
function checkIn(): bool { function checkIn(): bool {
@ -69,8 +75,8 @@ class Service {
$limit = new \DateTime(); $limit = new \DateTime();
$limit->sub($int); $limit->sub($int);
$limit->sub($int); $limit->sub($int);
// return whether the check-in time is less than the acceptable limit // return whether the check-in time is within the acceptable limit
return ($checkin < $limit); return ($checkin >= $limit);
} }
static function cleanupPre(): bool { static function cleanupPre(): bool {

2
lib/User.php

@ -245,7 +245,7 @@ class User {
} }
} }
public function propertiesGet(string $user): array { public function propertiesGet(string $user, bool $withAvatar = false): array {
// prepare default values // prepare default values
$domain = null; $domain = null;
if(Arsse::$conf->userComposeNames) $domain = substr($user,strrpos($user,"@")+1); if(Arsse::$conf->userComposeNames) $domain = substr($user,strrpos($user,"@")+1);

21
tests/REST/NextCloudNews/TestNCNV1_2.php

@ -4,6 +4,7 @@ namespace JKingWeb\Arsse;
use JKingWeb\Arsse\REST\Request; use JKingWeb\Arsse\REST\Request;
use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\REST\Response;
use JKingWeb\Arsse\Test\Result; use JKingWeb\Arsse\Test\Result;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Misc\Context;
use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Db\Transaction; use JKingWeb\Arsse\Db\Transaction;
@ -260,6 +261,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
function setUp() { function setUp() {
$this->clearData(); $this->clearData();
Arsse::$conf = new Conf();
// create a mock user manager // create a mock user manager
Arsse::$user = Phake::mock(User::class); Arsse::$user = Phake::mock(User::class);
Phake::when(Arsse::$user)->authHTTP->thenReturn(true); Phake::when(Arsse::$user)->authHTTP->thenReturn(true);
@ -715,7 +717,7 @@ class TestNCNV1_2 extends Test\AbstractTest {
} }
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), $this->anything())->thenReturn(true); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), $this->anything())->thenReturn(true);
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function for limited to 50 items for multiples Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function limited to 50 items for multiples
$exp = new Response(422); $exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple"))); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple")));
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple"))); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple")));
@ -762,4 +764,21 @@ class TestNCNV1_2 extends Test\AbstractTest {
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->articles($in[2])); Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->articles($in[2]));
Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->articles($in[3])); Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->articles($in[3]));
} }
function testQueryTheServerStatus() {
$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"));
$arr1 = $arr2 = [
'version' => REST\NextCloudNews\V1_2::VERSION,
'arsse_version' => VERSION,
'warnings' => [
'improperlyConfiguredCron' => false,
]
];
$arr2['warnings']['improperlyConfiguredCron'] = true;
$exp = new Response(200, $arr1);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/status")));
}
} }

51
tests/Service/TestService.php

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace JKingWeb\Arsse;
use JKingWeb\Arsse\Misc\Date;
use Phake;
class TestService extends Test\AbstractTest {
protected $srv;
function setUp() {
$this->clearData();
Arsse::$conf = new Conf();
Arsse::$db = Phake::mock(Database::class);
$this->srv = new Service();
}
function testComputeInterval() {
$in = [
Arsse::$conf->serviceFrequency,
"PT2M",
"PT5M",
"P2M",
"5M",
"interval",
];
foreach($in as $index => $spec) {
try{$exp = new \DateInterval($spec);} catch(\Exception $e) {$exp = new \DateInterval("PT2M");}
Arsse::$conf->serviceFrequency = $spec;
$this->assertEquals($exp, Service::interval(), "Interval #$index '$spec' was not correctly calculated");
}
}
function testCheckIn() {
$now = time();
$this->srv->checkIn();
Phake::verify(Arsse::$db)->metaSet("service_last_checkin", Phake::capture($then), "datetime");
$this->assertTime($now, $then);
}
function testReportHavingCheckedIn() {
// the mock's metaGet() returns null by default
$this->assertFalse(Service::hasCheckedIn());
$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"));
$this->assertTrue(Service::hasCheckedIn());
$this->assertFalse(Service::hasCheckedIn());
}
}

4
tests/phpunit.xml

@ -53,5 +53,9 @@
<file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file> <file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file>
<file>REST/NextCloudNews/TestNCNV1_2.php</file> <file>REST/NextCloudNews/TestNCNV1_2.php</file>
</testsuite> </testsuite>
<testsuite name="Refresh service">
<file>Service/TestService.php</file>
</testsuite>
</testsuites> </testsuites>
</phpunit> </phpunit>
Loading…
Cancel
Save