Improved public API in Visit entity, reducing anemic model

This commit is contained in:
Alejandro Celaya 2018-10-28 16:20:10 +01:00
parent 8e1e8ba7de
commit 951d08f914
9 changed files with 60 additions and 94 deletions

View file

@ -9,8 +9,10 @@ use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\ShortUrl\GetVisitsCommand;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
@ -79,9 +81,9 @@ class GetVisitsCommandTest extends TestCase
{
$shortCode = 'abc123';
$this->visitsTracker->info($shortCode, Argument::any())->willReturn([
(new Visit())->setReferer('foo')
->setVisitLocation(new VisitLocation(['country_name' => 'Spain']))
->setUserAgent('bar'),
(new Visit(new ShortUrl(''), new Visitor('bar', 'foo', '')))->setVisitLocation(
new VisitLocation(['country_name' => 'Spain'])
),
])->shouldBeCalledTimes(1);
$this->commandTester->execute([

View file

@ -8,7 +8,9 @@ use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Visit\ProcessVisitsCommand;
use Shlinkio\Shlink\Common\Service\IpApiLocationResolver;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Service\VisitService;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Output\OutputInterface;
@ -54,10 +56,12 @@ class ProcessVisitsCommandTest extends TestCase
*/
public function allReturnedVisitsIpsAreProcessed()
{
$shortUrl = new ShortUrl('');
$visits = [
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
];
$this->visitService->getUnlocatedVisits()->willReturn($visits)
->shouldBeCalledTimes(1);
@ -80,14 +84,16 @@ class ProcessVisitsCommandTest extends TestCase
*/
public function localhostAndEmptyAddressIsIgnored()
{
$shortUrl = new ShortUrl('');
$visits = [
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
(new Visit())->setRemoteAddr('127.0.0.1'),
(new Visit())->setRemoteAddr('127.0.0.1'),
(new Visit())->setRemoteAddr(''),
(new Visit())->setRemoteAddr(null),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
new Visit($shortUrl, new Visitor('', '', '127.0.0.1')),
new Visit($shortUrl, new Visitor('', '', '127.0.0.1')),
new Visit($shortUrl, new Visitor('', '', '')),
new Visit($shortUrl, new Visitor('', '', null)),
];
$this->visitService->getUnlocatedVisits()->willReturn($visits)
->shouldBeCalledTimes(1);
@ -109,17 +115,19 @@ class ProcessVisitsCommandTest extends TestCase
*/
public function sleepsEveryTimeTheApiLimitIsReached()
{
$shortUrl = new ShortUrl('');
$visits = [
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
(new Visit())->setRemoteAddr('4.3.2.1'),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
];
$apiLimit = 3;

View file

@ -9,6 +9,7 @@ use JsonSerializable;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\Util\IpAddress;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\VisitRepository;
/**
@ -54,58 +55,13 @@ class Visit extends AbstractEntity implements JsonSerializable
*/
private $visitLocation;
public function __construct()
{
$this->date = Chronos::now();
}
public function getReferer(): string
{
return $this->referer;
}
public function setReferer(string $referer): self
{
$this->referer = $referer;
return $this;
}
public function getDate(): Chronos
{
return $this->date;
}
public function setDate(Chronos $date): self
{
$this->date = $date;
return $this;
}
public function getShortUrl(): ShortUrl
{
return $this->shortUrl;
}
public function setShortUrl(ShortUrl $shortUrl): self
public function __construct(ShortUrl $shortUrl, Visitor $visitor, ?Chronos $date = null)
{
$this->shortUrl = $shortUrl;
return $this;
}
public function getRemoteAddr(): ?string
{
return $this->remoteAddr;
}
public function setRemoteAddr(?string $remoteAddr): self
{
$this->remoteAddr = $this->obfuscateAddress($remoteAddr);
return $this;
}
public function hasRemoteAddr(): bool
{
return ! empty($this->remoteAddr);
$this->date = $date ?? Chronos::now();
$this->userAgent = $visitor->getUserAgent();
$this->referer = $visitor->getReferer();
$this->remoteAddr = $this->obfuscateAddress($visitor->getRemoteAddress());
}
private function obfuscateAddress(?string $address): ?string
@ -122,15 +78,14 @@ class Visit extends AbstractEntity implements JsonSerializable
}
}
public function getUserAgent(): string
public function getRemoteAddr(): ?string
{
return $this->userAgent;
return $this->remoteAddr;
}
public function setUserAgent(string $userAgent): self
public function hasRemoteAddr(): bool
{
$this->userAgent = $userAgent;
return $this;
return ! empty($this->remoteAddr);
}
public function getVisitLocation(): VisitLocation

View file

@ -34,11 +34,7 @@ class VisitsTracker implements VisitsTrackerInterface
'shortCode' => $shortCode,
]);
$visit = new Visit();
$visit->setShortUrl($shortUrl)
->setUserAgent($visitor->getUserAgent())
->setReferer($visitor->getReferer())
->setRemoteAddr($visitor->getRemoteAddress());
$visit = new Visit($shortUrl, $visitor);
/** @var ORM\EntityManager $em */
$em = $this->em;

View file

@ -9,6 +9,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use ShlinkioTest\Shlink\Common\DbUnit\DatabaseTestCase;
use function count;
@ -44,13 +45,13 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$bar->setShortCode('bar_very_long_text');
$this->getEntityManager()->persist($bar);
$baz = new ShortUrl('baz', ShortUrlMeta::createFromRawData(['maxVisits' => 3]));
$visits = [];
for ($i = 0; $i < 3; $i++) {
$visit = new Visit();
$visit = new Visit($baz, Visitor::emptyInstance());
$this->getEntityManager()->persist($visit);
$visits[] = $visit;
}
$baz = new ShortUrl('baz', ShortUrlMeta::createFromRawData(['maxVisits' => 3]));
$baz->setShortCode('baz')
->setVisits(new ArrayCollection($visits));
$this->getEntityManager()->persist($baz);

View file

@ -8,6 +8,7 @@ use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\VisitRepository;
use ShlinkioTest\Shlink\Common\DbUnit\DatabaseTestCase;
use function sprintf;
@ -35,8 +36,11 @@ class VisitRepositoryTest extends DatabaseTestCase
*/
public function findUnlocatedVisitsReturnsProperVisits()
{
$shortUrl = new ShortUrl('');
$this->getEntityManager()->persist($shortUrl);
for ($i = 0; $i < 6; $i++) {
$visit = new Visit();
$visit = new Visit($shortUrl, Visitor::emptyInstance());
if ($i % 2 === 0) {
$location = new VisitLocation([]);
@ -60,10 +64,7 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->persist($shortUrl);
for ($i = 0; $i < 6; $i++) {
$visit = new Visit();
$visit->setShortUrl($shortUrl)
->setDate(Chronos::parse(sprintf('2016-01-0%s', $i + 1)));
$visit = new Visit($shortUrl, Visitor::emptyInstance(), Chronos::parse(sprintf('2016-01-0%s', $i + 1)));
$this->getEntityManager()->persist($visit);
}
$this->getEntityManager()->flush();

View file

@ -11,6 +11,7 @@ use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Exception\DeleteShortUrlException;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Options\DeleteShortUrlsOptions;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Service\ShortUrl\DeleteShortUrlService;
@ -32,7 +33,7 @@ class DeleteShortUrlServiceTest extends TestCase
{
$shortUrl = (new ShortUrl(''))->setShortCode('abc123')
->setVisits(new ArrayCollection(array_map(function () {
return new Visit();
return new Visit(new ShortUrl(''), Visitor::emptyInstance());
}, range(0, 10))));
$this->em = $this->prophesize(EntityManagerInterface::class);

View file

@ -6,7 +6,9 @@ namespace ShlinkioTest\Shlink\Core\Service;
use Doctrine\ORM\EntityManager;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\VisitRepository;
use Shlinkio\Shlink\Core\Service\VisitService;
@ -32,7 +34,7 @@ class VisitServiceTest extends TestCase
*/
public function saveVisitsPersistsProvidedVisit()
{
$visit = new Visit();
$visit = new Visit(new ShortUrl(''), Visitor::emptyInstance());
$this->em->persist($visit)->shouldBeCalledTimes(1);
$this->em->flush()->shouldBeCalledTimes(1);
$this->visitService->saveVisit($visit);

View file

@ -80,8 +80,8 @@ class VisitsTrackerTest extends TestCase
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
$list = [
new Visit(),
new Visit(),
new Visit(new ShortUrl(''), Visitor::emptyInstance()),
new Visit(new ShortUrl(''), Visitor::emptyInstance()),
];
$repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByShortUrl($shortUrl, null)->willReturn($list);