Completed
Pull Request — develop (#724)
by Alejandro
04:45
created

NotifyVisitToWebHooks::logWebhookFailure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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