Completed
Push — develop ( 8109ce...b15e90 )
by Alejandro
17s queued 12s
created

GeolocationDbUpdater   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 67
Duplicated Lines 0 %

Test Coverage

Coverage 93.55%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 9
eloc 29
dl 0
loc 67
ccs 29
cts 31
cp 0.9355
rs 10
c 1
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A buildIsTooOld() 0 5 1
A downloadIfNeeded() 0 10 3
A downloadNewDb() 0 10 3
A __construct() 0 5 1
A checkDbUpdate() 0 9 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\CLI\Util;
6
7
use Cake\Chronos\Chronos;
8
use GeoIp2\Database\Reader;
9
use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException;
10
use Shlinkio\Shlink\IpGeolocation\Exception\RuntimeException;
11
use Shlinkio\Shlink\IpGeolocation\GeoLite2\DbUpdaterInterface;
12
use Symfony\Component\Lock\LockFactory;
13
14
class GeolocationDbUpdater implements GeolocationDbUpdaterInterface
15
{
16
    private const LOCK_NAME = 'geolocation-db-update';
17
18
    private DbUpdaterInterface $dbUpdater;
19
    private Reader $geoLiteDbReader;
20
    private LockFactory $locker;
21
22 40
    public function __construct(DbUpdaterInterface $dbUpdater, Reader $geoLiteDbReader, LockFactory $locker)
23
    {
24 40
        $this->dbUpdater = $dbUpdater;
25 40
        $this->geoLiteDbReader = $geoLiteDbReader;
26 40
        $this->locker = $locker;
27 40
    }
28
29
    /**
30
     * @throws GeolocationDbUpdateFailedException
31
     */
32 40
    public function checkDbUpdate(?callable $mustBeUpdated = null, ?callable $handleProgress = null): void
33
    {
34 40
        $lock = $this->locker->createLock(self::LOCK_NAME);
35 40
        $lock->acquire(true); // Block until lock is released
36
37
        try {
38 40
            $this->downloadIfNeeded($mustBeUpdated, $handleProgress);
39 35
        } finally {
40 40
            $lock->release();
41
        }
42 35
    }
43
44
    /**
45
     * @throws GeolocationDbUpdateFailedException
46
     */
47 40
    private function downloadIfNeeded(?callable $mustBeUpdated, ?callable $handleProgress): void
48
    {
49 40
        if (! $this->dbUpdater->databaseFileExists()) {
50 1
            $this->downloadNewDb(false, $mustBeUpdated, $handleProgress);
51
            return;
52
        }
53
54 39
        $meta = $this->geoLiteDbReader->metadata();
55 39
        if ($this->buildIsTooOld($meta->buildEpoch)) {
56 4
            $this->downloadNewDb(true, $mustBeUpdated, $handleProgress);
57
        }
58 35
    }
59
60
    /**
61
     * @throws GeolocationDbUpdateFailedException
62
     */
63 5
    private function downloadNewDb(bool $olderDbExists, ?callable $mustBeUpdated, ?callable $handleProgress): void
64
    {
65 5
        if ($mustBeUpdated !== null) {
66 1
            $mustBeUpdated($olderDbExists);
67
        }
68
69
        try {
70 5
            $this->dbUpdater->downloadFreshCopy($handleProgress);
71 5
        } catch (RuntimeException $e) {
72 5
            throw GeolocationDbUpdateFailedException::create($olderDbExists, $e);
73
        }
74
    }
75
76 39
    private function buildIsTooOld(int $buildTimestamp): bool
77
    {
78 39
        $buildDate = Chronos::createFromTimestamp($buildTimestamp);
79 39
        $now = Chronos::now();
80 39
        return $now->gt($buildDate->addDays(35));
81
    }
82
}
83