diff --git a/CHANGELOG.md b/CHANGELOG.md index d18995b9..771906c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this #### Fixed * [#624](https://github.com/shlinkio/shlink/issues/624) Fixed order in which headers for remote IP detection are inspected. +* [#623](https://github.com/shlinkio/shlink/issues/623) Fixed short URLs metadata being impossible to reset. ## 2.0.2 - 2020-01-12 diff --git a/composer.json b/composer.json index fb786eec..06c0be62 100644 --- a/composer.json +++ b/composer.json @@ -58,6 +58,7 @@ }, "require-dev": { "devster/ubench": "^2.0", + "dms/phpunit-arraysubset-asserts": "^0.1.0", "eaglewu/swoole-ide-helper": "dev-master", "infection/infection": "^0.15.0", "phpstan/phpstan": "^0.12.3", diff --git a/docker/README.md b/docker/README.md index 4b1fdea1..0af66442 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,6 +1,6 @@ # Shlink Docker image -[![Docker build status](https://img.shields.io/docker/cloud/build/shlinkio/shlink.svg?style=flat-square)](https://hub.docker.com/r/shlinkio/shlink/) +[![Docker build status](https://img.shields.io/docker/build/shlinkio/shlink.svg?style=flat-square)](https://hub.docker.com/r/shlinkio/shlink/) [![Docker pulls](https://img.shields.io/docker/pulls/shlinkio/shlink.svg?style=flat-square)](https://hub.docker.com/r/shlinkio/shlink/) This image provides an easy way to set up [shlink](https://shlink.io) on a container-based runtime. diff --git a/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php index 1e9b41ce..28d192b1 100644 --- a/module/CLI/src/Command/ShortUrl/GenerateShortUrlCommand.php +++ b/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\Validation\ShortUrlMetaInputFilter; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -121,14 +122,14 @@ class GenerateShortUrlCommand extends Command $shortUrl = $this->urlShortener->urlToShortCode( new Uri($longUrl), $tags, - ShortUrlMeta::createFromParams( - $input->getOption('validSince'), - $input->getOption('validUntil'), - $customSlug, - $maxVisits !== null ? (int) $maxVisits : null, - $input->getOption('findIfExists'), - $input->getOption('domain'), - ), + ShortUrlMeta::fromRawData([ + ShortUrlMetaInputFilter::VALID_SINCE => $input->getOption('validSince'), + ShortUrlMetaInputFilter::VALID_UNTIL => $input->getOption('validUntil'), + ShortUrlMetaInputFilter::CUSTOM_SLUG => $customSlug, + ShortUrlMetaInputFilter::MAX_VISITS => $maxVisits !== null ? (int) $maxVisits : null, + ShortUrlMetaInputFilter::FIND_IF_EXISTS => $input->getOption('findIfExists'), + ShortUrlMetaInputFilter::DOMAIN => $input->getOption('domain'), + ]), ); $io->writeln([ diff --git a/module/Core/src/Model/ShortUrlMeta.php b/module/Core/src/Model/ShortUrlMeta.php index f9491d02..f0c487b7 100644 --- a/module/Core/src/Model/ShortUrlMeta.php +++ b/module/Core/src/Model/ShortUrlMeta.php @@ -9,11 +9,16 @@ use DateTimeInterface; use Shlinkio\Shlink\Core\Exception\ValidationException; use Shlinkio\Shlink\Core\Validation\ShortUrlMetaInputFilter; +use function array_key_exists; + final class ShortUrlMeta { + private bool $validSincePropWasProvided = false; private ?Chronos $validSince = null; + private bool $validUntilPropWasProvided = false; private ?Chronos $validUntil = null; private ?string $customSlug = null; + private bool $maxVisitsPropWasProvided = false; private ?int $maxVisits = null; private ?bool $findIfExists = null; private ?string $domain = null; @@ -32,44 +37,13 @@ final class ShortUrlMeta * @param array $data * @throws ValidationException */ - public static function createFromRawData(array $data): self + public static function fromRawData(array $data): self { $instance = new self(); $instance->validate($data); return $instance; } - /** - * @param string|Chronos|null $validSince - * @param string|Chronos|null $validUntil - * @param string|null $customSlug - * @param int|null $maxVisits - * @param bool|null $findIfExists - * @param string|null $domain - * @throws ValidationException - */ - public static function createFromParams( // phpcs:ignore - $validSince = null, - $validUntil = null, - $customSlug = null, - $maxVisits = null, - $findIfExists = null, - $domain = null - ): self { - // We do not type hint the arguments because that will be done by the validation process and we would get a - // type error if any of them do not match - $instance = new self(); - $instance->validate([ - ShortUrlMetaInputFilter::VALID_SINCE => $validSince, - ShortUrlMetaInputFilter::VALID_UNTIL => $validUntil, - ShortUrlMetaInputFilter::CUSTOM_SLUG => $customSlug, - ShortUrlMetaInputFilter::MAX_VISITS => $maxVisits, - ShortUrlMetaInputFilter::FIND_IF_EXISTS => $findIfExists, - ShortUrlMetaInputFilter::DOMAIN => $domain, - ]); - return $instance; - } - /** * @param array $data * @throws ValidationException @@ -82,10 +56,13 @@ final class ShortUrlMeta } $this->validSince = $this->parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_SINCE)); + $this->validSincePropWasProvided = array_key_exists(ShortUrlMetaInputFilter::VALID_SINCE, $data); $this->validUntil = $this->parseDateField($inputFilter->getValue(ShortUrlMetaInputFilter::VALID_UNTIL)); + $this->validUntilPropWasProvided = array_key_exists(ShortUrlMetaInputFilter::VALID_UNTIL, $data); $this->customSlug = $inputFilter->getValue(ShortUrlMetaInputFilter::CUSTOM_SLUG); $maxVisits = $inputFilter->getValue(ShortUrlMetaInputFilter::MAX_VISITS); $this->maxVisits = $maxVisits !== null ? (int) $maxVisits : null; + $this->maxVisitsPropWasProvided = array_key_exists(ShortUrlMetaInputFilter::MAX_VISITS, $data); $this->findIfExists = $inputFilter->getValue(ShortUrlMetaInputFilter::FIND_IF_EXISTS); $this->domain = $inputFilter->getValue(ShortUrlMetaInputFilter::DOMAIN); } @@ -113,7 +90,7 @@ final class ShortUrlMeta public function hasValidSince(): bool { - return $this->validSince !== null; + return $this->validSincePropWasProvided; } public function getValidUntil(): ?Chronos @@ -123,7 +100,7 @@ final class ShortUrlMeta public function hasValidUntil(): bool { - return $this->validUntil !== null; + return $this->validUntilPropWasProvided; } public function getCustomSlug(): ?string @@ -143,7 +120,7 @@ final class ShortUrlMeta public function hasMaxVisits(): bool { - return $this->maxVisits !== null; + return $this->maxVisitsPropWasProvided; } public function findIfExists(): bool diff --git a/module/Core/test-db/Repository/ShortUrlRepositoryTest.php b/module/Core/test-db/Repository/ShortUrlRepositoryTest.php index 81f32bd8..2b04d722 100644 --- a/module/Core/test-db/Repository/ShortUrlRepositoryTest.php +++ b/module/Core/test-db/Repository/ShortUrlRepositoryTest.php @@ -38,22 +38,20 @@ class ShortUrlRepositoryTest extends DatabaseTestCase /** @test */ public function findOneByShortCodeReturnsProperData(): void { - $regularOne = new ShortUrl('foo', ShortUrlMeta::createFromParams(null, null, 'foo')); + $regularOne = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'foo'])); $this->getEntityManager()->persist($regularOne); - $notYetValid = new ShortUrl( - 'bar', - ShortUrlMeta::createFromParams(Chronos::now()->addMonth(), null, 'bar_very_long_text'), - ); + $notYetValid = new ShortUrl('bar', ShortUrlMeta::fromRawData( + ['validSince' => Chronos::now()->addMonth(), 'customSlug' => 'bar_very_long_text'], + )); $this->getEntityManager()->persist($notYetValid); - $expired = new ShortUrl('expired', ShortUrlMeta::createFromParams(null, Chronos::now()->subMonth(), 'expired')); + $expired = new ShortUrl('expired', ShortUrlMeta::fromRawData( + ['validUntil' => Chronos::now()->subMonth(), 'customSlug' => 'expired'], + )); $this->getEntityManager()->persist($expired); - $allVisitsComplete = new ShortUrl('baz', ShortUrlMeta::createFromRawData([ - 'maxVisits' => 3, - 'customSlug' => 'baz', - ])); + $allVisitsComplete = new ShortUrl('baz', ShortUrlMeta::fromRawData(['maxVisits' => 3, 'customSlug' => 'baz'])); $visits = []; for ($i = 0; $i < 3; $i++) { $visit = new Visit($allVisitsComplete, Visitor::emptyInstance()); @@ -63,16 +61,14 @@ class ShortUrlRepositoryTest extends DatabaseTestCase $allVisitsComplete->setVisits(new ArrayCollection($visits)); $this->getEntityManager()->persist($allVisitsComplete); - $withDomain = new ShortUrl('foo', ShortUrlMeta::createFromRawData([ - 'domain' => 'example.com', - 'customSlug' => 'domain-short-code', - ])); + $withDomain = new ShortUrl('foo', ShortUrlMeta::fromRawData( + ['domain' => 'example.com', 'customSlug' => 'domain-short-code'], + )); $this->getEntityManager()->persist($withDomain); - $withDomainDuplicatingRegular = new ShortUrl('foo_with_domain', ShortUrlMeta::createFromRawData([ - 'domain' => 'doma.in', - 'customSlug' => 'foo', - ])); + $withDomainDuplicatingRegular = new ShortUrl('foo_with_domain', ShortUrlMeta::fromRawData( + ['domain' => 'doma.in', 'customSlug' => 'foo'], + )); $this->getEntityManager()->persist($withDomainDuplicatingRegular); $this->getEntityManager()->flush(); @@ -187,12 +183,12 @@ class ShortUrlRepositoryTest extends DatabaseTestCase /** @test */ public function shortCodeIsInUseLooksForShortUrlInProperSetOfTables(): void { - $shortUrlWithoutDomain = new ShortUrl('foo', ShortUrlMeta::createFromRawData(['customSlug' => 'my-cool-slug'])); + $shortUrlWithoutDomain = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'my-cool-slug'])); $this->getEntityManager()->persist($shortUrlWithoutDomain); $shortUrlWithDomain = new ShortUrl( 'foo', - ShortUrlMeta::createFromRawData(['domain' => 'doma.in', 'customSlug' => 'another-slug']), + ShortUrlMeta::fromRawData(['domain' => 'doma.in', 'customSlug' => 'another-slug']), ); $this->getEntityManager()->persist($shortUrlWithDomain); diff --git a/module/Core/test/Entity/ShortUrlTest.php b/module/Core/test/Entity/ShortUrlTest.php index 98404ca1..9aba83fa 100644 --- a/module/Core/test/Entity/ShortUrlTest.php +++ b/module/Core/test/Entity/ShortUrlTest.php @@ -28,7 +28,7 @@ class ShortUrlTest extends TestCase public function provideInvalidShortUrls(): iterable { yield 'with custom slug' => [ - new ShortUrl('', ShortUrlMeta::createFromRawData(['customSlug' => 'custom-slug'])), + new ShortUrl('', ShortUrlMeta::fromRawData(['customSlug' => 'custom-slug'])), 'The short code cannot be regenerated on ShortUrls where a custom slug was provided.', ]; yield 'already persisted' => [ diff --git a/module/Core/test/Model/ShortUrlMetaTest.php b/module/Core/test/Model/ShortUrlMetaTest.php index fb02fcbc..13c5ae14 100644 --- a/module/Core/test/Model/ShortUrlMetaTest.php +++ b/module/Core/test/Model/ShortUrlMetaTest.php @@ -21,7 +21,7 @@ class ShortUrlMetaTest extends TestCase public function exceptionIsThrownIfProvidedDataIsInvalid(array $data): void { $this->expectException(ValidationException::class); - ShortUrlMeta::createFromRawData($data); + ShortUrlMeta::fromRawData($data); } public function provideInvalidData(): iterable @@ -49,7 +49,9 @@ class ShortUrlMetaTest extends TestCase /** @test */ public function properlyCreatedInstanceReturnsValues(): void { - $meta = ShortUrlMeta::createFromParams(Chronos::parse('2015-01-01')->toAtomString(), null, 'foobar'); + $meta = ShortUrlMeta::fromRawData( + ['validSince' => Chronos::parse('2015-01-01')->toAtomString(), 'customSlug' => 'foobar'], + ); $this->assertTrue($meta->hasValidSince()); $this->assertEquals(Chronos::parse('2015-01-01'), $meta->getValidSince()); diff --git a/module/Core/test/Service/ShortUrlServiceTest.php b/module/Core/test/Service/ShortUrlServiceTest.php index 93cb7ad0..719c69d5 100644 --- a/module/Core/test/Service/ShortUrlServiceTest.php +++ b/module/Core/test/Service/ShortUrlServiceTest.php @@ -93,12 +93,11 @@ class ShortUrlServiceTest extends TestCase $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $flush = $this->em->flush()->willReturn(null); - $result = $this->service->updateMetadataByShortCode('abc123', ShortUrlMeta::createFromParams( - Chronos::parse('2017-01-01 00:00:00')->toAtomString(), - Chronos::parse('2017-01-05 00:00:00')->toAtomString(), - null, - 5, - )); + $result = $this->service->updateMetadataByShortCode('abc123', ShortUrlMeta::fromRawData([ + 'validSince' => Chronos::parse('2017-01-01 00:00:00')->toAtomString(), + 'validUntil' => Chronos::parse('2017-01-05 00:00:00')->toAtomString(), + 'maxVisits' => 5, + ])); $this->assertSame($shortUrl, $result); $this->assertEquals(Chronos::parse('2017-01-01 00:00:00'), $shortUrl->getValidSince()); diff --git a/module/Core/test/Service/UrlShortenerTest.php b/module/Core/test/Service/UrlShortenerTest.php index 2ec3024d..0b531ea1 100644 --- a/module/Core/test/Service/UrlShortenerTest.php +++ b/module/Core/test/Service/UrlShortenerTest.php @@ -153,7 +153,7 @@ class UrlShortenerTest extends TestCase $this->urlShortener->urlToShortCode( new Uri('http://foobar.com/12345/hello?foo=bar'), [], - ShortUrlMeta::createFromRawData(['customSlug' => 'custom-slug']), + ShortUrlMeta::fromRawData(['customSlug' => 'custom-slug']), ); } @@ -183,49 +183,49 @@ class UrlShortenerTest extends TestCase { $url = 'http://foo.com'; - yield [$url, [], ShortUrlMeta::createFromRawData(['findIfExists' => true]), new ShortUrl($url)]; - yield [$url, [], ShortUrlMeta::createFromRawData( + yield [$url, [], ShortUrlMeta::fromRawData(['findIfExists' => true]), new ShortUrl($url)]; + yield [$url, [], ShortUrlMeta::fromRawData( ['findIfExists' => true, 'customSlug' => 'foo'], ), new ShortUrl($url)]; yield [ $url, ['foo', 'bar'], - ShortUrlMeta::createFromRawData(['findIfExists' => true]), + ShortUrlMeta::fromRawData(['findIfExists' => true]), (new ShortUrl($url))->setTags(new ArrayCollection([new Tag('bar'), new Tag('foo')])), ]; yield [ $url, [], - ShortUrlMeta::createFromRawData(['findIfExists' => true, 'maxVisits' => 3]), - new ShortUrl($url, ShortUrlMeta::createFromRawData(['maxVisits' => 3])), + ShortUrlMeta::fromRawData(['findIfExists' => true, 'maxVisits' => 3]), + new ShortUrl($url, ShortUrlMeta::fromRawData(['maxVisits' => 3])), ]; yield [ $url, [], - ShortUrlMeta::createFromRawData(['findIfExists' => true, 'validSince' => Chronos::parse('2017-01-01')]), - new ShortUrl($url, ShortUrlMeta::createFromRawData(['validSince' => Chronos::parse('2017-01-01')])), + ShortUrlMeta::fromRawData(['findIfExists' => true, 'validSince' => Chronos::parse('2017-01-01')]), + new ShortUrl($url, ShortUrlMeta::fromRawData(['validSince' => Chronos::parse('2017-01-01')])), ]; yield [ $url, [], - ShortUrlMeta::createFromRawData(['findIfExists' => true, 'validUntil' => Chronos::parse('2017-01-01')]), - new ShortUrl($url, ShortUrlMeta::createFromRawData(['validUntil' => Chronos::parse('2017-01-01')])), + ShortUrlMeta::fromRawData(['findIfExists' => true, 'validUntil' => Chronos::parse('2017-01-01')]), + new ShortUrl($url, ShortUrlMeta::fromRawData(['validUntil' => Chronos::parse('2017-01-01')])), ]; yield [ $url, [], - ShortUrlMeta::createFromRawData(['findIfExists' => true, 'domain' => 'example.com']), - new ShortUrl($url, ShortUrlMeta::createFromRawData(['domain' => 'example.com'])), + ShortUrlMeta::fromRawData(['findIfExists' => true, 'domain' => 'example.com']), + new ShortUrl($url, ShortUrlMeta::fromRawData(['domain' => 'example.com'])), ]; yield [ $url, ['baz', 'foo', 'bar'], - ShortUrlMeta::createFromRawData([ + ShortUrlMeta::fromRawData([ 'findIfExists' => true, 'validUntil' => Chronos::parse('2017-01-01'), 'maxVisits' => 4, ]), - (new ShortUrl($url, ShortUrlMeta::createFromRawData([ + (new ShortUrl($url, ShortUrlMeta::fromRawData([ 'validUntil' => Chronos::parse('2017-01-01'), 'maxVisits' => 4, ])))->setTags(new ArrayCollection([new Tag('foo'), new Tag('bar'), new Tag('baz')])), @@ -237,7 +237,7 @@ class UrlShortenerTest extends TestCase { $url = 'http://foo.com'; $tags = ['baz', 'foo', 'bar']; - $meta = ShortUrlMeta::createFromRawData([ + $meta = ShortUrlMeta::fromRawData([ 'findIfExists' => true, 'validUntil' => Chronos::parse('2017-01-01'), 'maxVisits' => 4, diff --git a/module/Core/test/Transformer/ShortUrlDataTransformerTest.php b/module/Core/test/Transformer/ShortUrlDataTransformerTest.php index 094e09d6..a65b9506 100644 --- a/module/Core/test/Transformer/ShortUrlDataTransformerTest.php +++ b/module/Core/test/Transformer/ShortUrlDataTransformerTest.php @@ -42,13 +42,13 @@ class ShortUrlDataTransformerTest extends TestCase 'validUntil' => null, 'maxVisits' => null, ]]; - yield 'max visits only' => [new ShortUrl('', ShortUrlMeta::createFromParams(null, null, null, $maxVisits)), [ + yield 'max visits only' => [new ShortUrl('', ShortUrlMeta::fromRawData(['maxVisits' => $maxVisits])), [ 'validSince' => null, 'validUntil' => null, 'maxVisits' => $maxVisits, ]]; yield 'max visits and valid since' => [ - new ShortUrl('', ShortUrlMeta::createFromParams($now, null, null, $maxVisits)), + new ShortUrl('', ShortUrlMeta::fromRawData(['validSince' => $now, 'maxVisits' => $maxVisits])), [ 'validSince' => $now->toAtomString(), 'validUntil' => null, @@ -56,7 +56,9 @@ class ShortUrlDataTransformerTest extends TestCase ], ]; yield 'both dates' => [ - new ShortUrl('', ShortUrlMeta::createFromParams($now, $now->subDays(10))), + new ShortUrl('', ShortUrlMeta::fromRawData( + ['validSince' => $now, 'validUntil' => $now->subDays(10)], + )), [ 'validSince' => $now->toAtomString(), 'validUntil' => $now->subDays(10)->toAtomString(), @@ -64,7 +66,9 @@ class ShortUrlDataTransformerTest extends TestCase ], ]; yield 'everything' => [ - new ShortUrl('', ShortUrlMeta::createFromParams($now, $now->subDays(5), null, $maxVisits)), + new ShortUrl('', ShortUrlMeta::fromRawData( + ['validSince' => $now, 'validUntil' => $now->subDays(5), 'maxVisits' => $maxVisits], + )), [ 'validSince' => $now->toAtomString(), 'validUntil' => $now->subDays(5)->toAtomString(), diff --git a/module/Rest/src/Action/ShortUrl/CreateShortUrlAction.php b/module/Rest/src/Action/ShortUrl/CreateShortUrlAction.php index e85d2465..489d1277 100644 --- a/module/Rest/src/Action/ShortUrl/CreateShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/CreateShortUrlAction.php @@ -27,15 +27,7 @@ class CreateShortUrlAction extends AbstractCreateShortUrlAction ]); } - $meta = ShortUrlMeta::createFromParams( - $postData['validSince'] ?? null, - $postData['validUntil'] ?? null, - $postData['customSlug'] ?? null, - $postData['maxVisits'] ?? null, - $postData['findIfExists'] ?? null, - $postData['domain'] ?? null, - ); - + $meta = ShortUrlMeta::fromRawData($postData); return new CreateShortUrlData(new Uri($postData['longUrl']), (array) ($postData['tags'] ?? []), $meta); } } diff --git a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php index 4270f326..a6bc5538 100644 --- a/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/EditShortUrlAction.php @@ -30,7 +30,7 @@ class EditShortUrlAction extends AbstractRestAction $postData = (array) $request->getParsedBody(); $shortCode = $request->getAttribute('shortCode', ''); - $this->shortUrlService->updateMetadataByShortCode($shortCode, ShortUrlMeta::createFromRawData($postData)); + $this->shortUrlService->updateMetadataByShortCode($shortCode, ShortUrlMeta::fromRawData($postData)); return new EmptyResponse(); } } diff --git a/module/Rest/test-api/Action/EditShortUrlActionTest.php b/module/Rest/test-api/Action/EditShortUrlActionTest.php index 024ffb79..62b297d9 100644 --- a/module/Rest/test-api/Action/EditShortUrlActionTest.php +++ b/module/Rest/test-api/Action/EditShortUrlActionTest.php @@ -4,11 +4,73 @@ declare(strict_types=1); namespace ShlinkioApiTest\Shlink\Rest\Action; +use Cake\Chronos\Chronos; +use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use GuzzleHttp\RequestOptions; use Shlinkio\Shlink\TestUtils\ApiTest\ApiTestCase; +use function Functional\first; +use function sprintf; + class EditShortUrlActionTest extends ApiTestCase { + use ArraySubsetAsserts; + + /** + * @test + * @dataProvider provideDisablingMeta + */ + public function metadataCanBeReset(array $meta): void + { + $shortCode = 'abc123'; + $url = sprintf('/short-urls/%s', $shortCode); + $resetMeta = [ + 'validSince' => null, + 'validUntil' => null, + 'maxVisits' => null, + ]; + + // Setting meta that disables the URL should not let it be visited + $editWithProvidedMeta = $this->callApiWithKey(self::METHOD_PATCH, $url, [RequestOptions::JSON => $meta]); + $metaAfterEditing = $this->findShortUrlMetaByShortCode($shortCode); + + // Resetting all meta should allow the URL to be visitable again + $editWithResetMeta = $this->callApiWithKey(self::METHOD_PATCH, $url, [ + RequestOptions::JSON => $resetMeta, + ]); + $metaAfterResetting = $this->findShortUrlMetaByShortCode($shortCode); + + $this->assertEquals(self::STATUS_NO_CONTENT, $editWithProvidedMeta->getStatusCode()); + $this->assertEquals(self::STATUS_NO_CONTENT, $editWithResetMeta->getStatusCode()); + $this->assertEquals($resetMeta, $metaAfterResetting); + self::assertArraySubset($meta, $metaAfterEditing); + } + + public function provideDisablingMeta(): iterable + { + $now = Chronos::now(); + + yield [['validSince' => $now->addMonth()->toAtomString()]]; + yield [['validUntil' => $now->subMonth()->toAtomString()]]; + yield [['maxVisits' => 20]]; + yield [['validUntil' => $now->addYear()->toAtomString(), 'maxVisits' => 100]]; + yield [[ + 'validSince' => $now->subYear()->toAtomString(), + 'validUntil' => $now->addYear()->toAtomString(), + 'maxVisits' => 100, + ]]; + } + + private function findShortUrlMetaByShortCode(string $shortCode): ?array + { + // FIXME Call GET /short-urls/{shortCode} once issue https://github.com/shlinkio/shlink/issues/628 is fixed + $allShortUrls = $this->getJsonResponsePayload($this->callApiWithKey(self::METHOD_GET, '/short-urls')); + $list = $allShortUrls['shortUrls']['data'] ?? []; + $matchingShortUrl = first($list, fn (array $shortUrl) => $shortUrl['shortCode'] ?? '' === $shortCode); + + return $matchingShortUrl['meta'] ?? null; + } + /** @test */ public function tryingToEditInvalidUrlReturnsNotFoundError(): void { diff --git a/module/Rest/test-api/Fixtures/ShortUrlsFixture.php b/module/Rest/test-api/Fixtures/ShortUrlsFixture.php index 0b5a841a..d7566063 100644 --- a/module/Rest/test-api/Fixtures/ShortUrlsFixture.php +++ b/module/Rest/test-api/Fixtures/ShortUrlsFixture.php @@ -20,32 +20,32 @@ class ShortUrlsFixture extends AbstractFixture public function load(ObjectManager $manager): void { $abcShortUrl = $this->setShortUrlDate( - new ShortUrl('https://shlink.io', ShortUrlMeta::createFromRawData(['customSlug' => 'abc123'])), + new ShortUrl('https://shlink.io', ShortUrlMeta::fromRawData(['customSlug' => 'abc123'])), '2018-05-01', ); $manager->persist($abcShortUrl); $defShortUrl = $this->setShortUrlDate(new ShortUrl( 'https://blog.alejandrocelaya.com/2017/12/09/acmailer-7-0-the-most-important-release-in-a-long-time/', - ShortUrlMeta::createFromParams(Chronos::parse('2020-05-01'), null, 'def456'), + ShortUrlMeta::fromRawData(['validSince' => Chronos::parse('2020-05-01'), 'customSlug' => 'def456']), ), '2019-01-01 00:00:10'); $manager->persist($defShortUrl); $customShortUrl = $this->setShortUrlDate(new ShortUrl( 'https://shlink.io', - ShortUrlMeta::createFromParams(null, null, 'custom', 2), + ShortUrlMeta::fromRawData(['customSlug' => 'custom', 'maxVisits' => 2]), ), '2019-01-01 00:00:20'); $manager->persist($customShortUrl); $withDomainShortUrl = $this->setShortUrlDate(new ShortUrl( 'https://blog.alejandrocelaya.com/2019/04/27/considerations-to-properly-use-open-source-software-projects/', - ShortUrlMeta::createFromRawData(['domain' => 'example.com', 'customSlug' => 'ghi789']), + ShortUrlMeta::fromRawData(['domain' => 'example.com', 'customSlug' => 'ghi789']), ), '2019-01-01 00:00:30'); $manager->persist($withDomainShortUrl); $withDomainAndSlugShortUrl = $this->setShortUrlDate(new ShortUrl( 'https://google.com', - ShortUrlMeta::createFromRawData(['domain' => 'some-domain.com', 'customSlug' => 'custom-with-domain']), + ShortUrlMeta::fromRawData(['domain' => 'some-domain.com', 'customSlug' => 'custom-with-domain']), ), '2018-10-20'); $manager->persist($withDomainAndSlugShortUrl); diff --git a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php index b459765c..3a343d60 100644 --- a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php @@ -76,7 +76,7 @@ class CreateShortUrlActionTest extends TestCase ]; yield [['longUrl' => 'http://www.domain.com/foo/bar'], ShortUrlMeta::createEmpty()]; - yield [$fullMeta, ShortUrlMeta::createFromRawData($fullMeta)]; + yield [$fullMeta, ShortUrlMeta::fromRawData($fullMeta)]; } /**