Create a notification when the update server couldn't be reached for some days

Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Joas Schilling 2017-03-17 12:47:26 +01:00 committed by Roeland Jago Douma
parent f3917cfea1
commit 3668673d7b
No known key found for this signature in database
GPG Key ID: F941078878347C0C
5 changed files with 155 additions and 51 deletions

View File

@ -34,6 +34,8 @@ use OCP\Notification\IManager;
class BackgroundJob extends TimedJob { class BackgroundJob extends TimedJob {
protected $connectionNotifications = [3, 7, 14, 30];
/** @var IConfig */ /** @var IConfig */
protected $config; protected $config;
@ -81,7 +83,7 @@ class BackgroundJob extends TimedJob {
* Check for ownCloud update * Check for ownCloud update
*/ */
protected function checkCoreUpdate() { protected function checkCoreUpdate() {
if (in_array($this->getChannel(), ['daily', 'git'])) { if (in_array($this->getChannel(), ['daily', 'git'], true)) {
// "These aren't the update channels you're looking for." - Ben Obi-Wan Kenobi // "These aren't the update channels you're looking for." - Ben Obi-Wan Kenobi
return; return;
} }
@ -89,11 +91,59 @@ class BackgroundJob extends TimedJob {
$updater = $this->createVersionCheck(); $updater = $this->createVersionCheck();
$status = $updater->check(); $status = $updater->check();
if (isset($status['version'])) { if ($status === false) {
$errors = 1 + (int) $this->config->getAppValue('updatenotification', 'update_check_errors', 0);
$this->config->setAppValue('updatenotification', 'update_check_errors', $errors);
if (in_array($errors, $this->connectionNotifications, true)) {
$this->sendErrorNotifications($errors);
}
} else if (isset($status['version'])) {
$this->config->setAppValue('updatenotification', 'update_check_errors', 0);
$this->clearErrorNotifications();
$this->createNotifications('core', $status['version'], $status['versionstring']); $this->createNotifications('core', $status['version'], $status['versionstring']);
} }
} }
/**
* Send a message to the admin when the update server could not be reached
* @param int $numDays
*/
protected function sendErrorNotifications($numDays) {
$this->clearErrorNotifications();
$notification = $this->notificationManager->createNotification();
try {
$notification->setApp('updatenotification')
->setDateTime(new \DateTime())
->setObject('updatenotification', 'error')
->setSubject('connection_error', ['days' => $numDays]);
foreach ($this->getUsersToNotify() as $uid) {
$notification->setUser($uid);
$this->notificationManager->notify($notification);
}
} catch (\InvalidArgumentException $e) {
return;
}
}
/**
* Remove error notifications again
*/
protected function clearErrorNotifications() {
$notification = $this->notificationManager->createNotification();
try {
$notification->setApp('updatenotification')
->setSubject('connection_error')
->setObject('updatenotification', 'error');
} catch (\InvalidArgumentException $e) {
return;
}
$this->notificationManager->markProcessed($notification);
}
/** /**
* Check all installed apps for updates * Check all installed apps for updates
*/ */

View File

@ -24,6 +24,7 @@
namespace OCA\UpdateNotification\Notification; namespace OCA\UpdateNotification\Notification;
use OCP\IConfig;
use OCP\IGroupManager; use OCP\IGroupManager;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\IUser; use OCP\IUser;
@ -38,6 +39,9 @@ class Notifier implements INotifier {
/** @var IURLGenerator */ /** @var IURLGenerator */
protected $url; protected $url;
/** @var IConfig */
protected $config;
/** @var IManager */ /** @var IManager */
protected $notificationManager; protected $notificationManager;
@ -57,14 +61,16 @@ class Notifier implements INotifier {
* Notifier constructor. * Notifier constructor.
* *
* @param IURLGenerator $url * @param IURLGenerator $url
* @param IConfig $config
* @param IManager $notificationManager * @param IManager $notificationManager
* @param IFactory $l10NFactory * @param IFactory $l10NFactory
* @param IUserSession $userSession * @param IUserSession $userSession
* @param IGroupManager $groupManager * @param IGroupManager $groupManager
*/ */
public function __construct(IURLGenerator $url, IManager $notificationManager, IFactory $l10NFactory, IUserSession $userSession, IGroupManager $groupManager) { public function __construct(IURLGenerator $url, IConfig $config, IManager $notificationManager, IFactory $l10NFactory, IUserSession $userSession, IGroupManager $groupManager) {
$this->url = $url; $this->url = $url;
$this->notificationManager = $notificationManager; $this->notificationManager = $notificationManager;
$this->config = $config;
$this->l10NFactory = $l10NFactory; $this->l10NFactory = $l10NFactory;
$this->userSession = $userSession; $this->userSession = $userSession;
$this->groupManager = $groupManager; $this->groupManager = $groupManager;
@ -84,7 +90,16 @@ class Notifier implements INotifier {
} }
$l = $this->l10NFactory->get('updatenotification', $languageCode); $l = $this->l10NFactory->get('updatenotification', $languageCode);
if ($notification->getObjectType() === 'core') { if ($notification->getSubject() === 'connection_error') {
$errors = (int) $this->config->getAppValue('updatenotification', 'update_check_errors', 0);
if ($errors === 0) {
$this->notificationManager->markProcessed($notification);
throw new \InvalidArgumentException();
}
$notification->setParsedSubject($l->t('The update server could not be reached since %d days to check for new updates.', [$errors]))
->setParsedMessage($l->t('Please check the nextcloud and server log files for errors.'));
} elseif ($notification->getObjectType() === 'core') {
$this->updateAlreadyInstalledCheck($notification, $this->getCoreVersions()); $this->updateAlreadyInstalledCheck($notification, $this->getCoreVersions());
$parameters = $notification->getSubjectParameters(); $parameters = $notification->getSubjectParameters();

View File

@ -28,10 +28,12 @@ use OCP\App\IAppManager;
use OCP\Http\Client\IClientService; use OCP\Http\Client\IClientService;
use OCP\IConfig; use OCP\IConfig;
use OCP\IGroupManager; use OCP\IGroupManager;
use OCP\IURLGenerator;
use OCP\IUser; use OCP\IUser;
use OCP\Notification\IManager; use OCP\Notification\IManager;
use Test\TestCase; use Test\TestCase;
use OC\Updater\VersionCheck;
use OCP\Notification\INotification;
use OCP\IGroup;
class BackgroundJobTest extends TestCase { class BackgroundJobTest extends TestCase {
@ -49,11 +51,11 @@ class BackgroundJobTest extends TestCase {
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
$this->config = $this->createMock(\OCP\IConfig::class); $this->config = $this->createMock(IConfig::class);
$this->notificationManager = $this->createMock(\OCP\Notification\IManager::class); $this->notificationManager = $this->createMock(IManager::class);
$this->groupManager = $this->createMock(\OCP\IGroupManager::class); $this->groupManager = $this->createMock(IGroupManager::class);
$this->appManager = $this->createMock(\OCP\App\IAppManager::class); $this->appManager = $this->createMock(IAppManager::class);
$this->client = $this->createMock(\OCP\Http\Client\IClientService::class); $this->client = $this->createMock(IClientService::class);
} }
/** /**
@ -70,7 +72,7 @@ class BackgroundJobTest extends TestCase {
$this->client $this->client
); );
} { } {
return $this->getMockBuilder('OCA\UpdateNotification\Notification\BackgroundJob') return $this->getMockBuilder(BackgroundJob::class)
->setConstructorArgs([ ->setConstructorArgs([
$this->config, $this->config,
$this->notificationManager, $this->notificationManager,
@ -94,28 +96,34 @@ class BackgroundJobTest extends TestCase {
$job->expects($this->once()) $job->expects($this->once())
->method('checkAppUpdates'); ->method('checkAppUpdates');
$this->invokePrivate($job, 'run', [null]); self::invokePrivate($job, 'run', [null]);
} }
public function dataCheckCoreUpdate() { public function dataCheckCoreUpdate() {
return [ return [
['daily', null, null, null], ['daily', null, null, null, null],
['git', null, null, null], ['git', null, null, null, null],
['beta', false, null, null], ['beta', [], null, null, null],
['beta', false, false, null, null],
['beta', false, false, null, 13],
['beta', [ ['beta', [
'version' => '9.2.0', 'version' => '9.2.0',
'versionstring' => 'Nextcloud 11.0.0', 'versionstring' => 'Nextcloud 11.0.0',
], '9.2.0', 'Nextcloud 11.0.0'], ], '9.2.0', 'Nextcloud 11.0.0', null],
['stable', false, null, null], ['stable', [], null, null, null],
['stable', false, false, null, null],
['stable', false, false, null, 6],
['stable', [ ['stable', [
'version' => '9.2.0', 'version' => '9.2.0',
'versionstring' => 'Nextcloud 11.0.0', 'versionstring' => 'Nextcloud 11.0.0',
], '9.2.0', 'Nextcloud 11.0.0'], ], '9.2.0', 'Nextcloud 11.0.0', null],
['production', false, null, null], ['production', [], null, null, null],
['production', false, false, null, null],
['production', false, false, null, 2],
['production', [ ['production', [
'version' => '9.2.0', 'version' => '9.2.0',
'versionstring' => 'Nextcloud 11.0.0', 'versionstring' => 'Nextcloud 11.0.0',
], '9.2.0', 'Nextcloud 11.0.0'], ], '9.2.0', 'Nextcloud 11.0.0', null],
]; ];
} }
@ -124,14 +132,17 @@ class BackgroundJobTest extends TestCase {
* *
* @param string $channel * @param string $channel
* @param mixed $versionCheck * @param mixed $versionCheck
* @param null|string $notification * @param null|string $version
* @param null|string $readableVersion * @param null|string $readableVersion
* @param null|int $errorDays
*/ */
public function testCheckCoreUpdate($channel, $versionCheck, $notification, $readableVersion) { public function testCheckCoreUpdate($channel, $versionCheck, $version, $readableVersion, $errorDays) {
$job = $this->getJob([ $job = $this->getJob([
'getChannel', 'getChannel',
'createVersionCheck', 'createVersionCheck',
'createNotifications', 'createNotifications',
'clearErrorNotifications',
'sendErrorNotifications',
]); ]);
$job->expects($this->once()) $job->expects($this->once())
@ -142,9 +153,7 @@ class BackgroundJobTest extends TestCase {
$job->expects($this->never()) $job->expects($this->never())
->method('createVersionCheck'); ->method('createVersionCheck');
} else { } else {
$check = $this->getMockBuilder('OC\Updater\VersionCheck') $check = $this->createMock(VersionCheck::class);
->disableOriginalConstructor()
->getMock();
$check->expects($this->once()) $check->expects($this->once())
->method('check') ->method('check')
->willReturn($versionCheck); ->willReturn($versionCheck);
@ -154,16 +163,38 @@ class BackgroundJobTest extends TestCase {
->willReturn($check); ->willReturn($check);
} }
if ($notification === null) { if ($version === null) {
$job->expects($this->never()) $job->expects($this->never())
->method('createNotifications'); ->method('createNotifications');
$job->expects($this->never())
->method('clearErrorNotifications');
} else if ($version === false) {
$job->expects($this->never())
->method('createNotifications');
$job->expects($this->never())
->method('clearErrorNotifications');
$this->config->expects($this->once())
->method('getAppValue')
->willReturn($errorDays);
$this->config->expects($this->once())
->method('setAppValue')
->with('updatenotification', 'update_check_errors', $errorDays + 1);
$job->expects($errorDays !== null ? $this->once() : $this->never())
->method('sendErrorNotifications')
->with($errorDays + 1);
} else { } else {
$this->config->expects($this->once())
->method('setAppValue')
->with('updatenotification', 'update_check_errors', 0);
$job->expects($this->once())
->method('clearErrorNotifications');
$job->expects($this->once()) $job->expects($this->once())
->method('createNotifications') ->method('createNotifications')
->willReturn('core', $notification, $readableVersion); ->willReturn('core', $version, $readableVersion);
} }
$this->invokePrivate($job, 'checkCoreUpdate'); self::invokePrivate($job, 'checkCoreUpdate');
} }
public function dataCheckAppUpdates() { public function dataCheckAppUpdates() {
@ -198,15 +229,15 @@ class BackgroundJobTest extends TestCase {
->method('getInstalledApps') ->method('getInstalledApps')
->willReturn($apps); ->willReturn($apps);
$job->expects($this->exactly(sizeof($apps))) $job->expects($this->exactly(count($apps)))
->method('isUpdateAvailable') ->method('isUpdateAvailable')
->willReturnMap($isUpdateAvailable); ->willReturnMap($isUpdateAvailable);
$mockedMethod = $job->expects($this->exactly(sizeof($notifications))) $mockedMethod = $job->expects($this->exactly(count($notifications)))
->method('createNotifications'); ->method('createNotifications');
call_user_func_array([$mockedMethod, 'withConsecutive'], $notifications); call_user_func_array([$mockedMethod, 'withConsecutive'], $notifications);
$this->invokePrivate($job, 'checkAppUpdates'); self::invokePrivate($job, 'checkAppUpdates');
} }
public function dataCreateNotifications() { public function dataCreateNotifications() {
@ -264,7 +295,7 @@ class BackgroundJobTest extends TestCase {
} }
if ($createNotification) { if ($createNotification) {
$notification = $this->getMockBuilder('OCP\Notification\INotification')->getMock(); $notification = $this->createMock(INotification::class);
$notification->expects($this->once()) $notification->expects($this->once())
->method('setApp') ->method('setApp')
->with('updatenotification') ->with('updatenotification')
@ -282,12 +313,12 @@ class BackgroundJobTest extends TestCase {
->willReturnSelf(); ->willReturnSelf();
if ($userNotifications !== null) { if ($userNotifications !== null) {
$mockedMethod = $notification->expects($this->exactly(sizeof($userNotifications))) $mockedMethod = $notification->expects($this->exactly(count($userNotifications)))
->method('setUser') ->method('setUser')
->willReturnSelf(); ->willReturnSelf();
call_user_func_array([$mockedMethod, 'withConsecutive'], $userNotifications); call_user_func_array([$mockedMethod, 'withConsecutive'], $userNotifications);
$this->notificationManager->expects($this->exactly(sizeof($userNotifications))) $this->notificationManager->expects($this->exactly(count($userNotifications)))
->method('notify') ->method('notify')
->willReturn($notification); ->willReturn($notification);
} }
@ -300,7 +331,7 @@ class BackgroundJobTest extends TestCase {
->method('createNotification'); ->method('createNotification');
} }
$this->invokePrivate($job, 'createNotifications', [$app, $version]); self::invokePrivate($job, 'createNotifications', [$app, $version]);
} }
public function dataGetUsersToNotify() { public function dataGetUsersToNotify() {
@ -336,15 +367,15 @@ class BackgroundJobTest extends TestCase {
} }
$groupMap[] = [$gid, $group]; $groupMap[] = [$gid, $group];
} }
$this->groupManager->expects($this->exactly(sizeof($groups))) $this->groupManager->expects($this->exactly(count($groups)))
->method('get') ->method('get')
->willReturnMap($groupMap); ->willReturnMap($groupMap);
$result = $this->invokePrivate($job, 'getUsersToNotify'); $result = self::invokePrivate($job, 'getUsersToNotify');
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
// Test caching // Test caching
$result = $this->invokePrivate($job, 'getUsersToNotify'); $result = self::invokePrivate($job, 'getUsersToNotify');
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
@ -361,7 +392,7 @@ class BackgroundJobTest extends TestCase {
* @param string $version * @param string $version
*/ */
public function testDeleteOutdatedNotifications($app, $version) { public function testDeleteOutdatedNotifications($app, $version) {
$notification = $this->getMockBuilder('OCP\Notification\INotification')->getMock(); $notification = $this->createMock(INotification::class);
$notification->expects($this->once()) $notification->expects($this->once())
->method('setApp') ->method('setApp')
->with('updatenotification') ->with('updatenotification')
@ -379,7 +410,7 @@ class BackgroundJobTest extends TestCase {
->with($notification); ->with($notification);
$job = $this->getJob(); $job = $this->getJob();
$this->invokePrivate($job, 'deleteOutdatedNotifications', [$app, $version]); self::invokePrivate($job, 'deleteOutdatedNotifications', [$app, $version]);
} }
/** /**
@ -389,7 +420,7 @@ class BackgroundJobTest extends TestCase {
protected function getUsers(array $userIds) { protected function getUsers(array $userIds) {
$users = []; $users = [];
foreach ($userIds as $uid) { foreach ($userIds as $uid) {
$user = $this->getMockBuilder('OCP\IUser')->getMock(); $user = $this->createMock(IUser::class);
$user->expects($this->any()) $user->expects($this->any())
->method('getUID') ->method('getUID')
->willReturn($uid); ->willReturn($uid);
@ -403,7 +434,7 @@ class BackgroundJobTest extends TestCase {
* @return \OCP\IGroup|\PHPUnit_Framework_MockObject_MockObject * @return \OCP\IGroup|\PHPUnit_Framework_MockObject_MockObject
*/ */
protected function getGroup($gid) { protected function getGroup($gid) {
$group = $this->getMockBuilder('OCP\IGroup')->getMock(); $group = $this->createMock(IGroup::class);
$group->expects($this->any()) $group->expects($this->any())
->method('getGID') ->method('getGID')
->willReturn($gid); ->willReturn($gid);

View File

@ -24,6 +24,7 @@ namespace OCA\UpdateNotification\Tests\Notification;
use OCA\UpdateNotification\Notification\Notifier; use OCA\UpdateNotification\Notification\Notifier;
use OCP\IConfig;
use OCP\IGroupManager; use OCP\IGroupManager;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\IUserSession; use OCP\IUserSession;
@ -36,6 +37,8 @@ class NotifierTest extends TestCase {
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */
protected $urlGenerator; protected $urlGenerator;
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
protected $config;
/** @var IManager|\PHPUnit_Framework_MockObject_MockObject */ /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */
protected $notificationManager; protected $notificationManager;
/** @var IFactory|\PHPUnit_Framework_MockObject_MockObject */ /** @var IFactory|\PHPUnit_Framework_MockObject_MockObject */
@ -49,6 +52,7 @@ class NotifierTest extends TestCase {
parent::setUp(); parent::setUp();
$this->urlGenerator = $this->createMock(IURLGenerator::class); $this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->config = $this->createMock(IConfig::class);
$this->notificationManager = $this->createMock(IManager::class); $this->notificationManager = $this->createMock(IManager::class);
$this->l10nFactory = $this->createMock(IFactory::class); $this->l10nFactory = $this->createMock(IFactory::class);
$this->userSession = $this->createMock(IUserSession::class); $this->userSession = $this->createMock(IUserSession::class);
@ -63,6 +67,7 @@ class NotifierTest extends TestCase {
if (empty($methods)) { if (empty($methods)) {
return new Notifier( return new Notifier(
$this->urlGenerator, $this->urlGenerator,
$this->config,
$this->notificationManager, $this->notificationManager,
$this->l10nFactory, $this->l10nFactory,
$this->userSession, $this->userSession,
@ -72,6 +77,7 @@ class NotifierTest extends TestCase {
return $this->getMockBuilder(Notifier::class) return $this->getMockBuilder(Notifier::class)
->setConstructorArgs([ ->setConstructorArgs([
$this->urlGenerator, $this->urlGenerator,
$this->config,
$this->notificationManager, $this->notificationManager,
$this->l10nFactory, $this->l10nFactory,
$this->userSession, $this->userSession,

View File

@ -82,7 +82,12 @@ class VersionCheck {
$url = $updaterUrl . '?version=' . $versionString; $url = $updaterUrl . '?version=' . $versionString;
$tmp = []; $tmp = [];
try {
$xml = $this->getUrlContent($url); $xml = $this->getUrlContent($url);
} catch (\Exception $e) {
return false;
}
if ($xml) { if ($xml) {
$loadEntities = libxml_disable_entity_loader(true); $loadEntities = libxml_disable_entity_loader(true);
$data = @simplexml_load_string($xml); $data = @simplexml_load_string($xml);
@ -108,16 +113,13 @@ class VersionCheck {
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
* @param string $url * @param string $url
* @return bool|resource|string * @return resource|string
* @throws \Exception
*/ */
protected function getUrlContent($url) { protected function getUrlContent($url) {
try {
$client = $this->clientService->newClient(); $client = $this->clientService->newClient();
$response = $client->get($url); $response = $client->get($url);
return $response->getBody(); return $response->getBody();
} catch (\Exception $e) {
return false;
}
} }
} }