From 9cddedcdbab90b9b2f3efc358c26f84517e2242a Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 1 Feb 2021 22:55:52 +0100 Subject: [PATCH 1/4] Extracted logic to stringify ShortUrls to its own service --- module/CLI/config/dependencies.config.php | 9 ++- .../ShortUrl/GenerateShortUrlCommand.php | 14 ++-- .../Command/ShortUrl/ListShortUrlsCommand.php | 8 +- .../ShortUrl/GenerateShortUrlCommandTest.php | 20 +++-- .../ShortUrl/ListShortUrlsCommandTest.php | 7 +- module/Core/config/dependencies.config.php | 8 +- .../Core/config/event_dispatcher.config.php | 2 +- module/Core/src/Action/QrCodeAction.php | 9 ++- module/Core/src/Entity/ShortUrl.php | 78 +++++++------------ .../EventDispatcher/NotifyVisitToWebHooks.php | 8 +- .../src/Mercure/MercureUpdatesGenerator.php | 8 +- .../ShortUrl/Helper/ShortUrlStringifier.php | 36 +++++++++ .../Helper/ShortUrlStringifierInterface.php | 12 +++ .../Transformer/ShortUrlDataTransformer.php | 11 +-- module/Core/test/Action/QrCodeActionTest.php | 6 +- .../NotifyVisitToWebHooksTest.php | 4 +- .../Mercure/MercureUpdatesGeneratorTest.php | 4 +- .../ShortUrlDataTransformerTest.php | 7 +- module/Rest/config/dependencies.config.php | 11 +-- .../ShortUrl/AbstractCreateShortUrlAction.php | 8 +- .../Action/ShortUrl/EditShortUrlAction.php | 8 +- .../Action/ShortUrl/ListShortUrlsAction.php | 8 +- .../Action/ShortUrl/ResolveShortUrlAction.php | 8 +- .../ShortUrl/CreateShortUrlActionTest.php | 21 ++--- .../ShortUrl/EditShortUrlActionTest.php | 6 +- .../ShortUrl/ListShortUrlsActionTest.php | 12 ++- .../ShortUrl/ResolveShortUrlActionTest.php | 6 +- .../SingleStepCreateShortUrlActionTest.php | 11 +-- 28 files changed, 215 insertions(+), 135 deletions(-) create mode 100644 module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php create mode 100644 module/Core/src/ShortUrl/Helper/ShortUrlStringifierInterface.php rename module/Core/src/{ => ShortUrl}/Transformer/ShortUrlDataTransformer.php (77%) rename module/Core/test/{ => ShortUrl}/Transformer/ShortUrlDataTransformerTest.php (89%) diff --git a/module/CLI/config/dependencies.config.php b/module/CLI/config/dependencies.config.php index 3c9d74ce..685dc9fd 100644 --- a/module/CLI/config/dependencies.config.php +++ b/module/CLI/config/dependencies.config.php @@ -11,6 +11,8 @@ use Laminas\ServiceManager\Factory\InvokableFactory; use Shlinkio\Shlink\Common\Doctrine\NoDbNameConnectionFactory; use Shlinkio\Shlink\Core\Domain\DomainService; use Shlinkio\Shlink\Core\Service; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Core\Tag\TagService; use Shlinkio\Shlink\Core\Visit; use Shlinkio\Shlink\Installer\Factory\ProcessHelperFactory; @@ -64,11 +66,14 @@ return [ Command\ShortUrl\GenerateShortUrlCommand::class => [ Service\UrlShortener::class, - 'config.url_shortener.domain', + ShortUrlStringifier::class, 'config.url_shortener.default_short_codes_length', ], Command\ShortUrl\ResolveUrlCommand::class => [Service\ShortUrl\ShortUrlResolver::class], - Command\ShortUrl\ListShortUrlsCommand::class => [Service\ShortUrlService::class, 'config.url_shortener.domain'], + Command\ShortUrl\ListShortUrlsCommand::class => [ + Service\ShortUrlService::class, + ShortUrlDataTransformer::class, + ], Command\ShortUrl\GetVisitsCommand::class => [Service\VisitsTracker::class], Command\ShortUrl\DeleteShortUrlCommand::class => [Service\ShortUrl\DeleteShortUrlService::class], diff --git a/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php index a618af83..cafd0e5a 100644 --- a/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php @@ -10,6 +10,7 @@ use Shlinkio\Shlink\Core\Exception\InvalidUrlException; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Service\UrlShortenerInterface; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\Validation\ShortUrlInputFilter; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -30,14 +31,17 @@ class GenerateShortUrlCommand extends BaseCommand public const NAME = 'short-url:generate'; private UrlShortenerInterface $urlShortener; - private array $domainConfig; + private ShortUrlStringifierInterface $stringifier; private int $defaultShortCodeLength; - public function __construct(UrlShortenerInterface $urlShortener, array $domainConfig, int $defaultShortCodeLength) - { + public function __construct( + UrlShortenerInterface $urlShortener, + ShortUrlStringifierInterface $stringifier, + int $defaultShortCodeLength + ) { parent::__construct(); $this->urlShortener = $urlShortener; - $this->domainConfig = $domainConfig; + $this->stringifier = $stringifier; $this->defaultShortCodeLength = $defaultShortCodeLength; } @@ -163,7 +167,7 @@ class GenerateShortUrlCommand extends BaseCommand $io->writeln([ sprintf('Processed long URL: %s', $longUrl), - sprintf('Generated short URL: %s', $shortUrl->toString($this->domainConfig)), + sprintf('Generated short URL: %s', $this->stringifier->stringify($shortUrl)), ]); return ExitCodes::EXIT_SUCCESS; } catch (InvalidUrlException | NonUniqueSlugException $e) { diff --git a/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php b/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php index cf20e328..21beecaa 100644 --- a/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php +++ b/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php @@ -9,10 +9,10 @@ use Shlinkio\Shlink\CLI\Util\ExitCodes; use Shlinkio\Shlink\CLI\Util\ShlinkTable; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtilsTrait; +use Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Model\ShortUrlsOrdering; use Shlinkio\Shlink\Core\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; -use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Core\Validation\ShortUrlsParamsInputFilter; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -42,13 +42,13 @@ class ListShortUrlsCommand extends AbstractWithDateRangeCommand ]; private ShortUrlServiceInterface $shortUrlService; - private ShortUrlDataTransformer $transformer; + private DataTransformerInterface $transformer; - public function __construct(ShortUrlServiceInterface $shortUrlService, array $domainConfig) + public function __construct(ShortUrlServiceInterface $shortUrlService, DataTransformerInterface $transformer) { parent::__construct(); $this->shortUrlService = $shortUrlService; - $this->transformer = new ShortUrlDataTransformer($domainConfig); + $this->transformer = $transformer; } protected function doConfigure(): void diff --git a/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php index f2e8d610..25953d38 100644 --- a/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php @@ -16,6 +16,7 @@ use Shlinkio\Shlink\Core\Exception\InvalidUrlException; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Service\UrlShortener; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; @@ -23,18 +24,17 @@ class GenerateShortUrlCommandTest extends TestCase { use ProphecyTrait; - private const DOMAIN_CONFIG = [ - 'schema' => 'http', - 'hostname' => 'foo.com', - ]; - private CommandTester $commandTester; private ObjectProphecy $urlShortener; + private ObjectProphecy $stringifier; public function setUp(): void { $this->urlShortener = $this->prophesize(UrlShortener::class); - $command = new GenerateShortUrlCommand($this->urlShortener->reveal(), self::DOMAIN_CONFIG, 5); + $this->stringifier = $this->prophesize(ShortUrlStringifierInterface::class); + $this->stringifier->stringify(Argument::type(ShortUrl::class))->willReturn(''); + + $command = new GenerateShortUrlCommand($this->urlShortener->reveal(), $this->stringifier->reveal(), 5); $app = new Application(); $app->add($command); $this->commandTester = new CommandTester($command); @@ -45,6 +45,7 @@ class GenerateShortUrlCommandTest extends TestCase { $shortUrl = ShortUrl::createEmpty(); $urlToShortCode = $this->urlShortener->shorten(Argument::cetera())->willReturn($shortUrl); + $stringify = $this->stringifier->stringify($shortUrl)->willReturn('stringified_short_url'); $this->commandTester->execute([ 'longUrl' => 'http://domain.com/foo/bar', @@ -53,8 +54,9 @@ class GenerateShortUrlCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); - self::assertStringContainsString($shortUrl->toString(self::DOMAIN_CONFIG), $output); + self::assertStringContainsString('stringified_short_url', $output); $urlToShortCode->shouldHaveBeenCalledOnce(); + $stringify->shouldHaveBeenCalledOnce(); } /** @test */ @@ -97,6 +99,7 @@ class GenerateShortUrlCommandTest extends TestCase return true; }), )->willReturn($shortUrl); + $stringify = $this->stringifier->stringify($shortUrl)->willReturn('stringified_short_url'); $this->commandTester->execute([ 'longUrl' => 'http://domain.com/foo/bar', @@ -105,8 +108,9 @@ class GenerateShortUrlCommandTest extends TestCase $output = $this->commandTester->getDisplay(); self::assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode()); - self::assertStringContainsString($shortUrl->toString(self::DOMAIN_CONFIG), $output); + self::assertStringContainsString('stringified_short_url', $output); $urlToShortCode->shouldHaveBeenCalledOnce(); + $stringify->shouldHaveBeenCalledOnce(); } /** diff --git a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php index 784391e0..3f2b38b1 100644 --- a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php @@ -15,6 +15,8 @@ use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; @@ -31,7 +33,9 @@ class ListShortUrlsCommandTest extends TestCase { $this->shortUrlService = $this->prophesize(ShortUrlServiceInterface::class); $app = new Application(); - $command = new ListShortUrlsCommand($this->shortUrlService->reveal(), []); + $command = new ListShortUrlsCommand($this->shortUrlService->reveal(), new ShortUrlDataTransformer( + new ShortUrlStringifier([]), + )); $app->add($command); $this->commandTester = new CommandTester($command); } @@ -56,6 +60,7 @@ class ListShortUrlsCommandTest extends TestCase self::assertStringContainsString('Continue with page 2?', $output); self::assertStringContainsString('Continue with page 3?', $output); self::assertStringContainsString('Continue with page 4?', $output); + self::assertStringNotContainsString('Continue with page 5?', $output); } /** @test */ diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 0eaa7a8e..7a5950bc 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -43,6 +43,8 @@ return [ Action\QrCodeAction::class => ConfigAbstractFactory::class, ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class => ConfigAbstractFactory::class, + ShortUrl\Helper\ShortUrlStringifier::class => ConfigAbstractFactory::class, + ShortUrl\Transformer\ShortUrlDataTransformer::class => ConfigAbstractFactory::class, Mercure\MercureUpdatesGenerator::class => ConfigAbstractFactory::class, @@ -114,13 +116,15 @@ return [ ], Action\QrCodeAction::class => [ Service\ShortUrl\ShortUrlResolver::class, - 'config.url_shortener.domain', + ShortUrl\Helper\ShortUrlStringifier::class, 'Logger_Shlink', ], ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class => ['em'], + ShortUrl\Helper\ShortUrlStringifier::class => ['config.url_shortener.domain'], + ShortUrl\Transformer\ShortUrlDataTransformer::class => [ShortUrl\Helper\ShortUrlStringifier::class], - Mercure\MercureUpdatesGenerator::class => ['config.url_shortener.domain'], + Mercure\MercureUpdatesGenerator::class => [ShortUrl\Transformer\ShortUrlDataTransformer::class], Importer\ImportedLinksProcessor::class => [ 'em', diff --git a/module/Core/config/event_dispatcher.config.php b/module/Core/config/event_dispatcher.config.php index 83390fdd..66a23637 100644 --- a/module/Core/config/event_dispatcher.config.php +++ b/module/Core/config/event_dispatcher.config.php @@ -53,7 +53,7 @@ return [ 'em', 'Logger_Shlink', 'config.url_shortener.visits_webhooks', - 'config.url_shortener.domain', + ShortUrl\Transformer\ShortUrlDataTransformer::class, Options\AppOptions::class, ], EventDispatcher\NotifyVisitToMercure::class => [ diff --git a/module/Core/src/Action/QrCodeAction.php b/module/Core/src/Action/QrCodeAction.php index 919682d5..b39159fd 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -16,6 +16,7 @@ use Shlinkio\Shlink\Common\Response\QrCodeResponse; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; class QrCodeAction implements MiddlewareInterface { @@ -24,17 +25,17 @@ class QrCodeAction implements MiddlewareInterface private const MAX_SIZE = 1000; private ShortUrlResolverInterface $urlResolver; - private array $domainConfig; + private ShortUrlStringifierInterface $stringifier; private LoggerInterface $logger; public function __construct( ShortUrlResolverInterface $urlResolver, - array $domainConfig, + ShortUrlStringifierInterface $stringifier, ?LoggerInterface $logger = null ) { $this->urlResolver = $urlResolver; - $this->domainConfig = $domainConfig; $this->logger = $logger ?? new NullLogger(); + $this->stringifier = $stringifier; } public function process(Request $request, RequestHandlerInterface $handler): Response @@ -52,7 +53,7 @@ class QrCodeAction implements MiddlewareInterface // Size attribute is deprecated $size = $this->normalizeSize((int) $request->getAttribute('size', $query['size'] ?? self::DEFAULT_SIZE)); - $qrCode = new QrCode($shortUrl->toString($this->domainConfig)); + $qrCode = new QrCode($this->stringifier->stringify($shortUrl)); $qrCode->setSize($size); $qrCode->setMargin(0); diff --git a/module/Core/src/Entity/ShortUrl.php b/module/Core/src/Entity/ShortUrl.php index 2919be17..c41d506e 100644 --- a/module/Core/src/Entity/ShortUrl.php +++ b/module/Core/src/Entity/ShortUrl.php @@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Core\Entity; use Cake\Chronos\Chronos; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; -use Laminas\Diactoros\Uri; use Shlinkio\Shlink\Common\Entity\AbstractEntity; use Shlinkio\Shlink\Core\Exception\ShortCodeCannotBeRegeneratedException; use Shlinkio\Shlink\Core\Model\ShortUrlEdit; @@ -128,6 +127,36 @@ class ShortUrl extends AbstractEntity return $this->tags; } + public function getValidSince(): ?Chronos + { + return $this->validSince; + } + + public function getValidUntil(): ?Chronos + { + return $this->validUntil; + } + + public function getVisitsCount(): int + { + return count($this->visits); + } + + /** + * @param Collection|Visit[] $visits + * @internal + */ + public function setVisits(Collection $visits): self + { + $this->visits = $visits; + return $this; + } + + public function getMaxVisits(): ?int + { + return $this->maxVisits; + } + public function update( ShortUrlEdit $shortUrlEdit, ?ShortUrlRelationResolverInterface $relationResolver = null @@ -168,36 +197,6 @@ class ShortUrl extends AbstractEntity $this->shortCode = generateRandomShortCode($this->shortCodeLength); } - public function getValidSince(): ?Chronos - { - return $this->validSince; - } - - public function getValidUntil(): ?Chronos - { - return $this->validUntil; - } - - public function getVisitsCount(): int - { - return count($this->visits); - } - - /** - * @param Collection|Visit[] $visits - * @internal - */ - public function setVisits(Collection $visits): self - { - $this->visits = $visits; - return $this; - } - - public function getMaxVisits(): ?int - { - return $this->maxVisits; - } - public function isEnabled(): bool { $maxVisitsReached = $this->maxVisits !== null && $this->getVisitsCount() >= $this->maxVisits; @@ -218,21 +217,4 @@ class ShortUrl extends AbstractEntity return true; } - - public function toString(array $domainConfig): string - { - return (new Uri())->withPath($this->shortCode) - ->withScheme($domainConfig['schema'] ?? 'http') - ->withHost($this->resolveDomain($domainConfig['hostname'] ?? '')) - ->__toString(); - } - - private function resolveDomain(string $fallback = ''): string - { - if ($this->domain === null) { - return $fallback; - } - - return $this->domain->getAuthority(); - } } diff --git a/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php b/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php index 2add5698..d3b27602 100644 --- a/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php +++ b/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php @@ -12,10 +12,10 @@ use GuzzleHttp\Promise\Promise; use GuzzleHttp\Promise\PromiseInterface; use GuzzleHttp\RequestOptions; use Psr\Log\LoggerInterface; +use Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated; use Shlinkio\Shlink\Core\Options\AppOptions; -use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; use Throwable; use function Functional\map; @@ -29,7 +29,7 @@ class NotifyVisitToWebHooks private LoggerInterface $logger; /** @var string[] */ private array $webhooks; - private ShortUrlDataTransformer $transformer; + private DataTransformerInterface $transformer; private AppOptions $appOptions; public function __construct( @@ -37,14 +37,14 @@ class NotifyVisitToWebHooks EntityManagerInterface $em, LoggerInterface $logger, array $webhooks, - array $domainConfig, + DataTransformerInterface $transformer, AppOptions $appOptions ) { $this->httpClient = $httpClient; $this->em = $em; $this->logger = $logger; $this->webhooks = $webhooks; - $this->transformer = new ShortUrlDataTransformer($domainConfig); + $this->transformer = $transformer; $this->appOptions = $appOptions; } diff --git a/module/Core/src/Mercure/MercureUpdatesGenerator.php b/module/Core/src/Mercure/MercureUpdatesGenerator.php index aad072f2..bd00e836 100644 --- a/module/Core/src/Mercure/MercureUpdatesGenerator.php +++ b/module/Core/src/Mercure/MercureUpdatesGenerator.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Mercure; +use Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Entity\Visit; -use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; use Symfony\Component\Mercure\Update; use function json_encode; @@ -17,11 +17,11 @@ final class MercureUpdatesGenerator implements MercureUpdatesGeneratorInterface { private const NEW_VISIT_TOPIC = 'https://shlink.io/new-visit'; - private ShortUrlDataTransformer $transformer; + private DataTransformerInterface $transformer; - public function __construct(array $domainConfig) + public function __construct(DataTransformerInterface $transformer) { - $this->transformer = new ShortUrlDataTransformer($domainConfig); + $this->transformer = $transformer; } public function newVisitUpdate(Visit $visit): Update diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php new file mode 100644 index 00000000..d5edba52 --- /dev/null +++ b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php @@ -0,0 +1,36 @@ +domainConfig = $domainConfig; + } + + public function stringify(ShortUrl $shortUrl): string + { + return (new Uri())->withPath($shortUrl->getShortCode()) + ->withScheme($this->domainConfig['schema'] ?? 'http') + ->withHost($this->resolveDomain($shortUrl)) + ->__toString(); + } + + private function resolveDomain(ShortUrl $shortUrl): string + { + $domain = $shortUrl->getDomain(); + if ($domain === null) { + return $this->domainConfig['hostname'] ?? ''; + } + + return $domain->getAuthority(); + } +} diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlStringifierInterface.php b/module/Core/src/ShortUrl/Helper/ShortUrlStringifierInterface.php new file mode 100644 index 00000000..360861ee --- /dev/null +++ b/module/Core/src/ShortUrl/Helper/ShortUrlStringifierInterface.php @@ -0,0 +1,12 @@ +domainConfig = $domainConfig; + $this->stringifier = $stringifier; } /** @@ -26,7 +27,7 @@ class ShortUrlDataTransformer implements DataTransformerInterface { return [ 'shortCode' => $shortUrl->getShortCode(), - 'shortUrl' => $shortUrl->toString($this->domainConfig), + 'shortUrl' => $this->stringifier->stringify($shortUrl), 'longUrl' => $shortUrl->getLongUrl(), 'dateCreated' => $shortUrl->getDateCreated()->toAtomString(), 'visitsCount' => $shortUrl->getVisitsCount(), diff --git a/module/Core/test/Action/QrCodeActionTest.php b/module/Core/test/Action/QrCodeActionTest.php index 5593be7c..245ac6de 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -20,6 +20,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use function getimagesizefromstring; @@ -37,7 +38,10 @@ class QrCodeActionTest extends TestCase $this->urlResolver = $this->prophesize(ShortUrlResolverInterface::class); - $this->action = new QrCodeAction($this->urlResolver->reveal(), ['domain' => 'doma.in']); + $this->action = new QrCodeAction( + $this->urlResolver->reveal(), + new ShortUrlStringifier(['domain' => 'doma.in']), + ); } /** @test */ diff --git a/module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php b/module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php index ff382f13..9599c2c8 100644 --- a/module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php +++ b/module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php @@ -23,6 +23,8 @@ use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated; use Shlinkio\Shlink\Core\EventDispatcher\NotifyVisitToWebHooks; use Shlinkio\Shlink\Core\Model\Visitor; use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use function count; use function Functional\contains; @@ -127,7 +129,7 @@ class NotifyVisitToWebHooksTest extends TestCase $this->em->reveal(), $this->logger->reveal(), $webhooks, - [], + new ShortUrlDataTransformer(new ShortUrlStringifier([])), new AppOptions(['name' => 'Shlink', 'version' => '1.2.3']), ); } diff --git a/module/Core/test/Mercure/MercureUpdatesGeneratorTest.php b/module/Core/test/Mercure/MercureUpdatesGeneratorTest.php index b7382f84..c3a8463f 100644 --- a/module/Core/test/Mercure/MercureUpdatesGeneratorTest.php +++ b/module/Core/test/Mercure/MercureUpdatesGeneratorTest.php @@ -10,6 +10,8 @@ use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\Mercure\MercureUpdatesGenerator; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\Visitor; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use function Shlinkio\Shlink\Common\json_decode; @@ -19,7 +21,7 @@ class MercureUpdatesGeneratorTest extends TestCase public function setUp(): void { - $this->generator = new MercureUpdatesGenerator([]); + $this->generator = new MercureUpdatesGenerator(new ShortUrlDataTransformer(new ShortUrlStringifier([]))); } /** diff --git a/module/Core/test/Transformer/ShortUrlDataTransformerTest.php b/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php similarity index 89% rename from module/Core/test/Transformer/ShortUrlDataTransformerTest.php rename to module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php index 0a621799..81c0d203 100644 --- a/module/Core/test/Transformer/ShortUrlDataTransformerTest.php +++ b/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php @@ -2,13 +2,14 @@ declare(strict_types=1); -namespace ShlinkioTest\Shlink\Core\Transformer; +namespace ShlinkioTest\Shlink\Core\ShortUrl\Transformer; use Cake\Chronos\Chronos; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; -use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use function random_int; @@ -18,7 +19,7 @@ class ShortUrlDataTransformerTest extends TestCase public function setUp(): void { - $this->transformer = new ShortUrlDataTransformer([]); + $this->transformer = new ShortUrlDataTransformer(new ShortUrlStringifier([])); } /** diff --git a/module/Rest/config/dependencies.config.php b/module/Rest/config/dependencies.config.php index 7891b2a0..cfb97320 100644 --- a/module/Rest/config/dependencies.config.php +++ b/module/Rest/config/dependencies.config.php @@ -11,6 +11,7 @@ use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider; use Shlinkio\Shlink\Core\Domain\DomainService; use Shlinkio\Shlink\Core\Options\AppOptions; use Shlinkio\Shlink\Core\Service; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Core\Tag\TagService; use Shlinkio\Shlink\Core\Visit; use Shlinkio\Shlink\Rest\Service\ApiKeyService; @@ -54,21 +55,21 @@ return [ 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\CreateShortUrlAction::class => [Service\UrlShortener::class, ShortUrlDataTransformer::class], Action\ShortUrl\SingleStepCreateShortUrlAction::class => [ Service\UrlShortener::class, - 'config.url_shortener.domain', + ShortUrlDataTransformer::class, ], - Action\ShortUrl\EditShortUrlAction::class => [Service\ShortUrlService::class, 'config.url_shortener.domain'], + Action\ShortUrl\EditShortUrlAction::class => [Service\ShortUrlService::class, ShortUrlDataTransformer::class], Action\ShortUrl\DeleteShortUrlAction::class => [Service\ShortUrl\DeleteShortUrlService::class], Action\ShortUrl\ResolveShortUrlAction::class => [ Service\ShortUrl\ShortUrlResolver::class, - 'config.url_shortener.domain', + ShortUrlDataTransformer::class, ], Action\Visit\ShortUrlVisitsAction::class => [Service\VisitsTracker::class], Action\Visit\TagVisitsAction::class => [Service\VisitsTracker::class], Action\Visit\GlobalVisitsAction::class => [Visit\VisitsStatsHelper::class], - Action\ShortUrl\ListShortUrlsAction::class => [Service\ShortUrlService::class, 'config.url_shortener.domain'], + Action\ShortUrl\ListShortUrlsAction::class => [Service\ShortUrlService::class, ShortUrlDataTransformer::class], Action\ShortUrl\EditShortUrlTagsAction::class => [Service\ShortUrlService::class], Action\Tag\ListTagsAction::class => [TagService::class], Action\Tag\DeleteTagsAction::class => [TagService::class], diff --git a/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php b/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php index a7278457..587c4bc5 100644 --- a/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php @@ -7,21 +7,21 @@ 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 Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Exception\ValidationException; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Service\UrlShortenerInterface; -use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; abstract class AbstractCreateShortUrlAction extends AbstractRestAction { private UrlShortenerInterface $urlShortener; - private ShortUrlDataTransformer $transformer; + private DataTransformerInterface $transformer; - public function __construct(UrlShortenerInterface $urlShortener, array $domainConfig) + public function __construct(UrlShortenerInterface $urlShortener, DataTransformerInterface $transformer) { $this->urlShortener = $urlShortener; - $this->transformer = new ShortUrlDataTransformer($domainConfig); + $this->transformer = $transformer; } public function handle(Request $request): Response diff --git a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php index 672d3963..49187314 100644 --- a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php @@ -7,10 +7,10 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl; use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Model\ShortUrlEdit; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; -use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware; @@ -20,12 +20,12 @@ class EditShortUrlAction extends AbstractRestAction protected const ROUTE_ALLOWED_METHODS = [self::METHOD_PATCH, self::METHOD_PUT]; private ShortUrlServiceInterface $shortUrlService; - private ShortUrlDataTransformer $transformer; + private DataTransformerInterface $transformer; - public function __construct(ShortUrlServiceInterface $shortUrlService, array $domainConfig) + public function __construct(ShortUrlServiceInterface $shortUrlService, DataTransformerInterface $transformer) { $this->shortUrlService = $shortUrlService; - $this->transformer = new ShortUrlDataTransformer($domainConfig); + $this->transformer = $transformer; } public function handle(ServerRequestInterface $request): ResponseInterface diff --git a/module/Rest/src/Action/ShortUrl/ListShortUrlsAction.php b/module/Rest/src/Action/ShortUrl/ListShortUrlsAction.php index cd1bb4af..ee077790 100644 --- a/module/Rest/src/Action/ShortUrl/ListShortUrlsAction.php +++ b/module/Rest/src/Action/ShortUrl/ListShortUrlsAction.php @@ -8,9 +8,9 @@ use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtilsTrait; +use Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; -use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware; @@ -22,12 +22,12 @@ class ListShortUrlsAction extends AbstractRestAction protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET]; private ShortUrlServiceInterface $shortUrlService; - private ShortUrlDataTransformer $transformer; + private DataTransformerInterface $transformer; - public function __construct(ShortUrlServiceInterface $shortUrlService, array $domainConfig) + public function __construct(ShortUrlServiceInterface $shortUrlService, DataTransformerInterface $transformer) { $this->shortUrlService = $shortUrlService; - $this->transformer = new ShortUrlDataTransformer($domainConfig); + $this->transformer = $transformer; } public function handle(Request $request): Response diff --git a/module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php b/module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php index fafd15df..c14423ce 100644 --- a/module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php @@ -7,9 +7,9 @@ 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 Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface; -use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware; @@ -19,12 +19,12 @@ class ResolveShortUrlAction extends AbstractRestAction protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET]; private ShortUrlResolverInterface $urlResolver; - private ShortUrlDataTransformer $transformer; + private DataTransformerInterface $transformer; - public function __construct(ShortUrlResolverInterface $urlResolver, array $domainConfig) + public function __construct(ShortUrlResolverInterface $urlResolver, DataTransformerInterface $transformer) { $this->urlResolver = $urlResolver; - $this->transformer = new ShortUrlDataTransformer($domainConfig); + $this->transformer = $transformer; } public function handle(Request $request): Response diff --git a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php index 4d4d5468..f8e95659 100644 --- a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php @@ -5,12 +5,14 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl; use Cake\Chronos\Chronos; +use Laminas\Diactoros\Response\JsonResponse; use Laminas\Diactoros\ServerRequest; use Laminas\Diactoros\ServerRequestFactory; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; +use Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\ValidationException; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; @@ -18,24 +20,21 @@ use Shlinkio\Shlink\Core\Service\UrlShortener; use Shlinkio\Shlink\Rest\Action\ShortUrl\CreateShortUrlAction; use Shlinkio\Shlink\Rest\Entity\ApiKey; -use function strpos; - class CreateShortUrlActionTest extends TestCase { use ProphecyTrait; - private const DOMAIN_CONFIG = [ - 'schema' => 'http', - 'hostname' => 'foo.com', - ]; - private CreateShortUrlAction $action; private ObjectProphecy $urlShortener; + private ObjectProphecy $transformer; public function setUp(): void { $this->urlShortener = $this->prophesize(UrlShortener::class); - $this->action = new CreateShortUrlAction($this->urlShortener->reveal(), self::DOMAIN_CONFIG); + $this->transformer = $this->prophesize(DataTransformerInterface::class); + $this->transformer->transform(Argument::type(ShortUrl::class))->willReturn([]); + + $this->action = new CreateShortUrlAction($this->urlShortener->reveal(), $this->transformer->reveal()); } /** @test */ @@ -55,14 +54,18 @@ class CreateShortUrlActionTest extends TestCase $expectedMeta['apiKey'] = $apiKey; $shorten = $this->urlShortener->shorten(ShortUrlMeta::fromRawData($expectedMeta))->willReturn($shortUrl); + $transform = $this->transformer->transform($shortUrl)->willReturn(['shortUrl' => 'stringified_short_url']); $request = ServerRequestFactory::fromGlobals()->withParsedBody($body)->withAttribute(ApiKey::class, $apiKey); + /** @var JsonResponse $response */ $response = $this->action->handle($request); + $payload = $response->getPayload(); self::assertEquals(200, $response->getStatusCode()); - self::assertTrue(strpos($response->getBody()->getContents(), $shortUrl->toString(self::DOMAIN_CONFIG)) > 0); + self::assertEquals('stringified_short_url', $payload['shortUrl']); $shorten->shouldHaveBeenCalledOnce(); + $transform->shouldHaveBeenCalledOnce(); } /** diff --git a/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php index be70eec8..eee75dbf 100644 --- a/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php @@ -12,6 +12,8 @@ use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\ValidationException; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Rest\Action\ShortUrl\EditShortUrlAction; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -25,7 +27,9 @@ class EditShortUrlActionTest extends TestCase public function setUp(): void { $this->shortUrlService = $this->prophesize(ShortUrlServiceInterface::class); - $this->action = new EditShortUrlAction($this->shortUrlService->reveal(), []); + $this->action = new EditShortUrlAction($this->shortUrlService->reveal(), new ShortUrlDataTransformer( + new ShortUrlStringifier([]), + )); } /** @test */ diff --git a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php index fd51fa16..2683b514 100644 --- a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php @@ -14,6 +14,8 @@ use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\Service\ShortUrlService; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Rest\Action\ShortUrl\ListShortUrlsAction; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -28,10 +30,12 @@ class ListShortUrlsActionTest extends TestCase { $this->service = $this->prophesize(ShortUrlService::class); - $this->action = new ListShortUrlsAction($this->service->reveal(), [ - 'hostname' => 'doma.in', - 'schema' => 'https', - ]); + $this->action = new ListShortUrlsAction($this->service->reveal(), new ShortUrlDataTransformer( + new ShortUrlStringifier([ + 'hostname' => 'doma.in', + 'schema' => 'https', + ]), + )); } /** diff --git a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php index 2f47089f..748ab642 100644 --- a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php @@ -11,6 +11,8 @@ use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface; +use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; +use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Rest\Action\ShortUrl\ResolveShortUrlAction; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -26,7 +28,9 @@ class ResolveShortUrlActionTest extends TestCase public function setUp(): void { $this->urlResolver = $this->prophesize(ShortUrlResolverInterface::class); - $this->action = new ResolveShortUrlAction($this->urlResolver->reveal(), []); + $this->action = new ResolveShortUrlAction($this->urlResolver->reveal(), new ShortUrlDataTransformer( + new ShortUrlStringifier([]), + )); } /** @test */ diff --git a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php index 9dbbb716..f78a9de5 100644 --- a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php @@ -6,8 +6,10 @@ namespace ShlinkioTest\Shlink\Rest\Action\ShortUrl; use Laminas\Diactoros\ServerRequest; use PHPUnit\Framework\TestCase; +use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; +use Shlinkio\Shlink\Common\Rest\DataTransformerInterface; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Service\UrlShortenerInterface; @@ -20,18 +22,17 @@ class SingleStepCreateShortUrlActionTest extends TestCase private SingleStepCreateShortUrlAction $action; private ObjectProphecy $urlShortener; - private ObjectProphecy $apiKeyService; + private ObjectProphecy $transformer; public function setUp(): void { $this->urlShortener = $this->prophesize(UrlShortenerInterface::class); + $this->transformer = $this->prophesize(DataTransformerInterface::class); + $this->transformer->transform(Argument::type(ShortUrl::class))->willReturn([]); $this->action = new SingleStepCreateShortUrlAction( $this->urlShortener->reveal(), - [ - 'schema' => 'http', - 'hostname' => 'foo.com', - ], + $this->transformer->reveal(), ); } From 4b4a859722aedb01742051428de18ccc792fb1a9 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 1 Feb 2021 23:08:30 +0100 Subject: [PATCH 2/4] Created ShortUrlStringifierTest --- .../Helper/ShortUrlStringifierTest.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php new file mode 100644 index 00000000..80cff5ed --- /dev/null +++ b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php @@ -0,0 +1,55 @@ +stringify($shortUrl)); + } + + public function provideConfigAndShortUrls(): iterable + { + $shortUrlWithShortCode = fn (string $shortCode, ?string $domain = null) => ShortUrl::fromMeta( + ShortUrlMeta::fromRawData([ + 'longUrl' => '', + 'customSlug' => $shortCode, + 'domain' => $domain, + ]), + ); + + yield 'no config' => [[], $shortUrlWithShortCode('foo'), 'http:/foo']; + yield 'hostname in config' => [ + ['hostname' => 'example.com'], + $shortUrlWithShortCode('bar'), + 'http://example.com/bar', + ]; + yield 'full config' => [ + ['schema' => 'https', 'hostname' => 'foo.com'], + $shortUrlWithShortCode('baz'), + 'https://foo.com/baz', + ]; + yield 'custom domain' => [ + ['schema' => 'https', 'hostname' => 'foo.com'], + $shortUrlWithShortCode('baz', 'mydom.es'), + 'https://mydom.es/baz', + ]; + } +} From 8fa0c95f5ab0d95b74d64d315c0488124e9cad63 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 1 Feb 2021 23:18:19 +0100 Subject: [PATCH 3/4] Ensured base path is honored when stringifying short URLs with a custom domain --- module/Core/config/dependencies.config.php | 2 +- .../ShortUrl/Helper/ShortUrlStringifier.php | 8 ++++++-- .../Helper/ShortUrlStringifierTest.php | 20 +++++++++++++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 7a5950bc..48166aeb 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -121,7 +121,7 @@ return [ ], ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class => ['em'], - ShortUrl\Helper\ShortUrlStringifier::class => ['config.url_shortener.domain'], + ShortUrl\Helper\ShortUrlStringifier::class => ['config.url_shortener.domain', 'config.router.base_path'], ShortUrl\Transformer\ShortUrlDataTransformer::class => [ShortUrl\Helper\ShortUrlStringifier::class], Mercure\MercureUpdatesGenerator::class => [ShortUrl\Transformer\ShortUrlDataTransformer::class], diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php index d5edba52..4d34e26b 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php @@ -7,13 +7,17 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Helper; use Laminas\Diactoros\Uri; use Shlinkio\Shlink\Core\Entity\ShortUrl; +use function sprintf; + class ShortUrlStringifier implements ShortUrlStringifierInterface { private array $domainConfig; + private string $basePath; - public function __construct(array $domainConfig) + public function __construct(array $domainConfig, string $basePath = '') { $this->domainConfig = $domainConfig; + $this->basePath = $basePath; } public function stringify(ShortUrl $shortUrl): string @@ -31,6 +35,6 @@ class ShortUrlStringifier implements ShortUrlStringifierInterface return $this->domainConfig['hostname'] ?? ''; } - return $domain->getAuthority(); + return sprintf('%s%s', $domain->getAuthority(), $this->basePath); } } diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php index 80cff5ed..483fd57d 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php @@ -17,10 +17,11 @@ class ShortUrlStringifierTest extends TestCase */ public function generatesExpectedOutputBasedOnConfigAndShortUrl( array $config, + string $basePath, ShortUrl $shortUrl, string $expected ): void { - $stringifier = new ShortUrlStringifier($config); + $stringifier = new ShortUrlStringifier($config, $basePath); self::assertEquals($expected, $stringifier->stringify($shortUrl)); } @@ -35,21 +36,36 @@ class ShortUrlStringifierTest extends TestCase ]), ); - yield 'no config' => [[], $shortUrlWithShortCode('foo'), 'http:/foo']; + yield 'no config' => [[], '', $shortUrlWithShortCode('foo'), 'http:/foo']; yield 'hostname in config' => [ ['hostname' => 'example.com'], + '', $shortUrlWithShortCode('bar'), 'http://example.com/bar', ]; + yield 'hostname with base path in config' => [ + ['hostname' => 'example.com/foo/bar'], + '', + $shortUrlWithShortCode('abc'), + 'http://example.com/foo/bar/abc', + ]; yield 'full config' => [ ['schema' => 'https', 'hostname' => 'foo.com'], + '', $shortUrlWithShortCode('baz'), 'https://foo.com/baz', ]; yield 'custom domain' => [ ['schema' => 'https', 'hostname' => 'foo.com'], + '', $shortUrlWithShortCode('baz', 'mydom.es'), 'https://mydom.es/baz', ]; + yield 'custom domain with base path' => [ + ['schema' => 'https', 'hostname' => 'foo.com'], + '/foo/bar', + $shortUrlWithShortCode('baz', 'mydom.es'), + 'https://mydom.es/foo/bar/baz', + ]; } } From e20df481a4d366b544f2fe437b4d56ea0eb7e0ae Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 1 Feb 2021 23:20:48 +0100 Subject: [PATCH 4/4] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1c408f0..6079f750 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this ### Fixed * [#988](https://github.com/shlinkio/shlink/issues/988) Fixed serving zero-byte static files in apache and apache-compatible web servers. +* [#990](https://github.com/shlinkio/shlink/issues/990) Fixed short URLs not properly composed in REST API endpoints when both custom domain and custom base path are used. ## [2.5.2] - 2021-01-24