re-implements document lock manager for wopi

This commit is contained in:
Julien Fastré 2022-06-27 22:44:10 +02:00
parent bf536aab38
commit 03bc94178f
6 changed files with 161 additions and 1 deletions

View File

@ -96,7 +96,8 @@
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"App\\": "tests/app/src/", "App\\": "tests/app/src/",
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests" "Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
} }
}, },
"config": { "config": {

View File

@ -40,6 +40,9 @@
<testsuite name="DocGeneratorBundle"> <testsuite name="DocGeneratorBundle">
<directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory> <directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory>
</testsuite> </testsuite>
<testsuite name="WopiBundle">
<directory suffix="Test.php">src/Bundle/ChillWopiBundle/tests/</directory>
</testsuite>
</testsuites> </testsuites>
<listeners> <listeners>

View File

@ -13,7 +13,9 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface; use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
use ChampsLibres\WopiBundle\Service\Wopi as CLWopi; use ChampsLibres\WopiBundle\Service\Wopi as CLWopi;
use ChampsLibres\WopiLib\Contract\Service\DocumentLockManagerInterface;
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface; use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
use Chill\WopiBundle\Service\Wopi\ChillDocumentLockManager;
use Chill\WopiBundle\Service\Wopi\ChillDocumentManager; use Chill\WopiBundle\Service\Wopi\ChillDocumentManager;
use Chill\WopiBundle\Service\Wopi\ChillWopi; use Chill\WopiBundle\Service\Wopi\ChillWopi;
@ -41,6 +43,11 @@ return static function (ContainerConfigurator $container) {
$services $services
->alias(DocumentManagerInterface::class, ChillDocumentManager::class); ->alias(DocumentManagerInterface::class, ChillDocumentManager::class);
$services
->set(ChillDocumentLockManager::class)
->decorate(DocumentLockManagerInterface::class)
;
// TODO: Move this into the async bundle (low priority) // TODO: Move this into the async bundle (low priority)
$services $services
->alias(TempUrlGeneratorInterface::class, 'async_uploader.temp_url_generator'); ->alias(TempUrlGeneratorInterface::class, 'async_uploader.temp_url_generator');

View File

@ -0,0 +1,66 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\WopiBundle\Service\Wopi;
use ChampsLibres\WopiLib\Contract\Entity\Document;
use ChampsLibres\WopiLib\Contract\Service\DocumentLockManagerInterface;
use Chill\MainBundle\Redis\ChillRedis;
use Psr\Http\Message\RequestInterface;
use RuntimeException;
class ChillDocumentLockManager implements DocumentLockManagerInterface
{
private const LOCK_DURATION = 60 * 30 * 1000;
private int $postDeleteLockDurationMs;
private ChillRedis $redis;
public function __construct(ChillRedis $redis)
{
$this->redis = $redis;
}
public function deleteLock(Document $document, RequestInterface $request): bool
{
$this->redis->del($this->getCacheId($document));
return true;
}
public function getLock(Document $document, RequestInterface $request): string
{
if (false !== $value = $this->redis->get($this->getCacheId($document))) {
return $value;
}
throw new RuntimeException('wopi key does not exists');
}
public function hasLock(Document $document, RequestInterface $request): bool
{
return $this->redis->exists($this->getCacheId($document)) > 0;
}
public function setLock(Document $document, string $lockId, RequestInterface $request): bool
{
$key = $this->getCacheId($document);
$this->redis->setex($key, self::LOCK_DURATION, $lockId);
return true;
}
private function getCacheId(Document $document): string
{
return sprintf('wopi_lib_lock_%s', $document->getWopiDocId());
}
}

View File

@ -188,6 +188,11 @@ final class ChillWopi implements WopiInterface
string $xWopiLock, string $xWopiLock,
RequestInterface $request RequestInterface $request
): ResponseInterface { ): ResponseInterface {
// this is because there are a delay between the request which PUT the file content,
// and the unlock: the PUT request last longer and this make the request fails, because
// the unlock terminate before.
sleep(5);
return $this->wopi->unlock($fileId, $accessToken, $xWopiLock, $request); return $this->wopi->unlock($fileId, $accessToken, $xWopiLock, $request);
} }

View File

@ -0,0 +1,78 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\WopiBundle\Tests\Service\Wopi;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Redis\ChillRedis;
use Chill\WopiBundle\Service\Wopi\ChillDocumentLockManager;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Http\Message\RequestInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
/**
* @internal
* @coversNothing
*/
final class ChillDocumentLockManagerTest extends KernelTestCase
{
use ProphecyTrait;
protected function setUp(): void
{
self::bootKernel();
}
public function testMultipleLoops()
{
$manager = $this->makeManager(1);
$document = new StoredObject();
$request = $this->prophesize(RequestInterface::class);
$i = 0;
while (50 > ++$i) {
$this->assertFalse($manager->hasLock($document, $request->reveal()));
$this->assertTrue($manager->setLock($document, 'dummy', $request->reveal()));
$this->assertEquals('dummy', $manager->getLock($document, $request->reveal()));
$this->assertTrue($manager->deleteLock($document, $request->reveal()));
$this->assertFalse($manager->hasLock($document, $request->reveal()));
}
}
public function testSingleLock()
{
$manager = $this->makeManager(1);
$document = new StoredObject();
$request = $this->prophesize(RequestInterface::class);
$this->assertFalse($manager->hasLock($document, $request->reveal()));
$this->assertTrue($manager->setLock($document, 'dummy', $request->reveal()));
$this->assertEquals('dummy', $manager->getLock($document, $request->reveal()));
$this->assertTrue($manager->deleteLock($document, $request->reveal()));
$this->assertFalse($manager->hasLock($document, $request->reveal()));
}
private function makeManager(int $ttlAfterDeleteSeconds): ChillDocumentLockManager
{
$redis = self::$container->get(ChillRedis::class);
return new ChillDocumentLockManager($redis, $ttlAfterDeleteSeconds);
}
}