Moved duplicated code in visit listeners to an abstract class

This commit is contained in:
Alejandro Celaya 2022-07-27 18:18:36 +02:00
parent 26037327f9
commit e36c4d397c
8 changed files with 133 additions and 155 deletions

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\EventDispatcher\Async;
abstract class AbstractAsyncListener
{
abstract protected function isEnabled(): bool;
abstract protected function getRemoteSystemName(): string;
}

View File

@ -12,10 +12,10 @@ use Shlinkio\Shlink\Core\EventDispatcher\Event\ShortUrlCreated;
use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface;
use Throwable;
abstract class AbstractNotifyNewShortUrlListener
abstract class AbstractNotifyNewShortUrlListener extends AbstractAsyncListener
{
public function __construct(
private readonly PublishingHelperInterface $mercureHelper,
private readonly PublishingHelperInterface $publishingHelper,
private readonly PublishingUpdatesGeneratorInterface $updatesGenerator,
private readonly EntityManagerInterface $em,
private readonly LoggerInterface $logger,
@ -41,7 +41,7 @@ abstract class AbstractNotifyNewShortUrlListener
}
try {
$this->mercureHelper->publishUpdate($this->updatesGenerator->newShortUrlUpdate($shortUrl));
$this->publishingHelper->publishUpdate($this->updatesGenerator->newShortUrlUpdate($shortUrl));
} catch (Throwable $e) {
$this->logger->debug(
'Error while trying to notify {name} with new short URL. {e}',
@ -49,8 +49,4 @@ abstract class AbstractNotifyNewShortUrlListener
);
}
}
abstract protected function isEnabled(): bool;
abstract protected function getRemoteSystemName(): string;
}

View File

@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\EventDispatcher\Async;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface;
use Shlinkio\Shlink\Common\UpdatePublishing\Update;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated;
use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface;
use Throwable;
use function Functional\each;
abstract class AbstractNotifyVisitListener extends AbstractAsyncListener
{
public function __construct(
private readonly PublishingHelperInterface $publishingHelper,
private readonly PublishingUpdatesGeneratorInterface $updatesGenerator,
private readonly EntityManagerInterface $em,
private readonly LoggerInterface $logger,
) {
}
public function __invoke(VisitLocated $visitLocated): void
{
if (! $this->isEnabled()) {
return;
}
$visitId = $visitLocated->visitId;
$visit = $this->em->find(Visit::class, $visitId);
$name = $this->getRemoteSystemName();
if ($visit === null) {
$this->logger->warning(
'Tried to notify {name} for visit with id "{visitId}", but it does not exist.',
['visitId' => $visitId, 'name' => $name],
);
return;
}
$updates = $this->determineUpdatesForVisit($visit);
try {
each($updates, fn (Update $update) => $this->publishingHelper->publishUpdate($update));
} catch (Throwable $e) {
$this->logger->debug(
'Error while trying to notify {name} with new visit. {e}',
['e' => $e, 'name' => $name],
);
}
}
/**
* @return Update[]
*/
protected function determineUpdatesForVisit(Visit $visit): array
{
if ($visit->isOrphan()) {
return [$this->updatesGenerator->newOrphanVisitUpdate($visit)];
}
return [
$this->updatesGenerator->newShortUrlVisitUpdate($visit),
$this->updatesGenerator->newVisitUpdate($visit),
];
}
}

View File

