Added option to disable orphan visitstracking

This commit is contained in:
Alejandro Celaya 2021-02-10 20:09:25 +01:00
parent a18486cc2e
commit 2fc6fb0a9a
6 changed files with 75 additions and 11 deletions

View File

@ -20,6 +20,7 @@ return [
'redirect_status_code' => DEFAULT_REDIRECT_STATUS_CODE,
'redirect_cache_lifetime' => DEFAULT_REDIRECT_CACHE_LIFETIME,
'auto_resolve_titles' => false,
'track_orphan_visits' => true,
],
];

View File

@ -126,6 +126,7 @@ return [
'redirect_status_code' => (int) env('REDIRECT_STATUS_CODE', DEFAULT_REDIRECT_STATUS_CODE),
'redirect_cache_lifetime' => (int) env('REDIRECT_CACHE_LIFETIME', DEFAULT_REDIRECT_CACHE_LIFETIME),
'auto_resolve_titles' => (bool) env('AUTO_RESOLVE_TITLES', false),
'track_orphan_visits' => (bool) env('TRACK_ORPHAN_VISITS', true),
],
'not_found_redirects' => $helper->getNotFoundRedirectsConfig(),

View File

@ -85,7 +85,7 @@ return [
Visit\VisitsTracker::class => [
'em',
EventDispatcherInterface::class,
'config.url_shortener.anonymize_remote_addr',
Options\UrlShortenerOptions::class,
],
Service\ShortUrlService::class => [
'em',

View File

@ -19,6 +19,8 @@ class UrlShortenerOptions extends AbstractOptions
private int $redirectStatusCode = DEFAULT_REDIRECT_STATUS_CODE;
private int $redirectCacheLifetime = DEFAULT_REDIRECT_CACHE_LIFETIME;
private bool $autoResolveTitles = false;
private bool $anonymizeRemoteAddr = true;
private bool $trackOrphanVisits = true;
public function isUrlValidationEnabled(): bool
{
@ -62,9 +64,28 @@ class UrlShortenerOptions extends AbstractOptions
return $this->autoResolveTitles;
}
protected function setAutoResolveTitles(bool $autoResolveTitles): self
protected function setAutoResolveTitles(bool $autoResolveTitles): void
{
$this->autoResolveTitles = $autoResolveTitles;
return $this;
}
public function anonymizeRemoteAddr(): bool
{
return $this->anonymizeRemoteAddr;
}
protected function setAnonymizeRemoteAddr(bool $anonymizeRemoteAddr): void
{
$this->anonymizeRemoteAddr = $anonymizeRemoteAddr;
}
public function trackOrphanVisits(): bool
{
return $this->trackOrphanVisits;
}
protected function setTrackOrphanVisits(bool $trackOrphanVisits): void
{
$this->trackOrphanVisits = $trackOrphanVisits;
}
}

View File

@ -10,41 +10,57 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\EventDispatcher\Event\UrlVisited;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
class VisitsTracker implements VisitsTrackerInterface
{
private ORM\EntityManagerInterface $em;
private EventDispatcherInterface $eventDispatcher;
private bool $anonymizeRemoteAddr;
private UrlShortenerOptions $options;
public function __construct(
ORM\EntityManagerInterface $em,
EventDispatcherInterface $eventDispatcher,
bool $anonymizeRemoteAddr
UrlShortenerOptions $options
) {
$this->em = $em;
$this->eventDispatcher = $eventDispatcher;
$this->anonymizeRemoteAddr = $anonymizeRemoteAddr;
$this->options = $options;
}
public function track(ShortUrl $shortUrl, Visitor $visitor): void
{
$this->trackVisit(Visit::forValidShortUrl($shortUrl, $visitor, $this->anonymizeRemoteAddr), $visitor);
$this->trackVisit(
Visit::forValidShortUrl($shortUrl, $visitor, $this->options->anonymizeRemoteAddr()),
$visitor,
);
}
public function trackInvalidShortUrlVisit(Visitor $visitor): void
{
$this->trackVisit(Visit::forInvalidShortUrl($visitor, $this->anonymizeRemoteAddr), $visitor);
if (! $this->options->trackOrphanVisits()) {
return;
}
$this->trackVisit(Visit::forInvalidShortUrl($visitor, $this->options->anonymizeRemoteAddr()), $visitor);
}
public function trackBaseUrlVisit(Visitor $visitor): void
{
$this->trackVisit(Visit::forBasePath($visitor, $this->anonymizeRemoteAddr), $visitor);
if (! $this->options->trackOrphanVisits()) {
return;
}
$this->trackVisit(Visit::forBasePath($visitor, $this->options->anonymizeRemoteAddr()), $visitor);
}
public function trackRegularNotFoundVisit(Visitor $visitor): void
{
$this->trackVisit(Visit::forRegularNotFound($visitor, $this->anonymizeRemoteAddr), $visitor);
if (! $this->options->trackOrphanVisits()) {
return;
}
$this->trackVisit(Visit::forRegularNotFound($visitor, $this->options->anonymizeRemoteAddr()), $visitor);
}
private function trackVisit(Visit $visit, Visitor $visitor): void

View File

@ -14,6 +14,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\EventDispatcher\Event\UrlVisited;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Shlinkio\Shlink\Core\Visit\VisitsTracker;
class VisitsTrackerTest extends TestCase
@ -23,13 +24,15 @@ class VisitsTrackerTest extends TestCase
private VisitsTracker $visitsTracker;
private ObjectProphecy $em;
private ObjectProphecy $eventDispatcher;
private UrlShortenerOptions $options;
public function setUp(): void
{
$this->em = $this->prophesize(EntityManager::class);
$this->eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
$this->options = new UrlShortenerOptions();
$this->visitsTracker = new VisitsTracker($this->em->reveal(), $this->eventDispatcher->reveal(), true);
$this->visitsTracker = new VisitsTracker($this->em->reveal(), $this->eventDispatcher->reveal(), $this->options);
}
/**
@ -53,4 +56,26 @@ class VisitsTrackerTest extends TestCase
yield 'trackBaseUrlVisit' => ['trackBaseUrlVisit', [Visitor::emptyInstance()]];
yield 'trackRegularNotFoundVisit' => ['trackRegularNotFoundVisit', [Visitor::emptyInstance()]];
}
/**
* @test
* @dataProvider provideOrphanTrackingMethodNames
*/
public function orphanVisitsAreNotTrackedWhenDisabled(string $method): void
{
$this->options->trackOrphanVisits = false;
$this->visitsTracker->{$method}(Visitor::emptyInstance());
$this->eventDispatcher->dispatch(Argument::cetera())->shouldNotHaveBeenCalled();
$this->em->persist(Argument::cetera())->shouldNotHaveBeenCalled();
$this->em->flush()->shouldNotHaveBeenCalled();
}
public function provideOrphanTrackingMethodNames(): iterable
{
yield 'trackInvalidShortUrlVisit' => ['trackInvalidShortUrlVisit'];
yield 'trackBaseUrlVisit' => ['trackBaseUrlVisit'];
yield 'trackRegularNotFoundVisit' => ['trackRegularNotFoundVisit'];
}
}