Allowed domain to be provided when editing short URL meta

This commit is contained in:
Alejandro Celaya 2020-02-01 22:54:02 +01:00
parent 732bb06c62
commit 5d1d9dcac3
8 changed files with 49 additions and 78 deletions

View file

@ -53,10 +53,14 @@ return [
Service\UrlShortener::class => [Util\UrlValidator::class, 'em', Options\UrlShortenerOptions::class],
Service\VisitsTracker::class => ['em', EventDispatcherInterface::class],
Service\ShortUrlService::class => ['em'],
Service\ShortUrlService::class => ['em', Service\ShortUrl\ShortUrlResolver::class],
Service\VisitService::class => ['em'],
Service\Tag\TagService::class => ['em'],
Service\ShortUrl\DeleteShortUrlService::class => ['em', Options\DeleteShortUrlsOptions::class],
Service\ShortUrl\DeleteShortUrlService::class => [
'em',
Options\DeleteShortUrlsOptions::class,
Service\ShortUrl\ShortUrlResolver::class,
],
Service\ShortUrl\ShortUrlResolver::class => ['em'],
Util\UrlValidator::class => ['httpClient'],

View file

@ -12,15 +12,18 @@ use Shlinkio\Shlink\Core\Options\DeleteShortUrlsOptions;
class DeleteShortUrlService implements DeleteShortUrlServiceInterface
{
use FindShortCodeTrait;
private EntityManagerInterface $em;
private DeleteShortUrlsOptions $deleteShortUrlsOptions;
private ShortUrlResolverInterface $urlResolver;
public function __construct(EntityManagerInterface $em, DeleteShortUrlsOptions $deleteShortUrlsOptions)
{
public function __construct(
EntityManagerInterface $em,
DeleteShortUrlsOptions $deleteShortUrlsOptions,
ShortUrlResolverInterface $urlResolver
) {
$this->em = $em;
$this->deleteShortUrlsOptions = $deleteShortUrlsOptions;
$this->urlResolver = $urlResolver;
}
/**
@ -29,7 +32,7 @@ class DeleteShortUrlService implements DeleteShortUrlServiceInterface
*/
public function deleteByShortCode(ShortUrlIdentifier $identifier, bool $ignoreThreshold = false): void
{
$shortUrl = $this->findByShortCode($this->em, $identifier);
$shortUrl = $this->urlResolver->resolveShortUrl($identifier);
if (! $ignoreThreshold && $this->isThresholdReached($shortUrl)) {
throw Exception\DeleteShortUrlException::fromVisitsThreshold(
$this->deleteShortUrlsOptions->getVisitsThreshold(),

View file

@ -1,29 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Service\ShortUrl;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
trait FindShortCodeTrait
{
/**
* @throws ShortUrlNotFoundException
*/
private function findByShortCode(EntityManagerInterface $em, ShortUrlIdentifier $identifier): ShortUrl
{
/** @var ShortUrlRepositoryInterface $repo */
$repo = $em->getRepository(ShortUrl::class);
$shortUrl = $repo->findOneByShortCode($identifier->shortCode(), $identifier->domain());
if ($shortUrl === null) {
throw ShortUrlNotFoundException::fromNotFound($identifier);
}
return $shortUrl;
}
}

View file

@ -13,19 +13,20 @@ use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
use Shlinkio\Shlink\Core\Paginator\Adapter\ShortUrlRepositoryAdapter;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\Service\ShortUrl\FindShortCodeTrait;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
class ShortUrlService implements ShortUrlServiceInterface
{
use FindShortCodeTrait;
use TagManagerTrait;
private ORM\EntityManagerInterface $em;
private ShortUrlResolverInterface $urlResolver;
public function __construct(ORM\EntityManagerInterface $em)
public function __construct(ORM\EntityManagerInterface $em, ShortUrlResolverInterface $urlResolver)
{
$this->em = $em;
$this->urlResolver = $urlResolver;
}
/**
@ -48,8 +49,9 @@ class ShortUrlService implements ShortUrlServiceInterface
*/
public function setTagsByShortCode(string $shortCode, array $tags = []): ShortUrl
{
$shortUrl = $this->findByShortCode($this->em, new ShortUrlIdentifier($shortCode));
$shortUrl = $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode));
$shortUrl->setTags($this->tagNamesToEntities($this->em, $tags));
$this->em->flush();
return $shortUrl;
@ -58,9 +60,9 @@ class ShortUrlService implements ShortUrlServiceInterface
/**
* @throws ShortUrlNotFoundException
*/
public function updateMetadataByShortCode(string $shortCode, ShortUrlMeta $shortUrlMeta): ShortUrl
public function updateMetadataByShortCode(ShortUrlIdentifier $identifier, ShortUrlMeta $shortUrlMeta): ShortUrl
{
$shortUrl = $this->findByShortCode($this->em, new ShortUrlIdentifier($shortCode));
$shortUrl = $this->urlResolver->resolveShortUrl($identifier);
$shortUrl->updateMeta($shortUrlMeta);
$this->em->flush();

View file

@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Core\Service;
use Laminas\Paginator\Paginator;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
@ -26,5 +27,5 @@ interface ShortUrlServiceInterface
/**
* @throws ShortUrlNotFoundException
*/
public function updateMetadataByShortCode(string $shortCode, ShortUrlMeta $shortUrlMeta): ShortUrl;
public function updateMetadataByShortCode(ShortUrlIdentifier $identifier, ShortUrlMeta $shortUrlMeta): ShortUrl;
}

View file

@ -15,8 +15,8 @@ use Shlinkio\Shlink\Core\Exception\DeleteShortUrlException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Options\DeleteShortUrlsOptions;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Service\ShortUrl\DeleteShortUrlService;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use function Functional\map;
use function range;
@ -25,6 +25,7 @@ use function sprintf;
class DeleteShortUrlServiceTest extends TestCase
{
private ObjectProphecy $em;
private ObjectProphecy $urlResolver;
private string $shortCode;
public function setUp(): void
@ -36,9 +37,8 @@ class DeleteShortUrlServiceTest extends TestCase
$this->em = $this->prophesize(EntityManagerInterface::class);
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$repo->findOneByShortCode(Argument::cetera())->willReturn($shortUrl);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$this->urlResolver = $this->prophesize(ShortUrlResolverInterface::class);
$this->urlResolver->resolveShortUrl(Argument::cetera())->willReturn($shortUrl);
}
/** @test */
@ -102,6 +102,6 @@ class DeleteShortUrlServiceTest extends TestCase
return new DeleteShortUrlService($this->em->reveal(), new DeleteShortUrlsOptions([
'visitsThreshold' => $visitsThreshold,
'checkVisitsThreshold' => $checkVisitsThreshold,
]));
]), $this->urlResolver->reveal());
}
}

View file

@ -12,10 +12,11 @@ use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use Shlinkio\Shlink\Core\Service\ShortUrlService;
use function count;
@ -24,13 +25,17 @@ class ShortUrlServiceTest extends TestCase
{
private ShortUrlService $service;
private ObjectProphecy $em;
private ObjectProphecy $urlResolver;
public function setUp(): void
{
$this->em = $this->prophesize(EntityManagerInterface::class);
$this->em->persist(Argument::any())->willReturn(null);
$this->em->flush()->willReturn(null);
$this->service = new ShortUrlService($this->em->reveal());
$this->urlResolver = $this->prophesize(ShortUrlResolverInterface::class);
$this->service = new ShortUrlService($this->em->reveal(), $this->urlResolver->reveal());
}
/** @test */
@ -52,29 +57,14 @@ class ShortUrlServiceTest extends TestCase
$this->assertEquals(4, $list->getCurrentItemCount());
}
/** @test */
public function exceptionIsThrownWhenSettingTagsOnInvalidShortcode(): void
{
$shortCode = 'abc123';
$repo = $this->prophesize(ShortUrlRepository::class);
$repo->findOneByShortCode($shortCode, null)->willReturn(null)
->shouldBeCalledOnce();
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$this->expectException(ShortUrlNotFoundException::class);
$this->service->setTagsByShortCode($shortCode);
}
/** @test */
public function providedTagsAreGetFromRepoAndSetToTheShortUrl(): void
{
$shortUrl = $this->prophesize(ShortUrl::class);
$shortUrl->setTags(Argument::any())->shouldBeCalledOnce();
$shortCode = 'abc123';
$repo = $this->prophesize(ShortUrlRepository::class);
$repo->findOneByShortCode($shortCode, null)->willReturn($shortUrl->reveal())
->shouldBeCalledOnce();
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode))->willReturn($shortUrl->reveal())
->shouldBeCalledOnce();
$tagRepo = $this->prophesize(EntityRepository::class);
$tagRepo->findOneBy(['name' => 'foo'])->willReturn(new Tag('foo'))->shouldBeCalledOnce();
@ -89,23 +79,22 @@ class ShortUrlServiceTest extends TestCase
{
$shortUrl = new ShortUrl('');
$repo = $this->prophesize(ShortUrlRepository::class);
$findShortUrl = $repo->findOneByShortCode('abc123', null)->willReturn($shortUrl);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$findShortUrl = $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier('abc123'))->willReturn($shortUrl);
$flush = $this->em->flush()->willReturn(null);
$result = $this->service->updateMetadataByShortCode('abc123', ShortUrlMeta::fromRawData([
'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(),
'validUntil' => Chronos::parse('2017-01-05 00:00:00')->toAtomString(),
'maxVisits' => 5,
]));
$result = $this->service->updateMetadataByShortCode(new ShortUrlIdentifier('abc123'), ShortUrlMeta::fromRawData(
[
'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(),
'validUntil' => Chronos::parse('2017-01-05 00:00:00')->toAtomString(),
'maxVisits' => 5,
],
));
$this->assertSame($shortUrl, $result);
$this->assertEquals(Chronos::parse('2017-01-01 00:00:00'), $shortUrl->getValidSince());
$this->assertEquals(Chronos::parse('2017-01-05 00:00:00'), $shortUrl->getValidUntil());
$this->assertEquals(5, $shortUrl->getMaxVisits());
$findShortUrl->shouldHaveBeenCalled();
$getRepo->shouldHaveBeenCalled();
$flush->shouldHaveBeenCalled();
}
}

View file

@ -8,6 +8,7 @@ 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\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
@ -28,9 +29,9 @@ class EditShortUrlAction extends AbstractRestAction
public function handle(ServerRequestInterface $request): ResponseInterface
{
$postData = (array) $request->getParsedBody();
$shortCode = $request->getAttribute('shortCode', '');
$identifier = ShortUrlIdentifier::fromApiRequest($request);
$this->shortUrlService->updateMetadataByShortCode($shortCode, ShortUrlMeta::fromRawData($postData));
$this->shortUrlService->updateMetadataByShortCode($identifier, ShortUrlMeta::fromRawData($postData));
return new EmptyResponse();
}
}