@ -4,64 +4,17 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\EventDispatcher\Mercure;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface;
use Shlinkio\Shlink\Common\UpdatePublishing\Update;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated;
use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface;
use Throwable;
use Shlinkio\Shlink\Core\EventDispatcher\Async\AbstractNotifyVisitListener;
use function Functional\each;
class NotifyVisitToMercure
class NotifyVisitToMercure extends AbstractNotifyVisitListener
{
public function __construct(
private readonly PublishingHelperInterface $mercureHelper,
private readonly PublishingUpdatesGeneratorInterface $updatesGenerator,
private readonly EntityManagerInterface $em,
private readonly LoggerInterface $logger,
) {
protected function isEnabled(): bool
{
return true;
}
public function __invoke(VisitLocated $visitLocated): void
protected function getRemoteSystemName(): string
{
$visitId = $visitLocated->visitId;
/** @var Visit|null $visit */
$visit = $this->em->find(Visit::class, $visitId);
if ($visit === null) {
$this->logger->warning('Tried to notify mercure for visit with id "{visitId}", but it does not exist.', [
'visitId' => $visitId,
]);
return;
}
try {
each(
$this->determineUpdatesForVisit($visit),
fn (Update $update) => $this->mercureHelper->publishUpdate($update),
);
} catch (Throwable $e) {
$this->logger->debug('Error while trying to notify mercure hub with new visit. {e}', [
'e' => $e,
]);
}
}
/**
* @return Update[]
*/
private function determineUpdatesForVisit(Visit $visit): array
{
if ($visit->isOrphan()) {
return [$this->updatesGenerator->newOrphanVisitUpdate($visit)];
}
return [
$this->updatesGenerator->newShortUrlVisitUpdate($visit),
$this->updatesGenerator->newVisitUpdate($visit),
];
return 'Mercure';
}
}

View File

@ -10,55 +10,28 @@ use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface;
use Shlinkio\Shlink\Common\UpdatePublishing\Update;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated;
use Shlinkio\Shlink\Core\EventDispatcher\Async\AbstractNotifyVisitListener;
use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface;
use Shlinkio\Shlink\Core\EventDispatcher\Topic;
use Shlinkio\Shlink\Core\Options\RabbitMqOptions;
use Throwable;
use function Functional\each;
class NotifyVisitToRabbitMq
class NotifyVisitToRabbitMq extends AbstractNotifyVisitListener
{
public function __construct(
private readonly PublishingHelperInterface $rabbitMqHelper,
private readonly PublishingUpdatesGeneratorInterface $updatesGenerator,
private readonly EntityManagerInterface $em,
private readonly LoggerInterface $logger,
PublishingHelperInterface $rabbitMqHelper,
PublishingUpdatesGeneratorInterface $updatesGenerator,
EntityManagerInterface $em,
LoggerInterface $logger,
private readonly DataTransformerInterface $orphanVisitTransformer,
private readonly RabbitMqOptions $options,
) {
}
public function __invoke(VisitLocated $visitLocated): void
{
if (! $this->options->isEnabled()) {
return;
}
$visitId = $visitLocated->visitId;
$visit = $this->em->find(Visit::class, $visitId);
if ($visit === null) {
$this->logger->warning('Tried to notify RabbitMQ for visit with id "{visitId}", but it does not exist.', [
'visitId' => $visitId,
]);
return;
}
$updates = $this->determineUpdatesForVisit($visit);
try {
each($updates, fn (Update $update) => $this->rabbitMqHelper->publishUpdate($update));
} catch (Throwable $e) {
$this->logger->debug('Error while trying to notify RabbitMQ with new visit. {e}', ['e' => $e]);
}
parent::__construct($rabbitMqHelper, $updatesGenerator, $em, $logger);
}
/**
* @return Update[]
*/
private function determineUpdatesForVisit(Visit $visit): array
protected function determineUpdatesForVisit(Visit $visit): array
{
return match (true) {
// This was defined incorrectly.
@ -79,12 +52,18 @@ class NotifyVisitToRabbitMq
),
],
// Once the two deprecated cases above have been remove, replace this with a simple "if" and early return.
$visit->isOrphan() => [$this->updatesGenerator->newOrphanVisitUpdate($visit)],
default => [
$this->updatesGenerator->newShortUrlVisitUpdate($visit),
$this->updatesGenerator->newVisitUpdate($visit),
],
// Once the two deprecated cases above have been remove, make parent method private
default => parent::determineUpdatesForVisit($visit),
};
}
protected function isEnabled(): bool
{
return $this->options->isEnabled();
}
protected function getRemoteSystemName(): string
{
return 'RabbitMQ';
}
}

View File

