From f17b641d46708fbd676040557b48d8bdeaa5edaa Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 10 Feb 2024 18:19:28 +0100 Subject: [PATCH] Allow filtering orphan visits by type from the CLI --- .../Command/Visit/GetOrphanVisitsCommand.php | 15 +++++++++++++-- .../Visit/GetOrphanVisitsCommandTest.php | 17 ++++++++++++----- module/Core/functions/functions.php | 9 +++++++++ .../Core/src/Visit/Model/OrphanVisitsParams.php | 7 +++---- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/module/CLI/src/Command/Visit/GetOrphanVisitsCommand.php b/module/CLI/src/Command/Visit/GetOrphanVisitsCommand.php index 3b89e339..7beae19a 100644 --- a/module/CLI/src/Command/Visit/GetOrphanVisitsCommand.php +++ b/module/CLI/src/Command/Visit/GetOrphanVisitsCommand.php @@ -8,7 +8,12 @@ use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams; +use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitType; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; + +use function Shlinkio\Shlink\Core\enumToString; +use function sprintf; class GetOrphanVisitsCommand extends AbstractVisitsListCommand { @@ -18,12 +23,18 @@ class GetOrphanVisitsCommand extends AbstractVisitsListCommand { $this ->setName(self::NAME) - ->setDescription('Returns the list of orphan visits.'); + ->setDescription('Returns the list of orphan visits.') + ->addOption('type', 't', InputOption::VALUE_REQUIRED, sprintf( + 'Return visits only with this type. One of %s', + enumToString(OrphanVisitType::class), + )); } protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator { - return $this->visitsHelper->orphanVisits(new OrphanVisitsParams($dateRange)); + $rawType = $input->getOption('type'); + $type = $rawType !== null ? OrphanVisitType::from($rawType) : null; + return $this->visitsHelper->orphanVisits(new OrphanVisitsParams(dateRange: $dateRange, type: $type)); } /** diff --git a/module/CLI/test/Command/Visit/GetOrphanVisitsCommandTest.php b/module/CLI/test/Command/Visit/GetOrphanVisitsCommandTest.php index b90e6af6..a9e2a50c 100644 --- a/module/CLI/test/Command/Visit/GetOrphanVisitsCommandTest.php +++ b/module/CLI/test/Command/Visit/GetOrphanVisitsCommandTest.php @@ -6,12 +6,15 @@ namespace ShlinkioTest\Shlink\CLI\Command\Visit; use Pagerfanta\Adapter\ArrayAdapter; use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Command\Visit\GetOrphanVisitsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Entity\VisitLocation; +use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams; +use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitType; use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\IpGeolocation\Model\Location; @@ -30,16 +33,20 @@ class GetOrphanVisitsCommandTest extends TestCase } #[Test] - public function outputIsProperlyGenerated(): void + #[TestWith([[], false])] + #[TestWith([['--type' => OrphanVisitType::BASE_URL->value], true])] + public function outputIsProperlyGenerated(array $args, bool $includesType): void { $visit = Visit::forBasePath(new Visitor('bar', 'foo', '', ''))->locate( VisitLocation::fromGeolocation(new Location('', 'Spain', '', 'Madrid', 0, 0, '')), ); - $this->visitsHelper->expects($this->once())->method('orphanVisits')->withAnyParameters()->willReturn( - new Paginator(new ArrayAdapter([$visit])), - ); + $this->visitsHelper->expects($this->once())->method('orphanVisits')->with($this->callback( + fn (OrphanVisitsParams $param) => ( + ($includesType && $param->type !== null) || (!$includesType && $param->type === null) + ), + ))->willReturn(new Paginator(new ArrayAdapter([$visit]))); - $this->commandTester->execute([]); + $this->commandTester->execute($args); $output = $this->commandTester->getDisplay(); self::assertEquals( diff --git a/module/Core/functions/functions.php b/module/Core/functions/functions.php index d07bc9e2..f26cb84f 100644 --- a/module/Core/functions/functions.php +++ b/module/Core/functions/functions.php @@ -20,6 +20,7 @@ use function array_keys; use function array_map; use function array_reduce; use function date_default_timezone_get; +use function implode; use function is_array; use function print_r; use function Shlinkio\Shlink\Common\buildDateRange; @@ -182,3 +183,11 @@ function enumValues(string $enum): array $cache[$enum] = array_map(static fn (BackedEnum $type) => (string) $type->value, $enum::cases()) ); } + +/** + * @param class-string $enum + */ +function enumToString(string $enum): string +{ + return sprintf('["%s"]', implode('", "', enumValues($enum))); +} diff --git a/module/Core/src/Visit/Model/OrphanVisitsParams.php b/module/Core/src/Visit/Model/OrphanVisitsParams.php index 45efd6bb..0fb2e99b 100644 --- a/module/Core/src/Visit/Model/OrphanVisitsParams.php +++ b/module/Core/src/Visit/Model/OrphanVisitsParams.php @@ -6,8 +6,7 @@ use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Core\Exception\ValidationException; use ValueError; -use function implode; -use function Shlinkio\Shlink\Core\enumValues; +use function Shlinkio\Shlink\Core\enumToString; use function sprintf; final class OrphanVisitsParams extends VisitsParams @@ -43,9 +42,9 @@ final class OrphanVisitsParams extends VisitsParams } catch (ValueError) { throw ValidationException::fromArray([ 'type' => sprintf( - '%s is not a valid orphan visit type. Expected one of ["%s"]', + '%s is not a valid orphan visit type. Expected one of %s', $type, - implode('", "', enumValues(OrphanVisitType::class)), + enumToString(OrphanVisitType::class), ), ]); }