Ensured domain can be passed when fetching visits for a short URL

This commit is contained in:
Alejandro Celaya 2020-02-01 17:34:16 +01:00
parent 1b2a0d674f
commit 279bd12a2d
12 changed files with 100 additions and 48 deletions

View file

@ -9,6 +9,7 @@ use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\CLI\Util\ShlinkTable; use Shlinkio\Shlink\CLI\Util\ShlinkTable;
use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Model\VisitsParams;
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface; use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
@ -65,11 +66,11 @@ class GetVisitsCommand extends AbstractWithDateRangeCommand
protected function execute(InputInterface $input, OutputInterface $output): ?int protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$shortCode = $input->getArgument('shortCode'); $identifier = ShortUrlIdentifier::fromCli($input);
$startDate = $this->getDateOption($input, $output, 'startDate'); $startDate = $this->getDateOption($input, $output, 'startDate');
$endDate = $this->getDateOption($input, $output, 'endDate'); $endDate = $this->getDateOption($input, $output, 'endDate');
$paginator = $this->visitsTracker->info($shortCode, new VisitsParams(new DateRange($startDate, $endDate))); $paginator = $this->visitsTracker->info($identifier, new VisitsParams(new DateRange($startDate, $endDate)));
$rows = map($paginator->getCurrentItems(), function (Visit $visit) { $rows = map($paginator->getCurrentItems(), function (Visit $visit) {
$rowData = $visit->jsonSerialize(); $rowData = $visit->jsonSerialize();

View file

@ -15,6 +15,7 @@ use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation; use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\Visitor; use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Model\VisitsParams;
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface; use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
@ -42,9 +43,12 @@ class GetVisitsCommandTest extends TestCase
public function noDateFlagsTriesToListWithoutDateRange(): void public function noDateFlagsTriesToListWithoutDateRange(): void
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$this->visitsTracker->info($shortCode, new VisitsParams(new DateRange(null, null)))->willReturn( $this->visitsTracker->info(
new Paginator(new ArrayAdapter([])), new ShortUrlIdentifier($shortCode),
)->shouldBeCalledOnce(); new VisitsParams(new DateRange(null, null)),
)
->willReturn(new Paginator(new ArrayAdapter([])))
->shouldBeCalledOnce();
$this->commandTester->execute(['shortCode' => $shortCode]); $this->commandTester->execute(['shortCode' => $shortCode]);
} }
@ -56,7 +60,7 @@ class GetVisitsCommandTest extends TestCase
$startDate = '2016-01-01'; $startDate = '2016-01-01';
$endDate = '2016-02-01'; $endDate = '2016-02-01';
$this->visitsTracker->info( $this->visitsTracker->info(
$shortCode, new ShortUrlIdentifier($shortCode),
new VisitsParams(new DateRange(Chronos::parse($startDate), Chronos::parse($endDate))), new VisitsParams(new DateRange(Chronos::parse($startDate), Chronos::parse($endDate))),
) )
->willReturn(new Paginator(new ArrayAdapter([]))) ->willReturn(new Paginator(new ArrayAdapter([])))
@ -74,7 +78,7 @@ class GetVisitsCommandTest extends TestCase
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$startDate = 'foo'; $startDate = 'foo';
$info = $this->visitsTracker->info($shortCode, new VisitsParams(new DateRange())) $info = $this->visitsTracker->info(new ShortUrlIdentifier($shortCode), new VisitsParams(new DateRange()))
->willReturn(new Paginator(new ArrayAdapter([]))); ->willReturn(new Paginator(new ArrayAdapter([])));
$this->commandTester->execute([ $this->commandTester->execute([
@ -94,7 +98,7 @@ class GetVisitsCommandTest extends TestCase
public function outputIsProperlyGenerated(): void public function outputIsProperlyGenerated(): void
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$this->visitsTracker->info($shortCode, Argument::any())->willReturn( $this->visitsTracker->info(new ShortUrlIdentifier($shortCode), Argument::any())->willReturn(
new Paginator(new ArrayAdapter([ new Paginator(new ArrayAdapter([
(new Visit(new ShortUrl(''), new Visitor('bar', 'foo', '')))->locate( (new Visit(new ShortUrl(''), new Visitor('bar', 'foo', '')))->locate(
new VisitLocation(new Location('', 'Spain', '', '', 0, 0, '')), new VisitLocation(new Location('', 'Spain', '', '', 0, 0, '')),

View file

@ -36,8 +36,8 @@ final class ShortUrlIdentifier
public static function fromCli(InputInterface $input): self public static function fromCli(InputInterface $input): self
{ {
$shortCode = $input->getArgument('shortCode'); $shortCode = $input->getArguments()['shortCode'] ?? '';
$domain = $input->getOption('domain'); $domain = $input->getOptions()['domain'] ?? null;
return new self($shortCode, $domain); return new self($shortCode, $domain);
} }

View file

@ -5,26 +5,31 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Paginator\Adapter; namespace Shlinkio\Shlink\Core\Paginator\Adapter;
use Laminas\Paginator\Adapter\AdapterInterface; use Laminas\Paginator\Adapter\AdapterInterface;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Model\VisitsParams;
use Shlinkio\Shlink\Core\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Core\Repository\VisitRepositoryInterface;
class VisitsPaginatorAdapter implements AdapterInterface class VisitsPaginatorAdapter implements AdapterInterface
{ {
private VisitRepositoryInterface $visitRepository; private VisitRepositoryInterface $visitRepository;
private string $shortCode; private ShortUrlIdentifier $identifier;
private VisitsParams $params; private VisitsParams $params;
public function __construct(VisitRepositoryInterface $visitRepository, string $shortCode, VisitsParams $params) public function __construct(
{ VisitRepositoryInterface $visitRepository,
ShortUrlIdentifier $identifier,
VisitsParams $params
) {
$this->visitRepository = $visitRepository; $this->visitRepository = $visitRepository;
$this->shortCode = $shortCode;
$this->params = $params; $this->params = $params;
$this->identifier = $identifier;
} }
public function getItems($offset, $itemCountPerPage): array // phpcs:ignore public function getItems($offset, $itemCountPerPage): array // phpcs:ignore
{ {
return $this->visitRepository->findVisitsByShortCode( return $this->visitRepository->findVisitsByShortCode(
$this->shortCode, $this->identifier->shortCode(),
$this->identifier->domain(),
$this->params->getDateRange(), $this->params->getDateRange(),
$itemCountPerPage, $itemCountPerPage,
$offset, $offset,
@ -33,6 +38,10 @@ class VisitsPaginatorAdapter implements AdapterInterface
public function count(): int public function count(): int
{ {
return $this->visitRepository->countVisitsByShortCode($this->shortCode, $this->params->getDateRange()); return $this->visitRepository->countVisitsByShortCode(
$this->identifier->shortCode(),
$this->identifier->domain(),
$this->params->getDateRange(),
);
} }
} }

View file

@ -46,11 +46,12 @@ DQL;
*/ */
public function findVisitsByShortCode( public function findVisitsByShortCode(
string $shortCode, string $shortCode,
?string $domain = null,
?DateRange $dateRange = null, ?DateRange $dateRange = null,
?int $limit = null, ?int $limit = null,
?int $offset = null ?int $offset = null
): array { ): array {
$qb = $this->createVisitsByShortCodeQueryBuilder($shortCode, $dateRange); $qb = $this->createVisitsByShortCodeQueryBuilder($shortCode, $domain, $dateRange);
$qb->select('v') $qb->select('v')
->orderBy('v.date', 'DESC'); ->orderBy('v.date', 'DESC');
@ -64,22 +65,34 @@ DQL;
return $qb->getQuery()->getResult(); return $qb->getQuery()->getResult();
} }
public function countVisitsByShortCode(string $shortCode, ?DateRange $dateRange = null): int public function countVisitsByShortCode(string $shortCode, ?string $domain = null, ?DateRange $dateRange = null): int
{ {
$qb = $this->createVisitsByShortCodeQueryBuilder($shortCode, $dateRange); $qb = $this->createVisitsByShortCodeQueryBuilder($shortCode, $domain, $dateRange);
$qb->select('COUNT(DISTINCT v.id)'); $qb->select('COUNT(DISTINCT v.id)');
return (int) $qb->getQuery()->getSingleScalarResult(); return (int) $qb->getQuery()->getSingleScalarResult();
} }
private function createVisitsByShortCodeQueryBuilder(string $shortCode, ?DateRange $dateRange = null): QueryBuilder private function createVisitsByShortCodeQueryBuilder(
{ string $shortCode,
?string $domain,
?DateRange $dateRange
): QueryBuilder {
$qb = $this->getEntityManager()->createQueryBuilder(); $qb = $this->getEntityManager()->createQueryBuilder();
$qb->from(Visit::class, 'v') $qb->from(Visit::class, 'v')
->join('v.shortUrl', 'su') ->join('v.shortUrl', 'su')
->where($qb->expr()->eq('su.shortCode', ':shortCode')) ->where($qb->expr()->eq('su.shortCode', ':shortCode'))
->setParameter('shortCode', $shortCode); ->setParameter('shortCode', $shortCode);
// Apply domain filtering
if ($domain !== null) {
$qb->join('su.domain', 'd')
->andWhere($qb->expr()->eq('d.authority', ':domain'))
->setParameter('domain', $domain);
} else {
$qb->andWhere($qb->expr()->isNull('su.domain'));
}
// Apply date range filtering // Apply date range filtering
if ($dateRange !== null && $dateRange->getStartDate() !== null) { if ($dateRange !== null && $dateRange->getStartDate() !== null) {
$qb->andWhere($qb->expr()->gte('v.date', ':startDate')) $qb->andWhere($qb->expr()->gte('v.date', ':startDate'))

View file

@ -28,10 +28,15 @@ interface VisitRepositoryInterface extends ObjectRepository
*/ */
public function findVisitsByShortCode( public function findVisitsByShortCode(
string $shortCode, string $shortCode,
?string $domain = null,
?DateRange $dateRange = null, ?DateRange $dateRange = null,
?int $limit = null, ?int $limit = null,
?int $offset = null ?int $offset = null
): array; ): array;
public function countVisitsByShortCode(string $shortCode, ?DateRange $dateRange = null): int; public function countVisitsByShortCode(
string $shortCode,
?string $domain = null,
?DateRange $dateRange = null
): int;
} }

View file

@ -15,6 +15,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\Visitor; use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Model\VisitsParams;
use Shlinkio\Shlink\Core\Paginator\Adapter\VisitsPaginatorAdapter; use Shlinkio\Shlink\Core\Paginator\Adapter\VisitsPaginatorAdapter;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Repository\VisitRepository; use Shlinkio\Shlink\Core\Repository\VisitRepository;
class VisitsTracker implements VisitsTrackerInterface class VisitsTracker implements VisitsTrackerInterface
@ -47,17 +48,17 @@ class VisitsTracker implements VisitsTrackerInterface
* @return Visit[]|Paginator * @return Visit[]|Paginator
* @throws ShortUrlNotFoundException * @throws ShortUrlNotFoundException
*/ */
public function info(string $shortCode, VisitsParams $params): Paginator public function info(ShortUrlIdentifier $identifier, VisitsParams $params): Paginator
{ {
/** @var ORM\EntityRepository $repo */ /** @var ShortUrlRepositoryInterface $repo */
$repo = $this->em->getRepository(ShortUrl::class); $repo = $this->em->getRepository(ShortUrl::class);
if ($repo->count(['shortCode' => $shortCode]) < 1) { if (! $repo->shortCodeIsInUse($identifier->shortCode(), $identifier->domain())) {
throw ShortUrlNotFoundException::fromNotFound(new ShortUrlIdentifier($shortCode)); // FIXME throw ShortUrlNotFoundException::fromNotFound($identifier);
} }
/** @var VisitRepository $repo */ /** @var VisitRepository $repo */
$repo = $this->em->getRepository(Visit::class); $repo = $this->em->getRepository(Visit::class);
$paginator = new Paginator(new VisitsPaginatorAdapter($repo, $shortCode, $params)); $paginator = new Paginator(new VisitsPaginatorAdapter($repo, $identifier, $params));
$paginator->setItemCountPerPage($params->getItemsPerPage()) $paginator->setItemCountPerPage($params->getItemsPerPage())
->setCurrentPageNumber($params->getPage()); ->setCurrentPageNumber($params->getPage());

View file

@ -8,6 +8,7 @@ use Laminas\Paginator\Paginator;
use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\Visitor; use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Model\VisitsParams;
@ -24,5 +25,5 @@ interface VisitsTrackerInterface
* @return Visit[]|Paginator * @return Visit[]|Paginator
* @throws ShortUrlNotFoundException * @throws ShortUrlNotFoundException
*/ */
public function info(string $shortCode, VisitsParams $params): Paginator; // FIXME public function info(ShortUrlIdentifier $identifier, VisitsParams $params): Paginator; // FIXME
} }

View file

@ -83,15 +83,15 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->assertCount(0, $this->repo->findVisitsByShortCode('invalid')); $this->assertCount(0, $this->repo->findVisitsByShortCode('invalid'));
$this->assertCount(6, $this->repo->findVisitsByShortCode($shortUrl->getShortCode())); $this->assertCount(6, $this->repo->findVisitsByShortCode($shortUrl->getShortCode()));
$this->assertCount(2, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), new DateRange( $this->assertCount(2, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), null, new DateRange(
Chronos::parse('2016-01-02'), Chronos::parse('2016-01-02'),
Chronos::parse('2016-01-03'), Chronos::parse('2016-01-03'),
))); )));
$this->assertCount(4, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), new DateRange( $this->assertCount(4, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), null, new DateRange(
Chronos::parse('2016-01-03'), Chronos::parse('2016-01-03'),
))); )));
$this->assertCount(3, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), null, 3, 2)); $this->assertCount(3, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), null, null, 3, 2));
$this->assertCount(2, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), null, 5, 4)); $this->assertCount(2, $this->repo->findVisitsByShortCode($shortUrl->getShortCode(), null, null, 5, 4));
} }
/** @test */ /** @test */
@ -108,11 +108,11 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->assertEquals(0, $this->repo->countVisitsByShortCode('invalid')); $this->assertEquals(0, $this->repo->countVisitsByShortCode('invalid'));
$this->assertEquals(6, $this->repo->countVisitsByShortCode($shortUrl->getShortCode())); $this->assertEquals(6, $this->repo->countVisitsByShortCode($shortUrl->getShortCode()));
$this->assertEquals(2, $this->repo->countVisitsByShortCode($shortUrl->getShortCode(), new DateRange( $this->assertEquals(2, $this->repo->countVisitsByShortCode($shortUrl->getShortCode(), null, new DateRange(
Chronos::parse('2016-01-02'), Chronos::parse('2016-01-02'),
Chronos::parse('2016-01-03'), Chronos::parse('2016-01-03'),
))); )));
$this->assertEquals(4, $this->repo->countVisitsByShortCode($shortUrl->getShortCode(), new DateRange( $this->assertEquals(4, $this->repo->countVisitsByShortCode($shortUrl->getShortCode(), null, new DateRange(
Chronos::parse('2016-01-03'), Chronos::parse('2016-01-03'),
))); )));
} }

View file

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Service; namespace ShlinkioTest\Shlink\Core\Service;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Laminas\Stdlib\ArrayUtils; use Laminas\Stdlib\ArrayUtils;
use PHPUnit\Framework\Assert; use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -16,11 +15,17 @@ use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\EventDispatcher\ShortUrlVisited; use Shlinkio\Shlink\Core\EventDispatcher\ShortUrlVisited;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\Visitor; use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Model\VisitsParams;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Repository\VisitRepository; use Shlinkio\Shlink\Core\Repository\VisitRepository;
use Shlinkio\Shlink\Core\Service\VisitsTracker; use Shlinkio\Shlink\Core\Service\VisitsTracker;
use function Functional\map;
use function range;
class VisitsTrackerTest extends TestCase class VisitsTrackerTest extends TestCase
{ {
private VisitsTracker $visitsTracker; private VisitsTracker $visitsTracker;
@ -71,22 +76,33 @@ class VisitsTrackerTest extends TestCase
public function infoReturnsVisitsForCertainShortCode(): void public function infoReturnsVisitsForCertainShortCode(): void
{ {
$shortCode = '123ABC'; $shortCode = '123ABC';
$repo = $this->prophesize(EntityRepository::class); $repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$count = $repo->count(['shortCode' => $shortCode])->willReturn(1); $count = $repo->shortCodeIsInUse($shortCode, null)->willReturn(true);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledOnce(); $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledOnce();
$list = [ $list = map(range(0, 1), fn () => new Visit(new ShortUrl(''), Visitor::emptyInstance()));
new Visit(new ShortUrl(''), Visitor::emptyInstance()),
new Visit(new ShortUrl(''), Visitor::emptyInstance()),
];
$repo2 = $this->prophesize(VisitRepository::class); $repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByShortCode($shortCode, Argument::type(DateRange::class), 1, 0)->willReturn($list); $repo2->findVisitsByShortCode($shortCode, null, Argument::type(DateRange::class), 1, 0)->willReturn($list);
$repo2->countVisitsByShortCode($shortCode, Argument::type(DateRange::class))->willReturn(1); $repo2->countVisitsByShortCode($shortCode, null, Argument::type(DateRange::class))->willReturn(1);
$this->em->getRepository(Visit::class)->willReturn($repo2->reveal())->shouldBeCalledOnce(); $this->em->getRepository(Visit::class)->willReturn($repo2->reveal())->shouldBeCalledOnce();
$paginator = $this->visitsTracker->info($shortCode, new VisitsParams()); $paginator = $this->visitsTracker->info(new ShortUrlIdentifier($shortCode), new VisitsParams());
$this->assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentItems())); $this->assertEquals($list, ArrayUtils::iteratorToArray($paginator->getCurrentItems()));
$count->shouldHaveBeenCalledOnce(); $count->shouldHaveBeenCalledOnce();
} }
/** @test */
public function throwsExceptionWhenRequestingVisitsForInvalidShortCode(): void
{
$shortCode = '123ABC';
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$count = $repo->shortCodeIsInUse($shortCode, null)->willReturn(false);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledOnce();
$this->expectException(ShortUrlNotFoundException::class);
$count->shouldBeCalledOnce();
$this->visitsTracker->info(new ShortUrlIdentifier($shortCode), new VisitsParams());
}
} }

View file

@ -9,6 +9,7 @@ use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait; use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Model\VisitsParams;
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface; use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
@ -30,8 +31,8 @@ class GetVisitsAction extends AbstractRestAction
public function handle(Request $request): Response public function handle(Request $request): Response
{ {
$shortCode = $request->getAttribute('shortCode'); $identifier = ShortUrlIdentifier::fromApiRequest($request);
$visits = $this->visitsTracker->info($shortCode, VisitsParams::fromRawData($request->getQueryParams())); $visits = $this->visitsTracker->info($identifier, VisitsParams::fromRawData($request->getQueryParams()));
return new JsonResponse([ return new JsonResponse([
'visits' => $this->serializePaginator($visits), 'visits' => $this->serializePaginator($visits),

View file

@ -12,6 +12,7 @@ use PHPUnit\Framework\TestCase;
use Prophecy\Argument; use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\VisitsParams; use Shlinkio\Shlink\Core\Model\VisitsParams;
use Shlinkio\Shlink\Core\Service\VisitsTracker; use Shlinkio\Shlink\Core\Service\VisitsTracker;
use Shlinkio\Shlink\Rest\Action\Visit\GetVisitsAction; use Shlinkio\Shlink\Rest\Action\Visit\GetVisitsAction;
@ -31,7 +32,7 @@ class GetVisitsActionTest extends TestCase
public function providingCorrectShortCodeReturnsVisits(): void public function providingCorrectShortCodeReturnsVisits(): void
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$this->visitsTracker->info($shortCode, Argument::type(VisitsParams::class))->willReturn( $this->visitsTracker->info(new ShortUrlIdentifier($shortCode), Argument::type(VisitsParams::class))->willReturn(
new Paginator(new ArrayAdapter([])), new Paginator(new ArrayAdapter([])),
)->shouldBeCalledOnce(); )->shouldBeCalledOnce();
@ -43,7 +44,7 @@ class GetVisitsActionTest extends TestCase
public function paramsAreReadFromQuery(): void public function paramsAreReadFromQuery(): void
{ {
$shortCode = 'abc123'; $shortCode = 'abc123';
$this->visitsTracker->info($shortCode, new VisitsParams( $this->visitsTracker->info(new ShortUrlIdentifier($shortCode), new VisitsParams(
new DateRange(null, Chronos::parse('2016-01-01 00:00:00')), new DateRange(null, Chronos::parse('2016-01-01 00:00:00')),
3, 3,
10, 10,