Bladeren bron

Merge pull request #998 from acelaya-forks/feature/fix-base-path-with-domain

Feature/fix base path with domain
Alejandro Celaya 3 jaren geleden
bovenliggende
commit
36a172308a
30 gewijzigde bestanden met toevoegingen van 291 en 135 verwijderingen
  1. 1 0
      CHANGELOG.md
  2. 7 2
      module/CLI/config/dependencies.config.php
  3. 9 5
      module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php
  4. 4 4
      module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php
  5. 12 8
      module/CLI/test/Command/ShortUrl/GenerateShortUrlCommandTest.php
  6. 6 1
      module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php
  7. 6 2
      module/Core/config/dependencies.config.php
  8. 1 1
      module/Core/config/event_dispatcher.config.php
  9. 5 4
      module/Core/src/Action/QrCodeAction.php
  10. 30 48
      module/Core/src/Entity/ShortUrl.php
  11. 4 4
      module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php
  12. 4 4
      module/Core/src/Mercure/MercureUpdatesGenerator.php
  13. 40 0
      module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php
  14. 12 0
      module/Core/src/ShortUrl/Helper/ShortUrlStringifierInterface.php
  15. 6 5
      module/Core/src/ShortUrl/Transformer/ShortUrlDataTransformer.php
  16. 5 1
      module/Core/test/Action/QrCodeActionTest.php
  17. 3 1
      module/Core/test/EventDispatcher/NotifyVisitToWebHooksTest.php
  18. 3 1
      module/Core/test/Mercure/MercureUpdatesGeneratorTest.php
  19. 71 0
      module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php
  20. 4 3
      module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php
  21. 6 5
      module/Rest/config/dependencies.config.php
  22. 4 4
      module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php
  23. 4 4
      module/Rest/src/Action/ShortUrl/EditShortUrlAction.php
  24. 4 4
      module/Rest/src/Action/ShortUrl/ListShortUrlsAction.php
  25. 4 4
      module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php
  26. 12 9
      module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php
  27. 5 1
      module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php
  28. 8 4
      module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php
  29. 5 1
      module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php
  30. 6 5
      module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php

+ 1 - 0
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

+ 7 - 2
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],
 

+ 9 - 5
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: <info>%s</info>', $longUrl),
-                sprintf('Generated short URL: <info>%s</info>', $shortUrl->toString($this->domainConfig)),
+                sprintf('Generated short URL: <info>%s</info>', $this->stringifier->stringify($shortUrl)),
             ]);
             return ExitCodes::EXIT_SUCCESS;
         } catch (InvalidUrlException | NonUniqueSlugException $e) {

+ 4 - 4
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

+ 12 - 8
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();
     }
 
     /**

+ 6 - 1
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 */

+ 6 - 2
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', 'config.router.base_path'],
+        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',

+ 1 - 1
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 => [

+ 5 - 4
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);
 

+ 30 - 48
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();
-    }
 }

+ 4 - 4
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;
     }
 

+ 4 - 4
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

+ 40 - 0
module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php

@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+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, string $basePath = '')
+    {
+        $this->domainConfig = $domainConfig;
+        $this->basePath = $basePath;
+    }
+
+    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 sprintf('%s%s', $domain->getAuthority(), $this->basePath);
+    }
+}

+ 12 - 0
module/Core/src/ShortUrl/Helper/ShortUrlStringifierInterface.php

@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Shlinkio\Shlink\Core\ShortUrl\Helper;
+
+use Shlinkio\Shlink\Core\Entity\ShortUrl;
+
+interface ShortUrlStringifierInterface
+{
+    public function stringify(ShortUrl $shortUrl): string;
+}

+ 6 - 5
module/Core/src/Transformer/ShortUrlDataTransformer.php → module/Core/src/ShortUrl/Transformer/ShortUrlDataTransformer.php

@@ -2,21 +2,22 @@
 
 declare(strict_types=1);
 
-namespace Shlinkio\Shlink\Core\Transformer;
+namespace Shlinkio\Shlink\Core\ShortUrl\Transformer;
 
 use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
 use Shlinkio\Shlink\Core\Entity\ShortUrl;
+use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface;
 
 use function Functional\invoke;
 use function Functional\invoke_if;
 
 class ShortUrlDataTransformer implements DataTransformerInterface
 {
-    private array $domainConfig;
+    private ShortUrlStringifierInterface $stringifier;
 
-    public function __construct(array $domainConfig)
+    public function __construct(ShortUrlStringifierInterface $stringifier)
     {
-        $this->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(),

+ 5 - 1
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 */

+ 3 - 1
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']),
         );
     }

+ 3 - 1
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([])));
     }
 
     /**

+ 71 - 0
module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php

@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ShlinkioTest\Shlink\Core\ShortUrl\Helper;
+
+use PHPUnit\Framework\TestCase;
+use Shlinkio\Shlink\Core\Entity\ShortUrl;
+use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
+use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier;
+
+class ShortUrlStringifierTest extends TestCase
+{
+    /**
+     * @test
+     * @dataProvider provideConfigAndShortUrls
+     */
+    public function generatesExpectedOutputBasedOnConfigAndShortUrl(
+        array $config,
+        string $basePath,
+        ShortUrl $shortUrl,
+        string $expected
+    ): void {
+        $stringifier = new ShortUrlStringifier($config, $basePath);
+
+        self::assertEquals($expected, $stringifier->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 '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',
+        ];
+    }
+}

+ 4 - 3
module/Core/test/Transformer/ShortUrlDataTransformerTest.php → 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([]));
     }
 
     /**

+ 6 - 5
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],

+ 4 - 4
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

+ 4 - 4
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

+ 4 - 4
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

+ 4 - 4
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

+ 12 - 9
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();
     }
 
     /**

+ 5 - 1
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 */

+ 8 - 4
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',
+            ]),
+        ));
     }
 
     /**

+ 5 - 1
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 */

+ 6 - 5
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(),
         );
     }