diff --git a/docs/adr/2022-09-18-allow-delaying-initial-geolite2-db-download-in-docker-images.md b/docs/adr/2022-09-18-allow-delaying-initial-geolite2-db-download-in-docker-images.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/adr/README.md b/docs/adr/README.md index 7cfccdf7..f12d17c1 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -2,6 +2,7 @@ Here listed you will find the different architectural decisions taken in the project, including all the reasoning behind it, options considered, and final outcome. +* [2022-09-18 Allow delaying initial GeoLite2 DB download in docker images](2022-09-18-allow-delaying-initial-geolite2-db-download-in-docker-images.md) * [2022-08-05 Support multi-segment custom slugs](2022-08-05-support-multi-segment-custom-slugs.md) * [2022-01-15 Update env vars behavior to have precedence over installer options](2022-01-15-update-env-vars-behavior-to-have-precedence-over-installer-options.md) * [2021-08-05 Migrate to a new caching library](2021-08-05-migrate-to-a-new-caching-library.md) diff --git a/module/Core/config/event_dispatcher.config.php b/module/Core/config/event_dispatcher.config.php index 4b6c9048..3a4d10f5 100644 --- a/module/Core/config/event_dispatcher.config.php +++ b/module/Core/config/event_dispatcher.config.php @@ -20,6 +20,9 @@ return [ EventDispatcher\Event\UrlVisited::class => [ EventDispatcher\LocateVisit::class, ], + EventDispatcher\Event\GeoLiteDbCreated::class => [ +// EventDispatcher\LocateUnloctedVisits::class, + ], ], 'async' => [ EventDispatcher\Event\VisitLocated::class => [ @@ -132,7 +135,11 @@ return [ 'Logger_Shlink', 'config.redis.pub_sub_enabled', ], - EventDispatcher\UpdateGeoLiteDb::class => [GeolocationDbUpdater::class, 'Logger_Shlink'], + EventDispatcher\UpdateGeoLiteDb::class => [ + GeolocationDbUpdater::class, + 'Logger_Shlink', + EventDispatcherInterface::class, + ], ], ]; diff --git a/module/Core/src/EventDispatcher/Event/GeoLiteDbCreated.php b/module/Core/src/EventDispatcher/Event/GeoLiteDbCreated.php new file mode 100644 index 00000000..3fc86cd7 --- /dev/null +++ b/module/Core/src/EventDispatcher/Event/GeoLiteDbCreated.php @@ -0,0 +1,9 @@ +dbUpdater->checkDbUpdate($beforeDownload, $handleProgress); + $result = $this->dbUpdater->checkDbUpdate($beforeDownload, $handleProgress); + if ($result === GeolocationResult::DB_CREATED) { + $this->eventDispatcher->dispatch(new GeoLiteDbCreated()); + } } catch (Throwable $e) { $this->logger->error('GeoLite2 database download failed. {e}', ['e' => $e]); } diff --git a/module/Core/test/EventDispatcher/UpdateGeoLiteDbTest.php b/module/Core/test/EventDispatcher/UpdateGeoLiteDbTest.php index a811fdc4..9ce20801 100644 --- a/module/Core/test/EventDispatcher/UpdateGeoLiteDbTest.php +++ b/module/Core/test/EventDispatcher/UpdateGeoLiteDbTest.php @@ -8,12 +8,16 @@ use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Log\LoggerInterface; use RuntimeException; use Shlinkio\Shlink\CLI\GeoLite\GeolocationDbUpdaterInterface; use Shlinkio\Shlink\CLI\GeoLite\GeolocationResult; +use Shlinkio\Shlink\Core\EventDispatcher\Event\GeoLiteDbCreated; use Shlinkio\Shlink\Core\EventDispatcher\UpdateGeoLiteDb; +use function Functional\map; + class UpdateGeoLiteDbTest extends TestCase { use ProphecyTrait; @@ -21,13 +25,19 @@ class UpdateGeoLiteDbTest extends TestCase private UpdateGeoLiteDb $listener; private ObjectProphecy $dbUpdater; private ObjectProphecy $logger; + private ObjectProphecy $eventDispatcher; protected function setUp(): void { $this->dbUpdater = $this->prophesize(GeolocationDbUpdaterInterface::class); $this->logger = $this->prophesize(LoggerInterface::class); + $this->eventDispatcher = $this->prophesize(EventDispatcherInterface::class); - $this->listener = new UpdateGeoLiteDb($this->dbUpdater->reveal(), $this->logger->reveal()); + $this->listener = new UpdateGeoLiteDb( + $this->dbUpdater->reveal(), + $this->logger->reveal(), + $this->eventDispatcher->reveal(), + ); } /** @test */ @@ -43,6 +53,7 @@ class UpdateGeoLiteDbTest extends TestCase $checkDbUpdate->shouldHaveBeenCalledOnce(); $logError->shouldHaveBeenCalledOnce(); $this->logger->notice(Argument::cetera())->shouldNotHaveBeenCalled(); + $this->eventDispatcher->dispatch(Argument::cetera())->shouldNotHaveBeenCalled(); } /** @@ -56,7 +67,7 @@ class UpdateGeoLiteDbTest extends TestCase [$firstCallback] = $args; $firstCallback($oldDbExists); - return GeolocationResult::DB_CREATED; + return GeolocationResult::DB_IS_UP_TO_DATE; }, ); $logNotice = $this->logger->notice($expectedMessage); @@ -66,6 +77,7 @@ class UpdateGeoLiteDbTest extends TestCase $checkDbUpdate->shouldHaveBeenCalledOnce(); $logNotice->shouldHaveBeenCalledOnce(); $this->logger->error(Argument::cetera())->shouldNotHaveBeenCalled(); + $this->eventDispatcher->dispatch(Argument::cetera())->shouldNotHaveBeenCalled(); } public function provideFlags(): iterable @@ -93,7 +105,7 @@ class UpdateGeoLiteDbTest extends TestCase $secondCallback($total, $downloaded, $oldDbExists); $secondCallback($total, $downloaded, $oldDbExists); - return GeolocationResult::DB_CREATED; + return GeolocationResult::DB_UPDATED; }, ); $logNotice = $this->logger->notice($expectedMessage ?? Argument::cetera()); @@ -107,6 +119,7 @@ class UpdateGeoLiteDbTest extends TestCase } $checkDbUpdate->shouldHaveBeenCalledOnce(); $this->logger->error(Argument::cetera())->shouldNotHaveBeenCalled(); + $this->eventDispatcher->dispatch(Argument::cetera())->shouldNotHaveBeenCalled(); } public function provideDownloaded(): iterable @@ -120,4 +133,28 @@ class UpdateGeoLiteDbTest extends TestCase yield [100, 101, true, 'Finished updating GeoLite2 db file']; yield [100, 101, false, 'Finished downloading GeoLite2 db file']; } + + /** + * @test + * @dataProvider provideGeolocationResults + */ + public function dispatchesEventOnlyWhenDbFileHasBeenCreatedForTheFirstTime( + GeolocationResult $result, + int $expectedDispatches, + ): void { + $checkDbUpdate = $this->dbUpdater->checkDbUpdate(Argument::cetera())->willReturn($result); + + ($this->listener)(); + + $checkDbUpdate->shouldHaveBeenCalledOnce(); + $this->eventDispatcher->dispatch(new GeoLiteDbCreated())->shouldHaveBeenCalledTimes($expectedDispatches); + } + + public function provideGeolocationResults(): iterable + { + return map(GeolocationResult::cases(), static fn (GeolocationResult $value) => [ + $value, + $value === GeolocationResult::DB_CREATED ? 1 : 0, + ]); + } }