Add EtagPropagator to handle etag changes when external storages are changed
This commit is contained in:
parent
9a5d0f6084
commit
5d7bd8be42
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Files_External;
|
||||||
|
|
||||||
|
use OC\Files\Filesystem;
|
||||||
|
|
||||||
|
class EtagPropagator {
|
||||||
|
/**
|
||||||
|
* @var \OCP\IUser
|
||||||
|
*/
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OC\Files\Cache\ChangePropagator
|
||||||
|
*/
|
||||||
|
protected $changePropagator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OCP\IConfig
|
||||||
|
*/
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \OCP\IUser $user
|
||||||
|
* @param \OC\Files\Cache\ChangePropagator $changePropagator
|
||||||
|
* @param \OCP\IConfig $config
|
||||||
|
*/
|
||||||
|
public function __construct($user, $changePropagator, $config) {
|
||||||
|
$this->user = $user;
|
||||||
|
$this->changePropagator = $changePropagator;
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function propagateDirtyMountPoints($time = null) {
|
||||||
|
if ($time === null) {
|
||||||
|
$time = time();
|
||||||
|
}
|
||||||
|
$mountPoints = $this->getDirtyMountPoints();
|
||||||
|
foreach ($mountPoints as $mountPoint) {
|
||||||
|
$this->changePropagator->addChange($mountPoint);
|
||||||
|
$this->config->setUserValue($this->user->getUID(), 'files_external', $mountPoint, $time);
|
||||||
|
}
|
||||||
|
if (count($mountPoints)) {
|
||||||
|
$this->changePropagator->propagateChanges($time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all mountpoints we need to update the etag for
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function getDirtyMountPoints() {
|
||||||
|
$dirty = array();
|
||||||
|
$mountPoints = $this->config->getAppKeys('files_external');
|
||||||
|
foreach ($mountPoints as $mountPoint) {
|
||||||
|
if (substr($mountPoint, 0, 1) === '/') {
|
||||||
|
$updateTime = $this->config->getAppValue('files_external', $mountPoint);
|
||||||
|
$userTime = $this->config->getUserValue($this->user->getUID(), 'files_external', $mountPoint);
|
||||||
|
if ($updateTime > $userTime) {
|
||||||
|
$dirty[] = $mountPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $mountPoint
|
||||||
|
* @param int $time
|
||||||
|
*/
|
||||||
|
protected function markDirty($mountPoint, $time = null) {
|
||||||
|
if ($time === null) {
|
||||||
|
$time = time();
|
||||||
|
}
|
||||||
|
$this->config->setAppValue('files_external', $mountPoint, $time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update etags for mount points for known user
|
||||||
|
* For global or group mount points, updating the etag for every user is not feasible
|
||||||
|
* instead we mark the mount point as dirty and update the etag when the filesystem is loaded for the user
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
* @param int $time
|
||||||
|
*/
|
||||||
|
public function updateHook($params, $time = null) {
|
||||||
|
if ($time === null) {
|
||||||
|
$time = time();
|
||||||
|
}
|
||||||
|
$users = $params[Filesystem::signal_param_users];
|
||||||
|
$type = $params[Filesystem::signal_param_mount_type];
|
||||||
|
$mountPoint = $params[Filesystem::signal_param_path];
|
||||||
|
if ($type === \OC_Mount_Config::MOUNT_TYPE_GROUP or $users === 'all') {
|
||||||
|
$this->markDirty($mountPoint, $time);
|
||||||
|
} else {
|
||||||
|
$this->changePropagator->addChange($mountPoint);
|
||||||
|
$this->changePropagator->propagateChanges($time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,328 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tests\Files_External;
|
||||||
|
|
||||||
|
use OC\Files\Filesystem;
|
||||||
|
use OC\User\User;
|
||||||
|
|
||||||
|
class EtagPropagator extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected function getUser() {
|
||||||
|
return new User(uniqid(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \PHPUnit_Framework_MockObject_MockObject | \OC\Files\Cache\ChangePropagator
|
||||||
|
*/
|
||||||
|
protected function getChangePropagator() {
|
||||||
|
return $this->getMockBuilder('\OC\Files\Cache\ChangePropagator')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \PHPUnit_Framework_MockObject_MockObject | \OCP\IConfig
|
||||||
|
*/
|
||||||
|
protected function getConfig() {
|
||||||
|
$appConfig = array();
|
||||||
|
$userConfig = array();
|
||||||
|
$mock = $this->getMockBuilder('\OCP\IConfig')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mock->expects($this->any())
|
||||||
|
->method('getAppValue')
|
||||||
|
->will($this->returnCallback(function ($appId, $key, $default = null) use (&$appConfig) {
|
||||||
|
if (isset($appConfig[$appId]) and isset($appConfig[$appId][$key])) {
|
||||||
|
return $appConfig[$appId][$key];
|
||||||
|
} else {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
$mock->expects($this->any())
|
||||||
|
->method('setAppValue')
|
||||||
|
->will($this->returnCallback(function ($appId, $key, $value) use (&$appConfig) {
|
||||||
|
if (!isset($appConfig[$appId])) {
|
||||||
|
$appConfig[$appId] = array();
|
||||||
|
}
|
||||||
|
$appConfig[$appId][$key] = $value;
|
||||||
|
}));
|
||||||
|
$mock->expects($this->any())
|
||||||
|
->method('getAppKeys')
|
||||||
|
->will($this->returnCallback(function ($appId) use (&$appConfig) {
|
||||||
|
if (!isset($appConfig[$appId])) {
|
||||||
|
$appConfig[$appId] = array();
|
||||||
|
}
|
||||||
|
return array_keys($appConfig[$appId]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
$mock->expects($this->any())
|
||||||
|
->method('getUserValue')
|
||||||
|
->will($this->returnCallback(function ($userId, $appId, $key, $default = null) use (&$userConfig) {
|
||||||
|
if (isset($userConfig[$userId]) and isset($userConfig[$userId][$appId]) and isset($userConfig[$userId][$appId][$key])) {
|
||||||
|
return $userConfig[$userId][$appId][$key];
|
||||||
|
} else {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
$mock->expects($this->any())
|
||||||
|
->method('setUserValue')
|
||||||
|
->will($this->returnCallback(function ($userId, $appId, $key, $value) use (&$userConfig) {
|
||||||
|
if (!isset($userConfig[$userId])) {
|
||||||
|
$userConfig[$userId] = array();
|
||||||
|
}
|
||||||
|
if (!isset($userConfig[$userId][$appId])) {
|
||||||
|
$userConfig[$userId][$appId] = array();
|
||||||
|
}
|
||||||
|
$userConfig[$userId][$appId][$key] = $value;
|
||||||
|
}));
|
||||||
|
|
||||||
|
return $mock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSingleUserMount() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('addChange')
|
||||||
|
->with('/test');
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('propagateChanges')
|
||||||
|
->with($time);
|
||||||
|
|
||||||
|
$propagator->updateHook(array(
|
||||||
|
Filesystem::signal_param_path => '/test',
|
||||||
|
Filesystem::signal_param_mount_type => \OC_Mount_Config::MOUNT_TYPE_USER,
|
||||||
|
Filesystem::signal_param_users => $user->getUID(),
|
||||||
|
), $time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGlobalMountNoDirectUpdate() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
// not updated directly
|
||||||
|
$changePropagator->expects($this->never())
|
||||||
|
->method('addChange');
|
||||||
|
$changePropagator->expects($this->never())
|
||||||
|
->method('propagateChanges');
|
||||||
|
|
||||||
|
$propagator->updateHook(array(
|
||||||
|
Filesystem::signal_param_path => '/test',
|
||||||
|
Filesystem::signal_param_mount_type => \OC_Mount_Config::MOUNT_TYPE_USER,
|
||||||
|
Filesystem::signal_param_users => 'all',
|
||||||
|
), $time);
|
||||||
|
|
||||||
|
// mount point marked as dirty
|
||||||
|
$this->assertEquals(array('/test'), $config->getAppKeys('files_external'));
|
||||||
|
$this->assertEquals($time, $config->getAppValue('files_external', '/test'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGroupMountNoDirectUpdate() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
// not updated directly
|
||||||
|
$changePropagator->expects($this->never())
|
||||||
|
->method('addChange');
|
||||||
|
$changePropagator->expects($this->never())
|
||||||
|
->method('propagateChanges');
|
||||||
|
|
||||||
|
$propagator->updateHook(array(
|
||||||
|
Filesystem::signal_param_path => '/test',
|
||||||
|
Filesystem::signal_param_mount_type => \OC_Mount_Config::MOUNT_TYPE_GROUP,
|
||||||
|
Filesystem::signal_param_users => 'test',
|
||||||
|
), $time);
|
||||||
|
|
||||||
|
// mount point marked as dirty
|
||||||
|
$this->assertEquals(array('/test'), $config->getAppKeys('files_external'));
|
||||||
|
$this->assertEquals($time, $config->getAppValue('files_external', '/test'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGlobalMountNoDirtyMountPoint() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
$changePropagator->expects($this->never())
|
||||||
|
->method('addChange');
|
||||||
|
$changePropagator->expects($this->never())
|
||||||
|
->method('propagateChanges');
|
||||||
|
|
||||||
|
$propagator->propagateDirtyMountPoints($time);
|
||||||
|
|
||||||
|
$this->assertEquals(0, $config->getUserValue($user->getUID(), 'files_external', '/test', 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGlobalMountDirtyMountPointFirstTime() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
$config->setAppValue('files_external', '/test', $time - 10);
|
||||||
|
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('addChange')
|
||||||
|
->with('/test');
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('propagateChanges')
|
||||||
|
->with($time);
|
||||||
|
|
||||||
|
$propagator->propagateDirtyMountPoints($time);
|
||||||
|
|
||||||
|
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGlobalMountNonDirtyMountPoint() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
$config->setAppValue('files_external', '/test', $time - 10);
|
||||||
|
$config->setUserValue($user->getUID(), 'files_external', '/test', $time - 10);
|
||||||
|
|
||||||
|
$changePropagator->expects($this->never())
|
||||||
|
->method('addChange');
|
||||||
|
$changePropagator->expects($this->never())
|
||||||
|
->method('propagateChanges');
|
||||||
|
|
||||||
|
$propagator->propagateDirtyMountPoints($time);
|
||||||
|
|
||||||
|
$this->assertEquals($time - 10, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGlobalMountNonDirtyMountPointOtherUser() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$user2 = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
$config->setAppValue('files_external', '/test', $time - 10);
|
||||||
|
$config->setUserValue($user2->getUID(), 'files_external', '/test', $time - 10);
|
||||||
|
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('addChange')
|
||||||
|
->with('/test');
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('propagateChanges')
|
||||||
|
->with($time);
|
||||||
|
|
||||||
|
$propagator->propagateDirtyMountPoints($time);
|
||||||
|
|
||||||
|
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGlobalMountDirtyMountPointSecondTime() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
$config->setAppValue('files_external', '/test', $time - 10);
|
||||||
|
$config->setUserValue($user->getUID(), 'files_external', '/test', $time - 20);
|
||||||
|
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('addChange')
|
||||||
|
->with('/test');
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('propagateChanges')
|
||||||
|
->with($time);
|
||||||
|
|
||||||
|
$propagator->propagateDirtyMountPoints($time);
|
||||||
|
|
||||||
|
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGlobalMountMultipleUsers() {
|
||||||
|
$time = time();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$user1 = $this->getUser();
|
||||||
|
$user2 = $this->getUser();
|
||||||
|
$user3 = $this->getUser();
|
||||||
|
$changePropagator1 = $this->getChangePropagator();
|
||||||
|
$changePropagator2 = $this->getChangePropagator();
|
||||||
|
$changePropagator3 = $this->getChangePropagator();
|
||||||
|
$propagator1 = new \OCA\Files_External\EtagPropagator($user1, $changePropagator1, $config);
|
||||||
|
$propagator2 = new \OCA\Files_External\EtagPropagator($user2, $changePropagator2, $config);
|
||||||
|
$propagator3 = new \OCA\Files_External\EtagPropagator($user3, $changePropagator3, $config);
|
||||||
|
|
||||||
|
$config->setAppValue('files_external', '/test', $time - 10);
|
||||||
|
|
||||||
|
$changePropagator1->expects($this->once())
|
||||||
|
->method('addChange')
|
||||||
|
->with('/test');
|
||||||
|
$changePropagator1->expects($this->once())
|
||||||
|
->method('propagateChanges')
|
||||||
|
->with($time);
|
||||||
|
|
||||||
|
$propagator1->propagateDirtyMountPoints($time);
|
||||||
|
|
||||||
|
$this->assertEquals($time, $config->getUserValue($user1->getUID(), 'files_external', '/test'));
|
||||||
|
$this->assertEquals(0, $config->getUserValue($user2->getUID(), 'files_external', '/test', 0));
|
||||||
|
$this->assertEquals(0, $config->getUserValue($user3->getUID(), 'files_external', '/test', 0));
|
||||||
|
|
||||||
|
$changePropagator2->expects($this->once())
|
||||||
|
->method('addChange')
|
||||||
|
->with('/test');
|
||||||
|
$changePropagator2->expects($this->once())
|
||||||
|
->method('propagateChanges')
|
||||||
|
->with($time);
|
||||||
|
|
||||||
|
$propagator2->propagateDirtyMountPoints($time);
|
||||||
|
|
||||||
|
$this->assertEquals($time, $config->getUserValue($user1->getUID(), 'files_external', '/test'));
|
||||||
|
$this->assertEquals($time, $config->getUserValue($user2->getUID(), 'files_external', '/test', 0));
|
||||||
|
$this->assertEquals(0, $config->getUserValue($user3->getUID(), 'files_external', '/test', 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGlobalMountMultipleDirtyMountPoints() {
|
||||||
|
$time = time();
|
||||||
|
$user = $this->getUser();
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$changePropagator = $this->getChangePropagator();
|
||||||
|
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||||
|
|
||||||
|
$config->setAppValue('files_external', '/test', $time - 10);
|
||||||
|
$config->setAppValue('files_external', '/foo', $time - 50);
|
||||||
|
$config->setAppValue('files_external', '/bar', $time - 70);
|
||||||
|
|
||||||
|
$config->setUserValue($user->getUID(), 'files_external', '/foo', $time - 70);
|
||||||
|
$config->setUserValue($user->getUID(), 'files_external', '/bar', $time - 70);
|
||||||
|
|
||||||
|
$changePropagator->expects($this->exactly(2))
|
||||||
|
->method('addChange');
|
||||||
|
$changePropagator->expects($this->once())
|
||||||
|
->method('propagateChanges')
|
||||||
|
->with($time);
|
||||||
|
|
||||||
|
$propagator->propagateDirtyMountPoints($time);
|
||||||
|
|
||||||
|
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||||
|
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/foo'));
|
||||||
|
$this->assertEquals($time - 70, $config->getUserValue($user->getUID(), 'files_external', '/bar'));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue