diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index e1954329..9809c5dd 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -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'], diff --git a/module/Core/src/Service/ShortUrl/DeleteShortUrlService.php b/module/Core/src/Service/ShortUrl/DeleteShortUrlService.php index f1795d82..35a540da 100644 --- a/module/Core/src/Service/ShortUrl/DeleteShortUrlService.php +++ b/module/Core/src/Service/ShortUrl/DeleteShortUrlService.php @@ -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(), diff --git a/module/Core/src/Service/ShortUrl/FindShortCodeTrait.php b/module/Core/src/Service/ShortUrl/FindShortCodeTrait.php deleted file mode 100644 index 654d587f..00000000 --- a/module/Core/src/Service/ShortUrl/FindShortCodeTrait.php +++ /dev/null @@ -1,29 +0,0 @@ -getRepository(ShortUrl::class); - $shortUrl = $repo->findOneByShortCode($identifier->shortCode(), $identifier->domain()); - if ($shortUrl === null) { - throw ShortUrlNotFoundException::fromNotFound($identifier); - } - - return $shortUrl; - } -} diff --git a/module/Core/src/Service/ShortUrlService.php b/module/Core/src/Service/ShortUrlService.php index a505c1fb..8d20bf8d 100644 --- a/module/Core/src/Service/ShortUrlService.php +++ b/module/Core/src/Service/ShortUrlService.php @@ -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(); diff --git a/module/Core/src/Service/ShortUrlServiceInterface.php b/module/Core/src/Service/ShortUrlServiceInterface.php index e431c51b..70dd3de5 100644 --- a/module/Core/src/Service/ShortUrlServiceInterface.php +++ b/module/Core/src/Service/ShortUrlServiceInterface.php @@ -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; } diff --git a/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php b/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php index ed2914af..0911cb7b 100644 --- a/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php +++ b/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php @@ -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()); } } diff --git a/module/Core/test/Service/ShortUrlServiceTest.php b/module/Core/test/Service/ShortUrlServiceTest.php index 2fb92f53..d962c204 100644 --- a/module/Core/test/Service/ShortUrlServiceTest.php +++ b/module/Core/test/Service/ShortUrlServiceTest.php @@ -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(); } } diff --git a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php index a6bc5538..8b3e65ab 100644 --- a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php @@ -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(); } }