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

LocateShortUrlVisit::__invoke()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 18
ccs 10
cts 10
cp 1
crap 3
rs 9.9666
c 2
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\Core\EventDispatcher;
6
7
use Doctrine\ORM\EntityManagerInterface;
8
use Psr\EventDispatcher\EventDispatcherInterface;
9
use Psr\Log\LoggerInterface;
10
use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException;
11
use Shlinkio\Shlink\CLI\Util\GeolocationDbUpdaterInterface;
12
use Shlinkio\Shlink\Core\Entity\Visit;
13
use Shlinkio\Shlink\Core\Entity\VisitLocation;
14
use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
15
use Shlinkio\Shlink\IpGeolocation\Model\Location;
16
use Shlinkio\Shlink\IpGeolocation\Resolver\IpLocationResolverInterface;
17
18
use function sprintf;
19
20
class LocateShortUrlVisit
21
{
22
    /** @var IpLocationResolverInterface */
23
    private $ipLocationResolver;
24
    /** @var EntityManagerInterface */
25
    private $em;
26
    /** @var LoggerInterface */
27
    private $logger;
28
    /** @var GeolocationDbUpdaterInterface */
29
    private $dbUpdater;
30
    /** @var EventDispatcherInterface */
31
    private $eventDispatcher;
32
33 8
    public function __construct(
34
        IpLocationResolverInterface $ipLocationResolver,
35
        EntityManagerInterface $em,
36
        LoggerInterface $logger,
37
        GeolocationDbUpdaterInterface $dbUpdater,
38
        EventDispatcherInterface $eventDispatcher
39
    ) {
40 8
        $this->ipLocationResolver = $ipLocationResolver;
41 8
        $this->em = $em;
42 8
        $this->logger = $logger;
43 8
        $this->dbUpdater = $dbUpdater;
44 8
        $this->eventDispatcher = $eventDispatcher;
45
    }
46
47 8
    public function __invoke(ShortUrlVisited $shortUrlVisited): void
48
    {
49 8
        $visitId = $shortUrlVisited->visitId();
50
51
        /** @var Visit|null $visit */
52 8
        $visit = $this->em->find(Visit::class, $visitId);
53 8
        if ($visit === null) {
54 1
            $this->logger->warning('Tried to locate visit with id "{visitId}", but it does not exist.', [
55 1
                'visitId' => $visitId,
56
            ]);
57 1
            return;
58
        }
59
60 7
        if ($this->downloadOrUpdateGeoLiteDb($visitId)) {
61 6
            $this->locateVisit($visitId, $visit);
62
        }
63
64 7
        $this->eventDispatcher->dispatch(new VisitLocated($visitId));
65
    }
66
67 7
    private function downloadOrUpdateGeoLiteDb(string $visitId): bool
68
    {
69
        try {
70
            $this->dbUpdater->checkDbUpdate(function (bool $olderDbExists) {
71
                $this->logger->notice(sprintf('%s GeoLite2 database...', $olderDbExists ? 'Updating' : 'Downloading'));
72 7
            });
73 2
        } catch (GeolocationDbUpdateFailedException $e) {
74 2
            if (! $e->olderDbExists()) {
75 1
                $this->logger->error(
76 1
                    'GeoLite2 database download failed. It is not possible to locate visit with id {visitId}. {e}',
77 1
                    ['e' => $e, 'visitId' => $visitId]
78
                );
79 1
                return false;
80
            }
81
82 1
            $this->logger->warning('GeoLite2 database update failed. Proceeding with old version. {e}', ['e' => $e]);
83
        }
84
85 6
        return true;
86
    }
87
88 6
    private function locateVisit(string $visitId, Visit $visit): void
89
    {
90
        try {
91 6
            $location = $visit->isLocatable()
92 3
                ? $this->ipLocationResolver->resolveIpLocation($visit->getRemoteAddr())
93 5
                : Location::emptyInstance();
94
95 5
            $visit->locate(new VisitLocation($location));
96 5
            $this->em->flush();
97 1
        } catch (WrongIpException $e) {
98 1
            $this->logger->warning(
99 1
                'Tried to locate visit with id "{visitId}", but its address seems to be wrong. {e}',
100 1
                ['e' => $e, 'visitId' => $visitId]
101
            );
102
        }
103
    }
104
}
105