Merge pull request #2276 from acelaya-forks/feature/visits-list-duplication

Reduce duplication in actions listing visits
This commit is contained in:
Alejandro Celaya 2024-11-20 09:51:54 +01:00 committed by GitHub
commit b2bfe9799a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 83 additions and 91 deletions

View File

@ -21,9 +21,8 @@ final class OrphanVisitsParams extends VisitsParams
parent::__construct($dateRange, $page, $itemsPerPage, $excludeBots); parent::__construct($dateRange, $page, $itemsPerPage, $excludeBots);
} }
public static function fromRawData(array $query): self public static function fromVisitsParamsAndRawData(VisitsParams $visitsParams, array $query): self
{ {
$visitsParams = parent::fromRawData($query);
$type = $query['type'] ?? null; $type = $query['type'] ?? null;
return new self( return new self(

View File

@ -27,7 +27,7 @@ class OrphanVisitsPaginatorAdapterTest extends TestCase
protected function setUp(): void protected function setUp(): void
{ {
$this->repo = $this->createMock(VisitRepositoryInterface::class); $this->repo = $this->createMock(VisitRepositoryInterface::class);
$this->params = OrphanVisitsParams::fromRawData([]); $this->params = new OrphanVisitsParams();
$this->apiKey = ApiKey::create(); $this->apiKey = ApiKey::create();
$this->adapter = new OrphanVisitsPaginatorAdapter($this->repo, $this->params, $this->apiKey); $this->adapter = new OrphanVisitsPaginatorAdapter($this->repo, $this->params, $this->apiKey);

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse;
use Pagerfanta\Pagerfanta;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
abstract class AbstractListVisitsAction extends AbstractRestAction
{
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
public function __construct(protected readonly VisitsStatsHelperInterface $visitsHelper)
{
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$params = VisitsParams::fromRawData($request->getQueryParams());
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
$visits = $this->getVisitsPaginator($request, $params, $apiKey);
return new JsonResponse(['visits' => PagerfantaUtils::serializePaginator($visits)]);
}
/**
* @return Pagerfanta<Visit>
*/
abstract protected function getVisitsPaginator(
ServerRequestInterface $request,
VisitsParams $params,
ApiKey $apiKey,
): Pagerfanta;
}

View File

@ -4,36 +4,29 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\Visit; namespace Shlinkio\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse; use Pagerfanta\Pagerfanta;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils;
use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions;
use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Entity\Domain;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
class DomainVisitsAction extends AbstractRestAction class DomainVisitsAction extends AbstractListVisitsAction
{ {
protected const ROUTE_PATH = '/domains/{domain}/visits'; protected const ROUTE_PATH = '/domains/{domain}/visits';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
public function __construct( public function __construct(
private readonly VisitsStatsHelperInterface $visitsHelper, VisitsStatsHelperInterface $visitsHelper,
private readonly UrlShortenerOptions $urlShortenerOptions, private readonly UrlShortenerOptions $urlShortenerOptions,
) { ) {
parent::__construct($visitsHelper);
} }
public function handle(Request $request): Response protected function getVisitsPaginator(Request $request, VisitsParams $params, ApiKey $apiKey): Pagerfanta
{ {
$domain = $this->resolveDomainParam($request); $domain = $this->resolveDomainParam($request);
$params = VisitsParams::fromRawData($request->getQueryParams()); return $this->visitsHelper->visitsForDomain($domain, $params, $apiKey);
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
$visits = $this->visitsHelper->visitsForDomain($domain, $params, $apiKey);
return new JsonResponse(['visits' => PagerfantaUtils::serializePaginator($visits)]);
} }
private function resolveDomainParam(Request $request): string private function resolveDomainParam(Request $request): string

View File

@ -4,30 +4,20 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\Visit; namespace Shlinkio\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse; use Pagerfanta\Pagerfanta;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
class NonOrphanVisitsAction extends AbstractRestAction class NonOrphanVisitsAction extends AbstractListVisitsAction
{ {
protected const ROUTE_PATH = '/visits/non-orphan'; protected const ROUTE_PATH = '/visits/non-orphan';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
public function __construct(private readonly VisitsStatsHelperInterface $visitsHelper) protected function getVisitsPaginator(
{ ServerRequestInterface $request,
} VisitsParams $params,
ApiKey $apiKey,
public function handle(ServerRequestInterface $request): ResponseInterface ): Pagerfanta {
{ return $this->visitsHelper->nonOrphanVisits($params, $apiKey);
$params = VisitsParams::fromRawData($request->getQueryParams());
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
$visits = $this->visitsHelper->nonOrphanVisits($params, $apiKey);
return new JsonResponse(['visits' => PagerfantaUtils::serializePaginator($visits)]);
} }
} }

View File

@ -4,30 +4,22 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\Visit; namespace Shlinkio\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse; use Pagerfanta\Pagerfanta;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils;
use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams; use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
class OrphanVisitsAction extends AbstractRestAction class OrphanVisitsAction extends AbstractListVisitsAction
{ {
protected const ROUTE_PATH = '/visits/orphan'; protected const ROUTE_PATH = '/visits/orphan';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
public function __construct(private readonly VisitsStatsHelperInterface $visitsHelper) protected function getVisitsPaginator(
{ ServerRequestInterface $request,
} VisitsParams $params,
ApiKey $apiKey,
public function handle(ServerRequestInterface $request): ResponseInterface ): Pagerfanta {
{ $orphanParams = OrphanVisitsParams::fromVisitsParamsAndRawData($params, $request->getQueryParams());
$params = OrphanVisitsParams::fromRawData($request->getQueryParams()); return $this->visitsHelper->orphanVisits($orphanParams, $apiKey);
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
$visits = $this->visitsHelper->orphanVisits($params, $apiKey);
return new JsonResponse(['visits' => PagerfantaUtils::serializePaginator($visits)]);
} }
} }

View File

@ -4,32 +4,19 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\Visit; namespace Shlinkio\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse; use Pagerfanta\Pagerfanta;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
class ShortUrlVisitsAction extends AbstractRestAction class ShortUrlVisitsAction extends AbstractListVisitsAction
{ {
protected const ROUTE_PATH = '/short-urls/{shortCode}/visits'; protected const ROUTE_PATH = '/short-urls/{shortCode}/visits';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
public function __construct(private readonly VisitsStatsHelperInterface $visitsHelper) protected function getVisitsPaginator(Request $request, VisitsParams $params, ApiKey $apiKey): Pagerfanta
{
}
public function handle(Request $request): Response
{ {
$identifier = ShortUrlIdentifier::fromApiRequest($request); $identifier = ShortUrlIdentifier::fromApiRequest($request);
$params = VisitsParams::fromRawData($request->getQueryParams()); return $this->visitsHelper->visitsForShortUrl($identifier, $params, $apiKey);
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
$visits = $this->visitsHelper->visitsForShortUrl($identifier, $params, $apiKey);
return new JsonResponse(['visits' => PagerfantaUtils::serializePaginator($visits)]);
} }
} }

View File

@ -4,31 +4,18 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\Visit; namespace Shlinkio\Shlink\Rest\Action\Visit;
use Laminas\Diactoros\Response\JsonResponse; use Pagerfanta\Pagerfanta;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
class TagVisitsAction extends AbstractRestAction class TagVisitsAction extends AbstractListVisitsAction
{ {
protected const ROUTE_PATH = '/tags/{tag}/visits'; protected const ROUTE_PATH = '/tags/{tag}/visits';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
public function __construct(private readonly VisitsStatsHelperInterface $visitsHelper) protected function getVisitsPaginator(Request $request, VisitsParams $params, ApiKey $apiKey): Pagerfanta
{
}
public function handle(Request $request): Response
{ {
$tag = $request->getAttribute('tag', ''); $tag = $request->getAttribute('tag', '');
$params = VisitsParams::fromRawData($request->getQueryParams()); return $this->visitsHelper->visitsForTag($tag, $params, $apiKey);
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
$visits = $this->visitsHelper->visitsForTag($tag, $params, $apiKey);
return new JsonResponse(['visits' => PagerfantaUtils::serializePaginator($visits)]);
} }
} }