mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2026-03-16 19:07:48 +00:00
Add AuditDumpRequestHandler to process audit dump requests and ensure transactional consistency
- Introduced `AuditDumpRequestHandler` to handle `AuditDumpRequestMessage` with transactional safety. - Added custom exceptions `AuditDumpTooMuchLines` and `AuditDumpAlreadyGeneratedException` for enhanced error reporting during processing. - Created unit tests in `AuditDumpRequestHandlerTest` to validate normal processing, exception handling, and edge cases. - Implemented locking mechanism and state management for `StoredObject` while processing requests.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Audit\Exception;
|
||||
|
||||
class AuditDumpAlreadyGeneratedException extends \RuntimeException {}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Audit\Exception;
|
||||
|
||||
class AuditDumpTooMuchLines extends \RuntimeException
|
||||
{
|
||||
public function __construct(int $lines, int $maxLines)
|
||||
{
|
||||
parent::__construct(sprintf('Audit dump contains too much lines (%d) compared to maximum allowed (%d)', $lines, $maxLines));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Audit\Messenger;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Repository\StoredObjectRepositoryInterface;
|
||||
use Chill\MainBundle\Audit\AuditEventDumper;
|
||||
use Chill\MainBundle\Audit\Exception\AuditDumpAlreadyGeneratedException;
|
||||
use Chill\MainBundle\Audit\Exception\AuditDumpTooMuchLines;
|
||||
use Chill\MainBundle\Audit\Subject;
|
||||
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
|
||||
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
|
||||
|
||||
final readonly class AuditDumpRequestHandler implements MessageHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private AuditEventDumper $auditEventDumper,
|
||||
private StoredObjectRepositoryInterface $storedObjectRepository,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private UserRepositoryInterface $userRepository,
|
||||
) {}
|
||||
|
||||
public function __invoke(AuditDumpRequestMessage $message): void
|
||||
{
|
||||
$conn = $this->entityManager->getConnection();
|
||||
$beganTx = false;
|
||||
if (!$conn->isTransactionActive()) {
|
||||
$conn->beginTransaction();
|
||||
$beganTx = true;
|
||||
}
|
||||
|
||||
try {
|
||||
// Lock the StoredObject until the end of the process
|
||||
$storedObject = $this->entityManager->find(StoredObject::class, $message->storedObjectId, LockMode::PESSIMISTIC_WRITE);
|
||||
if (!$storedObject instanceof StoredObject) {
|
||||
// Nothing to do if stored object does not exist anymore
|
||||
if ($beganTx) {
|
||||
$conn->commit();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Build criteria expected by AuditTrailRepository
|
||||
$criteria = [];
|
||||
if (null !== $message->from) {
|
||||
$criteria['from_date'] = $message->from;
|
||||
}
|
||||
if (null !== $message->to) {
|
||||
$criteria['to_date'] = $message->to;
|
||||
}
|
||||
|
||||
$subjects = [];
|
||||
foreach ($message->subjects as $s) {
|
||||
if (is_array($s)) {
|
||||
$subjects[] = Subject::fromArray($s);
|
||||
}
|
||||
}
|
||||
if ([] !== $subjects) {
|
||||
$criteria['subjects'] = $subjects;
|
||||
}
|
||||
|
||||
$byUsers = [];
|
||||
foreach ($message->byUsers as $userId) {
|
||||
$byUsers[] = $this->userRepository->find($userId);
|
||||
}
|
||||
if ([] !== $byUsers) {
|
||||
$criteria['by_users'] = $byUsers;
|
||||
}
|
||||
|
||||
$this->auditEventDumper->dump($criteria, $message->lang, $storedObject);
|
||||
|
||||
$this->entityManager->flush();
|
||||
if ($beganTx) {
|
||||
$conn->commit();
|
||||
}
|
||||
} catch (AuditDumpTooMuchLines|AuditDumpAlreadyGeneratedException $e) {
|
||||
if ($beganTx) {
|
||||
$this->entityManager->flush();
|
||||
$conn->commit();
|
||||
}
|
||||
|
||||
throw new UnrecoverableMessageHandlingException(previous: $e);
|
||||
} catch (\Throwable $e) {
|
||||
if ($beganTx && $conn->isTransactionActive()) {
|
||||
$conn->rollBack();
|
||||
}
|
||||
throw $e;
|
||||
} finally {
|
||||
// Clear the EntityManager state at the end
|
||||
$this->entityManager->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Audit\Messenger;
|
||||
|
||||
final readonly class AuditDumpRequestMessage
|
||||
{
|
||||
public function __construct(
|
||||
public string $lang,
|
||||
public int $storedObjectId,
|
||||
public ?\DateTimeImmutable $from = null,
|
||||
public ?\DateTimeImmutable $to = null,
|
||||
public array $subjects = [],
|
||||
public array $byUsers = [],
|
||||
) {}
|
||||
}
|
||||
Reference in New Issue
Block a user