mirror of
https://github.com/shlinkio/shlink.git
synced 2026-08-01 00:57:55 -05:00
Remove requirement of checker props in ShortUrlEdition
This commit is contained in:
@@ -57,9 +57,6 @@ final class ShortUrlDataInput
|
||||
return [
|
||||
...array_filter(get_object_vars($this), static fn (mixed $value) => $value !== null),
|
||||
'longUrl' => $longUrl,
|
||||
'validSinceWasProvided' => $this->validSince !== null,
|
||||
'validUntilWasProvided' => $this->validUntil !== null,
|
||||
'maxVisitsWasProvided' => $this->maxVisits !== null,
|
||||
'titleWasProvided' => $this->title !== null,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -10,37 +10,46 @@ use Shlinkio\Shlink\Common\ObjectMapper\LooseUriConverter;
|
||||
use Shlinkio\Shlink\Common\ObjectMapper\SubstringConverter;
|
||||
use Shlinkio\Shlink\Common\ObjectMapper\TagsConverter;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Helper\TitleResolutionModelInterface;
|
||||
use Shlinkio\Shlink\Core\Util\NoValue;
|
||||
|
||||
use function Shlinkio\Shlink\Common\normalizeOptionalDate;
|
||||
|
||||
final class ShortUrlEdition implements TitleResolutionModelInterface
|
||||
final readonly class ShortUrlEdition implements TitleResolutionModelInterface
|
||||
{
|
||||
public Chronos|null $validSince;
|
||||
public bool $validSinceWasProvided;
|
||||
public Chronos|null $validUntil;
|
||||
public bool $validUntilWasProvided;
|
||||
public int|null $maxVisits;
|
||||
public bool $maxVisitsWasProvided;
|
||||
|
||||
/**
|
||||
* @param positive-int|NoValue|null $maxVisits
|
||||
* @param string[]|null $tags
|
||||
*/
|
||||
public function __construct(
|
||||
#[LooseUriConverter]
|
||||
readonly public string|null $longUrl = null,
|
||||
readonly public bool $validSinceWasProvided = false,
|
||||
DateTimeInterface|string|null $validSince = null,
|
||||
readonly public bool $validUntilWasProvided = false,
|
||||
DateTimeInterface|string|null $validUntil = null,
|
||||
readonly public bool $maxVisitsWasProvided = false,
|
||||
readonly public int|null $maxVisits = null,
|
||||
public string|null $longUrl = null,
|
||||
DateTimeInterface|string|NoValue|null $validSince = NoValue::NO_VALUE,
|
||||
DateTimeInterface|string|NoValue|null $validUntil = NoValue::NO_VALUE,
|
||||
int|NoValue|null $maxVisits = null,
|
||||
#[TagsConverter]
|
||||
readonly public array|null $tags = null,
|
||||
readonly public bool $titleWasProvided = false,
|
||||
public array|null $tags = null,
|
||||
public bool $titleWasProvided = false,
|
||||
#[SubstringConverter(512)]
|
||||
readonly public string|null $title = null,
|
||||
readonly public bool $titleWasAutoResolved = false,
|
||||
readonly public bool|null $crawlable = null,
|
||||
readonly public bool|null $forwardQuery = null,
|
||||
public string|null $title = null,
|
||||
public bool $titleWasAutoResolved = false,
|
||||
public bool|null $crawlable = null,
|
||||
public bool|null $forwardQuery = null,
|
||||
) {
|
||||
$this->validSince = normalizeOptionalDate($validSince);
|
||||
$this->validUntil = normalizeOptionalDate($validUntil);
|
||||
$this->validSince = normalizeOptionalDate(NoValue::resolve($validSince));
|
||||
$this->validSinceWasProvided = $validSince !== NoValue::NO_VALUE;
|
||||
|
||||
$this->validUntil = normalizeOptionalDate(NoValue::resolve($validUntil));
|
||||
$this->validUntilWasProvided = $validUntil !== NoValue::NO_VALUE;
|
||||
|
||||
$this->maxVisits = NoValue::resolve($maxVisits);
|
||||
$this->maxVisitsWasProvided = $maxVisits !== NoValue::NO_VALUE;
|
||||
}
|
||||
|
||||
public function withResolvedTitle(string $title): static
|
||||
@@ -53,11 +62,8 @@ final class ShortUrlEdition implements TitleResolutionModelInterface
|
||||
|
||||
return new self(
|
||||
longUrl: $this->longUrl,
|
||||
validSinceWasProvided: $this->validSinceWasProvided,
|
||||
validSince: $this->validSince,
|
||||
validUntilWasProvided: $this->validUntilWasProvided,
|
||||
validUntil: $this->validUntil,
|
||||
maxVisitsWasProvided: $this->maxVisitsWasProvided,
|
||||
maxVisits: $this->maxVisits,
|
||||
tags: $this->tags,
|
||||
titleWasProvided: $this->titleWasProvided,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Util;
|
||||
|
||||
/**
|
||||
* To be used as the default value for arguments where we want to detect the param has not been explicitly provided,
|
||||
* where other typically "no value" values (like `null`) have their own meaning.
|
||||
*
|
||||
* An Enum is used so that there's a single instance, making it easier to perform comparison checks to narrow-down the
|
||||
* type.
|
||||
*
|
||||
* ```
|
||||
* function foo(int|null|NoValue $something = NoValue::NO_VALUE) { ... }
|
||||
*
|
||||
* foo(); - The value has not been provided
|
||||
* foo(null); - Null has been explicitly provided
|
||||
* foo(123); - 123 has been explicitly provided
|
||||
* ```
|
||||
*/
|
||||
enum NoValue
|
||||
{
|
||||
case NO_VALUE;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param T|null $value
|
||||
* @return (T is NoValue::NO_VALUE ? null : T)
|
||||
*/
|
||||
public static function resolve(mixed $value): mixed
|
||||
{
|
||||
return $value === NoValue::NO_VALUE ? null : $value;
|
||||
}
|
||||
}
|
||||
@@ -68,8 +68,14 @@ class ShortUrlServiceTest extends TestCase
|
||||
self::assertSame($shortUrl, $result);
|
||||
['validSince' => $since, 'validUntil' => $until, 'maxVisits' => $maxVisits] = $shortUrl->toArray()['meta'];
|
||||
|
||||
self::assertEquals($shortUrlEdit->validSince?->toAtomString(), $since);
|
||||
self::assertEquals($shortUrlEdit->validUntil?->toAtomString(), $until);
|
||||
self::assertEquals(
|
||||
$shortUrlEdit->validSince instanceof Chronos ? $shortUrlEdit->validSince->toAtomString() : null,
|
||||
$since,
|
||||
);
|
||||
self::assertEquals(
|
||||
$shortUrlEdit->validUntil instanceof Chronos ? $shortUrlEdit->validUntil->toAtomString() : null,
|
||||
$until,
|
||||
);
|
||||
self::assertEquals($shortUrlEdit->maxVisits, $maxVisits);
|
||||
self::assertEquals($shortUrlEdit->longUrl ?? $originalLongUrl, $shortUrl->getLongUrl());
|
||||
}
|
||||
@@ -77,18 +83,13 @@ class ShortUrlServiceTest extends TestCase
|
||||
public static function provideShortUrlEdits(): iterable
|
||||
{
|
||||
yield 'no long URL' => [new InvokedCount(0), new ShortUrlEdition(
|
||||
validSinceWasProvided: true,
|
||||
validSince: Chronos::parse('2017-01-01 00:00:00'),
|
||||
validUntilWasProvided: true,
|
||||
validUntil: Chronos::parse('2017-01-05 00:00:00'),
|
||||
maxVisitsWasProvided: true,
|
||||
maxVisits: 5,
|
||||
), null];
|
||||
yield 'long URL and API key' => [new InvokedCount(1), new ShortUrlEdition(
|
||||
longUrl: 'https://modifiedLongUrl',
|
||||
validSinceWasProvided: true,
|
||||
validSince: Chronos::parse('2017-01-01 00:00:00'),
|
||||
maxVisitsWasProvided: true,
|
||||
maxVisits: 10,
|
||||
), ApiKey::create()];
|
||||
}
|
||||
|
||||
@@ -34,9 +34,6 @@ class EditShortUrlAction extends AbstractRestAction
|
||||
$body = (array) $request->getParsedBody();
|
||||
$shortUrlEdit = $this->treeMapper->map(ShortUrlEdition::class, [
|
||||
...$body,
|
||||
'validSinceWasProvided' => array_key_exists('validSince', $body),
|
||||
'validUntilWasProvided' => array_key_exists('validUntil', $body),
|
||||
'maxVisitsWasProvided' => array_key_exists('maxVisits', $body),
|
||||
'titleWasProvided' => array_key_exists('title', $body),
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user