Added mechanisms to be able to provide the API key when creating a short URL

This commit is contained in:
Alejandro Celaya 2020-11-07 09:31:46 +01:00
parent 97f89bcede
commit 2732b05834
11 changed files with 113 additions and 25 deletions

View file

@ -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,
],

View file

@ -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());

View file

@ -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)) {

View file

@ -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;
}
}

View file

@ -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);

View file

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\ShortUrl\Resolver;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
class PersistenceShortUrlRelationResolver implements ShortUrlRelationResolverInterface
{
private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em)
{
$this->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;
}
}

View file

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\ShortUrl\Resolver;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
interface ShortUrlRelationResolverInterface
{
public function resolveDomain(?string $domain): ?Domain;
public function resolveApiKey(?string $key): ?ApiKey;
}

View file

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\ShortUrl\Resolver;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
class SimpleShortUrlRelationResolver implements ShortUrlRelationResolverInterface
{
public function resolveDomain(?string $domain): ?Domain
{
return $domain !== null ? new Domain($domain) : null;
}
public function resolveApiKey(?string $key): ?ApiKey
{
return null;
}
}

View file

@ -28,6 +28,7 @@ class ShortUrlMetaInputFilter extends InputFilter
public const SHORT_CODE_LENGTH = 'shortCodeLength';
public const LONG_URL = 'longUrl';
public const VALIDATE_URL = 'validateUrl';
public const API_KEY = 'apiKey';
public function __construct(array $data)
{
@ -70,6 +71,8 @@ class ShortUrlMetaInputFilter extends InputFilter
$domain = $this->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

View file

@ -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(),
);

View file

@ -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(),
);
}