mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-29 03:54:09 -06:00
Merge pull request #745 from acelaya-forks/feature/general-visits
Feature/general visits
This commit is contained in:
commit
84b38c4940
@ -21,6 +21,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
|
||||
Also, Shlink exposes a new endpoint `GET /rest/v2/mercure-info`, which returns the public URL of the mercure hub, and a valid JWT that can be used to subsribe to updates.
|
||||
|
||||
* [#673](https://github.com/shlinkio/shlink/issues/673) Added new `[GET /visits]` rest endpoint which returns basic visits stats.
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
10
docs/swagger/definitions/VisitStats.json
Normal file
10
docs/swagger/definitions/VisitStats.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["visitsCount"],
|
||||
"properties": {
|
||||
"visitsCount": {
|
||||
"type": "number",
|
||||
"description": "The total amount of visits received."
|
||||
}
|
||||
}
|
||||
}
|
54
docs/swagger/paths/v2_visits.json
Normal file
54
docs/swagger/paths/v2_visits.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"get": {
|
||||
"operationId": "getGlobalVisits",
|
||||
"tags": [
|
||||
"Visits"
|
||||
],
|
||||
"summary": "Get general visits stats",
|
||||
"description": "Get general visits stats not linked to one specific short URL.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "../parameters/version.json"
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"ApiKey": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Visits stats.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"visits": {
|
||||
"$ref": "../definitions/VisitStats.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"application/json": {
|
||||
"visits": {
|
||||
"visitsCount": 1569874
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Unexpected error.",
|
||||
"content": {
|
||||
"application/problem+json": {
|
||||
"schema": {
|
||||
"$ref": "../definitions/Error.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -78,6 +78,9 @@
|
||||
"$ref": "paths/v1_tags.json"
|
||||
},
|
||||
|
||||
"/rest/v{version}/visits": {
|
||||
"$ref": "paths/v2_visits.json"
|
||||
},
|
||||
"/rest/v{version}/short-urls/{shortCode}/visits": {
|
||||
"$ref": "paths/v1_short-urls_{shortCode}_visits.json"
|
||||
},
|
||||
|
@ -27,6 +27,7 @@ return [
|
||||
Service\VisitsTracker::class => ConfigAbstractFactory::class,
|
||||
Service\ShortUrlService::class => ConfigAbstractFactory::class,
|
||||
Visit\VisitLocator::class => ConfigAbstractFactory::class,
|
||||
Visit\VisitsStatsHelper::class => ConfigAbstractFactory::class,
|
||||
Service\Tag\TagService::class => ConfigAbstractFactory::class,
|
||||
Service\ShortUrl\DeleteShortUrlService::class => ConfigAbstractFactory::class,
|
||||
Service\ShortUrl\ShortUrlResolver::class => ConfigAbstractFactory::class,
|
||||
@ -56,6 +57,7 @@ return [
|
||||
Service\VisitsTracker::class => ['em', EventDispatcherInterface::class],
|
||||
Service\ShortUrlService::class => ['em', Service\ShortUrl\ShortUrlResolver::class, Util\UrlValidator::class],
|
||||
Visit\VisitLocator::class => ['em'],
|
||||
Visit\VisitsStatsHelper::class => ['em'],
|
||||
Service\Tag\TagService::class => ['em'],
|
||||
Service\ShortUrl\DeleteShortUrlService::class => [
|
||||
'em',
|
||||
|
24
module/Core/src/Visit/Model/VisitsStats.php
Normal file
24
module/Core/src/Visit/Model/VisitsStats.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Visit\Model;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
final class VisitsStats implements JsonSerializable
|
||||
{
|
||||
private int $visitsCount;
|
||||
|
||||
public function __construct(int $visitsCount)
|
||||
{
|
||||
$this->visitsCount = $visitsCount;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'visitsCount' => $this->visitsCount,
|
||||
];
|
||||
}
|
||||
}
|
32
module/Core/src/Visit/VisitsStatsHelper.php
Normal file
32
module/Core/src/Visit/VisitsStatsHelper.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Visit;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Repository\VisitRepository;
|
||||
use Shlinkio\Shlink\Core\Visit\Model\VisitsStats;
|
||||
|
||||
class VisitsStatsHelper implements VisitsStatsHelperInterface
|
||||
{
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
public function getVisitsStats(): VisitsStats
|
||||
{
|
||||
return new VisitsStats($this->getVisitsCount());
|
||||
}
|
||||
|
||||
private function getVisitsCount(): int
|
||||
{
|
||||
/** @var VisitRepository $visitsRepo */
|
||||
$visitsRepo = $this->em->getRepository(Visit::class);
|
||||
return $visitsRepo->count([]);
|
||||
}
|
||||
}
|
12
module/Core/src/Visit/VisitsStatsHelperInterface.php
Normal file
12
module/Core/src/Visit/VisitsStatsHelperInterface.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Visit;
|
||||
|
||||
use Shlinkio\Shlink\Core\Visit\Model\VisitsStats;
|
||||
|
||||
interface VisitsStatsHelperInterface
|
||||
{
|
||||
public function getVisitsStats(): VisitsStats;
|
||||
}
|
50
module/Core/test/Visit/VisitsStatsHelperTest.php
Normal file
50
module/Core/test/Visit/VisitsStatsHelperTest.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\Core\Visit;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Repository\VisitRepository;
|
||||
use Shlinkio\Shlink\Core\Visit\Model\VisitsStats;
|
||||
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelper;
|
||||
|
||||
use function Functional\map;
|
||||
use function range;
|
||||
|
||||
class VisitsStatsHelperTest extends TestCase
|
||||
{
|
||||
private VisitsStatsHelper $helper;
|
||||
private ObjectProphecy $em;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->em = $this->prophesize(EntityManagerInterface::class);
|
||||
$this->helper = new VisitsStatsHelper($this->em->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider provideCounts
|
||||
*/
|
||||
public function returnsExpectedVisitsStats(int $expectedCount): void
|
||||
{
|
||||
$repo = $this->prophesize(VisitRepository::class);
|
||||
$count = $repo->count([])->willReturn($expectedCount);
|
||||
$getRepo = $this->em->getRepository(Visit::class)->willReturn($repo->reveal());
|
||||
|
||||
$stats = $this->helper->getVisitsStats();
|
||||
|
||||
$this->assertEquals(new VisitsStats($expectedCount), $stats);
|
||||
$count->shouldHaveBeenCalledOnce();
|
||||
$getRepo->shouldHaveBeenCalledOnce();
|
||||
}
|
||||
|
||||
public function provideCounts(): iterable
|
||||
{
|
||||
return map(range(0, 50, 5), fn (int $value) => [$value]);
|
||||
}
|
||||
}
|
@ -7,10 +7,10 @@ namespace Shlinkio\Shlink\Rest;
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use Mezzio\Router\Middleware\ImplicitOptionsMiddleware;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider;
|
||||
use Shlinkio\Shlink\Core\Options\AppOptions;
|
||||
use Shlinkio\Shlink\Core\Service;
|
||||
use Shlinkio\Shlink\Core\Visit;
|
||||
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
|
||||
|
||||
return [
|
||||
@ -28,7 +28,8 @@ return [
|
||||
Action\ShortUrl\ResolveShortUrlAction::class => ConfigAbstractFactory::class,
|
||||
Action\ShortUrl\ListShortUrlsAction::class => ConfigAbstractFactory::class,
|
||||
Action\ShortUrl\EditShortUrlTagsAction::class => ConfigAbstractFactory::class,
|
||||
Action\Visit\GetVisitsAction::class => ConfigAbstractFactory::class,
|
||||
Action\Visit\ShortUrlVisitsAction::class => ConfigAbstractFactory::class,
|
||||
Action\Visit\GlobalVisitsAction::class => ConfigAbstractFactory::class,
|
||||
Action\Tag\ListTagsAction::class => ConfigAbstractFactory::class,
|
||||
Action\Tag\DeleteTagsAction::class => ConfigAbstractFactory::class,
|
||||
Action\Tag\CreateTagsAction::class => ConfigAbstractFactory::class,
|
||||
@ -46,36 +47,28 @@ return [
|
||||
ConfigAbstractFactory::class => [
|
||||
ApiKeyService::class => ['em'],
|
||||
|
||||
Action\HealthAction::class => ['em', AppOptions::class, 'Logger_Shlink'],
|
||||
Action\MercureInfoAction::class => [LcobucciJwtProvider::class, 'config.mercure', 'Logger_Shlink'],
|
||||
Action\ShortUrl\CreateShortUrlAction::class => [
|
||||
Service\UrlShortener::class,
|
||||
'config.url_shortener.domain',
|
||||
'Logger_Shlink',
|
||||
],
|
||||
Action\HealthAction::class => ['em', AppOptions::class],
|
||||
Action\MercureInfoAction::class => [LcobucciJwtProvider::class, 'config.mercure'],
|
||||
Action\ShortUrl\CreateShortUrlAction::class => [Service\UrlShortener::class, 'config.url_shortener.domain'],
|
||||
Action\ShortUrl\SingleStepCreateShortUrlAction::class => [
|
||||
Service\UrlShortener::class,
|
||||
ApiKeyService::class,
|
||||
'config.url_shortener.domain',
|
||||
'Logger_Shlink',
|
||||
],
|
||||
Action\ShortUrl\EditShortUrlAction::class => [Service\ShortUrlService::class, 'Logger_Shlink'],
|
||||
Action\ShortUrl\DeleteShortUrlAction::class => [Service\ShortUrl\DeleteShortUrlService::class, 'Logger_Shlink'],
|
||||
Action\ShortUrl\EditShortUrlAction::class => [Service\ShortUrlService::class],
|
||||
Action\ShortUrl\DeleteShortUrlAction::class => [Service\ShortUrl\DeleteShortUrlService::class],
|
||||
Action\ShortUrl\ResolveShortUrlAction::class => [
|
||||
Service\ShortUrl\ShortUrlResolver::class,
|
||||
'config.url_shortener.domain',
|
||||
],
|
||||
Action\Visit\GetVisitsAction::class => [Service\VisitsTracker::class, 'Logger_Shlink'],
|
||||
Action\ShortUrl\ListShortUrlsAction::class => [
|
||||
Service\ShortUrlService::class,
|
||||
'config.url_shortener.domain',
|
||||
'Logger_Shlink',
|
||||
],
|
||||
Action\ShortUrl\EditShortUrlTagsAction::class => [Service\ShortUrlService::class, 'Logger_Shlink'],
|
||||
Action\Tag\ListTagsAction::class => [Service\Tag\TagService::class, LoggerInterface::class],
|
||||
Action\Tag\DeleteTagsAction::class => [Service\Tag\TagService::class, LoggerInterface::class],
|
||||
Action\Tag\CreateTagsAction::class => [Service\Tag\TagService::class, LoggerInterface::class],
|
||||
Action\Tag\UpdateTagAction::class => [Service\Tag\TagService::class, LoggerInterface::class],
|
||||
Action\Visit\ShortUrlVisitsAction::class => [Service\VisitsTracker::class],
|
||||
Action\Visit\GlobalVisitsAction::class => [Visit\VisitsStatsHelper::class],
|
||||
Action\ShortUrl\ListShortUrlsAction::class => [Service\ShortUrlService::class, 'config.url_shortener.domain'],
|
||||
Action\ShortUrl\EditShortUrlTagsAction::class => [Service\ShortUrlService::class],
|
||||
Action\Tag\ListTagsAction::class => [Service\Tag\TagService::class],
|
||||
Action\Tag\DeleteTagsAction::class => [Service\Tag\TagService::class],
|
||||
Action\Tag\CreateTagsAction::class => [Service\Tag\TagService::class],
|
||||
Action\Tag\UpdateTagAction::class => [Service\Tag\TagService::class],
|
||||
|
||||
Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ['config.url_shortener.domain.hostname'],
|
||||
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [
|
||||
|
@ -26,7 +26,8 @@ return [
|
||||
Action\ShortUrl\EditShortUrlTagsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
|
||||
// Visits
|
||||
Action\Visit\GetVisitsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\Visit\ShortUrlVisitsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\Visit\GlobalVisitsAction::getRouteDef(),
|
||||
|
||||
// Tags
|
||||
Action\Tag\ListTagsAction::getRouteDef(),
|
||||
|
@ -7,8 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action;
|
||||
use Fig\Http\Message\RequestMethodInterface;
|
||||
use Fig\Http\Message\StatusCodeInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
use function array_merge;
|
||||
|
||||
@ -17,13 +15,6 @@ abstract class AbstractRestAction implements RequestHandlerInterface, RequestMet
|
||||
protected const ROUTE_PATH = '';
|
||||
protected const ROUTE_ALLOWED_METHODS = [];
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
public function __construct(?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->logger = $logger ?: new NullLogger();
|
||||
}
|
||||
|
||||
public static function getRouteDef(array $prevMiddleware = [], array $postMiddleware = []): array
|
||||
{
|
||||
return [
|
||||
|
@ -8,7 +8,6 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Options\AppOptions;
|
||||
use Throwable;
|
||||
|
||||
@ -24,9 +23,8 @@ class HealthAction extends AbstractRestAction
|
||||
private EntityManagerInterface $em;
|
||||
private AppOptions $options;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, AppOptions $options, ?LoggerInterface $logger = null)
|
||||
public function __construct(EntityManagerInterface $em, AppOptions $options)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->em = $em;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ use Cake\Chronos\Chronos;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Common\Mercure\JwtProviderInterface;
|
||||
use Shlinkio\Shlink\Rest\Exception\MercureException;
|
||||
use Throwable;
|
||||
@ -23,12 +22,8 @@ class MercureInfoAction extends AbstractRestAction
|
||||
private JwtProviderInterface $jwtProvider;
|
||||
private array $mercureConfig;
|
||||
|
||||
public function __construct(
|
||||
JwtProviderInterface $jwtProvider,
|
||||
array $mercureConfig,
|
||||
?LoggerInterface $logger = null
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
public function __construct(JwtProviderInterface $jwtProvider, array $mercureConfig)
|
||||
{
|
||||
$this->jwtProvider = $jwtProvider;
|
||||
$this->mercureConfig = $mercureConfig;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
||||
use Shlinkio\Shlink\Core\Model\CreateShortUrlData;
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
|
||||
@ -19,12 +18,8 @@ abstract class AbstractCreateShortUrlAction extends AbstractRestAction
|
||||
private UrlShortenerInterface $urlShortener;
|
||||
private array $domainConfig;
|
||||
|
||||
public function __construct(
|
||||
UrlShortenerInterface $urlShortener,
|
||||
array $domainConfig,
|
||||
?LoggerInterface $logger = null
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
public function __construct(UrlShortenerInterface $urlShortener, array $domainConfig)
|
||||
{
|
||||
$this->urlShortener = $urlShortener;
|
||||
$this->domainConfig = $domainConfig;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrl\DeleteShortUrlServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
@ -19,9 +18,8 @@ class DeleteShortUrlAction extends AbstractRestAction
|
||||
|
||||
private DeleteShortUrlServiceInterface $deleteShortUrlService;
|
||||
|
||||
public function __construct(DeleteShortUrlServiceInterface $deleteShortUrlService, ?LoggerInterface $logger = null)
|
||||
public function __construct(DeleteShortUrlServiceInterface $deleteShortUrlService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->deleteShortUrlService = $deleteShortUrlService;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlEdit;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
|
||||
@ -20,9 +19,8 @@ class EditShortUrlAction extends AbstractRestAction
|
||||
|
||||
private ShortUrlServiceInterface $shortUrlService;
|
||||
|
||||
public function __construct(ShortUrlServiceInterface $shortUrlService, ?LoggerInterface $logger = null)
|
||||
public function __construct(ShortUrlServiceInterface $shortUrlService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->shortUrlService = $shortUrlService;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
|
||||
@ -20,9 +19,8 @@ class EditShortUrlTagsAction extends AbstractRestAction
|
||||
|
||||
private ShortUrlServiceInterface $shortUrlService;
|
||||
|
||||
public function __construct(ShortUrlServiceInterface $shortUrlService, ?LoggerInterface $logger = null)
|
||||
public function __construct(ShortUrlServiceInterface $shortUrlService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->shortUrlService = $shortUrlService;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
|
||||
@ -24,12 +23,8 @@ class ListShortUrlsAction extends AbstractRestAction
|
||||
private ShortUrlServiceInterface $shortUrlService;
|
||||
private array $domainConfig;
|
||||
|
||||
public function __construct(
|
||||
ShortUrlServiceInterface $shortUrlService,
|
||||
array $domainConfig,
|
||||
?LoggerInterface $logger = null
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
public function __construct(ShortUrlServiceInterface $shortUrlService, array $domainConfig)
|
||||
{
|
||||
$this->shortUrlService = $shortUrlService;
|
||||
$this->domainConfig = $domainConfig;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
|
||||
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
|
||||
@ -21,12 +20,8 @@ class ResolveShortUrlAction extends AbstractRestAction
|
||||
private ShortUrlResolverInterface $urlResolver;
|
||||
private array $domainConfig;
|
||||
|
||||
public function __construct(
|
||||
ShortUrlResolverInterface $urlResolver,
|
||||
array $domainConfig,
|
||||
?LoggerInterface $logger = null
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
public function __construct(ShortUrlResolverInterface $urlResolver, array $domainConfig)
|
||||
{
|
||||
$this->urlResolver = $urlResolver;
|
||||
$this->domainConfig = $domainConfig;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
|
||||
|
||||
use Laminas\Diactoros\Uri;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
||||
use Shlinkio\Shlink\Core\Model\CreateShortUrlData;
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
|
||||
@ -22,10 +21,9 @@ class SingleStepCreateShortUrlAction extends AbstractCreateShortUrlAction
|
||||
public function __construct(
|
||||
UrlShortenerInterface $urlShortener,
|
||||
ApiKeyServiceInterface $apiKeyService,
|
||||
array $domainConfig,
|
||||
?LoggerInterface $logger = null
|
||||
array $domainConfig
|
||||
) {
|
||||
parent::__construct($urlShortener, $domainConfig, $logger);
|
||||
parent::__construct($urlShortener, $domainConfig);
|
||||
$this->apiKeyService = $apiKeyService;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\Tag;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
|
||||
@ -18,9 +17,8 @@ class CreateTagsAction extends AbstractRestAction
|
||||
|
||||
private TagServiceInterface $tagService;
|
||||
|
||||
public function __construct(TagServiceInterface $tagService, ?LoggerInterface $logger = null)
|
||||
public function __construct(TagServiceInterface $tagService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->tagService = $tagService;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\Tag;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
|
||||
@ -18,9 +17,8 @@ class DeleteTagsAction extends AbstractRestAction
|
||||
|
||||
private TagServiceInterface $tagService;
|
||||
|
||||
public function __construct(TagServiceInterface $tagService, ?LoggerInterface $logger = null)
|
||||
public function __construct(TagServiceInterface $tagService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->tagService = $tagService;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\Tag;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
|
||||
@ -18,9 +17,8 @@ class ListTagsAction extends AbstractRestAction
|
||||
|
||||
private TagServiceInterface $tagService;
|
||||
|
||||
public function __construct(TagServiceInterface $tagService, ?LoggerInterface $logger = null)
|
||||
public function __construct(TagServiceInterface $tagService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->tagService = $tagService;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Rest\Action\Tag;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
@ -19,9 +18,8 @@ class UpdateTagAction extends AbstractRestAction
|
||||
|
||||
private TagServiceInterface $tagService;
|
||||
|
||||
public function __construct(TagServiceInterface $tagService, ?LoggerInterface $logger = null)
|
||||
public function __construct(TagServiceInterface $tagService)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->tagService = $tagService;
|
||||
}
|
||||
|
||||
|
31
module/Rest/src/Action/Visit/GlobalVisitsAction.php
Normal file
31
module/Rest/src/Action/Visit/GlobalVisitsAction.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Rest\Action\Visit;
|
||||
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
|
||||
class GlobalVisitsAction extends AbstractRestAction
|
||||
{
|
||||
protected const ROUTE_PATH = '/visits';
|
||||
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
|
||||
|
||||
private VisitsStatsHelperInterface $statsHelper;
|
||||
|
||||
public function __construct(VisitsStatsHelperInterface $statsHelper)
|
||||
{
|
||||
$this->statsHelper = $statsHelper;
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
return new JsonResponse([
|
||||
'visits' => $this->statsHelper->getVisitsStats(),
|
||||
]);
|
||||
}
|
||||
}
|
@ -7,14 +7,13 @@ namespace Shlinkio\Shlink\Rest\Action\Visit;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Model\VisitsParams;
|
||||
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
|
||||
class GetVisitsAction extends AbstractRestAction
|
||||
class ShortUrlVisitsAction extends AbstractRestAction
|
||||
{
|
||||
use PaginatorUtilsTrait;
|
||||
|
||||
@ -23,9 +22,8 @@ class GetVisitsAction extends AbstractRestAction
|
||||
|
||||
private VisitsTrackerInterface $visitsTracker;
|
||||
|
||||
public function __construct(VisitsTrackerInterface $visitsTracker, ?LoggerInterface $logger = null)
|
||||
public function __construct(VisitsTrackerInterface $visitsTracker)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
$this->visitsTracker = $visitsTracker;
|
||||
}
|
||||
|
21
module/Rest/test-api/Action/GlobalVisitsActionTest.php
Normal file
21
module/Rest/test-api/Action/GlobalVisitsActionTest.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioApiTest\Shlink\Rest\Action;
|
||||
|
||||
use Shlinkio\Shlink\TestUtils\ApiTest\ApiTestCase;
|
||||
|
||||
class GlobalVisitsActionTest extends ApiTestCase
|
||||
{
|
||||
/** @test */
|
||||
public function returnsExpectedVisitsStats(): void
|
||||
{
|
||||
$resp = $this->callApiWithKey(self::METHOD_GET, '/visits');
|
||||
$payload = $this->getJsonResponsePayload($resp);
|
||||
|
||||
$this->assertArrayHasKey('visits', $payload);
|
||||
$this->assertArrayHasKey('visitsCount', $payload['visits']);
|
||||
$this->assertEquals(7, $payload['visits']['visitsCount']);
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ use ShlinkioApiTest\Shlink\Rest\Utils\NotFoundUrlHelpersTrait;
|
||||
use function GuzzleHttp\Psr7\build_query;
|
||||
use function sprintf;
|
||||
|
||||
class GetVisitsActionTest extends ApiTestCase
|
||||
class ShortUrlVisitsActionTest extends ApiTestCase
|
||||
{
|
||||
use NotFoundUrlHelpersTrait;
|
||||
|
39
module/Rest/test/Action/Visit/GlobalVisitsActionTest.php
Normal file
39
module/Rest/test/Action/Visit/GlobalVisitsActionTest.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\Rest\Action\Visit;
|
||||
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Core\Visit\Model\VisitsStats;
|
||||
use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\Visit\GlobalVisitsAction;
|
||||
|
||||
class GlobalVisitsActionTest extends TestCase
|
||||
{
|
||||
private GlobalVisitsAction $action;
|
||||
private ObjectProphecy $helper;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->helper = $this->prophesize(VisitsStatsHelperInterface::class);
|
||||
$this->action = new GlobalVisitsAction($this->helper->reveal());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function statsAreReturnedFromHelper(): void
|
||||
{
|
||||
$stats = new VisitsStats(5);
|
||||
$getStats = $this->helper->getVisitsStats()->willReturn($stats);
|
||||
|
||||
/** @var JsonResponse $resp */
|
||||
$resp = $this->action->handle(ServerRequestFactory::fromGlobals());
|
||||
$payload = $resp->getPayload();
|
||||
|
||||
$this->assertEquals($payload, ['visits' => $stats]);
|
||||
$getStats->shouldHaveBeenCalledOnce();
|
||||
}
|
||||
}
|
@ -15,17 +15,17 @@ use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\Model\VisitsParams;
|
||||
use Shlinkio\Shlink\Core\Service\VisitsTracker;
|
||||
use Shlinkio\Shlink\Rest\Action\Visit\GetVisitsAction;
|
||||
use Shlinkio\Shlink\Rest\Action\Visit\ShortUrlVisitsAction;
|
||||
|
||||
class GetVisitsActionTest extends TestCase
|
||||
class ShortUrlVisitsActionTest extends TestCase
|
||||
{
|
||||
private GetVisitsAction $action;
|
||||
private ShortUrlVisitsAction $action;
|
||||
private ObjectProphecy $visitsTracker;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->visitsTracker = $this->prophesize(VisitsTracker::class);
|
||||
$this->action = new GetVisitsAction($this->visitsTracker->reveal());
|
||||
$this->action = new ShortUrlVisitsAction($this->visitsTracker->reveal());
|
||||
}
|
||||
|
||||
/** @test */
|
Loading…
Reference in New Issue
Block a user