mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-14 04:00:57 +03:00
Add deviceLongUrls to short URL creation
This commit is contained in:
parent
12150f775d
commit
1447687ebe
23 changed files with 222 additions and 229 deletions
|
@ -100,9 +100,8 @@ class CreateShortUrlCommandTest extends TestCase
|
|||
{
|
||||
$shortUrl = ShortUrl::createEmpty();
|
||||
$this->urlShortener->expects($this->once())->method('shorten')->with(
|
||||
$this->callback(function (ShortUrlCreation $meta) {
|
||||
$tags = $meta->getTags();
|
||||
Assert::assertEquals(['foo', 'bar', 'baz', 'boo', 'zar'], $tags);
|
||||
$this->callback(function (ShortUrlCreation $creation) {
|
||||
Assert::assertEquals(['foo', 'bar', 'baz', 'boo', 'zar'], $creation->tags);
|
||||
return true;
|
||||
}),
|
||||
)->willReturn($shortUrl);
|
||||
|
@ -128,7 +127,7 @@ class CreateShortUrlCommandTest extends TestCase
|
|||
{
|
||||
$this->urlShortener->expects($this->once())->method('shorten')->with(
|
||||
$this->callback(function (ShortUrlCreation $meta) use ($expectedDomain) {
|
||||
Assert::assertEquals($expectedDomain, $meta->getDomain());
|
||||
Assert::assertEquals($expectedDomain, $meta->domain);
|
||||
return true;
|
||||
}),
|
||||
)->willReturn(ShortUrl::createEmpty());
|
||||
|
|
|
@ -9,10 +9,20 @@ use Shlinkio\Shlink\Core\Model\DeviceType;
|
|||
|
||||
class DeviceLongUrl extends AbstractEntity
|
||||
{
|
||||
private function __construct(
|
||||
public function __construct(
|
||||
public readonly ShortUrl $shortUrl,
|
||||
public readonly DeviceType $deviceType,
|
||||
public readonly string $longUrl,
|
||||
private string $longUrl,
|
||||
) {
|
||||
}
|
||||
|
||||
public function longUrl(): string
|
||||
{
|
||||
return $this->longUrl;
|
||||
}
|
||||
|
||||
public function updateLongUrl(string $longUrl): void
|
||||
{
|
||||
$this->longUrl = $longUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,9 @@ class ShortUrl extends AbstractEntity
|
|||
return self::create(ShortUrlCreation::createEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $longUrl
|
||||
*/
|
||||
public static function withLongUrl(string $longUrl): self
|
||||
{
|
||||
return self::create(ShortUrlCreation::fromRawData([ShortUrlInputFilter::LONG_URL => $longUrl]));
|
||||
|
@ -75,19 +78,19 @@ class ShortUrl extends AbstractEntity
|
|||
$instance->longUrl = $creation->getLongUrl();
|
||||
$instance->dateCreated = Chronos::now();
|
||||
$instance->visits = new ArrayCollection();
|
||||
$instance->tags = $relationResolver->resolveTags($creation->getTags());
|
||||
$instance->validSince = $creation->getValidSince();
|
||||
$instance->validUntil = $creation->getValidUntil();
|
||||
$instance->maxVisits = $creation->getMaxVisits();
|
||||
$instance->tags = $relationResolver->resolveTags($creation->tags);
|
||||
$instance->validSince = $creation->validSince;
|
||||
$instance->validUntil = $creation->validUntil;
|
||||
$instance->maxVisits = $creation->maxVisits;
|
||||
$instance->customSlugWasProvided = $creation->hasCustomSlug();
|
||||
$instance->shortCodeLength = $creation->getShortCodeLength();
|
||||
$instance->shortCode = $creation->getCustomSlug() ?? generateRandomShortCode($instance->shortCodeLength);
|
||||
$instance->domain = $relationResolver->resolveDomain($creation->getDomain());
|
||||
$instance->authorApiKey = $creation->getApiKey();
|
||||
$instance->title = $creation->getTitle();
|
||||
$instance->titleWasAutoResolved = $creation->titleWasAutoResolved();
|
||||
$instance->crawlable = $creation->isCrawlable();
|
||||
$instance->forwardQuery = $creation->forwardQuery();
|
||||
$instance->shortCodeLength = $creation->shortCodeLength;
|
||||
$instance->shortCode = $creation->customSlug ?? generateRandomShortCode($instance->shortCodeLength);
|
||||
$instance->domain = $relationResolver->resolveDomain($creation->domain);
|
||||
$instance->authorApiKey = $creation->apiKey;
|
||||
$instance->title = $creation->title;
|
||||
$instance->titleWasAutoResolved = $creation->titleWasAutoResolved;
|
||||
$instance->crawlable = $creation->crawlable;
|
||||
$instance->forwardQuery = $creation->forwardQuery;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
|
|
@ -6,85 +6,106 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Model;
|
|||
|
||||
use Cake\Chronos\Chronos;
|
||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Helper\TitleResolutionModelInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
use function Functional\map;
|
||||
use function Shlinkio\Shlink\Core\getNonEmptyOptionalValueFromInputFilter;
|
||||
use function Shlinkio\Shlink\Core\getOptionalBoolFromInputFilter;
|
||||
use function Shlinkio\Shlink\Core\getOptionalIntFromInputFilter;
|
||||
use function Shlinkio\Shlink\Core\normalizeOptionalDate;
|
||||
use function trim;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
|
||||
|
||||
final class ShortUrlCreation implements TitleResolutionModelInterface
|
||||
{
|
||||
private string $longUrl;
|
||||
private ?Chronos $validSince = null;
|
||||
private ?Chronos $validUntil = null;
|
||||
private ?string $customSlug = null;
|
||||
private ?int $maxVisits = null;
|
||||
private ?bool $findIfExists = null;
|
||||
private ?string $domain = null;
|
||||
private int $shortCodeLength = 5;
|
||||
private bool $validateUrl = false;
|
||||
private ?ApiKey $apiKey = null;
|
||||
private array $tags = [];
|
||||
private ?string $title = null;
|
||||
private bool $titleWasAutoResolved = false;
|
||||
private bool $crawlable = false;
|
||||
private bool $forwardQuery = true;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
/**
|
||||
* @param string[] $tags
|
||||
* @param array{DeviceType, string}[] $deviceLongUrls
|
||||
*/
|
||||
private function __construct(
|
||||
public readonly string $longUrl,
|
||||
public readonly array $deviceLongUrls = [],
|
||||
public readonly ?Chronos $validSince = null,
|
||||
public readonly ?Chronos $validUntil = null,
|
||||
public readonly ?string $customSlug = null,
|
||||
public readonly ?int $maxVisits = null,
|
||||
public readonly bool $findIfExists = false,
|
||||
public readonly ?string $domain = null,
|
||||
public readonly int $shortCodeLength = 5,
|
||||
public readonly bool $validateUrl = false,
|
||||
public readonly ?ApiKey $apiKey = null,
|
||||
public readonly array $tags = [],
|
||||
public readonly ?string $title = null,
|
||||
public readonly bool $titleWasAutoResolved = false,
|
||||
public readonly bool $crawlable = false,
|
||||
public readonly bool $forwardQuery = true,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function createEmpty(): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->longUrl = '';
|
||||
|
||||
return $instance;
|
||||
return new self('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function fromRawData(array $data): self
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->validateAndInit($data);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
private function validateAndInit(array $data): void
|
||||
{
|
||||
$inputFilter = ShortUrlInputFilter::withRequiredLongUrl($data);
|
||||
if (! $inputFilter->isValid()) {
|
||||
throw ValidationException::fromInputFilter($inputFilter);
|
||||
}
|
||||
|
||||
$this->longUrl = $inputFilter->getValue(ShortUrlInputFilter::LONG_URL);
|
||||
$this->validSince = normalizeOptionalDate($inputFilter->getValue(ShortUrlInputFilter::VALID_SINCE));
|
||||
$this->validUntil = normalizeOptionalDate($inputFilter->getValue(ShortUrlInputFilter::VALID_UNTIL));
|
||||
$this->customSlug = $inputFilter->getValue(ShortUrlInputFilter::CUSTOM_SLUG);
|
||||
$this->maxVisits = getOptionalIntFromInputFilter($inputFilter, ShortUrlInputFilter::MAX_VISITS);
|
||||
$this->findIfExists = $inputFilter->getValue(ShortUrlInputFilter::FIND_IF_EXISTS);
|
||||
$this->validateUrl = getOptionalBoolFromInputFilter($inputFilter, ShortUrlInputFilter::VALIDATE_URL) ?? false;
|
||||
$this->domain = getNonEmptyOptionalValueFromInputFilter($inputFilter, ShortUrlInputFilter::DOMAIN);
|
||||
$this->shortCodeLength = getOptionalIntFromInputFilter(
|
||||
$inputFilter,
|
||||
ShortUrlInputFilter::SHORT_CODE_LENGTH,
|
||||
) ?? DEFAULT_SHORT_CODES_LENGTH;
|
||||
$this->apiKey = $inputFilter->getValue(ShortUrlInputFilter::API_KEY);
|
||||
$this->tags = $inputFilter->getValue(ShortUrlInputFilter::TAGS);
|
||||
$this->title = $inputFilter->getValue(ShortUrlInputFilter::TITLE);
|
||||
$this->crawlable = $inputFilter->getValue(ShortUrlInputFilter::CRAWLABLE);
|
||||
$this->forwardQuery = getOptionalBoolFromInputFilter($inputFilter, ShortUrlInputFilter::FORWARD_QUERY) ?? true;
|
||||
return new self(
|
||||
longUrl: $inputFilter->getValue(ShortUrlInputFilter::LONG_URL),
|
||||
deviceLongUrls: map(
|
||||
$inputFilter->getValue(ShortUrlInputFilter::DEVICE_LONG_URLS) ?? [],
|
||||
static fn (string $longUrl, string $deviceType) => [DeviceType::from($deviceType), trim($longUrl)],
|
||||
),
|
||||
validSince: normalizeOptionalDate($inputFilter->getValue(ShortUrlInputFilter::VALID_SINCE)),
|
||||
validUntil: normalizeOptionalDate($inputFilter->getValue(ShortUrlInputFilter::VALID_UNTIL)),
|
||||
customSlug: $inputFilter->getValue(ShortUrlInputFilter::CUSTOM_SLUG),
|
||||
maxVisits: getOptionalIntFromInputFilter($inputFilter, ShortUrlInputFilter::MAX_VISITS),
|
||||
findIfExists: $inputFilter->getValue(ShortUrlInputFilter::FIND_IF_EXISTS) ?? false,
|
||||
domain: getNonEmptyOptionalValueFromInputFilter($inputFilter, ShortUrlInputFilter::DOMAIN),
|
||||
shortCodeLength: getOptionalIntFromInputFilter(
|
||||
$inputFilter,
|
||||
ShortUrlInputFilter::SHORT_CODE_LENGTH,
|
||||
) ?? DEFAULT_SHORT_CODES_LENGTH,
|
||||
validateUrl: getOptionalBoolFromInputFilter($inputFilter, ShortUrlInputFilter::VALIDATE_URL) ?? false,
|
||||
apiKey: $inputFilter->getValue(ShortUrlInputFilter::API_KEY),
|
||||
tags: $inputFilter->getValue(ShortUrlInputFilter::TAGS),
|
||||
title: $inputFilter->getValue(ShortUrlInputFilter::TITLE),
|
||||
crawlable: $inputFilter->getValue(ShortUrlInputFilter::CRAWLABLE),
|
||||
forwardQuery: getOptionalBoolFromInputFilter($inputFilter, ShortUrlInputFilter::FORWARD_QUERY) ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
public function withResolvedTitle(string $title): self
|
||||
{
|
||||
return new self(
|
||||
$this->longUrl,
|
||||
$this->deviceLongUrls,
|
||||
$this->validSince,
|
||||
$this->validUntil,
|
||||
$this->customSlug,
|
||||
$this->maxVisits,
|
||||
$this->findIfExists,
|
||||
$this->domain,
|
||||
$this->shortCodeLength,
|
||||
$this->validateUrl,
|
||||
$this->apiKey,
|
||||
$this->tags,
|
||||
$title,
|
||||
true,
|
||||
$this->crawlable,
|
||||
$this->forwardQuery,
|
||||
);
|
||||
}
|
||||
|
||||
public function getLongUrl(): string
|
||||
|
@ -92,115 +113,38 @@ final class ShortUrlCreation implements TitleResolutionModelInterface
|
|||
return $this->longUrl;
|
||||
}
|
||||
|
||||
public function getValidSince(): ?Chronos
|
||||
{
|
||||
return $this->validSince;
|
||||
}
|
||||
|
||||
public function hasValidSince(): bool
|
||||
{
|
||||
return $this->validSince !== null;
|
||||
}
|
||||
|
||||
public function getValidUntil(): ?Chronos
|
||||
{
|
||||
return $this->validUntil;
|
||||
}
|
||||
|
||||
public function hasValidUntil(): bool
|
||||
{
|
||||
return $this->validUntil !== null;
|
||||
}
|
||||
|
||||
public function getCustomSlug(): ?string
|
||||
{
|
||||
return $this->customSlug;
|
||||
}
|
||||
|
||||
public function hasCustomSlug(): bool
|
||||
{
|
||||
return $this->customSlug !== null;
|
||||
}
|
||||
|
||||
public function getMaxVisits(): ?int
|
||||
{
|
||||
return $this->maxVisits;
|
||||
}
|
||||
|
||||
public function hasMaxVisits(): bool
|
||||
{
|
||||
return $this->maxVisits !== null;
|
||||
}
|
||||
|
||||
public function findIfExists(): bool
|
||||
{
|
||||
return (bool) $this->findIfExists;
|
||||
}
|
||||
|
||||
public function hasDomain(): bool
|
||||
{
|
||||
return $this->domain !== null;
|
||||
}
|
||||
|
||||
public function getDomain(): ?string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
public function getShortCodeLength(): int
|
||||
{
|
||||
return $this->shortCodeLength;
|
||||
}
|
||||
|
||||
public function doValidateUrl(): bool
|
||||
{
|
||||
return $this->validateUrl;
|
||||
}
|
||||
|
||||
public function getApiKey(): ?ApiKey
|
||||
{
|
||||
return $this->apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getTags(): array
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function hasTitle(): bool
|
||||
{
|
||||
return $this->title !== null;
|
||||
}
|
||||
|
||||
public function titleWasAutoResolved(): bool
|
||||
{
|
||||
return $this->titleWasAutoResolved;
|
||||
}
|
||||
|
||||
public function withResolvedTitle(string $title): self
|
||||
{
|
||||
$copy = clone $this;
|
||||
$copy->title = $title;
|
||||
$copy->titleWasAutoResolved = true;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function isCrawlable(): bool
|
||||
{
|
||||
return $this->crawlable;
|
||||
}
|
||||
|
||||
public function forwardQuery(): bool
|
||||
{
|
||||
return $this->forwardQuery;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,20 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Model\Validation;
|
|||
|
||||
use DateTime;
|
||||
use Laminas\Filter;
|
||||
use Laminas\InputFilter\Input;
|
||||
use Laminas\InputFilter\InputFilter;
|
||||
use Laminas\Validator;
|
||||
use Shlinkio\Shlink\Common\Validation;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
use function array_keys;
|
||||
use function array_values;
|
||||
use function Functional\contains;
|
||||
use function Functional\every;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
use function str_replace;
|
||||
use function substr;
|
||||
use function trim;
|
||||
|
@ -32,6 +38,7 @@ class ShortUrlInputFilter extends InputFilter
|
|||
public const DOMAIN = 'domain';
|
||||
public const SHORT_CODE_LENGTH = 'shortCodeLength';
|
||||
public const LONG_URL = 'longUrl';
|
||||
public const DEVICE_LONG_URLS = 'deviceLongUrls';
|
||||
public const VALIDATE_URL = 'validateUrl';
|
||||
public const API_KEY = 'apiKey';
|
||||
public const TAGS = 'tags';
|
||||
|
@ -57,16 +64,40 @@ class ShortUrlInputFilter extends InputFilter
|
|||
|
||||
private function initialize(bool $requireLongUrl, bool $multiSegmentEnabled): void
|
||||
{
|
||||
$longUrlInput = $this->createInput(self::LONG_URL, $requireLongUrl);
|
||||
$longUrlInput->getValidatorChain()->attach(new Validator\NotEmpty([
|
||||
$notEmptyValidator = new Validator\NotEmpty([
|
||||
Validator\NotEmpty::OBJECT,
|
||||
Validator\NotEmpty::SPACE,
|
||||
Validator\NotEmpty::NULL,
|
||||
Validator\NotEmpty::EMPTY_ARRAY,
|
||||
Validator\NotEmpty::BOOLEAN,
|
||||
]));
|
||||
Validator\NotEmpty::STRING,
|
||||
]);
|
||||
|
||||
$longUrlInput = $this->createInput(self::LONG_URL, $requireLongUrl);
|
||||
$longUrlInput->getValidatorChain()->attach($notEmptyValidator);
|
||||
$this->add($longUrlInput);
|
||||
|
||||
$deviceLongUrlsInput = $this->createInput(self::DEVICE_LONG_URLS, false);
|
||||
$deviceLongUrlsInput->getValidatorChain()->attach(
|
||||
new Validator\Callback(function (mixed $value) use ($notEmptyValidator): bool {
|
||||
if (! is_array($value)) {
|
||||
// TODO Set proper error: Not array
|
||||
return false;
|
||||
}
|
||||
|
||||
$validValues = enumValues(DeviceType::class);
|
||||
$keys = array_keys($value);
|
||||
if (! every($keys, static fn ($key) => contains($validValues, $key))) {
|
||||
// TODO Set proper error: Provided invalid device type
|
||||
return false;
|
||||
}
|
||||
|
||||
$longUrls = array_values($value);
|
||||
return every($longUrls, $notEmptyValidator->isValid(...));
|
||||
}),
|
||||
);
|
||||
$this->add($deviceLongUrlsInput);
|
||||
|
||||
$validSince = $this->createInput(self::VALID_SINCE, false);
|
||||
$validSince->getValidatorChain()->attach(new Validator\Date(['format' => DateTime::ATOM]));
|
||||
$this->add($validSince);
|
||||
|
@ -75,8 +106,8 @@ class ShortUrlInputFilter extends InputFilter
|
|||
$validUntil->getValidatorChain()->attach(new Validator\Date(['format' => DateTime::ATOM]));
|
||||
$this->add($validUntil);
|
||||
|
||||
// FIXME The only way to enforce the NotEmpty validator to be evaluated when the value is provided but it's
|
||||
// empty, is by using the deprecated setContinueIfEmpty
|
||||
// The only way to enforce the NotEmpty validator to be evaluated when the key is present with an empty value
|
||||
// is by using the deprecated setContinueIfEmpty
|
||||
$customSlug = $this->createInput(self::CUSTOM_SLUG, false)->setContinueIfEmpty(true);
|
||||
$customSlug->getFilterChain()->attach(new Filter\Callback(match ($multiSegmentEnabled) {
|
||||
true => static fn (mixed $v) => is_string($v) ? trim(str_replace(' ', '-', $v), '/') : $v,
|
||||
|
@ -102,10 +133,8 @@ class ShortUrlInputFilter extends InputFilter
|
|||
$domain->getValidatorChain()->attach(new Validation\HostAndPortValidator());
|
||||
$this->add($domain);
|
||||
|
||||
$apiKeyInput = new Input(self::API_KEY);
|
||||
$apiKeyInput
|
||||
->setRequired(false)
|
||||
->getValidatorChain()->attach(new Validator\IsInstanceOf(['className' => ApiKey::class]));
|
||||
$apiKeyInput = $this->createInput(self::API_KEY, false);
|
||||
$apiKeyInput->getValidatorChain()->attach(new Validator\IsInstanceOf(['className' => ApiKey::class]));
|
||||
$this->add($apiKeyInput);
|
||||
|
||||
$this->add($this->createTagsInput(self::TAGS, false));
|
||||
|
|
|
@ -101,45 +101,45 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
|||
return $qb;
|
||||
}
|
||||
|
||||
public function findOneMatching(ShortUrlCreation $meta): ?ShortUrl
|
||||
public function findOneMatching(ShortUrlCreation $creation): ?ShortUrl
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('s')
|
||||
->from(ShortUrl::class, 's')
|
||||
->where($qb->expr()->eq('s.longUrl', ':longUrl'))
|
||||
->setParameter('longUrl', $meta->getLongUrl())
|
||||
->setParameter('longUrl', $creation->longUrl)
|
||||
->setMaxResults(1)
|
||||
->orderBy('s.id');
|
||||
|
||||
if ($meta->hasCustomSlug()) {
|
||||
if ($creation->hasCustomSlug()) {
|
||||
$qb->andWhere($qb->expr()->eq('s.shortCode', ':slug'))
|
||||
->setParameter('slug', $meta->getCustomSlug());
|
||||
->setParameter('slug', $creation->customSlug);
|
||||
}
|
||||
if ($meta->hasMaxVisits()) {
|
||||
if ($creation->hasMaxVisits()) {
|
||||
$qb->andWhere($qb->expr()->eq('s.maxVisits', ':maxVisits'))
|
||||
->setParameter('maxVisits', $meta->getMaxVisits());
|
||||
->setParameter('maxVisits', $creation->maxVisits);
|
||||
}
|
||||
if ($meta->hasValidSince()) {
|
||||
if ($creation->hasValidSince()) {
|
||||
$qb->andWhere($qb->expr()->eq('s.validSince', ':validSince'))
|
||||
->setParameter('validSince', $meta->getValidSince(), ChronosDateTimeType::CHRONOS_DATETIME);
|
||||
->setParameter('validSince', $creation->validSince, ChronosDateTimeType::CHRONOS_DATETIME);
|
||||
}
|
||||
if ($meta->hasValidUntil()) {
|
||||
if ($creation->hasValidUntil()) {
|
||||
$qb->andWhere($qb->expr()->eq('s.validUntil', ':validUntil'))
|
||||
->setParameter('validUntil', $meta->getValidUntil(), ChronosDateTimeType::CHRONOS_DATETIME);
|
||||
->setParameter('validUntil', $creation->validUntil, ChronosDateTimeType::CHRONOS_DATETIME);
|
||||
}
|
||||
if ($meta->hasDomain()) {
|
||||
if ($creation->hasDomain()) {
|
||||
$qb->join('s.domain', 'd')
|
||||
->andWhere($qb->expr()->eq('d.authority', ':domain'))
|
||||
->setParameter('domain', $meta->getDomain());
|
||||
->setParameter('domain', $creation->domain);
|
||||
}
|
||||
|
||||
$apiKey = $meta->getApiKey();
|
||||
$apiKey = $creation->apiKey;
|
||||
if ($apiKey !== null) {
|
||||
$this->applySpecification($qb, $apiKey->spec(), 's');
|
||||
}
|
||||
|
||||
$tags = $meta->getTags();
|
||||
$tags = $creation->tags;
|
||||
$tagsAmount = count($tags);
|
||||
if ($tagsAmount === 0) {
|
||||
return $qb->getQuery()->getOneOrNullResult();
|
||||
|
|
|
@ -22,7 +22,7 @@ interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificat
|
|||
|
||||
public function shortCodeIsInUseWithLock(ShortUrlIdentifier $identifier, ?Specification $spec = null): bool;
|
||||
|
||||
public function findOneMatching(ShortUrlCreation $meta): ?ShortUrl;
|
||||
public function findOneMatching(ShortUrlCreation $creation): ?ShortUrl;
|
||||
|
||||
public function findOneByImportedUrl(ImportedShlinkUrl $url): ?ShortUrl;
|
||||
}
|
||||
|
|
|
@ -57,15 +57,15 @@ class UrlShortener implements UrlShortenerInterface
|
|||
return $newShortUrl;
|
||||
}
|
||||
|
||||
private function findExistingShortUrlIfExists(ShortUrlCreation $meta): ?ShortUrl
|
||||
private function findExistingShortUrlIfExists(ShortUrlCreation $creation): ?ShortUrl
|
||||
{
|
||||
if (! $meta->findIfExists()) {
|
||||
if (! $creation->findIfExists) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var ShortUrlRepositoryInterface $repo */
|
||||
$repo = $this->em->getRepository(ShortUrl::class);
|
||||
return $repo->findOneMatching($meta);
|
||||
return $repo->findOneMatching($creation);
|
||||
}
|
||||
|
||||
private function verifyShortCodeUniqueness(ShortUrlCreation $meta, ShortUrl $shortUrlToBeCreated): void
|
||||
|
|
|
@ -75,7 +75,7 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||
[$firstUrlTags] = array_chunk($names, 3);
|
||||
$secondUrlTags = [$names[0]];
|
||||
$metaWithTags = static fn (array $tags, ?ApiKey $apiKey) => ShortUrlCreation::fromRawData(
|
||||
['longUrl' => '', 'tags' => $tags, 'apiKey' => $apiKey],
|
||||
['longUrl' => 'longUrl', 'tags' => $tags, 'apiKey' => $apiKey],
|
||||
);
|
||||
|
||||
$shortUrl = ShortUrl::create($metaWithTags($firstUrlTags, $apiKey), $this->relationResolver);
|
||||
|
@ -242,14 +242,14 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||
[$firstUrlTags, $secondUrlTags] = array_chunk($names, 3);
|
||||
|
||||
$shortUrl = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['apiKey' => $authorApiKey, 'longUrl' => '', 'tags' => $firstUrlTags]),
|
||||
ShortUrlCreation::fromRawData(['apiKey' => $authorApiKey, 'longUrl' => 'longUrl', 'tags' => $firstUrlTags]),
|
||||
$this->relationResolver,
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
|
||||
$shortUrl2 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(
|
||||
['domain' => $domain->getAuthority(), 'longUrl' => '', 'tags' => $secondUrlTags],
|
||||
['domain' => $domain->getAuthority(), 'longUrl' => 'longUrl', 'tags' => $secondUrlTags],
|
||||
),
|
||||
$this->relationResolver,
|
||||
);
|
||||
|
|
|
@ -264,7 +264,9 @@ class VisitRepositoryTest extends DatabaseTestCase
|
|||
$apiKey1 = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
|
||||
$this->getEntityManager()->persist($apiKey1);
|
||||
$shortUrl = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['apiKey' => $apiKey1, 'domain' => $domain->getAuthority(), 'longUrl' => '']),
|
||||
ShortUrlCreation::fromRawData(
|
||||
['apiKey' => $apiKey1, 'domain' => $domain->getAuthority(), 'longUrl' => 'longUrl'],
|
||||
),
|
||||
$this->relationResolver,
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
|
@ -272,12 +274,14 @@ class VisitRepositoryTest extends DatabaseTestCase
|
|||
|
||||
$apiKey2 = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
|
||||
$this->getEntityManager()->persist($apiKey2);
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData(['apiKey' => $apiKey2, 'longUrl' => '']));
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData(['apiKey' => $apiKey2, 'longUrl' => 'longUrl']));
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
$this->createVisitsForShortUrl($shortUrl2, 5);
|
||||
|
||||
$shortUrl3 = ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData(['apiKey' => $apiKey2, 'domain' => $domain->getAuthority(), 'longUrl' => '']),
|
||||
ShortUrlCreation::fromRawData(
|
||||
['apiKey' => $apiKey2, 'domain' => $domain->getAuthority(), 'longUrl' => 'longUrl'],
|
||||
),
|
||||
$this->relationResolver,
|
||||
);
|
||||
$this->getEntityManager()->persist($shortUrl3);
|
||||
|
@ -315,7 +319,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
|||
/** @test */
|
||||
public function findOrphanVisitsReturnsExpectedResult(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => '']));
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'longUrl']));
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
$this->createVisitsForShortUrl($shortUrl, 7);
|
||||
|
||||
|
@ -364,7 +368,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
|||
/** @test */
|
||||
public function countOrphanVisitsReturnsExpectedResult(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => '']));
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'longUrl']));
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
$this->createVisitsForShortUrl($shortUrl, 7);
|
||||
|
||||
|
@ -460,7 +464,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array{string, string, \Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl}
|
||||
* @return array{string, string, ShortUrl}
|
||||
*/
|
||||
private function createShortUrlsAndVisits(
|
||||
bool|string $withDomain = true,
|
||||
|
@ -468,7 +472,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
|||
?ApiKey $apiKey = null,
|
||||
): array {
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
ShortUrlInputFilter::LONG_URL => '',
|
||||
ShortUrlInputFilter::LONG_URL => 'longUrl',
|
||||
ShortUrlInputFilter::TAGS => $tags,
|
||||
ShortUrlInputFilter::API_KEY => $apiKey,
|
||||
]), $this->relationResolver);
|
||||
|
@ -482,7 +486,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
|||
$shortUrlWithDomain = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'customSlug' => $shortCode,
|
||||
'domain' => $domain,
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
]));
|
||||
$this->getEntityManager()->persist($shortUrlWithDomain);
|
||||
$this->createVisitsForShortUrl($shortUrlWithDomain, 3);
|
||||
|
|
|
@ -57,7 +57,7 @@ class NotifyNewShortUrlToMercureTest extends TestCase
|
|||
/** @test */
|
||||
public function expectedNotificationIsPublished(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::withLongUrl('');
|
||||
$shortUrl = ShortUrl::withLongUrl('longUrl');
|
||||
$update = Update::forTopicAndPayload('', []);
|
||||
|
||||
$this->em->expects($this->once())->method('find')->with(ShortUrl::class, '123')->willReturn($shortUrl);
|
||||
|
@ -74,7 +74,7 @@ class NotifyNewShortUrlToMercureTest extends TestCase
|
|||
/** @test */
|
||||
public function messageIsPrintedIfPublishingFails(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::withLongUrl('');
|
||||
$shortUrl = ShortUrl::withLongUrl('longUrl');
|
||||
$update = Update::forTopicAndPayload('', []);
|
||||
$e = new Exception('Error');
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
|
|||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'customSlug' => 'foo',
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
'title' => $title,
|
||||
]));
|
||||
$visit = Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance());
|
||||
|
@ -51,7 +51,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
|
|||
'shortUrl' => [
|
||||
'shortCode' => $shortUrl->getShortCode(),
|
||||
'shortUrl' => 'http:/' . $shortUrl->getShortCode(),
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
'dateCreated' => $shortUrl->getDateCreated()->toAtomString(),
|
||||
'visitsCount' => 0,
|
||||
'tags' => [],
|
||||
|
@ -118,7 +118,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
|
|||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'customSlug' => 'foo',
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
'title' => 'The title',
|
||||
]));
|
||||
|
||||
|
@ -128,7 +128,7 @@ class PublishingUpdatesGeneratorTest extends TestCase
|
|||
self::assertEquals(['shortUrl' => [
|
||||
'shortCode' => $shortUrl->getShortCode(),
|
||||
'shortUrl' => 'http:/' . $shortUrl->getShortCode(),
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
'dateCreated' => $shortUrl->getDateCreated()->toAtomString(),
|
||||
'visitsCount' => 0,
|
||||
'tags' => [],
|
||||
|
|
|
@ -68,7 +68,7 @@ class NotifyNewShortUrlToRabbitMqTest extends TestCase
|
|||
$shortUrlId = '123';
|
||||
$update = Update::forTopicAndPayload(Topic::NEW_SHORT_URL->value, []);
|
||||
$this->em->expects($this->once())->method('find')->with(ShortUrl::class, $shortUrlId)->willReturn(
|
||||
ShortUrl::withLongUrl(''),
|
||||
ShortUrl::withLongUrl('longUrl'),
|
||||
);
|
||||
$this->updatesGenerator->expects($this->once())->method('newShortUrlUpdate')->with(
|
||||
$this->isInstanceOf(ShortUrl::class),
|
||||
|
@ -88,7 +88,7 @@ class NotifyNewShortUrlToRabbitMqTest extends TestCase
|
|||
$shortUrlId = '123';
|
||||
$update = Update::forTopicAndPayload(Topic::NEW_SHORT_URL->value, []);
|
||||
$this->em->expects($this->once())->method('find')->with(ShortUrl::class, $shortUrlId)->willReturn(
|
||||
ShortUrl::withLongUrl(''),
|
||||
ShortUrl::withLongUrl('longUrl'),
|
||||
);
|
||||
$this->updatesGenerator->expects($this->once())->method('newShortUrlUpdate')->with(
|
||||
$this->isInstanceOf(ShortUrl::class),
|
||||
|
|
|
@ -159,7 +159,7 @@ class NotifyVisitToRabbitMqTest extends TestCase
|
|||
{
|
||||
yield 'legacy non-orphan visit' => [
|
||||
true,
|
||||
$visit = Visit::forValidShortUrl(ShortUrl::withLongUrl(''), Visitor::emptyInstance()),
|
||||
$visit = Visit::forValidShortUrl(ShortUrl::withLongUrl('longUrl'), Visitor::emptyInstance()),
|
||||
noop(...),
|
||||
function (MockObject & PublishingHelperInterface $helper) use ($visit): void {
|
||||
$helper->method('publishUpdate')->with($this->callback(function (Update $update) use ($visit): bool {
|
||||
|
@ -190,7 +190,7 @@ class NotifyVisitToRabbitMqTest extends TestCase
|
|||
];
|
||||
yield 'non-legacy non-orphan visit' => [
|
||||
false,
|
||||
Visit::forValidShortUrl(ShortUrl::withLongUrl(''), Visitor::emptyInstance()),
|
||||
Visit::forValidShortUrl(ShortUrl::withLongUrl('longUrl'), Visitor::emptyInstance()),
|
||||
function (MockObject & PublishingUpdatesGeneratorInterface $updatesGenerator): void {
|
||||
$update = Update::forTopicAndPayload('', []);
|
||||
$updatesGenerator->expects($this->never())->method('newOrphanVisitUpdate');
|
||||
|
|
|
@ -55,7 +55,7 @@ class NotifyNewShortUrlToRedisTest extends TestCase
|
|||
$shortUrlId = '123';
|
||||
$update = Update::forTopicAndPayload(Topic::NEW_SHORT_URL->value, []);
|
||||
$this->em->expects($this->once())->method('find')->with(ShortUrl::class, $shortUrlId)->willReturn(
|
||||
ShortUrl::withLongUrl(''),
|
||||
ShortUrl::withLongUrl('longUrl'),
|
||||
);
|
||||
$this->updatesGenerator->expects($this->once())->method('newShortUrlUpdate')->with(
|
||||
$this->isInstanceOf(ShortUrl::class),
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace ShlinkioTest\Shlink\Core\Functions;
|
||||
|
||||
use BackedEnum;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
|
@ -16,6 +17,7 @@ use function Shlinkio\Shlink\Core\enumValues;
|
|||
class FunctionsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param class-string<BackedEnum> $enum
|
||||
* @test
|
||||
* @dataProvider provideEnums
|
||||
*/
|
||||
|
|
|
@ -38,7 +38,7 @@ class ShortUrlTest extends TestCase
|
|||
public function provideInvalidShortUrls(): iterable
|
||||
{
|
||||
yield 'with custom slug' => [
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData(['customSlug' => 'custom-slug', 'longUrl' => ''])),
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData(['customSlug' => 'custom-slug', 'longUrl' => 'longUrl'])),
|
||||
'The short code cannot be regenerated on ShortUrls where a custom slug was provided.',
|
||||
];
|
||||
yield 'already persisted' => [
|
||||
|
@ -66,7 +66,7 @@ class ShortUrlTest extends TestCase
|
|||
{
|
||||
yield 'no custom slug' => [ShortUrl::createEmpty()];
|
||||
yield 'imported with custom slug' => [ShortUrl::fromImport(
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, '', [], Chronos::now(), null, 'custom-slug', null),
|
||||
new ImportedShlinkUrl(ImportSource::BITLY, 'longUrl', [], Chronos::now(), null, 'custom-slug', null),
|
||||
true,
|
||||
)];
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ class ShortUrlTest extends TestCase
|
|||
public function shortCodesHaveExpectedLength(?int $length, int $expectedLength): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
[ShortUrlInputFilter::SHORT_CODE_LENGTH => $length, 'longUrl' => ''],
|
||||
[ShortUrlInputFilter::SHORT_CODE_LENGTH => $length, 'longUrl' => 'longUrl'],
|
||||
));
|
||||
|
||||
self::assertEquals($expectedLength, strlen($shortUrl->getShortCode()));
|
||||
|
|
|
@ -30,7 +30,7 @@ class ShortUrlStringifierTest extends TestCase
|
|||
{
|
||||
$shortUrlWithShortCode = fn (string $shortCode, ?string $domain = null) => ShortUrl::create(
|
||||
ShortUrlCreation::fromRawData([
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
'customSlug' => $shortCode,
|
||||
'domain' => $domain,
|
||||
]),
|
||||
|
|
|
@ -142,7 +142,7 @@ class ExtraPathRedirectMiddlewareTest extends TestCase
|
|||
$type->method('isInvalidShortUrl')->willReturn(true);
|
||||
$request = ServerRequestFactory::fromGlobals()->withAttribute(NotFoundType::class, $type)
|
||||
->withUri(new Uri('https://s.test/shortCode/bar/baz'));
|
||||
$shortUrl = ShortUrl::withLongUrl('');
|
||||
$shortUrl = ShortUrl::withLongUrl('longUrl');
|
||||
|
||||
$currentIteration = 1;
|
||||
$this->resolver->expects($this->exactly($expectedResolveCalls))->method('resolveEnabledShortUrl')->with(
|
||||
|
|
|
@ -80,24 +80,24 @@ class ShortUrlCreationTest extends TestCase
|
|||
string $expectedSlug,
|
||||
bool $multiSegmentEnabled = false,
|
||||
): void {
|
||||
$meta = ShortUrlCreation::fromRawData([
|
||||
$creation = ShortUrlCreation::fromRawData([
|
||||
'validSince' => Chronos::parse('2015-01-01')->toAtomString(),
|
||||
'customSlug' => $customSlug,
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->value => $multiSegmentEnabled,
|
||||
]);
|
||||
|
||||
self::assertTrue($meta->hasValidSince());
|
||||
self::assertEquals(Chronos::parse('2015-01-01'), $meta->getValidSince());
|
||||
self::assertTrue($creation->hasValidSince());
|
||||
self::assertEquals(Chronos::parse('2015-01-01'), $creation->validSince);
|
||||
|
||||
self::assertFalse($meta->hasValidUntil());
|
||||
self::assertNull($meta->getValidUntil());
|
||||
self::assertFalse($creation->hasValidUntil());
|
||||
self::assertNull($creation->validUntil);
|
||||
|
||||
self::assertTrue($meta->hasCustomSlug());
|
||||
self::assertEquals($expectedSlug, $meta->getCustomSlug());
|
||||
self::assertTrue($creation->hasCustomSlug());
|
||||
self::assertEquals($expectedSlug, $creation->customSlug);
|
||||
|
||||
self::assertFalse($meta->hasMaxVisits());
|
||||
self::assertNull($meta->getMaxVisits());
|
||||
self::assertFalse($creation->hasMaxVisits());
|
||||
self::assertNull($creation->maxVisits);
|
||||
}
|
||||
|
||||
public function provideCustomSlugs(): iterable
|
||||
|
@ -127,12 +127,12 @@ class ShortUrlCreationTest extends TestCase
|
|||
*/
|
||||
public function titleIsCroppedIfTooLong(?string $title, ?string $expectedTitle): void
|
||||
{
|
||||
$meta = ShortUrlCreation::fromRawData([
|
||||
$creation = ShortUrlCreation::fromRawData([
|
||||
'title' => $title,
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
]);
|
||||
|
||||
self::assertEquals($expectedTitle, $meta->getTitle());
|
||||
self::assertEquals($expectedTitle, $creation->title);
|
||||
}
|
||||
|
||||
public function provideTitles(): iterable
|
||||
|
@ -153,12 +153,12 @@ class ShortUrlCreationTest extends TestCase
|
|||
*/
|
||||
public function emptyDomainIsDiscarded(?string $domain, ?string $expectedDomain): void
|
||||
{
|
||||
$meta = ShortUrlCreation::fromRawData([
|
||||
$creation = ShortUrlCreation::fromRawData([
|
||||
'domain' => $domain,
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
]);
|
||||
|
||||
self::assertSame($expectedDomain, $meta->getDomain());
|
||||
self::assertSame($expectedDomain, $creation->domain);
|
||||
}
|
||||
|
||||
public function provideDomains(): iterable
|
||||
|
|
|
@ -114,7 +114,7 @@ class ShortUrlResolverTest extends TestCase
|
|||
$now = Chronos::now();
|
||||
|
||||
yield 'maxVisits reached' => [(function () {
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => '']));
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['maxVisits' => 3, 'longUrl' => 'longUrl']));
|
||||
$shortUrl->setVisits(new ArrayCollection(map(
|
||||
range(0, 4),
|
||||
fn () => Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance()),
|
||||
|
@ -123,16 +123,16 @@ class ShortUrlResolverTest extends TestCase
|
|||
return $shortUrl;
|
||||
})()];
|
||||
yield 'future validSince' => [ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $now->addMonth()->toAtomString(), 'longUrl' => ''],
|
||||
['validSince' => $now->addMonth()->toAtomString(), 'longUrl' => 'longUrl'],
|
||||
))];
|
||||
yield 'past validUntil' => [ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validUntil' => $now->subMonth()->toAtomString(), 'longUrl' => ''],
|
||||
['validUntil' => $now->subMonth()->toAtomString(), 'longUrl' => 'longUrl'],
|
||||
))];
|
||||
yield 'mixed' => [(function () use ($now) {
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'maxVisits' => 3,
|
||||
'validUntil' => $now->subMonth()->toAtomString(),
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
]));
|
||||
$shortUrl->setVisits(new ArrayCollection(map(
|
||||
range(0, 4),
|
||||
|
|
|
@ -45,7 +45,7 @@ class ShortUrlDataTransformerTest extends TestCase
|
|||
]];
|
||||
yield 'max visits only' => [ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'maxVisits' => $maxVisits,
|
||||
'longUrl' => '',
|
||||
'longUrl' => 'longUrl',
|
||||
])), [
|
||||
'validSince' => null,
|
||||
'validUntil' => null,
|
||||
|
@ -53,7 +53,7 @@ class ShortUrlDataTransformerTest extends TestCase
|
|||
]];
|
||||
yield 'max visits and valid since' => [
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $now, 'maxVisits' => $maxVisits, 'longUrl' => ''],
|
||||
['validSince' => $now, 'maxVisits' => $maxVisits, 'longUrl' => 'longUrl'],
|
||||
)),
|
||||
[
|
||||
'validSince' => $now->toAtomString(),
|
||||
|
@ -63,7 +63,7 @@ class ShortUrlDataTransformerTest extends TestCase
|
|||
];
|
||||
yield 'both dates' => [
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $now, 'validUntil' => $now->subDays(10), 'longUrl' => ''],
|
||||
['validSince' => $now, 'validUntil' => $now->subDays(10), 'longUrl' => 'longUrl'],
|
||||
)),
|
||||
[
|
||||
'validSince' => $now->toAtomString(),
|
||||
|
@ -72,9 +72,12 @@ class ShortUrlDataTransformerTest extends TestCase
|
|||
],
|
||||
];
|
||||
yield 'everything' => [
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData(
|
||||
['validSince' => $now, 'validUntil' => $now->subDays(5), 'maxVisits' => $maxVisits, 'longUrl' => ''],
|
||||
)),
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'validSince' => $now,
|
||||
'validUntil' => $now->subDays(5),
|
||||
'maxVisits' => $maxVisits,
|
||||
'longUrl' => 'longUrl',
|
||||
])),
|
||||
[
|
||||
'validSince' => $now->toAtomString(),
|
||||
'validUntil' => $now->subDays(5)->toAtomString(),
|
||||
|
|
|
@ -261,9 +261,8 @@ class CreateShortUrlTest extends ApiTestCase
|
|||
|
||||
public function provideInvalidUrls(): iterable
|
||||
{
|
||||
yield 'empty URL' => ['', '2', 'INVALID_URL'];
|
||||
yield 'non-reachable URL' => ['https://this-has-to-be-invalid.com', '2', 'INVALID_URL'];
|
||||
yield 'API version 3' => ['', '3', 'https://shlink.io/api/error/invalid-url'];
|
||||
yield 'API version 2' => ['https://this-has-to-be-invalid.com', '2', 'INVALID_URL'];
|
||||
yield 'API version 3' => ['https://this-has-to-be-invalid.com', '3', 'https://shlink.io/api/error/invalid-url'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue