diff --git a/module/Core/config/event_dispatcher.config.php b/module/Core/config/event_dispatcher.config.php index 60869d04..ae2ca64a 100644 --- a/module/Core/config/event_dispatcher.config.php +++ b/module/Core/config/event_dispatcher.config.php @@ -40,6 +40,7 @@ return [ 'em', 'Logger_Shlink', 'config.url_shortener.visits_webhooks', + 'config.url_shortener.domain', ], ], diff --git a/module/Core/src/Entity/Visit.php b/module/Core/src/Entity/Visit.php index ee1b5394..b2ce3640 100644 --- a/module/Core/src/Entity/Visit.php +++ b/module/Core/src/Entity/Visit.php @@ -61,6 +61,11 @@ class Visit extends AbstractEntity implements JsonSerializable return ! empty($this->remoteAddr); } + public function getShortUrl(): ShortUrl + { + return $this->shortUrl; + } + public function getVisitLocation(): VisitLocationInterface { return $this->visitLocation ?? new UnknownVisitLocation(); diff --git a/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php b/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php index 995323e1..fea69ccd 100644 --- a/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php +++ b/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php @@ -10,8 +10,12 @@ use GuzzleHttp\ClientInterface; use GuzzleHttp\RequestOptions; use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Core\Entity\Visit; +use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; +use Throwable; use function Functional\map; +use function Functional\partial_left; +use function GuzzleHttp\Promise\settle; class NotifyVisitToWebHooks { @@ -23,17 +27,21 @@ class NotifyVisitToWebHooks private $logger; /** @var array */ private $webhooks; + /** @var ShortUrlDataTransformer */ + private $transformer; public function __construct( ClientInterface $httpClient, EntityManagerInterface $em, LoggerInterface $logger, - array $webhooks + array $webhooks, + array $domainConfig ) { $this->httpClient = $httpClient; $this->em = $em; $this->logger = $logger; $this->webhooks = $webhooks; + $this->transformer = new ShortUrlDataTransformer($domainConfig); } public function __invoke(ShortUrlLocated $shortUrlLocated): void @@ -51,17 +59,25 @@ class NotifyVisitToWebHooks $requestOptions = [ RequestOptions::TIMEOUT => 10, - RequestOptions::JSON => $visit->jsonSerialize(), + RequestOptions::JSON => [ + 'shortUrl' => $this->transformer->transform($visit->getShortUrl(), false), + 'visit' => $visit->jsonSerialize(), + ], ]; - $requestPromises = map($this->webhooks, function (string $webhook) use ($requestOptions, $visitId) { + $logWebhookWarning = function (string $webhook, Throwable $e) use ($visitId): void { + $this->logger->warning('Failed to notify visit with id "{visitId}" to webhook "{webhook}". {e}', [ + 'visitId' => $visitId, + 'webhook' => $webhook, + 'e' => $e, + ]); + }; + + $requestPromises = map($this->webhooks, function (string $webhook) use ($requestOptions, $logWebhookWarning) { $promise = $this->httpClient->requestAsync(RequestMethodInterface::METHOD_POST, $webhook, $requestOptions); - return $promise->otherwise(function () use ($webhook, $visitId) { - // Log failures - $this->logger->warning('Failed to notify visit with id "{visitId}" to "{webhook}" webhook', [ - 'visitId' => $visitId, - 'webhook' => $webhook, - ]); - }); + return $promise->otherwise(partial_left($logWebhookWarning, $webhook)); }); + + // Wait for all the promises to finish, ignoring rejections, as in those cases we only want to log the error. + settle($requestPromises)->wait(); } } diff --git a/module/Core/src/Transformer/ShortUrlDataTransformer.php b/module/Core/src/Transformer/ShortUrlDataTransformer.php index 4562782f..348ff0d5 100644 --- a/module/Core/src/Transformer/ShortUrlDataTransformer.php +++ b/module/Core/src/Transformer/ShortUrlDataTransformer.php @@ -23,11 +23,11 @@ class ShortUrlDataTransformer implements DataTransformerInterface /** * @param ShortUrl $shortUrl */ - public function transform($shortUrl): array + public function transform($shortUrl, bool $includeDeprecated = true): array { $longUrl = $shortUrl->getLongUrl(); - return [ + $rawData = [ 'shortCode' => $shortUrl->getShortCode(), 'shortUrl' => $shortUrl->toString($this->domainConfig), 'longUrl' => $longUrl, @@ -35,10 +35,13 @@ class ShortUrlDataTransformer implements DataTransformerInterface 'visitsCount' => $shortUrl->getVisitsCount(), 'tags' => invoke($shortUrl->getTags(), '__toString'), 'meta' => $this->buildMeta($shortUrl), - - // Deprecated - 'originalUrl' => $longUrl, ]; + + if ($includeDeprecated) { + $rawData['originalUrl'] = $longUrl; + } + + return $rawData; } private function buildMeta(ShortUrl $shortUrl): array