Completed
Push — develop ( 34d8b3...fd6151 )
by Alejandro
22s queued 14s
created

NotifyVisitToWebHooks::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
nc 1
nop 6
dl 0
loc 14
ccs 7
cts 7
cp 1
crap 1
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\Core\EventDispatcher;
6
7
use Closure;
8
use Doctrine\ORM\EntityManagerInterface;
9
use Fig\Http\Message\RequestMethodInterface;
10
use GuzzleHttp\ClientInterface;
11
use GuzzleHttp\Promise\Promise;
12
use GuzzleHttp\RequestOptions;
13
use Psr\Log\LoggerInterface;
14
use Shlinkio\Shlink\Core\Entity\Visit;
15
use Shlinkio\Shlink\Core\Options\AppOptions;
16
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
17
use Throwable;
18
19
use function Functional\map;
20
use function Functional\partial_left;
21
use function GuzzleHttp\Promise\settle;
22
23
class NotifyVisitToWebHooks
24
{
25
    /** @var ClientInterface */
26
    private $httpClient;
27
    /** @var EntityManagerInterface */
28
    private $em;
29
    /** @var LoggerInterface */
30
    private $logger;
31
    /** @var array */
32
    private $webhooks;
33
    /** @var ShortUrlDataTransformer */
34
    private $transformer;
35
    /** @var AppOptions */
36
    private $appOptions;
37
38 3
    public function __construct(
39
        ClientInterface $httpClient,
40
        EntityManagerInterface $em,
41
        LoggerInterface $logger,
42
        array $webhooks,
43
        array $domainConfig,
44
        AppOptions $appOptions
45
    ) {
46 3
        $this->httpClient = $httpClient;
47 3
        $this->em = $em;
48 3
        $this->logger = $logger;
49 3
        $this->webhooks = $webhooks;
50 3
        $this->transformer = new ShortUrlDataTransformer($domainConfig);
51 3
        $this->appOptions = $appOptions;
52
    }
53
54 3
    public function __invoke(VisitLocated $shortUrlLocated): void
55
    {
56 3
        if (empty($this->webhooks)) {
57 1
            return;
58
        }
59
60 2
        $visitId = $shortUrlLocated->visitId();
61
62
        /** @var Visit|null $visit */
63 2
        $visit = $this->em->find(Visit::class, $visitId);
64 2
        if ($visit === null) {
65 1
            $this->logger->warning('Tried to notify webhooks for visit with id "{visitId}", but it does not exist.', [
66 1
                'visitId' => $visitId,
67
            ]);
68 1
            return;
69
        }
70
71 1
        $requestOptions = $this->buildRequestOptions($visit);
72 1
        $requestPromises = $this->performRequests($requestOptions, $visitId);
73
74
        // Wait for all the promises to finish, ignoring rejections, as in those cases we only want to log the error.
75 1
        settle($requestPromises)->wait();
76
    }
77
78 1
    private function buildRequestOptions(Visit $visit): array
79
    {
80
        return [
81 1
            RequestOptions::TIMEOUT => 10,
82 1
            RequestOptions::HEADERS => [
83 1
                'User-Agent' => (string) $this->appOptions,
84
            ],
85 1
            RequestOptions::JSON => [
86 1
                'shortUrl' => $this->transformer->transform($visit->getShortUrl(), false),
87 1
                'visit' => $visit->jsonSerialize(),
88
            ],
89
        ];
90
    }
91
92
    /**
93
     * @param Promise[] $requestOptions
94
     */
95 1
    private function performRequests(array $requestOptions, string $visitId): array
96
    {
97
        return map($this->webhooks, function (string $webhook) use ($requestOptions, $visitId) {
98 1
            $promise = $this->httpClient->requestAsync(RequestMethodInterface::METHOD_POST, $webhook, $requestOptions);
99 1
            return $promise->otherwise(
100 1
                partial_left(Closure::fromCallable([$this, 'logWebhookFailure']), $webhook, $visitId)
101
            );
102 1
        });
103
    }
104
105 1
    private function logWebhookFailure(string $webhook, string $visitId, Throwable $e): void
106
    {
107 1
        $this->logger->warning('Failed to notify visit with id "{visitId}" to webhook "{webhook}". {e}', [
108 1
            'visitId' => $visitId,
109 1
            'webhook' => $webhook,
110 1
            'e' => $e,
111
        ]);
112
    }
113
}
114