From 5bb8d4167b60a0060b8b929a1b2ba59dddda4976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Burnichon?= Date: Wed, 9 Dec 2015 12:26:41 +0100 Subject: [PATCH] Add cascade delete on ApiLog accountId. Also add a postConnect listener to enable foreign keys in sqlite --- lib/Alchemy/Phrasea/Application.php | 20 +++- lib/Alchemy/Phrasea/Core/Version.php | 2 +- lib/Alchemy/Phrasea/Model/Entities/ApiLog.php | 7 +- .../Version20151209101524.php | 36 +++++++ lib/classes/patch/400alpha4a.php | 64 ++++++++++++ .../Phrasea/Functional/UserDeletionTest.php | 98 +++++++++++++++++++ 6 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20151209101524.php create mode 100644 lib/classes/patch/400alpha4a.php create mode 100644 tests/Alchemy/Tests/Phrasea/Functional/UserDeletionTest.php diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php index 78b3951ebe..14ac3b9bfe 100644 --- a/lib/Alchemy/Phrasea/Application.php +++ b/lib/Alchemy/Phrasea/Application.php @@ -83,6 +83,9 @@ use Alchemy\Phrasea\Twig\JSUniqueID; use Alchemy\Phrasea\Twig\PhraseanetExtension; use Alchemy\Phrasea\Utilities\CachedTranslator; use Dflydev\Silex\Provider\DoctrineOrm\DoctrineOrmServiceProvider; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; use Doctrine\ORM\Configuration; use FFMpeg\FFMpegServiceProvider; use Gedmo\DoctrineExtensions as GedmoExtension; @@ -1012,13 +1015,28 @@ class Application extends SilexApplication $this['dbs.event_manager'] = $this->share($this->extend('dbs.event_manager', function ($eventManagers, $app) { foreach ($eventManagers->keys() as $name) { - $app['dbal.evm.register.listeners']($eventManagers[$name]); + /** @var EventManager $eventManager */ + $eventManager = $eventManagers[$name]; + $app['dbal.evm.register.listeners']($eventManager); + + $eventManager->addEventListener(Events::postConnect, $this); } return $eventManagers; })); } + /** + * @param ConnectionEventArgs $args + * @throws \Doctrine\DBAL\DBALException + */ + public function postConnect(ConnectionEventArgs $args) + { + if ('sqlite' == $args->getDatabasePlatform()->getName()) { + $args->getConnection()->exec('PRAGMA foreign_keys = ON'); + } + } + private function setupMediaAlchemyst() { $this['media-alchemyst.configuration'] = $this->share(function (Application $app) { diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php index 44474c5d19..b627deba97 100644 --- a/lib/Alchemy/Phrasea/Core/Version.php +++ b/lib/Alchemy/Phrasea/Core/Version.php @@ -16,7 +16,7 @@ class Version /** * @var string */ - private $number = '4.0.0-alpha.3'; + private $number = '4.0.0-alpha.4'; /** * @var string diff --git a/lib/Alchemy/Phrasea/Model/Entities/ApiLog.php b/lib/Alchemy/Phrasea/Model/Entities/ApiLog.php index 35bbbde94a..b207e3a484 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/ApiLog.php +++ b/lib/Alchemy/Phrasea/Model/Entities/ApiLog.php @@ -28,7 +28,12 @@ class ApiLog /** * @ORM\ManyToOne(targetEntity="ApiAccount") - * @ORM\JoinColumn(name="account_id", referencedColumnName="id", nullable=false) + * @ORM\JoinColumn( + * name="account_id", + * referencedColumnName="id", + * nullable=false, + * onDelete="CASCADE" + * ) * * @return ApiAccount **/ diff --git a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20151209101524.php b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20151209101524.php new file mode 100644 index 0000000000..d080457172 --- /dev/null +++ b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/Version20151209101524.php @@ -0,0 +1,36 @@ +abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('ALTER TABLE ApiLogs DROP FOREIGN KEY FK_91E90F309B6B5FBA'); + $this->addSql('ALTER TABLE ApiLogs ADD CONSTRAINT FK_91E90F309B6B5FBA FOREIGN KEY (account_id) REFERENCES ApiAccounts (id) ON DELETE CASCADE'); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + // this down() migration is auto-generated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('ALTER TABLE ApiLogs DROP FOREIGN KEY FK_91E90F309B6B5FBA'); + $this->addSql('ALTER TABLE ApiLogs ADD CONSTRAINT FK_91E90F309B6B5FBA FOREIGN KEY (account_id) REFERENCES ApiAccounts (id)'); + } +} diff --git a/lib/classes/patch/400alpha4a.php b/lib/classes/patch/400alpha4a.php new file mode 100644 index 0000000000..f4424f3204 --- /dev/null +++ b/lib/classes/patch/400alpha4a.php @@ -0,0 +1,64 @@ +release; + } + + /** + * {@inheritdoc} + */ + public function getDoctrineMigrations() + { + return [ + '20151209101524' + ]; + } + + /** + * {@inheritdoc} + */ + public function require_all_upgrades() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function concern() + { + return $this->concern; + } + + /** + * {@inheritdoc} + */ + public function apply(base $databox, Application $app) + { + return true; + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Functional/UserDeletionTest.php b/tests/Alchemy/Tests/Phrasea/Functional/UserDeletionTest.php new file mode 100644 index 0000000000..0a4c11afa3 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Functional/UserDeletionTest.php @@ -0,0 +1,98 @@ +getApplication(); + + $this->userManipulator = $app['manipulator.user']; + + $this->user = $this->userManipulator->createUser('login', "test", 'test@example.com'); + + $this->apiApplicationManipulator = $app['manipulator.api-application']; + + $this->apiApplication = $this->apiApplicationManipulator->create( + 'test-desktop', + ApiApplication::WEB_TYPE, + '', + 'http://website.com/', + $this->user, + 'http://callback.com/callback/' + ); + $this->apiApplication->setGrantPassword(true); + $this->apiApplicationManipulator->update($this->apiApplication); + } + + public function testRemoveUserWhichLoggedViaOAuthDoesNotThrowException() + { + $app = $this->getApplication(); + /** @var ApiLogManipulator $apiLogManipulator */ + $apiLogManipulator = $app['manipulator.api-log']; + /** @var ApiLogRepository $apiLogRepository */ + $apiLogRepository = $app['repo.api-logs']; + /** @var ApiAccountManipulator $apiAccountManipulator */ + $apiAccountManipulator = $app['manipulator.api-account']; + + $account = $apiAccountManipulator->create($this->apiApplication, $this->user); + $this->assertInstanceOf(ApiAccount::class, $account); + + $apiLog = $apiLogManipulator->create($account, new Request(), new Response()); + $apiLogId = $apiLog->getId(); + + $this->userManipulator->delete($this->user); + $this->assertTrue($this->user->isDeleted(), 'User was not properly deleted'); + + $apiLogRepository->clear(); + $this->assertNull($apiLogRepository->find($apiLogId), 'Created api log should have been deleted'); + $this->user = null; + $this->apiApplication = null; + } + + public function tearDown() + { + if ($this->apiApplication) { + $this->apiApplicationManipulator->delete($this->apiApplication); + } + if ($this->user) { + $this->userManipulator->delete($this->user); + } + + parent::tearDown(); + } +}