Created new service to resolve short URLs

This commit is contained in:
Alejandro Celaya
2020-01-26 19:21:51 +01:00
parent f71bd84a20
commit 4ebd48b2b0
18 changed files with 193 additions and 89 deletions

View File

@@ -57,7 +57,10 @@ return [
],
Action\ShortUrl\EditShortUrlAction::class => [Service\ShortUrlService::class, 'Logger_Shlink'],
Action\ShortUrl\DeleteShortUrlAction::class => [Service\ShortUrl\DeleteShortUrlService::class, 'Logger_Shlink'],
Action\ShortUrl\ResolveShortUrlAction::class => [Service\UrlShortener::class, 'config.url_shortener.domain'],
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,

View File

@@ -4,12 +4,11 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
use InvalidArgumentException;
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\Service\UrlShortenerInterface;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
@@ -18,29 +17,26 @@ class ResolveShortUrlAction extends AbstractRestAction
protected const ROUTE_PATH = '/short-urls/{shortCode}';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
private UrlShortenerInterface $urlShortener;
private ShortUrlResolverInterface $urlResolver;
private array $domainConfig;
public function __construct(
UrlShortenerInterface $urlShortener,
ShortUrlResolverInterface $urlResolver,
array $domainConfig,
?LoggerInterface $logger = null
) {
parent::__construct($logger);
$this->urlShortener = $urlShortener;
$this->urlResolver = $urlResolver;
$this->domainConfig = $domainConfig;
}
/**
* @throws InvalidArgumentException
*/
public function handle(Request $request): Response
{
$shortCode = $request->getAttribute('shortCode');
$domain = $request->getQueryParams()['domain'] ?? null;
$transformer = new ShortUrlDataTransformer($this->domainConfig);
$url = $this->urlShortener->shortCodeToUrl($shortCode, $domain);
$url = $this->urlResolver->shortCodeToShortUrl($shortCode, $domain);
return new JsonResponse($transformer->transform($url));
}
}

View File

@@ -18,7 +18,7 @@ class EditShortUrlActionTest extends ApiTestCase
/**
* @test
* @dataProvider provideDisablingMeta
* @dataProvider provideMeta
*/
public function metadataCanBeReset(array $meta): void
{
@@ -30,11 +30,9 @@ class EditShortUrlActionTest extends ApiTestCase
'maxVisits' => null,
];
// Setting meta that disables the URL should not let it be visited
$editWithProvidedMeta = $this->callApiWithKey(self::METHOD_PATCH, $url, [RequestOptions::JSON => $meta]);
$metaAfterEditing = $this->findShortUrlMetaByShortCode($shortCode);
// Resetting all meta should allow the URL to be visitable again
$editWithResetMeta = $this->callApiWithKey(self::METHOD_PATCH, $url, [
RequestOptions::JSON => $resetMeta,
]);
@@ -46,7 +44,7 @@ class EditShortUrlActionTest extends ApiTestCase
self::assertArraySubset($meta, $metaAfterEditing);
}
public function provideDisablingMeta(): iterable
public function provideMeta(): iterable
{
$now = Chronos::now();

View File

@@ -4,10 +4,42 @@ declare(strict_types=1);
namespace ShlinkioApiTest\Shlink\Rest\Action;
use Cake\Chronos\Chronos;
use GuzzleHttp\RequestOptions;
use Shlinkio\Shlink\TestUtils\ApiTest\ApiTestCase;
use function sprintf;
class ResolveShortUrlActionTest extends ApiTestCase
{
/**
* @test
* @dataProvider provideDisabledMeta
*/
public function shortUrlIsProperlyResolvedEvenWhenNotEnabled(array $disabledMeta): void
{
$shortCode = 'abc123';
$url = sprintf('/short-urls/%s', $shortCode);
$this->callShortUrl($shortCode);
$editResp = $this->callApiWithKey(self::METHOD_PATCH, $url, [RequestOptions::JSON => $disabledMeta]);
$visitResp = $this->callShortUrl($shortCode);
$fetchResp = $this->callApiWithKey(self::METHOD_GET, $url);
$this->assertEquals(self::STATUS_NO_CONTENT, $editResp->getStatusCode());
$this->assertEquals(self::STATUS_NOT_FOUND, $visitResp->getStatusCode());
$this->assertEquals(self::STATUS_OK, $fetchResp->getStatusCode());
}
public function provideDisabledMeta(): iterable
{
$now = Chronos::now();
yield 'future validSince' => [['validSince' => $now->addMonth()->toAtomString()]];
yield 'past validUntil' => [['validUntil' => $now->subMonth()->toAtomString()]];
yield 'maxVisits reached' => [['maxVisits' => 1]];
}
/** @test */
public function tryingToResolveInvalidUrlReturnsNotFoundError(): void
{

View File

@@ -8,7 +8,7 @@ use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Service\UrlShortener;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use Shlinkio\Shlink\Rest\Action\ShortUrl\ResolveShortUrlAction;
use function strpos;
@@ -16,19 +16,19 @@ use function strpos;
class ResolveShortUrlActionTest extends TestCase
{
private ResolveShortUrlAction $action;
private ObjectProphecy $urlShortener;
private ObjectProphecy $urlResolver;
public function setUp(): void
{
$this->urlShortener = $this->prophesize(UrlShortener::class);
$this->action = new ResolveShortUrlAction($this->urlShortener->reveal(), []);
$this->urlResolver = $this->prophesize(ShortUrlResolverInterface::class);
$this->action = new ResolveShortUrlAction($this->urlResolver->reveal(), []);
}
/** @test */
public function correctShortCodeReturnsSuccess(): void
{
$shortCode = 'abc123';
$this->urlShortener->shortCodeToUrl($shortCode, null)->willReturn(
$this->urlResolver->shortCodeToShortUrl($shortCode, null)->willReturn(
new ShortUrl('http://domain.com/foo/bar'),
)->shouldBeCalledOnce();