From 68c601a5a899dffe8388f7a46033a13bfb8f93b8 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 4 Jan 2021 11:27:55 +0100 Subject: [PATCH] Applied API role specs to global visits --- module/Core/src/Repository/TagRepository.php | 2 +- module/Core/src/Repository/VisitRepository.php | 10 ++++++++++ .../Core/src/Repository/VisitRepositoryInterface.php | 3 +++ module/Core/src/Visit/VisitsStatsHelper.php | 9 +++++---- module/Core/src/Visit/VisitsStatsHelperInterface.php | 3 ++- module/Core/test/Visit/VisitsStatsHelperTest.php | 2 +- module/Rest/src/Action/Visit/GlobalVisitsAction.php | 5 ++++- .../src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php | 6 ++++-- .../Rest/test/Action/Visit/GlobalVisitsActionTest.php | 6 ++++-- 9 files changed, 34 insertions(+), 12 deletions(-) diff --git a/module/Core/src/Repository/TagRepository.php b/module/Core/src/Repository/TagRepository.php index dd15c292..7cb66a6a 100644 --- a/module/Core/src/Repository/TagRepository.php +++ b/module/Core/src/Repository/TagRepository.php @@ -56,7 +56,7 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito { $result = (int) $this->matchSingleScalarResult(Spec::andX( new CountTagsWithName($tag), - new WithApiKeySpecsEnsuringJoin($apiKey), + new WithApiKeySpecsEnsuringJoin($apiKey, 'shortUrls'), )); return $result > 0; diff --git a/module/Core/src/Repository/VisitRepository.php b/module/Core/src/Repository/VisitRepository.php index 8e750bc3..a1df73a5 100644 --- a/module/Core/src/Repository/VisitRepository.php +++ b/module/Core/src/Repository/VisitRepository.php @@ -7,11 +7,14 @@ namespace Shlinkio\Shlink\Core\Repository; use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\ORM\QueryBuilder; use Happyr\DoctrineSpecification\EntitySpecificationRepository; +use Happyr\DoctrineSpecification\Spec; use Happyr\DoctrineSpecification\Specification\Specification; 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\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin; +use Shlinkio\Shlink\Rest\Entity\ApiKey; use const PHP_INT_MAX; @@ -205,4 +208,11 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo return $query->getResult(); } + + public function countVisits(?ApiKey $apiKey = null): int + { + return (int) $this->matchSingleScalarResult( + Spec::countOf(new WithApiKeySpecsEnsuringJoin($apiKey, 'shortUrl')), + ); + } } diff --git a/module/Core/src/Repository/VisitRepositoryInterface.php b/module/Core/src/Repository/VisitRepositoryInterface.php index 71a6c4ae..526645df 100644 --- a/module/Core/src/Repository/VisitRepositoryInterface.php +++ b/module/Core/src/Repository/VisitRepositoryInterface.php @@ -9,6 +9,7 @@ use Happyr\DoctrineSpecification\EntitySpecificationRepositoryInterface; use Happyr\DoctrineSpecification\Specification\Specification; use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Core\Entity\Visit; +use Shlinkio\Shlink\Rest\Entity\ApiKey; interface VisitRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface { @@ -60,4 +61,6 @@ interface VisitRepositoryInterface extends ObjectRepository, EntitySpecification ): array; public function countVisitsByTag(string $tag, ?DateRange $dateRange = null, ?Specification $spec = null): int; + + public function countVisits(?ApiKey $apiKey = null): int; } diff --git a/module/Core/src/Visit/VisitsStatsHelper.php b/module/Core/src/Visit/VisitsStatsHelper.php index de3219ff..ab06079a 100644 --- a/module/Core/src/Visit/VisitsStatsHelper.php +++ b/module/Core/src/Visit/VisitsStatsHelper.php @@ -8,6 +8,7 @@ use Doctrine\ORM\EntityManagerInterface; use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\Repository\VisitRepository; use Shlinkio\Shlink\Core\Visit\Model\VisitsStats; +use Shlinkio\Shlink\Rest\Entity\ApiKey; class VisitsStatsHelper implements VisitsStatsHelperInterface { @@ -18,15 +19,15 @@ class VisitsStatsHelper implements VisitsStatsHelperInterface $this->em = $em; } - public function getVisitsStats(): VisitsStats + public function getVisitsStats(?ApiKey $apiKey = null): VisitsStats { - return new VisitsStats($this->getVisitsCount()); + return new VisitsStats($this->getVisitsCount($apiKey)); } - private function getVisitsCount(): int + private function getVisitsCount(?ApiKey $apiKey): int { /** @var VisitRepository $visitsRepo */ $visitsRepo = $this->em->getRepository(Visit::class); - return $visitsRepo->count([]); + return $visitsRepo->countVisits($apiKey); } } diff --git a/module/Core/src/Visit/VisitsStatsHelperInterface.php b/module/Core/src/Visit/VisitsStatsHelperInterface.php index 81423cb0..ca044d4b 100644 --- a/module/Core/src/Visit/VisitsStatsHelperInterface.php +++ b/module/Core/src/Visit/VisitsStatsHelperInterface.php @@ -5,8 +5,9 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Visit; use Shlinkio\Shlink\Core\Visit\Model\VisitsStats; +use Shlinkio\Shlink\Rest\Entity\ApiKey; interface VisitsStatsHelperInterface { - public function getVisitsStats(): VisitsStats; + public function getVisitsStats(?ApiKey $apiKey = null): VisitsStats; } diff --git a/module/Core/test/Visit/VisitsStatsHelperTest.php b/module/Core/test/Visit/VisitsStatsHelperTest.php index 2381a73a..cdc76bd4 100644 --- a/module/Core/test/Visit/VisitsStatsHelperTest.php +++ b/module/Core/test/Visit/VisitsStatsHelperTest.php @@ -36,7 +36,7 @@ class VisitsStatsHelperTest extends TestCase public function returnsExpectedVisitsStats(int $expectedCount): void { $repo = $this->prophesize(VisitRepository::class); - $count = $repo->count([])->willReturn($expectedCount); + $count = $repo->countVisits(null)->willReturn($expectedCount); $getRepo = $this->em->getRepository(Visit::class)->willReturn($repo->reveal()); $stats = $this->helper->getVisitsStats(); diff --git a/module/Rest/src/Action/Visit/GlobalVisitsAction.php b/module/Rest/src/Action/Visit/GlobalVisitsAction.php index a27412b2..4810b100 100644 --- a/module/Rest/src/Action/Visit/GlobalVisitsAction.php +++ b/module/Rest/src/Action/Visit/GlobalVisitsAction.php @@ -9,6 +9,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; +use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware; class GlobalVisitsAction extends AbstractRestAction { @@ -24,8 +25,10 @@ class GlobalVisitsAction extends AbstractRestAction public function handle(ServerRequestInterface $request): ResponseInterface { + $apiKey = AuthenticationMiddleware::apiKeyFromRequest($request); + return new JsonResponse([ - 'visits' => $this->statsHelper->getVisitsStats(), + 'visits' => $this->statsHelper->getVisitsStats($apiKey), ]); } } diff --git a/module/Rest/src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php b/module/Rest/src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php index 6bf8034d..04ed9565 100644 --- a/module/Rest/src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php +++ b/module/Rest/src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php @@ -12,17 +12,19 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey; class WithApiKeySpecsEnsuringJoin extends BaseSpecification { private ?ApiKey $apiKey; + private string $fieldToJoin; - public function __construct(?ApiKey $apiKey) + public function __construct(?ApiKey $apiKey, string $fieldToJoin) { parent::__construct(); $this->apiKey = $apiKey; + $this->fieldToJoin = $fieldToJoin; } protected function getSpec(): Specification { return $this->apiKey === null || $this->apiKey->isAdmin() ? Spec::andX() : Spec::andX( - Spec::join('shortUrls', 's'), + Spec::join($this->fieldToJoin, 's'), $this->apiKey->spec(), ); } diff --git a/module/Rest/test/Action/Visit/GlobalVisitsActionTest.php b/module/Rest/test/Action/Visit/GlobalVisitsActionTest.php index 6b91ba56..6e3ab1e4 100644 --- a/module/Rest/test/Action/Visit/GlobalVisitsActionTest.php +++ b/module/Rest/test/Action/Visit/GlobalVisitsActionTest.php @@ -12,6 +12,7 @@ use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\Core\Visit\Model\VisitsStats; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Action\Visit\GlobalVisitsAction; +use Shlinkio\Shlink\Rest\Entity\ApiKey; class GlobalVisitsActionTest extends TestCase { @@ -29,11 +30,12 @@ class GlobalVisitsActionTest extends TestCase /** @test */ public function statsAreReturnedFromHelper(): void { + $apiKey = new ApiKey(); $stats = new VisitsStats(5); - $getStats = $this->helper->getVisitsStats()->willReturn($stats); + $getStats = $this->helper->getVisitsStats($apiKey)->willReturn($stats); /** @var JsonResponse $resp */ - $resp = $this->action->handle(ServerRequestFactory::fromGlobals()); + $resp = $this->action->handle(ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, $apiKey)); $payload = $resp->getPayload(); self::assertEquals($payload, ['visits' => $stats]);