diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 4d68101b..5c7c0b54 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Core; use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory; use Mezzio\Template\TemplateRendererInterface; use Psr\EventDispatcher\EventDispatcherInterface; -use Shlinkio\Shlink\Core\Domain\Resolver; use Shlinkio\Shlink\Core\ErrorHandler; use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Importer\ImportedLinksProcessorInterface; @@ -42,7 +41,7 @@ return [ Action\PixelAction::class => ConfigAbstractFactory::class, Action\QrCodeAction::class => ConfigAbstractFactory::class, - Resolver\PersistenceDomainResolver::class => ConfigAbstractFactory::class, + ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class => ConfigAbstractFactory::class, Mercure\MercureUpdatesGenerator::class => ConfigAbstractFactory::class, @@ -66,7 +65,7 @@ return [ Service\UrlShortener::class => [ Util\UrlValidator::class, 'em', - Resolver\PersistenceDomainResolver::class, + ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class, Service\ShortUrl\ShortCodeHelper::class, ], Service\VisitsTracker::class => [ @@ -109,13 +108,13 @@ return [ 'Logger_Shlink', ], - Resolver\PersistenceDomainResolver::class => ['em'], + ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class => ['em'], Mercure\MercureUpdatesGenerator::class => ['config.url_shortener.domain'], Importer\ImportedLinksProcessor::class => [ 'em', - Resolver\PersistenceDomainResolver::class, + ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class, Service\ShortUrl\ShortCodeHelper::class, Util\DoctrineBatchHelper::class, ], diff --git a/module/Core/src/Entity/ShortUrl.php b/module/Core/src/Entity/ShortUrl.php index 3d7ea0dd..6f7493aa 100644 --- a/module/Core/src/Entity/ShortUrl.php +++ b/module/Core/src/Entity/ShortUrl.php @@ -9,11 +9,11 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Laminas\Diactoros\Uri; use Shlinkio\Shlink\Common\Entity\AbstractEntity; -use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface; -use Shlinkio\Shlink\Core\Domain\Resolver\SimpleDomainResolver; use Shlinkio\Shlink\Core\Exception\ShortCodeCannotBeRegeneratedException; use Shlinkio\Shlink\Core\Model\ShortUrlEdit; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; +use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface; +use Shlinkio\Shlink\Core\ShortUrl\Resolver\SimpleShortUrlRelationResolver; use Shlinkio\Shlink\Core\Validation\ShortUrlMetaInputFilter; use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl; use Shlinkio\Shlink\Rest\Entity\ApiKey; @@ -43,9 +43,10 @@ class ShortUrl extends AbstractEntity public function __construct( string $longUrl, ?ShortUrlMeta $meta = null, - ?DomainResolverInterface $domainResolver = null + ?ShortUrlRelationResolverInterface $relationResolver = null ) { $meta = $meta ?? ShortUrlMeta::createEmpty(); + $relationResolver = $relationResolver ?? new SimpleShortUrlRelationResolver(); $this->longUrl = $longUrl; $this->dateCreated = Chronos::now(); @@ -57,13 +58,14 @@ class ShortUrl extends AbstractEntity $this->customSlugWasProvided = $meta->hasCustomSlug(); $this->shortCodeLength = $meta->getShortCodeLength(); $this->shortCode = $meta->getCustomSlug() ?? generateRandomShortCode($this->shortCodeLength); - $this->domain = ($domainResolver ?? new SimpleDomainResolver())->resolveDomain($meta->getDomain()); + $this->domain = $relationResolver->resolveDomain($meta->getDomain()); + $this->authorApiKey = $relationResolver->resolveApiKey($meta->getApiKey()); } public static function fromImport( ImportedShlinkUrl $url, bool $importShortCode, - ?DomainResolverInterface $domainResolver = null + ?ShortUrlRelationResolverInterface $relationResolver = null ): self { $meta = [ ShortUrlMetaInputFilter::DOMAIN => $url->domain(), @@ -73,7 +75,7 @@ class ShortUrl extends AbstractEntity $meta[ShortUrlMetaInputFilter::CUSTOM_SLUG] = $url->shortCode(); } - $instance = new self($url->longUrl(), ShortUrlMeta::fromRawData($meta), $domainResolver); + $instance = new self($url->longUrl(), ShortUrlMeta::fromRawData($meta), $relationResolver); $instance->importSource = $url->source(); $instance->importOriginalShortCode = $url->shortCode(); $instance->dateCreated = Chronos::instance($url->createdAt()); diff --git a/module/Core/src/Importer/ImportedLinksProcessor.php b/module/Core/src/Importer/ImportedLinksProcessor.php index e072fbb8..8bac7395 100644 --- a/module/Core/src/Importer/ImportedLinksProcessor.php +++ b/module/Core/src/Importer/ImportedLinksProcessor.php @@ -5,10 +5,10 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Importer; use Doctrine\ORM\EntityManagerInterface; -use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortCodeHelperInterface; +use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface; use Shlinkio\Shlink\Core\Util\DoctrineBatchHelperInterface; use Shlinkio\Shlink\Core\Util\TagManagerTrait; use Shlinkio\Shlink\Importer\ImportedLinksProcessorInterface; @@ -22,18 +22,18 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface use TagManagerTrait; private EntityManagerInterface $em; - private DomainResolverInterface $domainResolver; + private ShortUrlRelationResolverInterface $relationResolver; private ShortCodeHelperInterface $shortCodeHelper; private DoctrineBatchHelperInterface $batchHelper; public function __construct( EntityManagerInterface $em, - DomainResolverInterface $domainResolver, + ShortUrlRelationResolverInterface $relationResolver, ShortCodeHelperInterface $shortCodeHelper, DoctrineBatchHelperInterface $batchHelper ) { $this->em = $em; - $this->domainResolver = $domainResolver; + $this->relationResolver = $relationResolver; $this->shortCodeHelper = $shortCodeHelper; $this->batchHelper = $batchHelper; } @@ -58,7 +58,7 @@ class ImportedLinksProcessor implements ImportedLinksProcessorInterface continue; } - $shortUrl = ShortUrl::fromImport($url, $importShortCodes, $this->domainResolver); + $shortUrl = ShortUrl::fromImport($url, $importShortCodes, $this->relationResolver); $shortUrl->setTags($this->tagNamesToEntities($this->em, $url->tags())); if (! $this->handleShortCodeUniqueness($url, $shortUrl, $io, $importShortCodes)) { diff --git a/module/Core/src/Model/ShortUrlMeta.php b/module/Core/src/Model/ShortUrlMeta.php index 23121d71..fa82919e 100644 --- a/module/Core/src/Model/ShortUrlMeta.php +++ b/module/Core/src/Model/ShortUrlMeta.php @@ -24,6 +24,7 @@ final class ShortUrlMeta private ?string $domain = null; private int $shortCodeLength = 5; private ?bool $validateUrl = null; + private ?string $apiKey = null; // Enforce named constructors private function __construct() @@ -66,6 +67,7 @@ final class ShortUrlMeta $inputFilter, ShortUrlMetaInputFilter::SHORT_CODE_LENGTH, ) ?? DEFAULT_SHORT_CODES_LENGTH; + $this->apiKey = $inputFilter->getValue(ShortUrlMetaInputFilter::API_KEY); } public function getValidSince(): ?Chronos @@ -132,4 +134,9 @@ final class ShortUrlMeta { return $this->validateUrl; } + + public function getApiKey(): ?string + { + return $this->apiKey; + } } diff --git a/module/Core/src/Service/UrlShortener.php b/module/Core/src/Service/UrlShortener.php index fb745114..3ed4d2df 100644 --- a/module/Core/src/Service/UrlShortener.php +++ b/module/Core/src/Service/UrlShortener.php @@ -5,13 +5,13 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Service; use Doctrine\ORM\EntityManagerInterface; -use Shlinkio\Shlink\Core\Domain\Resolver\DomainResolverInterface; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\InvalidUrlException; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortCodeHelperInterface; +use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface; use Shlinkio\Shlink\Core\Util\TagManagerTrait; use Shlinkio\Shlink\Core\Util\UrlValidatorInterface; use Throwable; @@ -22,18 +22,18 @@ class UrlShortener implements UrlShortenerInterface private EntityManagerInterface $em; private UrlValidatorInterface $urlValidator; - private DomainResolverInterface $domainResolver; + private ShortUrlRelationResolverInterface $relationResolver; private ShortCodeHelperInterface $shortCodeHelper; public function __construct( UrlValidatorInterface $urlValidator, EntityManagerInterface $em, - DomainResolverInterface $domainResolver, + ShortUrlRelationResolverInterface $relationResolver, ShortCodeHelperInterface $shortCodeHelper ) { $this->urlValidator = $urlValidator; $this->em = $em; - $this->domainResolver = $domainResolver; + $this->relationResolver = $relationResolver; $this->shortCodeHelper = $shortCodeHelper; } @@ -54,7 +54,7 @@ class UrlShortener implements UrlShortenerInterface $this->urlValidator->validateUrl($url, $meta->doValidateUrl()); return $this->em->transactional(function () use ($url, $tags, $meta) { - $shortUrl = new ShortUrl($url, $meta, $this->domainResolver); + $shortUrl = new ShortUrl($url, $meta, $this->relationResolver); $shortUrl->setTags($this->tagNamesToEntities($this->em, $tags)); $this->verifyShortCodeUniqueness($meta, $shortUrl); diff --git a/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php new file mode 100644 index 00000000..d898fb37 --- /dev/null +++ b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php @@ -0,0 +1,41 @@ +em = $em; + } + + public function resolveDomain(?string $domain): ?Domain + { + if ($domain === null) { + return null; + } + + /** @var Domain|null $existingDomain */ + $existingDomain = $this->em->getRepository(Domain::class)->findOneBy(['authority' => $domain]); + return $existingDomain ?? new Domain($domain); + } + + public function resolveApiKey(?string $key): ?ApiKey + { + if ($key === null) { + return null; + } + + /** @var ApiKey|null $existingApiKey */ + $existingApiKey = $this->em->getRepository(ApiKey::class)->findOneBy(['key' => $key]); + return $existingApiKey; + } +} diff --git a/module/Core/src/ShortUrl/Resolver/ShortUrlRelationResolverInterface.php b/module/Core/src/ShortUrl/Resolver/ShortUrlRelationResolverInterface.php new file mode 100644 index 00000000..0a708cf6 --- /dev/null +++ b/module/Core/src/ShortUrl/Resolver/ShortUrlRelationResolverInterface.php @@ -0,0 +1,15 @@ +createInput(self::DOMAIN, false); $domain->getValidatorChain()->attach(new Validation\HostAndPortValidator()); $this->add($domain); + + $this->add($this->createInput(self::API_KEY, false)); } private function createPositiveNumberInput(string $name, int $min = 1): Input diff --git a/module/Core/test/Importer/ImportedLinksProcessorTest.php b/module/Core/test/Importer/ImportedLinksProcessorTest.php index f8a54aee..174e9afc 100644 --- a/module/Core/test/Importer/ImportedLinksProcessorTest.php +++ b/module/Core/test/Importer/ImportedLinksProcessorTest.php @@ -10,11 +10,11 @@ use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; -use Shlinkio\Shlink\Core\Domain\Resolver\SimpleDomainResolver; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Importer\ImportedLinksProcessor; use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortCodeHelperInterface; +use Shlinkio\Shlink\Core\ShortUrl\Resolver\SimpleShortUrlRelationResolver; use Shlinkio\Shlink\Core\Util\DoctrineBatchHelperInterface; use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl; use Symfony\Component\Console\Style\StyleInterface; @@ -46,7 +46,7 @@ class ImportedLinksProcessorTest extends TestCase $this->processor = new ImportedLinksProcessor( $this->em->reveal(), - new SimpleDomainResolver(), + new SimpleShortUrlRelationResolver(), $this->shortCodeHelper->reveal(), $batchHelper->reveal(), ); diff --git a/module/Core/test/Service/UrlShortenerTest.php b/module/Core/test/Service/UrlShortenerTest.php index 436b42e3..316b7557 100644 --- a/module/Core/test/Service/UrlShortenerTest.php +++ b/module/Core/test/Service/UrlShortenerTest.php @@ -12,7 +12,6 @@ use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; -use Shlinkio\Shlink\Core\Domain\Resolver\SimpleDomainResolver; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\Tag; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; @@ -20,6 +19,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Repository\ShortUrlRepository; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortCodeHelperInterface; use Shlinkio\Shlink\Core\Service\UrlShortener; +use Shlinkio\Shlink\Core\ShortUrl\Resolver\SimpleShortUrlRelationResolver; use Shlinkio\Shlink\Core\Util\UrlValidatorInterface; class UrlShortenerTest extends TestCase @@ -63,7 +63,7 @@ class UrlShortenerTest extends TestCase $this->urlShortener = new UrlShortener( $this->urlValidator->reveal(), $this->em->reveal(), - new SimpleDomainResolver(), + new SimpleShortUrlRelationResolver(), $this->shortCodeHelper->reveal(), ); }