@ -7,63 +7,28 @@ namespace Shlinkio\Shlink\Core\EventDispatcher\RedisPubSub;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface;
use Shlinkio\Shlink\Common\UpdatePublishing\Update;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated;
use Shlinkio\Shlink\Core\EventDispatcher\Async\AbstractNotifyVisitListener;
use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface;
use Throwable;
use function Functional\each;
class NotifyVisitToRedis
class NotifyVisitToRedis extends AbstractNotifyVisitListener
{
public function __construct(
private readonly PublishingHelperInterface $redisHelper,
private readonly PublishingUpdatesGeneratorInterface $updatesGenerator,
private readonly EntityManagerInterface $em,
private readonly LoggerInterface $logger,
PublishingHelperInterface $redisHelper,
PublishingUpdatesGeneratorInterface $updatesGenerator,
EntityManagerInterface $em,
LoggerInterface $logger,
private readonly bool $enabled,
) {
parent::__construct($redisHelper, $updatesGenerator, $em, $logger);
}
public function __invoke(VisitLocated $visitLocated): void
protected function isEnabled(): bool
{
if (! $this->enabled) {
return;
}
$visitId = $visitLocated->visitId;
$visit = $this->em->find(Visit::class, $visitId);
if ($visit === null) {
$this->logger->warning(
'Tried to notify Redis pub/sub for visit with id "{visitId}", but it does not exist.',
['visitId' => $visitId],
);
return;
}
$updates = $this->determineUpdatesForVisit($visit);
try {
each($updates, fn (Update $update) => $this->redisHelper->publishUpdate($update));
} catch (Throwable $e) {
$this->logger->debug('Error while trying to notify Redis pub/sub with new visit. {e}', ['e' => $e]);
}
return $this->enabled;
}
/**
* @return Update[]
*/
private function determineUpdatesForVisit(Visit $visit): array
protected function getRemoteSystemName(): string
{
if ($visit->isOrphan()) {
return [$this->updatesGenerator->newOrphanVisitUpdate($visit)];
}
return [
$this->updatesGenerator->newShortUrlVisitUpdate($visit),
$this->updatesGenerator->newVisitUpdate($visit),
];
return 'Redis pub/sub';
}
}

View File

@ -52,8 +52,8 @@ class NotifyVisitToMercureTest extends TestCase
$visitId = '123';
$findVisit = $this->em->find(Visit::class, $visitId)->willReturn(null);
$logWarning = $this->logger->warning(
'Tried to notify mercure for visit with id "{visitId}", but it does not exist.',
['visitId' => $visitId],
'Tried to notify {name} for visit with id "{visitId}", but it does not exist.',
['visitId' => $visitId, 'name' => 'Mercure'],
);
$logDebug = $this->logger->debug(Argument::cetera());
$buildNewShortUrlVisitUpdate = $this->updatesGenerator->newShortUrlVisitUpdate(
@ -110,8 +110,9 @@ class NotifyVisitToMercureTest extends TestCase
$findVisit = $this->em->find(Visit::class, $visitId)->willReturn($visit);
$logWarning = $this->logger->warning(Argument::cetera());
$logDebug = $this->logger->debug('Error while trying to notify mercure hub with new visit. {e}', [
$logDebug = $this->logger->debug('Error while trying to notify {name} with new visit. {e}', [
'e' => $e,
'name' => 'Mercure',
]);
$buildNewShortUrlVisitUpdate = $this->updatesGenerator->newShortUrlVisitUpdate($visit)->willReturn($update);
$buildNewOrphanVisitUpdate = $this->updatesGenerator->newOrphanVisitUpdate($visit)->willReturn($update);

View File

@ -79,8 +79,8 @@ class NotifyVisitToRabbitMqTest extends TestCase
$visitId = '123';
$findVisit = $this->em->find(Visit::class, $visitId)->willReturn(null);
$logWarning = $this->logger->warning(
'Tried to notify RabbitMQ for visit with id "{visitId}", but it does not exist.',
['visitId' => $visitId],
'Tried to notify {name} for visit with id "{visitId}", but it does not exist.',
['visitId' => $visitId, 'name' => 'RabbitMQ'],
);
($this->listener)(new VisitLocated($visitId));
@ -147,8 +147,8 @@ class NotifyVisitToRabbitMqTest extends TestCase
($this->listener)(new VisitLocated($visitId));
$this->logger->debug(
'Error while trying to notify RabbitMQ with new visit. {e}',
['e' => $e],
'Error while trying to notify {name} with new visit. {e}',
['e' => $e, 'name' => 'RabbitMQ'],
)->shouldHaveBeenCalledOnce();
$findVisit->shouldHaveBeenCalledOnce();
$generateUpdate->shouldHaveBeenCalledOnce();