diff --git a/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php b/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php index 8aa6bb1d..0d637f5f 100644 --- a/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php +++ b/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php @@ -123,30 +123,7 @@ class ListShortUrlsCommand extends AbstractWithDateRangeCommand $startDate = $this->getStartDateOption($input, $output); $endDate = $this->getEndDateOption($input, $output); $orderBy = $this->processOrderBy($input); - - $pickProp = static fn (string $prop): callable => static fn (array $shortUrl) => $shortUrl[$prop]; - $columnMap = [ - 'Short Code' => $pickProp('shortCode'), - 'Title' => $pickProp('title'), - 'Short URL' => $pickProp('shortUrl'), - 'Long URL' => $pickProp('longUrl'), - 'Date created' => $pickProp('dateCreated'), - 'Visits count' => $pickProp('visitsCount'), - ]; - if ($this->getOptionWithDeprecatedFallback($input, 'show-tags')) { - $columnMap['Tags'] = static fn (array $shortUrl): string => implode(', ', $shortUrl['tags']); - } - if ($input->getOption('show-api-key')) { - $columnMap['API Key'] = static fn (array $_, ShortUrl $shortUrl): string => - (string) $shortUrl->authorApiKey(); - } - if ($input->getOption('show-api-key-name')) { - $columnMap['API Key Name'] = static function (array $_, ShortUrl $shortUrl): ?string { - $apiKey = $shortUrl->authorApiKey(); - - return $apiKey !== null ? $apiKey->name() : null; - }; - } + $columnsMap = $this->resolveColumnsMap($input); $data = [ ShortUrlsParamsInputFilter::SEARCH_TERM => $searchTerm, @@ -162,7 +139,7 @@ class ListShortUrlsCommand extends AbstractWithDateRangeCommand do { $data[ShortUrlsParamsInputFilter::PAGE] = $page; - $result = $this->renderPage($output, $columnMap, ShortUrlsParams::fromRawData($data), $all); + $result = $this->renderPage($output, $columnsMap, ShortUrlsParams::fromRawData($data), $all); $page++; $continue = $result->hasNextPage() && $io->confirm( @@ -179,19 +156,19 @@ class ListShortUrlsCommand extends AbstractWithDateRangeCommand private function renderPage( OutputInterface $output, - array $columnMap, + array $columnsMap, ShortUrlsParams $params, bool $all ): Paginator { $shortUrls = $this->shortUrlService->listShortUrls($params); - $rows = map($shortUrls, function (ShortUrl $shortUrl) use ($columnMap) { + $rows = map($shortUrls, function (ShortUrl $shortUrl) use ($columnsMap) { $rawShortUrl = $this->transformer->transform($shortUrl); - return map($columnMap, fn (callable $call) => $call($rawShortUrl, $shortUrl)); + return map($columnsMap, fn (callable $call) => $call($rawShortUrl, $shortUrl)); }); ShlinkTable::fromOutput($output)->render( - array_keys($columnMap), + array_keys($columnsMap), $rows, $all ? null : $this->formatCurrentPageMessage($shortUrls, 'Page %s of %s'), ); @@ -209,4 +186,33 @@ class ListShortUrlsCommand extends AbstractWithDateRangeCommand [$field, $dir] = array_pad(explode(',', $orderBy), 2, null); return $dir === null ? $field : sprintf('%s-%s', $field, $dir); } + + private function resolveColumnsMap(InputInterface $input): array + { + $pickProp = static fn (string $prop): callable => static fn (array $shortUrl) => $shortUrl[$prop]; + $columnsMap = [ + 'Short Code' => $pickProp('shortCode'), + 'Title' => $pickProp('title'), + 'Short URL' => $pickProp('shortUrl'), + 'Long URL' => $pickProp('longUrl'), + 'Date created' => $pickProp('dateCreated'), + 'Visits count' => $pickProp('visitsCount'), + ]; + if ($this->getOptionWithDeprecatedFallback($input, 'show-tags')) { + $columnsMap['Tags'] = static fn (array $shortUrl): string => implode(', ', $shortUrl['tags']); + } + if ($input->getOption('show-api-key')) { + $columnsMap['API Key'] = static fn (array $_, ShortUrl $shortUrl): string => + (string) $shortUrl->authorApiKey(); + } + if ($input->getOption('show-api-key-name')) { + $columnsMap['API Key Name'] = static function (array $_, ShortUrl $shortUrl): ?string { + $apiKey = $shortUrl->authorApiKey(); + + return $apiKey !== null ? $apiKey->name() : null; + }; + } + + return $columnsMap; + } } diff --git a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php index 08519b62..6f7b11a6 100644 --- a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php @@ -12,13 +12,17 @@ use Prophecy\Prophecy\ObjectProphecy; use Shlinkio\Shlink\CLI\Command\ShortUrl\ListShortUrlsCommand; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Core\Entity\ShortUrl; +use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; +use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta; +use Shlinkio\Shlink\Rest\Entity\ApiKey; use ShlinkioTest\Shlink\CLI\CliTestUtilsTrait; use Symfony\Component\Console\Tester\CommandTester; +use function count; use function explode; class ListShortUrlsCommandTest extends TestCase @@ -98,17 +102,77 @@ class ListShortUrlsCommandTest extends TestCase $this->commandTester->execute(['--page' => $page]); } - /** @test */ - public function ifTagsFlagIsProvidedTagsColumnIsIncluded(): void - { + /** + * @test + * @dataProvider provideOptionalFlags + */ + public function provideOptionalFlagsMakesNewColumnsToBeIncluded( + array $input, + array $expectedContents, + array $notExpectedContents, + ApiKey $apiKey + ): void { $this->shortUrlService->listShortUrls(ShortUrlsParams::emptyInstance()) - ->willReturn(new Paginator(new ArrayAdapter([]))) + ->willReturn(new Paginator(new ArrayAdapter([ + ShortUrl::fromMeta(ShortUrlMeta::fromRawData([ + 'longUrl' => 'foo.com', + 'tags' => ['foo', 'bar', 'baz'], + 'apiKey' => $apiKey, + ])), + ]))) ->shouldBeCalledOnce(); $this->commandTester->setInputs(['y']); - $this->commandTester->execute(['--show-tags' => true]); + $this->commandTester->execute($input); $output = $this->commandTester->getDisplay(); - self::assertStringContainsString('Tags', $output); + + if (count($expectedContents) === 0 && count($notExpectedContents) === 0) { + self::fail('No expectations were run'); + } + + foreach ($expectedContents as $column) { + self::assertStringContainsString($column, $output); + } + foreach ($notExpectedContents as $column) { + self::assertStringNotContainsString($column, $output); + } + } + + public function provideOptionalFlags(): iterable + { + $apiKey = ApiKey::fromMeta(ApiKeyMeta::withName('my api key')); + $key = $apiKey->toString(); + + yield 'tags only' => [ + ['--show-tags' => true], + ['| Tags ', '| foo, bar, baz'], + ['| API Key ', '| API Key Name |', $key, '| my api key'], + $apiKey, + ]; + yield 'api key only' => [ + ['--show-api-key' => true], + ['| API Key ', $key], + ['| Tags ', '| foo, bar, baz', '| API Key Name |', '| my api key'], + $apiKey, + ]; + yield 'api key name only' => [ + ['--show-api-key-name' => true], + ['| API Key Name |', '| my api key'], + ['| Tags ', '| foo, bar, baz', '| API Key ', $key], + $apiKey, + ]; + yield 'tags and api key' => [ + ['--show-tags' => true, '--show-api-key' => true], + ['| API Key ', '| Tags ', '| foo, bar, baz', $key], + ['| API Key Name |', '| my api key'], + $apiKey, + ]; + yield 'all' => [ + ['--show-tags' => true, '--show-api-key' => true, '--show-api-key-name' => true], + ['| API Key ', '| Tags ', '| API Key Name |', '| foo, bar, baz', $key, '| my api key'], + [], + $apiKey, + ]; } /**